xref: /linux/drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c (revision 1b98f357dadd6ea613a435fbaef1a5dd7b35fd21)
15cb8805dSMilena Olech // SPDX-License-Identifier: GPL-2.0-only
25cb8805dSMilena Olech /* Copyright (C) 2024 Intel Corporation */
35cb8805dSMilena Olech 
45cb8805dSMilena Olech #include "idpf.h"
55cb8805dSMilena Olech #include "idpf_ptp.h"
65cb8805dSMilena Olech #include "idpf_virtchnl.h"
75cb8805dSMilena Olech 
85cb8805dSMilena Olech /**
95cb8805dSMilena Olech  * idpf_ptp_get_caps - Send virtchnl get ptp capabilities message
105cb8805dSMilena Olech  * @adapter: Driver specific private structure
115cb8805dSMilena Olech  *
125cb8805dSMilena Olech  * Send virtchnl get PTP capabilities message.
135cb8805dSMilena Olech  *
145cb8805dSMilena Olech  * Return: 0 on success, -errno on failure.
155cb8805dSMilena Olech  */
165cb8805dSMilena Olech int idpf_ptp_get_caps(struct idpf_adapter *adapter)
175cb8805dSMilena Olech {
185cb8805dSMilena Olech 	struct virtchnl2_ptp_get_caps *recv_ptp_caps_msg __free(kfree) = NULL;
195cb8805dSMilena Olech 	struct virtchnl2_ptp_get_caps send_ptp_caps_msg = {
205cb8805dSMilena Olech 		.caps = cpu_to_le32(VIRTCHNL2_CAP_PTP_GET_DEVICE_CLK_TIME |
215cb8805dSMilena Olech 				    VIRTCHNL2_CAP_PTP_GET_DEVICE_CLK_TIME_MB |
225cb8805dSMilena Olech 				    VIRTCHNL2_CAP_PTP_GET_CROSS_TIME |
23d5dba8f7SMilena Olech 				    VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME_MB |
244901e83aSMilena Olech 				    VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK_MB |
254901e83aSMilena Olech 				    VIRTCHNL2_CAP_PTP_TX_TSTAMPS_MB)
265cb8805dSMilena Olech 	};
275cb8805dSMilena Olech 	struct idpf_vc_xn_params xn_params = {
285cb8805dSMilena Olech 		.vc_op = VIRTCHNL2_OP_PTP_GET_CAPS,
295cb8805dSMilena Olech 		.send_buf.iov_base = &send_ptp_caps_msg,
305cb8805dSMilena Olech 		.send_buf.iov_len = sizeof(send_ptp_caps_msg),
315cb8805dSMilena Olech 		.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
325cb8805dSMilena Olech 	};
33d5dba8f7SMilena Olech 	struct virtchnl2_ptp_clk_adj_reg_offsets clk_adj_offsets;
345cb8805dSMilena Olech 	struct virtchnl2_ptp_clk_reg_offsets clock_offsets;
355a27503dSMilena Olech 	struct idpf_ptp_secondary_mbx *scnd_mbx;
365cb8805dSMilena Olech 	struct idpf_ptp *ptp = adapter->ptp;
375cb8805dSMilena Olech 	enum idpf_ptp_access access_type;
385cb8805dSMilena Olech 	u32 temp_offset;
395cb8805dSMilena Olech 	int reply_sz;
405cb8805dSMilena Olech 
415cb8805dSMilena Olech 	recv_ptp_caps_msg = kzalloc(sizeof(struct virtchnl2_ptp_get_caps),
425cb8805dSMilena Olech 				    GFP_KERNEL);
435cb8805dSMilena Olech 	if (!recv_ptp_caps_msg)
445cb8805dSMilena Olech 		return -ENOMEM;
455cb8805dSMilena Olech 
465cb8805dSMilena Olech 	xn_params.recv_buf.iov_base = recv_ptp_caps_msg;
475cb8805dSMilena Olech 	xn_params.recv_buf.iov_len = sizeof(*recv_ptp_caps_msg);
485cb8805dSMilena Olech 
495cb8805dSMilena Olech 	reply_sz = idpf_vc_xn_exec(adapter, &xn_params);
505cb8805dSMilena Olech 	if (reply_sz < 0)
515cb8805dSMilena Olech 		return reply_sz;
525cb8805dSMilena Olech 	else if (reply_sz != sizeof(*recv_ptp_caps_msg))
535cb8805dSMilena Olech 		return -EIO;
545cb8805dSMilena Olech 
555cb8805dSMilena Olech 	ptp->caps = le32_to_cpu(recv_ptp_caps_msg->caps);
56d5dba8f7SMilena Olech 	ptp->base_incval = le64_to_cpu(recv_ptp_caps_msg->base_incval);
57d5dba8f7SMilena Olech 	ptp->max_adj = le32_to_cpu(recv_ptp_caps_msg->max_adj);
585cb8805dSMilena Olech 
595a27503dSMilena Olech 	scnd_mbx = &ptp->secondary_mbx;
605a27503dSMilena Olech 	scnd_mbx->peer_mbx_q_id = le16_to_cpu(recv_ptp_caps_msg->peer_mbx_q_id);
615a27503dSMilena Olech 
625a27503dSMilena Olech 	/* if the ptp_mb_q_id holds invalid value (0xffff), the secondary
635a27503dSMilena Olech 	 * mailbox is not supported.
645a27503dSMilena Olech 	 */
655a27503dSMilena Olech 	scnd_mbx->valid = scnd_mbx->peer_mbx_q_id != 0xffff;
665a27503dSMilena Olech 	if (scnd_mbx->valid)
675a27503dSMilena Olech 		scnd_mbx->peer_id = recv_ptp_caps_msg->peer_id;
685a27503dSMilena Olech 
695cb8805dSMilena Olech 	/* Determine the access type for the PTP features */
705cb8805dSMilena Olech 	idpf_ptp_get_features_access(adapter);
715cb8805dSMilena Olech 
725cb8805dSMilena Olech 	access_type = ptp->get_dev_clk_time_access;
735cb8805dSMilena Olech 	if (access_type != IDPF_PTP_DIRECT)
74d5dba8f7SMilena Olech 		goto discipline_clock;
755cb8805dSMilena Olech 
765cb8805dSMilena Olech 	clock_offsets = recv_ptp_caps_msg->clk_offsets;
775cb8805dSMilena Olech 
785cb8805dSMilena Olech 	temp_offset = le32_to_cpu(clock_offsets.dev_clk_ns_l);
795cb8805dSMilena Olech 	ptp->dev_clk_regs.dev_clk_ns_l = idpf_get_reg_addr(adapter,
805cb8805dSMilena Olech 							   temp_offset);
815cb8805dSMilena Olech 	temp_offset = le32_to_cpu(clock_offsets.dev_clk_ns_h);
825cb8805dSMilena Olech 	ptp->dev_clk_regs.dev_clk_ns_h = idpf_get_reg_addr(adapter,
835cb8805dSMilena Olech 							   temp_offset);
845cb8805dSMilena Olech 	temp_offset = le32_to_cpu(clock_offsets.phy_clk_ns_l);
855cb8805dSMilena Olech 	ptp->dev_clk_regs.phy_clk_ns_l = idpf_get_reg_addr(adapter,
865cb8805dSMilena Olech 							   temp_offset);
875cb8805dSMilena Olech 	temp_offset = le32_to_cpu(clock_offsets.phy_clk_ns_h);
885cb8805dSMilena Olech 	ptp->dev_clk_regs.phy_clk_ns_h = idpf_get_reg_addr(adapter,
895cb8805dSMilena Olech 							   temp_offset);
905cb8805dSMilena Olech 	temp_offset = le32_to_cpu(clock_offsets.cmd_sync_trigger);
915cb8805dSMilena Olech 	ptp->dev_clk_regs.cmd_sync = idpf_get_reg_addr(adapter, temp_offset);
925cb8805dSMilena Olech 
93d5dba8f7SMilena Olech discipline_clock:
94d5dba8f7SMilena Olech 	access_type = ptp->adj_dev_clk_time_access;
95d5dba8f7SMilena Olech 	if (access_type != IDPF_PTP_DIRECT)
96d5dba8f7SMilena Olech 		return 0;
97d5dba8f7SMilena Olech 
98d5dba8f7SMilena Olech 	clk_adj_offsets = recv_ptp_caps_msg->clk_adj_offsets;
99d5dba8f7SMilena Olech 
100d5dba8f7SMilena Olech 	/* Device clock offsets */
101d5dba8f7SMilena Olech 	temp_offset = le32_to_cpu(clk_adj_offsets.dev_clk_cmd_type);
102d5dba8f7SMilena Olech 	ptp->dev_clk_regs.cmd = idpf_get_reg_addr(adapter, temp_offset);
103d5dba8f7SMilena Olech 	temp_offset = le32_to_cpu(clk_adj_offsets.dev_clk_incval_l);
104d5dba8f7SMilena Olech 	ptp->dev_clk_regs.incval_l = idpf_get_reg_addr(adapter, temp_offset);
105d5dba8f7SMilena Olech 	temp_offset = le32_to_cpu(clk_adj_offsets.dev_clk_incval_h);
106d5dba8f7SMilena Olech 	ptp->dev_clk_regs.incval_h = idpf_get_reg_addr(adapter, temp_offset);
107d5dba8f7SMilena Olech 	temp_offset = le32_to_cpu(clk_adj_offsets.dev_clk_shadj_l);
108d5dba8f7SMilena Olech 	ptp->dev_clk_regs.shadj_l = idpf_get_reg_addr(adapter, temp_offset);
109d5dba8f7SMilena Olech 	temp_offset = le32_to_cpu(clk_adj_offsets.dev_clk_shadj_h);
110d5dba8f7SMilena Olech 	ptp->dev_clk_regs.shadj_h = idpf_get_reg_addr(adapter, temp_offset);
111d5dba8f7SMilena Olech 
112d5dba8f7SMilena Olech 	/* PHY clock offsets */
113d5dba8f7SMilena Olech 	temp_offset = le32_to_cpu(clk_adj_offsets.phy_clk_cmd_type);
114d5dba8f7SMilena Olech 	ptp->dev_clk_regs.phy_cmd = idpf_get_reg_addr(adapter, temp_offset);
115d5dba8f7SMilena Olech 	temp_offset = le32_to_cpu(clk_adj_offsets.phy_clk_incval_l);
116d5dba8f7SMilena Olech 	ptp->dev_clk_regs.phy_incval_l = idpf_get_reg_addr(adapter,
117d5dba8f7SMilena Olech 							   temp_offset);
118d5dba8f7SMilena Olech 	temp_offset = le32_to_cpu(clk_adj_offsets.phy_clk_incval_h);
119d5dba8f7SMilena Olech 	ptp->dev_clk_regs.phy_incval_h = idpf_get_reg_addr(adapter,
120d5dba8f7SMilena Olech 							   temp_offset);
121d5dba8f7SMilena Olech 	temp_offset = le32_to_cpu(clk_adj_offsets.phy_clk_shadj_l);
122d5dba8f7SMilena Olech 	ptp->dev_clk_regs.phy_shadj_l = idpf_get_reg_addr(adapter, temp_offset);
123d5dba8f7SMilena Olech 	temp_offset = le32_to_cpu(clk_adj_offsets.phy_clk_shadj_h);
124d5dba8f7SMilena Olech 	ptp->dev_clk_regs.phy_shadj_h = idpf_get_reg_addr(adapter, temp_offset);
125d5dba8f7SMilena Olech 
1265cb8805dSMilena Olech 	return 0;
1275cb8805dSMilena Olech }
1285a27503dSMilena Olech 
1295a27503dSMilena Olech /**
1305a27503dSMilena Olech  * idpf_ptp_get_dev_clk_time - Send virtchnl get device clk time message
1315a27503dSMilena Olech  * @adapter: Driver specific private structure
1325a27503dSMilena Olech  * @dev_clk_time: Pointer to the device clock structure where the value is set
1335a27503dSMilena Olech  *
1345a27503dSMilena Olech  * Send virtchnl get time message to get the time of the clock.
1355a27503dSMilena Olech  *
1365a27503dSMilena Olech  * Return: 0 on success, -errno otherwise.
1375a27503dSMilena Olech  */
1385a27503dSMilena Olech int idpf_ptp_get_dev_clk_time(struct idpf_adapter *adapter,
1395a27503dSMilena Olech 			      struct idpf_ptp_dev_timers *dev_clk_time)
1405a27503dSMilena Olech {
1415a27503dSMilena Olech 	struct virtchnl2_ptp_get_dev_clk_time get_dev_clk_time_msg;
1425a27503dSMilena Olech 	struct idpf_vc_xn_params xn_params = {
1435a27503dSMilena Olech 		.vc_op = VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME,
1445a27503dSMilena Olech 		.send_buf.iov_base = &get_dev_clk_time_msg,
1455a27503dSMilena Olech 		.send_buf.iov_len = sizeof(get_dev_clk_time_msg),
1465a27503dSMilena Olech 		.recv_buf.iov_base = &get_dev_clk_time_msg,
1475a27503dSMilena Olech 		.recv_buf.iov_len = sizeof(get_dev_clk_time_msg),
1485a27503dSMilena Olech 		.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
1495a27503dSMilena Olech 	};
1505a27503dSMilena Olech 	int reply_sz;
1515a27503dSMilena Olech 	u64 dev_time;
1525a27503dSMilena Olech 
1535a27503dSMilena Olech 	reply_sz = idpf_vc_xn_exec(adapter, &xn_params);
1545a27503dSMilena Olech 	if (reply_sz < 0)
1555a27503dSMilena Olech 		return reply_sz;
1565a27503dSMilena Olech 	if (reply_sz != sizeof(get_dev_clk_time_msg))
1575a27503dSMilena Olech 		return -EIO;
1585a27503dSMilena Olech 
1595a27503dSMilena Olech 	dev_time = le64_to_cpu(get_dev_clk_time_msg.dev_time_ns);
1605a27503dSMilena Olech 	dev_clk_time->dev_clk_time_ns = dev_time;
1615a27503dSMilena Olech 
1625a27503dSMilena Olech 	return 0;
1635a27503dSMilena Olech }
164d5dba8f7SMilena Olech 
165d5dba8f7SMilena Olech /**
166d5dba8f7SMilena Olech  * idpf_ptp_set_dev_clk_time - Send virtchnl set device time message
167d5dba8f7SMilena Olech  * @adapter: Driver specific private structure
168d5dba8f7SMilena Olech  * @time: New time value
169d5dba8f7SMilena Olech  *
170d5dba8f7SMilena Olech  * Send virtchnl set time message to set the time of the clock.
171d5dba8f7SMilena Olech  *
172d5dba8f7SMilena Olech  * Return: 0 on success, -errno otherwise.
173d5dba8f7SMilena Olech  */
174d5dba8f7SMilena Olech int idpf_ptp_set_dev_clk_time(struct idpf_adapter *adapter, u64 time)
175d5dba8f7SMilena Olech {
176d5dba8f7SMilena Olech 	struct virtchnl2_ptp_set_dev_clk_time set_dev_clk_time_msg = {
177d5dba8f7SMilena Olech 		.dev_time_ns = cpu_to_le64(time),
178d5dba8f7SMilena Olech 	};
179d5dba8f7SMilena Olech 	struct idpf_vc_xn_params xn_params = {
180d5dba8f7SMilena Olech 		.vc_op = VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME,
181d5dba8f7SMilena Olech 		.send_buf.iov_base = &set_dev_clk_time_msg,
182d5dba8f7SMilena Olech 		.send_buf.iov_len = sizeof(set_dev_clk_time_msg),
183d5dba8f7SMilena Olech 		.recv_buf.iov_base = &set_dev_clk_time_msg,
184d5dba8f7SMilena Olech 		.recv_buf.iov_len = sizeof(set_dev_clk_time_msg),
185d5dba8f7SMilena Olech 		.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
186d5dba8f7SMilena Olech 	};
187d5dba8f7SMilena Olech 	int reply_sz;
188d5dba8f7SMilena Olech 
189d5dba8f7SMilena Olech 	reply_sz = idpf_vc_xn_exec(adapter, &xn_params);
190d5dba8f7SMilena Olech 	if (reply_sz < 0)
191d5dba8f7SMilena Olech 		return reply_sz;
192d5dba8f7SMilena Olech 	if (reply_sz != sizeof(set_dev_clk_time_msg))
193d5dba8f7SMilena Olech 		return -EIO;
194d5dba8f7SMilena Olech 
195d5dba8f7SMilena Olech 	return 0;
196d5dba8f7SMilena Olech }
197d5dba8f7SMilena Olech 
198d5dba8f7SMilena Olech /**
199d5dba8f7SMilena Olech  * idpf_ptp_adj_dev_clk_time - Send virtchnl adj device clock time message
200d5dba8f7SMilena Olech  * @adapter: Driver specific private structure
201d5dba8f7SMilena Olech  * @delta: Offset in nanoseconds to adjust the time by
202d5dba8f7SMilena Olech  *
203d5dba8f7SMilena Olech  * Send virtchnl adj time message to adjust the clock by the indicated delta.
204d5dba8f7SMilena Olech  *
205d5dba8f7SMilena Olech  * Return: 0 on success, -errno otherwise.
206d5dba8f7SMilena Olech  */
207d5dba8f7SMilena Olech int idpf_ptp_adj_dev_clk_time(struct idpf_adapter *adapter, s64 delta)
208d5dba8f7SMilena Olech {
209d5dba8f7SMilena Olech 	struct virtchnl2_ptp_adj_dev_clk_time adj_dev_clk_time_msg = {
210d5dba8f7SMilena Olech 		.delta = cpu_to_le64(delta),
211d5dba8f7SMilena Olech 	};
212d5dba8f7SMilena Olech 	struct idpf_vc_xn_params xn_params = {
213d5dba8f7SMilena Olech 		.vc_op = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME,
214d5dba8f7SMilena Olech 		.send_buf.iov_base = &adj_dev_clk_time_msg,
215d5dba8f7SMilena Olech 		.send_buf.iov_len = sizeof(adj_dev_clk_time_msg),
216d5dba8f7SMilena Olech 		.recv_buf.iov_base = &adj_dev_clk_time_msg,
217d5dba8f7SMilena Olech 		.recv_buf.iov_len = sizeof(adj_dev_clk_time_msg),
218d5dba8f7SMilena Olech 		.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
219d5dba8f7SMilena Olech 	};
220d5dba8f7SMilena Olech 	int reply_sz;
221d5dba8f7SMilena Olech 
222d5dba8f7SMilena Olech 	reply_sz = idpf_vc_xn_exec(adapter, &xn_params);
223d5dba8f7SMilena Olech 	if (reply_sz < 0)
224d5dba8f7SMilena Olech 		return reply_sz;
225d5dba8f7SMilena Olech 	if (reply_sz != sizeof(adj_dev_clk_time_msg))
226d5dba8f7SMilena Olech 		return -EIO;
227d5dba8f7SMilena Olech 
228d5dba8f7SMilena Olech 	return 0;
229d5dba8f7SMilena Olech }
230d5dba8f7SMilena Olech 
231d5dba8f7SMilena Olech /**
232d5dba8f7SMilena Olech  * idpf_ptp_adj_dev_clk_fine - Send virtchnl adj time message
233d5dba8f7SMilena Olech  * @adapter: Driver specific private structure
234d5dba8f7SMilena Olech  * @incval: Source timer increment value per clock cycle
235d5dba8f7SMilena Olech  *
236d5dba8f7SMilena Olech  * Send virtchnl adj fine message to adjust the frequency of the clock by
237d5dba8f7SMilena Olech  * incval.
238d5dba8f7SMilena Olech  *
239d5dba8f7SMilena Olech  * Return: 0 on success, -errno otherwise.
240d5dba8f7SMilena Olech  */
241d5dba8f7SMilena Olech int idpf_ptp_adj_dev_clk_fine(struct idpf_adapter *adapter, u64 incval)
242d5dba8f7SMilena Olech {
243d5dba8f7SMilena Olech 	struct virtchnl2_ptp_adj_dev_clk_fine adj_dev_clk_fine_msg = {
244d5dba8f7SMilena Olech 		.incval = cpu_to_le64(incval),
245d5dba8f7SMilena Olech 	};
246d5dba8f7SMilena Olech 	struct idpf_vc_xn_params xn_params = {
247d5dba8f7SMilena Olech 		.vc_op = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE,
248d5dba8f7SMilena Olech 		.send_buf.iov_base = &adj_dev_clk_fine_msg,
249d5dba8f7SMilena Olech 		.send_buf.iov_len = sizeof(adj_dev_clk_fine_msg),
250d5dba8f7SMilena Olech 		.recv_buf.iov_base = &adj_dev_clk_fine_msg,
251d5dba8f7SMilena Olech 		.recv_buf.iov_len = sizeof(adj_dev_clk_fine_msg),
252d5dba8f7SMilena Olech 		.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
253d5dba8f7SMilena Olech 	};
254d5dba8f7SMilena Olech 	int reply_sz;
255d5dba8f7SMilena Olech 
256d5dba8f7SMilena Olech 	reply_sz = idpf_vc_xn_exec(adapter, &xn_params);
257d5dba8f7SMilena Olech 	if (reply_sz < 0)
258d5dba8f7SMilena Olech 		return reply_sz;
259d5dba8f7SMilena Olech 	if (reply_sz != sizeof(adj_dev_clk_fine_msg))
260d5dba8f7SMilena Olech 		return -EIO;
261d5dba8f7SMilena Olech 
262d5dba8f7SMilena Olech 	return 0;
263d5dba8f7SMilena Olech }
2644901e83aSMilena Olech 
2654901e83aSMilena Olech /**
2664901e83aSMilena Olech  * idpf_ptp_get_vport_tstamps_caps - Send virtchnl to get tstamps caps for vport
2674901e83aSMilena Olech  * @vport: Virtual port structure
2684901e83aSMilena Olech  *
2694901e83aSMilena Olech  * Send virtchnl get vport tstamps caps message to receive the set of tstamp
2704901e83aSMilena Olech  * capabilities per vport.
2714901e83aSMilena Olech  *
2724901e83aSMilena Olech  * Return: 0 on success, -errno otherwise.
2734901e83aSMilena Olech  */
2744901e83aSMilena Olech int idpf_ptp_get_vport_tstamps_caps(struct idpf_vport *vport)
2754901e83aSMilena Olech {
2764901e83aSMilena Olech 	struct virtchnl2_ptp_get_vport_tx_tstamp_caps send_tx_tstamp_caps;
2774901e83aSMilena Olech 	struct virtchnl2_ptp_get_vport_tx_tstamp_caps *rcv_tx_tstamp_caps;
2784901e83aSMilena Olech 	struct virtchnl2_ptp_tx_tstamp_latch_caps tx_tstamp_latch_caps;
2794901e83aSMilena Olech 	struct idpf_ptp_vport_tx_tstamp_caps *tstamp_caps;
2804901e83aSMilena Olech 	struct idpf_ptp_tx_tstamp *ptp_tx_tstamp, *tmp;
2814901e83aSMilena Olech 	struct idpf_vc_xn_params xn_params = {
2824901e83aSMilena Olech 		.vc_op = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS,
2834901e83aSMilena Olech 		.send_buf.iov_base = &send_tx_tstamp_caps,
2844901e83aSMilena Olech 		.send_buf.iov_len = sizeof(send_tx_tstamp_caps),
2854901e83aSMilena Olech 		.recv_buf.iov_len = IDPF_CTLQ_MAX_BUF_LEN,
2864901e83aSMilena Olech 		.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
2874901e83aSMilena Olech 	};
2884901e83aSMilena Olech 	enum idpf_ptp_access tstamp_access, get_dev_clk_access;
2894901e83aSMilena Olech 	struct idpf_ptp *ptp = vport->adapter->ptp;
2904901e83aSMilena Olech 	struct list_head *head;
2914901e83aSMilena Olech 	int err = 0, reply_sz;
2924901e83aSMilena Olech 	u16 num_latches;
2934901e83aSMilena Olech 	u32 size;
2944901e83aSMilena Olech 
2954901e83aSMilena Olech 	if (!ptp)
2964901e83aSMilena Olech 		return -EOPNOTSUPP;
2974901e83aSMilena Olech 
2984901e83aSMilena Olech 	tstamp_access = ptp->tx_tstamp_access;
2994901e83aSMilena Olech 	get_dev_clk_access = ptp->get_dev_clk_time_access;
3004901e83aSMilena Olech 	if (tstamp_access == IDPF_PTP_NONE ||
3014901e83aSMilena Olech 	    get_dev_clk_access == IDPF_PTP_NONE)
3024901e83aSMilena Olech 		return -EOPNOTSUPP;
3034901e83aSMilena Olech 
3044901e83aSMilena Olech 	rcv_tx_tstamp_caps = kzalloc(IDPF_CTLQ_MAX_BUF_LEN, GFP_KERNEL);
3054901e83aSMilena Olech 	if (!rcv_tx_tstamp_caps)
3064901e83aSMilena Olech 		return -ENOMEM;
3074901e83aSMilena Olech 
3084901e83aSMilena Olech 	send_tx_tstamp_caps.vport_id = cpu_to_le32(vport->vport_id);
3094901e83aSMilena Olech 	xn_params.recv_buf.iov_base = rcv_tx_tstamp_caps;
3104901e83aSMilena Olech 
3114901e83aSMilena Olech 	reply_sz = idpf_vc_xn_exec(vport->adapter, &xn_params);
3124901e83aSMilena Olech 	if (reply_sz < 0) {
3134901e83aSMilena Olech 		err = reply_sz;
3144901e83aSMilena Olech 		goto get_tstamp_caps_out;
3154901e83aSMilena Olech 	}
3164901e83aSMilena Olech 
3174901e83aSMilena Olech 	num_latches = le16_to_cpu(rcv_tx_tstamp_caps->num_latches);
3184901e83aSMilena Olech 	size = struct_size(rcv_tx_tstamp_caps, tstamp_latches, num_latches);
3194901e83aSMilena Olech 	if (reply_sz != size) {
3204901e83aSMilena Olech 		err = -EIO;
3214901e83aSMilena Olech 		goto get_tstamp_caps_out;
3224901e83aSMilena Olech 	}
3234901e83aSMilena Olech 
3244901e83aSMilena Olech 	size = struct_size(tstamp_caps, tx_tstamp_status, num_latches);
3254901e83aSMilena Olech 	tstamp_caps = kzalloc(size, GFP_KERNEL);
3264901e83aSMilena Olech 	if (!tstamp_caps) {
3274901e83aSMilena Olech 		err = -ENOMEM;
3284901e83aSMilena Olech 		goto get_tstamp_caps_out;
3294901e83aSMilena Olech 	}
3304901e83aSMilena Olech 
331*1a49cf81SMilena Olech 	tstamp_caps->access = true;
3324901e83aSMilena Olech 	tstamp_caps->num_entries = num_latches;
333*1a49cf81SMilena Olech 
3344901e83aSMilena Olech 	INIT_LIST_HEAD(&tstamp_caps->latches_in_use);
3354901e83aSMilena Olech 	INIT_LIST_HEAD(&tstamp_caps->latches_free);
3364901e83aSMilena Olech 
3374901e83aSMilena Olech 	spin_lock_init(&tstamp_caps->latches_lock);
3384901e83aSMilena Olech 	spin_lock_init(&tstamp_caps->status_lock);
3394901e83aSMilena Olech 
3404901e83aSMilena Olech 	tstamp_caps->tstamp_ns_lo_bit = rcv_tx_tstamp_caps->tstamp_ns_lo_bit;
3414901e83aSMilena Olech 
3424901e83aSMilena Olech 	for (u16 i = 0; i < tstamp_caps->num_entries; i++) {
3434901e83aSMilena Olech 		__le32 offset_l, offset_h;
3444901e83aSMilena Olech 
3454901e83aSMilena Olech 		ptp_tx_tstamp = kzalloc(sizeof(*ptp_tx_tstamp), GFP_KERNEL);
3464901e83aSMilena Olech 		if (!ptp_tx_tstamp) {
3474901e83aSMilena Olech 			err = -ENOMEM;
3484901e83aSMilena Olech 			goto err_free_ptp_tx_stamp_list;
3494901e83aSMilena Olech 		}
3504901e83aSMilena Olech 
3514901e83aSMilena Olech 		tx_tstamp_latch_caps = rcv_tx_tstamp_caps->tstamp_latches[i];
3524901e83aSMilena Olech 
3534901e83aSMilena Olech 		if (tstamp_access != IDPF_PTP_DIRECT)
3544901e83aSMilena Olech 			goto skip_offsets;
3554901e83aSMilena Olech 
3564901e83aSMilena Olech 		offset_l = tx_tstamp_latch_caps.tx_latch_reg_offset_l;
3574901e83aSMilena Olech 		offset_h = tx_tstamp_latch_caps.tx_latch_reg_offset_h;
3584901e83aSMilena Olech 		ptp_tx_tstamp->tx_latch_reg_offset_l = le32_to_cpu(offset_l);
3594901e83aSMilena Olech 		ptp_tx_tstamp->tx_latch_reg_offset_h = le32_to_cpu(offset_h);
3604901e83aSMilena Olech 
3614901e83aSMilena Olech skip_offsets:
3624901e83aSMilena Olech 		ptp_tx_tstamp->idx = tx_tstamp_latch_caps.index;
3634901e83aSMilena Olech 
3644901e83aSMilena Olech 		list_add(&ptp_tx_tstamp->list_member,
3654901e83aSMilena Olech 			 &tstamp_caps->latches_free);
3664901e83aSMilena Olech 
3674901e83aSMilena Olech 		tstamp_caps->tx_tstamp_status[i].state = IDPF_PTP_FREE;
3684901e83aSMilena Olech 	}
3694901e83aSMilena Olech 
3704901e83aSMilena Olech 	vport->tx_tstamp_caps = tstamp_caps;
3714901e83aSMilena Olech 	kfree(rcv_tx_tstamp_caps);
3724901e83aSMilena Olech 
3734901e83aSMilena Olech 	return 0;
3744901e83aSMilena Olech 
3754901e83aSMilena Olech err_free_ptp_tx_stamp_list:
3764901e83aSMilena Olech 	head = &tstamp_caps->latches_free;
3774901e83aSMilena Olech 	list_for_each_entry_safe(ptp_tx_tstamp, tmp, head, list_member) {
3784901e83aSMilena Olech 		list_del(&ptp_tx_tstamp->list_member);
3794901e83aSMilena Olech 		kfree(ptp_tx_tstamp);
3804901e83aSMilena Olech 	}
3814901e83aSMilena Olech 
3824901e83aSMilena Olech 	kfree(tstamp_caps);
3834901e83aSMilena Olech get_tstamp_caps_out:
3844901e83aSMilena Olech 	kfree(rcv_tx_tstamp_caps);
3854901e83aSMilena Olech 
3864901e83aSMilena Olech 	return err;
3874901e83aSMilena Olech }
388*1a49cf81SMilena Olech 
389*1a49cf81SMilena Olech /**
390*1a49cf81SMilena Olech  * idpf_ptp_update_tstamp_tracker - Update the Tx timestamp tracker based on
391*1a49cf81SMilena Olech  *				    the skb compatibility.
392*1a49cf81SMilena Olech  * @caps: Tx timestamp capabilities that monitor the latch status
393*1a49cf81SMilena Olech  * @skb: skb for which the tstamp value is returned through virtchnl message
394*1a49cf81SMilena Olech  * @current_state: Current state of the Tx timestamp latch
395*1a49cf81SMilena Olech  * @expected_state: Expected state of the Tx timestamp latch
396*1a49cf81SMilena Olech  *
397*1a49cf81SMilena Olech  * Find a proper skb tracker for which the Tx timestamp is received and change
398*1a49cf81SMilena Olech  * the state to expected value.
399*1a49cf81SMilena Olech  *
400*1a49cf81SMilena Olech  * Return: true if the tracker has been found and updated, false otherwise.
401*1a49cf81SMilena Olech  */
402*1a49cf81SMilena Olech static bool
403*1a49cf81SMilena Olech idpf_ptp_update_tstamp_tracker(struct idpf_ptp_vport_tx_tstamp_caps *caps,
404*1a49cf81SMilena Olech 			       struct sk_buff *skb,
405*1a49cf81SMilena Olech 			       enum idpf_ptp_tx_tstamp_state current_state,
406*1a49cf81SMilena Olech 			       enum idpf_ptp_tx_tstamp_state expected_state)
407*1a49cf81SMilena Olech {
408*1a49cf81SMilena Olech 	bool updated = false;
409*1a49cf81SMilena Olech 
410*1a49cf81SMilena Olech 	spin_lock(&caps->status_lock);
411*1a49cf81SMilena Olech 	for (u16 i = 0; i < caps->num_entries; i++) {
412*1a49cf81SMilena Olech 		struct idpf_ptp_tx_tstamp_status *status;
413*1a49cf81SMilena Olech 
414*1a49cf81SMilena Olech 		status = &caps->tx_tstamp_status[i];
415*1a49cf81SMilena Olech 
416*1a49cf81SMilena Olech 		if (skb == status->skb && status->state == current_state) {
417*1a49cf81SMilena Olech 			status->state = expected_state;
418*1a49cf81SMilena Olech 			updated = true;
419*1a49cf81SMilena Olech 			break;
420*1a49cf81SMilena Olech 		}
421*1a49cf81SMilena Olech 	}
422*1a49cf81SMilena Olech 	spin_unlock(&caps->status_lock);
423*1a49cf81SMilena Olech 
424*1a49cf81SMilena Olech 	return updated;
425*1a49cf81SMilena Olech }
426*1a49cf81SMilena Olech 
427*1a49cf81SMilena Olech /**
428*1a49cf81SMilena Olech  * idpf_ptp_get_tstamp_value - Get the Tx timestamp value and provide it
429*1a49cf81SMilena Olech  *			       back to the skb.
430*1a49cf81SMilena Olech  * @vport: Virtual port structure
431*1a49cf81SMilena Olech  * @tstamp_latch: Tx timestamp latch structure fulfilled by the Control Plane
432*1a49cf81SMilena Olech  * @ptp_tx_tstamp: Tx timestamp latch to add to the free list
433*1a49cf81SMilena Olech  *
434*1a49cf81SMilena Olech  * Read the value of the Tx timestamp for a given latch received from the
435*1a49cf81SMilena Olech  * Control Plane, extend it to 64 bit and provide back to the skb.
436*1a49cf81SMilena Olech  *
437*1a49cf81SMilena Olech  * Return: 0 on success, -errno otherwise.
438*1a49cf81SMilena Olech  */
439*1a49cf81SMilena Olech static int
440*1a49cf81SMilena Olech idpf_ptp_get_tstamp_value(struct idpf_vport *vport,
441*1a49cf81SMilena Olech 			  struct virtchnl2_ptp_tx_tstamp_latch *tstamp_latch,
442*1a49cf81SMilena Olech 			  struct idpf_ptp_tx_tstamp *ptp_tx_tstamp)
443*1a49cf81SMilena Olech {
444*1a49cf81SMilena Olech 	struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
445*1a49cf81SMilena Olech 	struct skb_shared_hwtstamps shhwtstamps;
446*1a49cf81SMilena Olech 	bool state_upd = false;
447*1a49cf81SMilena Olech 	u8 tstamp_ns_lo_bit;
448*1a49cf81SMilena Olech 	u64 tstamp;
449*1a49cf81SMilena Olech 
450*1a49cf81SMilena Olech 	tx_tstamp_caps = vport->tx_tstamp_caps;
451*1a49cf81SMilena Olech 	tstamp_ns_lo_bit = tx_tstamp_caps->tstamp_ns_lo_bit;
452*1a49cf81SMilena Olech 
453*1a49cf81SMilena Olech 	ptp_tx_tstamp->tstamp = le64_to_cpu(tstamp_latch->tstamp);
454*1a49cf81SMilena Olech 	ptp_tx_tstamp->tstamp >>= tstamp_ns_lo_bit;
455*1a49cf81SMilena Olech 
456*1a49cf81SMilena Olech 	state_upd = idpf_ptp_update_tstamp_tracker(tx_tstamp_caps,
457*1a49cf81SMilena Olech 						   ptp_tx_tstamp->skb,
458*1a49cf81SMilena Olech 						   IDPF_PTP_READ_VALUE,
459*1a49cf81SMilena Olech 						   IDPF_PTP_FREE);
460*1a49cf81SMilena Olech 	if (!state_upd)
461*1a49cf81SMilena Olech 		return -EINVAL;
462*1a49cf81SMilena Olech 
463*1a49cf81SMilena Olech 	tstamp = idpf_ptp_extend_ts(vport, ptp_tx_tstamp->tstamp);
464*1a49cf81SMilena Olech 	shhwtstamps.hwtstamp = ns_to_ktime(tstamp);
465*1a49cf81SMilena Olech 	skb_tstamp_tx(ptp_tx_tstamp->skb, &shhwtstamps);
466*1a49cf81SMilena Olech 	consume_skb(ptp_tx_tstamp->skb);
467*1a49cf81SMilena Olech 
468*1a49cf81SMilena Olech 	list_add(&ptp_tx_tstamp->list_member,
469*1a49cf81SMilena Olech 		 &tx_tstamp_caps->latches_free);
470*1a49cf81SMilena Olech 
471*1a49cf81SMilena Olech 	return 0;
472*1a49cf81SMilena Olech }
473*1a49cf81SMilena Olech 
474*1a49cf81SMilena Olech /**
475*1a49cf81SMilena Olech  * idpf_ptp_get_tx_tstamp_async_handler - Async callback for getting Tx tstamps
476*1a49cf81SMilena Olech  * @adapter: Driver specific private structure
477*1a49cf81SMilena Olech  * @xn: transaction for message
478*1a49cf81SMilena Olech  * @ctlq_msg: received message
479*1a49cf81SMilena Olech  *
480*1a49cf81SMilena Olech  * Read the tstamps Tx tstamp values from a received message and put them
481*1a49cf81SMilena Olech  * directly to the skb. The number of timestamps to read is specified by
482*1a49cf81SMilena Olech  * the virtchnl message.
483*1a49cf81SMilena Olech  *
484*1a49cf81SMilena Olech  * Return: 0 on success, -errno otherwise.
485*1a49cf81SMilena Olech  */
486*1a49cf81SMilena Olech static int
487*1a49cf81SMilena Olech idpf_ptp_get_tx_tstamp_async_handler(struct idpf_adapter *adapter,
488*1a49cf81SMilena Olech 				     struct idpf_vc_xn *xn,
489*1a49cf81SMilena Olech 				     const struct idpf_ctlq_msg *ctlq_msg)
490*1a49cf81SMilena Olech {
491*1a49cf81SMilena Olech 	struct virtchnl2_ptp_get_vport_tx_tstamp_latches *recv_tx_tstamp_msg;
492*1a49cf81SMilena Olech 	struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
493*1a49cf81SMilena Olech 	struct virtchnl2_ptp_tx_tstamp_latch tstamp_latch;
494*1a49cf81SMilena Olech 	struct idpf_ptp_tx_tstamp *tx_tstamp, *tmp;
495*1a49cf81SMilena Olech 	struct idpf_vport *tstamp_vport = NULL;
496*1a49cf81SMilena Olech 	struct list_head *head;
497*1a49cf81SMilena Olech 	u16 num_latches;
498*1a49cf81SMilena Olech 	u32 vport_id;
499*1a49cf81SMilena Olech 	int err = 0;
500*1a49cf81SMilena Olech 
501*1a49cf81SMilena Olech 	recv_tx_tstamp_msg = ctlq_msg->ctx.indirect.payload->va;
502*1a49cf81SMilena Olech 	vport_id = le32_to_cpu(recv_tx_tstamp_msg->vport_id);
503*1a49cf81SMilena Olech 
504*1a49cf81SMilena Olech 	idpf_for_each_vport(adapter, vport) {
505*1a49cf81SMilena Olech 		if (!vport)
506*1a49cf81SMilena Olech 			continue;
507*1a49cf81SMilena Olech 
508*1a49cf81SMilena Olech 		if (vport->vport_id == vport_id) {
509*1a49cf81SMilena Olech 			tstamp_vport = vport;
510*1a49cf81SMilena Olech 			break;
511*1a49cf81SMilena Olech 		}
512*1a49cf81SMilena Olech 	}
513*1a49cf81SMilena Olech 
514*1a49cf81SMilena Olech 	if (!tstamp_vport || !tstamp_vport->tx_tstamp_caps)
515*1a49cf81SMilena Olech 		return -EINVAL;
516*1a49cf81SMilena Olech 
517*1a49cf81SMilena Olech 	tx_tstamp_caps = tstamp_vport->tx_tstamp_caps;
518*1a49cf81SMilena Olech 	num_latches = le16_to_cpu(recv_tx_tstamp_msg->num_latches);
519*1a49cf81SMilena Olech 
520*1a49cf81SMilena Olech 	spin_lock_bh(&tx_tstamp_caps->latches_lock);
521*1a49cf81SMilena Olech 	head = &tx_tstamp_caps->latches_in_use;
522*1a49cf81SMilena Olech 
523*1a49cf81SMilena Olech 	for (u16 i = 0; i < num_latches; i++) {
524*1a49cf81SMilena Olech 		tstamp_latch = recv_tx_tstamp_msg->tstamp_latches[i];
525*1a49cf81SMilena Olech 
526*1a49cf81SMilena Olech 		if (!tstamp_latch.valid)
527*1a49cf81SMilena Olech 			continue;
528*1a49cf81SMilena Olech 
529*1a49cf81SMilena Olech 		if (list_empty(head)) {
530*1a49cf81SMilena Olech 			err = -ENOBUFS;
531*1a49cf81SMilena Olech 			goto unlock;
532*1a49cf81SMilena Olech 		}
533*1a49cf81SMilena Olech 
534*1a49cf81SMilena Olech 		list_for_each_entry_safe(tx_tstamp, tmp, head, list_member) {
535*1a49cf81SMilena Olech 			if (tstamp_latch.index == tx_tstamp->idx) {
536*1a49cf81SMilena Olech 				list_del(&tx_tstamp->list_member);
537*1a49cf81SMilena Olech 				err = idpf_ptp_get_tstamp_value(tstamp_vport,
538*1a49cf81SMilena Olech 								&tstamp_latch,
539*1a49cf81SMilena Olech 								tx_tstamp);
540*1a49cf81SMilena Olech 				if (err)
541*1a49cf81SMilena Olech 					goto unlock;
542*1a49cf81SMilena Olech 
543*1a49cf81SMilena Olech 				break;
544*1a49cf81SMilena Olech 			}
545*1a49cf81SMilena Olech 		}
546*1a49cf81SMilena Olech 	}
547*1a49cf81SMilena Olech 
548*1a49cf81SMilena Olech unlock:
549*1a49cf81SMilena Olech 	spin_unlock_bh(&tx_tstamp_caps->latches_lock);
550*1a49cf81SMilena Olech 
551*1a49cf81SMilena Olech 	return err;
552*1a49cf81SMilena Olech }
553*1a49cf81SMilena Olech 
554*1a49cf81SMilena Olech /**
555*1a49cf81SMilena Olech  * idpf_ptp_get_tx_tstamp - Send virtchnl get Tx timestamp latches message
556*1a49cf81SMilena Olech  * @vport: Virtual port structure
557*1a49cf81SMilena Olech  *
558*1a49cf81SMilena Olech  * Send virtchnl get Tx tstamp message to read the value of the HW timestamp.
559*1a49cf81SMilena Olech  * The message contains a list of indexes set in the Tx descriptors.
560*1a49cf81SMilena Olech  *
561*1a49cf81SMilena Olech  * Return: 0 on success, -errno otherwise.
562*1a49cf81SMilena Olech  */
563*1a49cf81SMilena Olech int idpf_ptp_get_tx_tstamp(struct idpf_vport *vport)
564*1a49cf81SMilena Olech {
565*1a49cf81SMilena Olech 	struct virtchnl2_ptp_get_vport_tx_tstamp_latches *send_tx_tstamp_msg;
566*1a49cf81SMilena Olech 	struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
567*1a49cf81SMilena Olech 	struct idpf_vc_xn_params xn_params = {
568*1a49cf81SMilena Olech 		.vc_op = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP,
569*1a49cf81SMilena Olech 		.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
570*1a49cf81SMilena Olech 		.async = true,
571*1a49cf81SMilena Olech 		.async_handler = idpf_ptp_get_tx_tstamp_async_handler,
572*1a49cf81SMilena Olech 	};
573*1a49cf81SMilena Olech 	struct idpf_ptp_tx_tstamp *ptp_tx_tstamp;
574*1a49cf81SMilena Olech 	int reply_sz, size, msg_size;
575*1a49cf81SMilena Olech 	struct list_head *head;
576*1a49cf81SMilena Olech 	bool state_upd;
577*1a49cf81SMilena Olech 	u16 id = 0;
578*1a49cf81SMilena Olech 
579*1a49cf81SMilena Olech 	tx_tstamp_caps = vport->tx_tstamp_caps;
580*1a49cf81SMilena Olech 	head = &tx_tstamp_caps->latches_in_use;
581*1a49cf81SMilena Olech 
582*1a49cf81SMilena Olech 	size = struct_size(send_tx_tstamp_msg, tstamp_latches,
583*1a49cf81SMilena Olech 			   tx_tstamp_caps->num_entries);
584*1a49cf81SMilena Olech 	send_tx_tstamp_msg = kzalloc(size, GFP_KERNEL);
585*1a49cf81SMilena Olech 	if (!send_tx_tstamp_msg)
586*1a49cf81SMilena Olech 		return -ENOMEM;
587*1a49cf81SMilena Olech 
588*1a49cf81SMilena Olech 	spin_lock_bh(&tx_tstamp_caps->latches_lock);
589*1a49cf81SMilena Olech 	list_for_each_entry(ptp_tx_tstamp, head, list_member) {
590*1a49cf81SMilena Olech 		u8 idx;
591*1a49cf81SMilena Olech 
592*1a49cf81SMilena Olech 		state_upd = idpf_ptp_update_tstamp_tracker(tx_tstamp_caps,
593*1a49cf81SMilena Olech 							   ptp_tx_tstamp->skb,
594*1a49cf81SMilena Olech 							   IDPF_PTP_REQUEST,
595*1a49cf81SMilena Olech 							   IDPF_PTP_READ_VALUE);
596*1a49cf81SMilena Olech 		if (!state_upd)
597*1a49cf81SMilena Olech 			continue;
598*1a49cf81SMilena Olech 
599*1a49cf81SMilena Olech 		idx = ptp_tx_tstamp->idx;
600*1a49cf81SMilena Olech 		send_tx_tstamp_msg->tstamp_latches[id].index = idx;
601*1a49cf81SMilena Olech 		id++;
602*1a49cf81SMilena Olech 	}
603*1a49cf81SMilena Olech 	spin_unlock_bh(&tx_tstamp_caps->latches_lock);
604*1a49cf81SMilena Olech 
605*1a49cf81SMilena Olech 	msg_size = struct_size(send_tx_tstamp_msg, tstamp_latches, id);
606*1a49cf81SMilena Olech 	send_tx_tstamp_msg->vport_id = cpu_to_le32(vport->vport_id);
607*1a49cf81SMilena Olech 	send_tx_tstamp_msg->num_latches = cpu_to_le16(id);
608*1a49cf81SMilena Olech 	xn_params.send_buf.iov_base = send_tx_tstamp_msg;
609*1a49cf81SMilena Olech 	xn_params.send_buf.iov_len = msg_size;
610*1a49cf81SMilena Olech 
611*1a49cf81SMilena Olech 	reply_sz = idpf_vc_xn_exec(vport->adapter, &xn_params);
612*1a49cf81SMilena Olech 	kfree(send_tx_tstamp_msg);
613*1a49cf81SMilena Olech 
614*1a49cf81SMilena Olech 	return min(reply_sz, 0);
615*1a49cf81SMilena Olech }
616