1c78c70faSSudarsana Reddy Kalluru /* QLogic qed NIC Driver
2c78c70faSSudarsana Reddy Kalluru  * Copyright (c) 2015-2017  QLogic Corporation
3c78c70faSSudarsana Reddy Kalluru  *
4c78c70faSSudarsana Reddy Kalluru  * This software is available to you under a choice of one of two
5c78c70faSSudarsana Reddy Kalluru  * licenses.  You may choose to be licensed under the terms of the GNU
6c78c70faSSudarsana Reddy Kalluru  * General Public License (GPL) Version 2, available from the file
7c78c70faSSudarsana Reddy Kalluru  * COPYING in the main directory of this source tree, or the
8c78c70faSSudarsana Reddy Kalluru  * OpenIB.org BSD license below:
9c78c70faSSudarsana Reddy Kalluru  *
10c78c70faSSudarsana Reddy Kalluru  *     Redistribution and use in source and binary forms, with or
11c78c70faSSudarsana Reddy Kalluru  *     without modification, are permitted provided that the following
12c78c70faSSudarsana Reddy Kalluru  *     conditions are met:
13c78c70faSSudarsana Reddy Kalluru  *
14c78c70faSSudarsana Reddy Kalluru  *      - Redistributions of source code must retain the above
15c78c70faSSudarsana Reddy Kalluru  *        copyright notice, this list of conditions and the following
16c78c70faSSudarsana Reddy Kalluru  *        disclaimer.
17c78c70faSSudarsana Reddy Kalluru  *
18c78c70faSSudarsana Reddy Kalluru  *      - Redistributions in binary form must reproduce the above
19c78c70faSSudarsana Reddy Kalluru  *        copyright notice, this list of conditions and the following
20c78c70faSSudarsana Reddy Kalluru  *        disclaimer in the documentation and /or other materials
21c78c70faSSudarsana Reddy Kalluru  *        provided with the distribution.
22c78c70faSSudarsana Reddy Kalluru  *
23c78c70faSSudarsana Reddy Kalluru  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24c78c70faSSudarsana Reddy Kalluru  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25c78c70faSSudarsana Reddy Kalluru  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26c78c70faSSudarsana Reddy Kalluru  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27c78c70faSSudarsana Reddy Kalluru  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28c78c70faSSudarsana Reddy Kalluru  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29c78c70faSSudarsana Reddy Kalluru  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30c78c70faSSudarsana Reddy Kalluru  * SOFTWARE.
31c78c70faSSudarsana Reddy Kalluru  */
32c78c70faSSudarsana Reddy Kalluru #include <linux/types.h>
33c78c70faSSudarsana Reddy Kalluru #include "qed.h"
34c78c70faSSudarsana Reddy Kalluru #include "qed_dev_api.h"
35c78c70faSSudarsana Reddy Kalluru #include "qed_hw.h"
36c78c70faSSudarsana Reddy Kalluru #include "qed_l2.h"
37db82f70eSsudarsana.kalluru@cavium.com #include "qed_mcp.h"
38c78c70faSSudarsana Reddy Kalluru #include "qed_reg_addr.h"
39c78c70faSSudarsana Reddy Kalluru 
40c78c70faSSudarsana Reddy Kalluru /* 16 nano second time quantas to wait before making a Drift adjustment */
41c78c70faSSudarsana Reddy Kalluru #define QED_DRIFT_CNTR_TIME_QUANTA_SHIFT	0
42c78c70faSSudarsana Reddy Kalluru /* Nano seconds to add/subtract when making a Drift adjustment */
43c78c70faSSudarsana Reddy Kalluru #define QED_DRIFT_CNTR_ADJUSTMENT_SHIFT		28
44c78c70faSSudarsana Reddy Kalluru /* Add/subtract the Adjustment_Value when making a Drift adjustment */
45c78c70faSSudarsana Reddy Kalluru #define QED_DRIFT_CNTR_DIRECTION_SHIFT		31
46c78c70faSSudarsana Reddy Kalluru #define QED_TIMESTAMP_MASK			BIT(16)
47c78c70faSSudarsana Reddy Kalluru 
48db82f70eSsudarsana.kalluru@cavium.com static enum qed_resc_lock qed_ptcdev_to_resc(struct qed_hwfn *p_hwfn)
49db82f70eSsudarsana.kalluru@cavium.com {
50db82f70eSsudarsana.kalluru@cavium.com 	switch (qed_device_get_port_id(p_hwfn->cdev)) {
51db82f70eSsudarsana.kalluru@cavium.com 	case 0:
52db82f70eSsudarsana.kalluru@cavium.com 		return QED_RESC_LOCK_PTP_PORT0;
53db82f70eSsudarsana.kalluru@cavium.com 	case 1:
54db82f70eSsudarsana.kalluru@cavium.com 		return QED_RESC_LOCK_PTP_PORT1;
55db82f70eSsudarsana.kalluru@cavium.com 	case 2:
56db82f70eSsudarsana.kalluru@cavium.com 		return QED_RESC_LOCK_PTP_PORT2;
57db82f70eSsudarsana.kalluru@cavium.com 	case 3:
58db82f70eSsudarsana.kalluru@cavium.com 		return QED_RESC_LOCK_PTP_PORT3;
59db82f70eSsudarsana.kalluru@cavium.com 	default:
60db82f70eSsudarsana.kalluru@cavium.com 		return QED_RESC_LOCK_RESC_INVALID;
61db82f70eSsudarsana.kalluru@cavium.com 	}
62db82f70eSsudarsana.kalluru@cavium.com }
63db82f70eSsudarsana.kalluru@cavium.com 
64db82f70eSsudarsana.kalluru@cavium.com static int qed_ptp_res_lock(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
65db82f70eSsudarsana.kalluru@cavium.com {
66db82f70eSsudarsana.kalluru@cavium.com 	struct qed_resc_lock_params params;
67db82f70eSsudarsana.kalluru@cavium.com 	enum qed_resc_lock resource;
68db82f70eSsudarsana.kalluru@cavium.com 	int rc;
69db82f70eSsudarsana.kalluru@cavium.com 
70db82f70eSsudarsana.kalluru@cavium.com 	resource = qed_ptcdev_to_resc(p_hwfn);
71db82f70eSsudarsana.kalluru@cavium.com 	if (resource == QED_RESC_LOCK_RESC_INVALID)
72db82f70eSsudarsana.kalluru@cavium.com 		return -EINVAL;
73db82f70eSsudarsana.kalluru@cavium.com 
74db82f70eSsudarsana.kalluru@cavium.com 	qed_mcp_resc_lock_default_init(&params, NULL, resource, true);
75db82f70eSsudarsana.kalluru@cavium.com 
76db82f70eSsudarsana.kalluru@cavium.com 	rc = qed_mcp_resc_lock(p_hwfn, p_ptt, &params);
77db82f70eSsudarsana.kalluru@cavium.com 	if (rc && rc != -EINVAL) {
78db82f70eSsudarsana.kalluru@cavium.com 		return rc;
79db82f70eSsudarsana.kalluru@cavium.com 	} else if (rc == -EINVAL) {
80db82f70eSsudarsana.kalluru@cavium.com 		/* MFW doesn't support resource locking, first PF on the port
81db82f70eSsudarsana.kalluru@cavium.com 		 * has lock ownership.
82db82f70eSsudarsana.kalluru@cavium.com 		 */
83db82f70eSsudarsana.kalluru@cavium.com 		if (p_hwfn->abs_pf_id < p_hwfn->cdev->num_ports_in_engines)
84db82f70eSsudarsana.kalluru@cavium.com 			return 0;
85db82f70eSsudarsana.kalluru@cavium.com 
86db82f70eSsudarsana.kalluru@cavium.com 		DP_INFO(p_hwfn, "PF doesn't have lock ownership\n");
87db82f70eSsudarsana.kalluru@cavium.com 		return -EBUSY;
88db82f70eSsudarsana.kalluru@cavium.com 	} else if (!rc && !params.b_granted) {
89db82f70eSsudarsana.kalluru@cavium.com 		DP_INFO(p_hwfn, "Failed to acquire ptp resource lock\n");
90db82f70eSsudarsana.kalluru@cavium.com 		return -EBUSY;
91db82f70eSsudarsana.kalluru@cavium.com 	}
92db82f70eSsudarsana.kalluru@cavium.com 
93db82f70eSsudarsana.kalluru@cavium.com 	return rc;
94db82f70eSsudarsana.kalluru@cavium.com }
95db82f70eSsudarsana.kalluru@cavium.com 
96db82f70eSsudarsana.kalluru@cavium.com static int qed_ptp_res_unlock(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
97db82f70eSsudarsana.kalluru@cavium.com {
98db82f70eSsudarsana.kalluru@cavium.com 	struct qed_resc_unlock_params params;
99db82f70eSsudarsana.kalluru@cavium.com 	enum qed_resc_lock resource;
100db82f70eSsudarsana.kalluru@cavium.com 	int rc;
101db82f70eSsudarsana.kalluru@cavium.com 
102db82f70eSsudarsana.kalluru@cavium.com 	resource = qed_ptcdev_to_resc(p_hwfn);
103db82f70eSsudarsana.kalluru@cavium.com 	if (resource == QED_RESC_LOCK_RESC_INVALID)
104db82f70eSsudarsana.kalluru@cavium.com 		return -EINVAL;
105db82f70eSsudarsana.kalluru@cavium.com 
106db82f70eSsudarsana.kalluru@cavium.com 	qed_mcp_resc_lock_default_init(NULL, &params, resource, true);
107db82f70eSsudarsana.kalluru@cavium.com 
108db82f70eSsudarsana.kalluru@cavium.com 	rc = qed_mcp_resc_unlock(p_hwfn, p_ptt, &params);
109db82f70eSsudarsana.kalluru@cavium.com 	if (rc == -EINVAL) {
110db82f70eSsudarsana.kalluru@cavium.com 		/* MFW doesn't support locking, first PF has lock ownership */
111db82f70eSsudarsana.kalluru@cavium.com 		if (p_hwfn->abs_pf_id < p_hwfn->cdev->num_ports_in_engines) {
112db82f70eSsudarsana.kalluru@cavium.com 			rc = 0;
113db82f70eSsudarsana.kalluru@cavium.com 		} else {
114db82f70eSsudarsana.kalluru@cavium.com 			DP_INFO(p_hwfn, "PF doesn't have lock ownership\n");
115db82f70eSsudarsana.kalluru@cavium.com 			return -EINVAL;
116db82f70eSsudarsana.kalluru@cavium.com 		}
117db82f70eSsudarsana.kalluru@cavium.com 	} else if (rc) {
118db82f70eSsudarsana.kalluru@cavium.com 		DP_INFO(p_hwfn, "Failed to release the ptp resource lock\n");
119db82f70eSsudarsana.kalluru@cavium.com 	}
120db82f70eSsudarsana.kalluru@cavium.com 
121db82f70eSsudarsana.kalluru@cavium.com 	return rc;
122db82f70eSsudarsana.kalluru@cavium.com }
123db82f70eSsudarsana.kalluru@cavium.com 
124c78c70faSSudarsana Reddy Kalluru /* Read Rx timestamp */
125c78c70faSSudarsana Reddy Kalluru static int qed_ptp_hw_read_rx_ts(struct qed_dev *cdev, u64 *timestamp)
126c78c70faSSudarsana Reddy Kalluru {
127c78c70faSSudarsana Reddy Kalluru 	struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
128c78c70faSSudarsana Reddy Kalluru 	struct qed_ptt *p_ptt = p_hwfn->p_ptp_ptt;
129c78c70faSSudarsana Reddy Kalluru 	u32 val;
130c78c70faSSudarsana Reddy Kalluru 
131c78c70faSSudarsana Reddy Kalluru 	*timestamp = 0;
132c78c70faSSudarsana Reddy Kalluru 	val = qed_rd(p_hwfn, p_ptt, NIG_REG_LLH_PTP_HOST_BUF_SEQID);
133c78c70faSSudarsana Reddy Kalluru 	if (!(val & QED_TIMESTAMP_MASK)) {
134c78c70faSSudarsana Reddy Kalluru 		DP_INFO(p_hwfn, "Invalid Rx timestamp, buf_seqid = %d\n", val);
135c78c70faSSudarsana Reddy Kalluru 		return -EINVAL;
136c78c70faSSudarsana Reddy Kalluru 	}
137c78c70faSSudarsana Reddy Kalluru 
138c78c70faSSudarsana Reddy Kalluru 	val = qed_rd(p_hwfn, p_ptt, NIG_REG_LLH_PTP_HOST_BUF_TS_LSB);
139c78c70faSSudarsana Reddy Kalluru 	*timestamp = qed_rd(p_hwfn, p_ptt, NIG_REG_LLH_PTP_HOST_BUF_TS_MSB);
140c78c70faSSudarsana Reddy Kalluru 	*timestamp <<= 32;
141c78c70faSSudarsana Reddy Kalluru 	*timestamp |= val;
142c78c70faSSudarsana Reddy Kalluru 
143c78c70faSSudarsana Reddy Kalluru 	/* Reset timestamp register to allow new timestamp */
144c78c70faSSudarsana Reddy Kalluru 	qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_HOST_BUF_SEQID,
145c78c70faSSudarsana Reddy Kalluru 	       QED_TIMESTAMP_MASK);
146c78c70faSSudarsana Reddy Kalluru 
147c78c70faSSudarsana Reddy Kalluru 	return 0;
148c78c70faSSudarsana Reddy Kalluru }
149c78c70faSSudarsana Reddy Kalluru 
150c78c70faSSudarsana Reddy Kalluru /* Read Tx timestamp */
151c78c70faSSudarsana Reddy Kalluru static int qed_ptp_hw_read_tx_ts(struct qed_dev *cdev, u64 *timestamp)
152c78c70faSSudarsana Reddy Kalluru {
153c78c70faSSudarsana Reddy Kalluru 	struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
154c78c70faSSudarsana Reddy Kalluru 	struct qed_ptt *p_ptt = p_hwfn->p_ptp_ptt;
155c78c70faSSudarsana Reddy Kalluru 	u32 val;
156c78c70faSSudarsana Reddy Kalluru 
157c78c70faSSudarsana Reddy Kalluru 	*timestamp = 0;
158c78c70faSSudarsana Reddy Kalluru 	val = qed_rd(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_BUF_SEQID);
159c78c70faSSudarsana Reddy Kalluru 	if (!(val & QED_TIMESTAMP_MASK)) {
160c78c70faSSudarsana Reddy Kalluru 		DP_INFO(p_hwfn, "Invalid Tx timestamp, buf_seqid = %d\n", val);
161c78c70faSSudarsana Reddy Kalluru 		return -EINVAL;
162c78c70faSSudarsana Reddy Kalluru 	}
163c78c70faSSudarsana Reddy Kalluru 
164c78c70faSSudarsana Reddy Kalluru 	val = qed_rd(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_BUF_TS_LSB);
165c78c70faSSudarsana Reddy Kalluru 	*timestamp = qed_rd(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_BUF_TS_MSB);
166c78c70faSSudarsana Reddy Kalluru 	*timestamp <<= 32;
167c78c70faSSudarsana Reddy Kalluru 	*timestamp |= val;
168c78c70faSSudarsana Reddy Kalluru 
169c78c70faSSudarsana Reddy Kalluru 	/* Reset timestamp register to allow new timestamp */
170c78c70faSSudarsana Reddy Kalluru 	qed_wr(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_BUF_SEQID, QED_TIMESTAMP_MASK);
171c78c70faSSudarsana Reddy Kalluru 
172c78c70faSSudarsana Reddy Kalluru 	return 0;
173c78c70faSSudarsana Reddy Kalluru }
174c78c70faSSudarsana Reddy Kalluru 
175c78c70faSSudarsana Reddy Kalluru /* Read Phy Hardware Clock */
176c78c70faSSudarsana Reddy Kalluru static int qed_ptp_hw_read_cc(struct qed_dev *cdev, u64 *phc_cycles)
177c78c70faSSudarsana Reddy Kalluru {
178c78c70faSSudarsana Reddy Kalluru 	struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
179c78c70faSSudarsana Reddy Kalluru 	struct qed_ptt *p_ptt = p_hwfn->p_ptp_ptt;
180c78c70faSSudarsana Reddy Kalluru 	u32 temp = 0;
181c78c70faSSudarsana Reddy Kalluru 
182c78c70faSSudarsana Reddy Kalluru 	temp = qed_rd(p_hwfn, p_ptt, NIG_REG_TSGEN_SYNC_TIME_LSB);
183c78c70faSSudarsana Reddy Kalluru 	*phc_cycles = qed_rd(p_hwfn, p_ptt, NIG_REG_TSGEN_SYNC_TIME_MSB);
184c78c70faSSudarsana Reddy Kalluru 	*phc_cycles <<= 32;
185c78c70faSSudarsana Reddy Kalluru 	*phc_cycles |= temp;
186c78c70faSSudarsana Reddy Kalluru 
187c78c70faSSudarsana Reddy Kalluru 	return 0;
188c78c70faSSudarsana Reddy Kalluru }
189c78c70faSSudarsana Reddy Kalluru 
190c78c70faSSudarsana Reddy Kalluru /* Filter PTP protocol packets that need to be timestamped */
191c78c70faSSudarsana Reddy Kalluru static int qed_ptp_hw_cfg_rx_filters(struct qed_dev *cdev,
192c78c70faSSudarsana Reddy Kalluru 				     enum qed_ptp_filter_type type)
193c78c70faSSudarsana Reddy Kalluru {
194c78c70faSSudarsana Reddy Kalluru 	struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
195c78c70faSSudarsana Reddy Kalluru 	struct qed_ptt *p_ptt = p_hwfn->p_ptp_ptt;
196c78c70faSSudarsana Reddy Kalluru 	u32 rule_mask, parm_mask;
197c78c70faSSudarsana Reddy Kalluru 
198c78c70faSSudarsana Reddy Kalluru 	switch (type) {
199c78c70faSSudarsana Reddy Kalluru 	case QED_PTP_FILTER_L2_IPV4_IPV6:
200c78c70faSSudarsana Reddy Kalluru 		parm_mask = 0x6AA;
201c78c70faSSudarsana Reddy Kalluru 		rule_mask = 0x3EEE;
202c78c70faSSudarsana Reddy Kalluru 		break;
203c78c70faSSudarsana Reddy Kalluru 	case QED_PTP_FILTER_L2:
204c78c70faSSudarsana Reddy Kalluru 		parm_mask = 0x6BF;
205c78c70faSSudarsana Reddy Kalluru 		rule_mask = 0x3EFF;
206c78c70faSSudarsana Reddy Kalluru 		break;
207c78c70faSSudarsana Reddy Kalluru 	case QED_PTP_FILTER_IPV4_IPV6:
208c78c70faSSudarsana Reddy Kalluru 		parm_mask = 0x7EA;
209c78c70faSSudarsana Reddy Kalluru 		rule_mask = 0x3FFE;
210c78c70faSSudarsana Reddy Kalluru 		break;
211c78c70faSSudarsana Reddy Kalluru 	case QED_PTP_FILTER_IPV4:
212c78c70faSSudarsana Reddy Kalluru 		parm_mask = 0x7EE;
213c78c70faSSudarsana Reddy Kalluru 		rule_mask = 0x3FFE;
214c78c70faSSudarsana Reddy Kalluru 		break;
215c78c70faSSudarsana Reddy Kalluru 	default:
216c78c70faSSudarsana Reddy Kalluru 		DP_INFO(p_hwfn, "Invalid PTP filter type %d\n", type);
217c78c70faSSudarsana Reddy Kalluru 		return -EINVAL;
218c78c70faSSudarsana Reddy Kalluru 	}
219c78c70faSSudarsana Reddy Kalluru 
220c78c70faSSudarsana Reddy Kalluru 	qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_PARAM_MASK, parm_mask);
221c78c70faSSudarsana Reddy Kalluru 	qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_RULE_MASK, rule_mask);
222c78c70faSSudarsana Reddy Kalluru 
223c78c70faSSudarsana Reddy Kalluru 	qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_TO_HOST, 0x1);
224c78c70faSSudarsana Reddy Kalluru 
225c78c70faSSudarsana Reddy Kalluru 	/* Reset possibly old timestamps */
226c78c70faSSudarsana Reddy Kalluru 	qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_HOST_BUF_SEQID,
227c78c70faSSudarsana Reddy Kalluru 	       QED_TIMESTAMP_MASK);
228c78c70faSSudarsana Reddy Kalluru 
229c78c70faSSudarsana Reddy Kalluru 	return 0;
230c78c70faSSudarsana Reddy Kalluru }
231c78c70faSSudarsana Reddy Kalluru 
232c78c70faSSudarsana Reddy Kalluru /* Adjust the HW clock by a rate given in parts-per-billion (ppb) units.
233c78c70faSSudarsana Reddy Kalluru  * FW/HW accepts the adjustment value in terms of 3 parameters:
234c78c70faSSudarsana Reddy Kalluru  *   Drift period - adjustment happens once in certain number of nano seconds.
235c78c70faSSudarsana Reddy Kalluru  *   Drift value - time is adjusted by a certain value, for example by 5 ns.
236c78c70faSSudarsana Reddy Kalluru  *   Drift direction - add or subtract the adjustment value.
237c78c70faSSudarsana Reddy Kalluru  * The routine translates ppb into the adjustment triplet in an optimal manner.
238c78c70faSSudarsana Reddy Kalluru  */
239c78c70faSSudarsana Reddy Kalluru static int qed_ptp_hw_adjfreq(struct qed_dev *cdev, s32 ppb)
240c78c70faSSudarsana Reddy Kalluru {
241c78c70faSSudarsana Reddy Kalluru 	s64 best_val = 0, val, best_period = 0, period, approx_dev, dif, dif2;
242c78c70faSSudarsana Reddy Kalluru 	struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
243c78c70faSSudarsana Reddy Kalluru 	struct qed_ptt *p_ptt = p_hwfn->p_ptp_ptt;
244c78c70faSSudarsana Reddy Kalluru 	u32 drift_ctr_cfg = 0, drift_state;
245c78c70faSSudarsana Reddy Kalluru 	int drift_dir = 1;
246c78c70faSSudarsana Reddy Kalluru 
247c78c70faSSudarsana Reddy Kalluru 	if (ppb < 0) {
248c78c70faSSudarsana Reddy Kalluru 		ppb = -ppb;
249c78c70faSSudarsana Reddy Kalluru 		drift_dir = 0;
250c78c70faSSudarsana Reddy Kalluru 	}
251c78c70faSSudarsana Reddy Kalluru 
252c78c70faSSudarsana Reddy Kalluru 	if (ppb > 1) {
253c78c70faSSudarsana Reddy Kalluru 		s64 best_dif = ppb, best_approx_dev = 1;
254c78c70faSSudarsana Reddy Kalluru 
255c78c70faSSudarsana Reddy Kalluru 		/* Adjustment value is up to +/-7ns, find an optimal value in
256c78c70faSSudarsana Reddy Kalluru 		 * this range.
257c78c70faSSudarsana Reddy Kalluru 		 */
258c78c70faSSudarsana Reddy Kalluru 		for (val = 7; val > 0; val--) {
259c78c70faSSudarsana Reddy Kalluru 			period = div_s64(val * 1000000000, ppb);
260c78c70faSSudarsana Reddy Kalluru 			period -= 8;
261c78c70faSSudarsana Reddy Kalluru 			period >>= 4;
262c78c70faSSudarsana Reddy Kalluru 			if (period < 1)
263c78c70faSSudarsana Reddy Kalluru 				period = 1;
264c78c70faSSudarsana Reddy Kalluru 			if (period > 0xFFFFFFE)
265c78c70faSSudarsana Reddy Kalluru 				period = 0xFFFFFFE;
266c78c70faSSudarsana Reddy Kalluru 
267c78c70faSSudarsana Reddy Kalluru 			/* Check both rounding ends for approximate error */
268c78c70faSSudarsana Reddy Kalluru 			approx_dev = period * 16 + 8;
269c78c70faSSudarsana Reddy Kalluru 			dif = ppb * approx_dev - val * 1000000000;
270c78c70faSSudarsana Reddy Kalluru 			dif2 = dif + 16 * ppb;
271c78c70faSSudarsana Reddy Kalluru 
272c78c70faSSudarsana Reddy Kalluru 			if (dif < 0)
273c78c70faSSudarsana Reddy Kalluru 				dif = -dif;
274c78c70faSSudarsana Reddy Kalluru 			if (dif2 < 0)
275c78c70faSSudarsana Reddy Kalluru 				dif2 = -dif2;
276c78c70faSSudarsana Reddy Kalluru 
277c78c70faSSudarsana Reddy Kalluru 			/* Determine which end gives better approximation */
278c78c70faSSudarsana Reddy Kalluru 			if (dif * (approx_dev + 16) > dif2 * approx_dev) {
279c78c70faSSudarsana Reddy Kalluru 				period++;
280c78c70faSSudarsana Reddy Kalluru 				approx_dev += 16;
281c78c70faSSudarsana Reddy Kalluru 				dif = dif2;
282c78c70faSSudarsana Reddy Kalluru 			}
283c78c70faSSudarsana Reddy Kalluru 
284c78c70faSSudarsana Reddy Kalluru 			/* Track best approximation found so far */
285c78c70faSSudarsana Reddy Kalluru 			if (best_dif * approx_dev > dif * best_approx_dev) {
286c78c70faSSudarsana Reddy Kalluru 				best_dif = dif;
287c78c70faSSudarsana Reddy Kalluru 				best_val = val;
288c78c70faSSudarsana Reddy Kalluru 				best_period = period;
289c78c70faSSudarsana Reddy Kalluru 				best_approx_dev = approx_dev;
290c78c70faSSudarsana Reddy Kalluru 			}
291c78c70faSSudarsana Reddy Kalluru 		}
292c78c70faSSudarsana Reddy Kalluru 	} else if (ppb == 1) {
293c78c70faSSudarsana Reddy Kalluru 		/* This is a special case as its the only value which wouldn't
294c78c70faSSudarsana Reddy Kalluru 		 * fit in a s64 variable. In order to prevent castings simple
295c78c70faSSudarsana Reddy Kalluru 		 * handle it seperately.
296c78c70faSSudarsana Reddy Kalluru 		 */
297c78c70faSSudarsana Reddy Kalluru 		best_val = 4;
298c78c70faSSudarsana Reddy Kalluru 		best_period = 0xee6b27f;
299c78c70faSSudarsana Reddy Kalluru 	} else {
300c78c70faSSudarsana Reddy Kalluru 		best_val = 0;
301c78c70faSSudarsana Reddy Kalluru 		best_period = 0xFFFFFFF;
302c78c70faSSudarsana Reddy Kalluru 	}
303c78c70faSSudarsana Reddy Kalluru 
304c78c70faSSudarsana Reddy Kalluru 	drift_ctr_cfg = (best_period << QED_DRIFT_CNTR_TIME_QUANTA_SHIFT) |
305c78c70faSSudarsana Reddy Kalluru 			(((int)best_val) << QED_DRIFT_CNTR_ADJUSTMENT_SHIFT) |
306c78c70faSSudarsana Reddy Kalluru 			(((int)drift_dir) << QED_DRIFT_CNTR_DIRECTION_SHIFT);
307c78c70faSSudarsana Reddy Kalluru 
308c78c70faSSudarsana Reddy Kalluru 	qed_wr(p_hwfn, p_ptt, NIG_REG_TSGEN_RST_DRIFT_CNTR, 0x1);
309c78c70faSSudarsana Reddy Kalluru 
310c78c70faSSudarsana Reddy Kalluru 	drift_state = qed_rd(p_hwfn, p_ptt, NIG_REG_TSGEN_RST_DRIFT_CNTR);
311c78c70faSSudarsana Reddy Kalluru 	if (drift_state & 1) {
312c78c70faSSudarsana Reddy Kalluru 		qed_wr(p_hwfn, p_ptt, NIG_REG_TSGEN_DRIFT_CNTR_CONF,
313c78c70faSSudarsana Reddy Kalluru 		       drift_ctr_cfg);
314c78c70faSSudarsana Reddy Kalluru 	} else {
315c78c70faSSudarsana Reddy Kalluru 		DP_INFO(p_hwfn, "Drift counter is not reset\n");
316c78c70faSSudarsana Reddy Kalluru 		return -EINVAL;
317c78c70faSSudarsana Reddy Kalluru 	}
318c78c70faSSudarsana Reddy Kalluru 
319c78c70faSSudarsana Reddy Kalluru 	qed_wr(p_hwfn, p_ptt, NIG_REG_TSGEN_RST_DRIFT_CNTR, 0x0);
320c78c70faSSudarsana Reddy Kalluru 
321c78c70faSSudarsana Reddy Kalluru 	return 0;
322c78c70faSSudarsana Reddy Kalluru }
323c78c70faSSudarsana Reddy Kalluru 
324c78c70faSSudarsana Reddy Kalluru static int qed_ptp_hw_enable(struct qed_dev *cdev)
325c78c70faSSudarsana Reddy Kalluru {
326c78c70faSSudarsana Reddy Kalluru 	struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
327d179bd16Ssudarsana.kalluru@cavium.com 	struct qed_ptt *p_ptt;
328db82f70eSsudarsana.kalluru@cavium.com 	int rc;
329db82f70eSsudarsana.kalluru@cavium.com 
330d179bd16Ssudarsana.kalluru@cavium.com 	p_ptt = qed_ptt_acquire(p_hwfn);
331d179bd16Ssudarsana.kalluru@cavium.com 	if (!p_ptt) {
332d179bd16Ssudarsana.kalluru@cavium.com 		DP_NOTICE(p_hwfn, "Failed to acquire PTT for PTP\n");
333d179bd16Ssudarsana.kalluru@cavium.com 		return -EBUSY;
334d179bd16Ssudarsana.kalluru@cavium.com 	}
335d179bd16Ssudarsana.kalluru@cavium.com 
336d179bd16Ssudarsana.kalluru@cavium.com 	p_hwfn->p_ptp_ptt = p_ptt;
337d179bd16Ssudarsana.kalluru@cavium.com 
338db82f70eSsudarsana.kalluru@cavium.com 	rc = qed_ptp_res_lock(p_hwfn, p_ptt);
339db82f70eSsudarsana.kalluru@cavium.com 	if (rc) {
340db82f70eSsudarsana.kalluru@cavium.com 		DP_INFO(p_hwfn,
341db82f70eSsudarsana.kalluru@cavium.com 			"Couldn't acquire the resource lock, skip ptp enable for this PF\n");
342d179bd16Ssudarsana.kalluru@cavium.com 		qed_ptt_release(p_hwfn, p_ptt);
343d179bd16Ssudarsana.kalluru@cavium.com 		p_hwfn->p_ptp_ptt = NULL;
344db82f70eSsudarsana.kalluru@cavium.com 		return rc;
345db82f70eSsudarsana.kalluru@cavium.com 	}
346c78c70faSSudarsana Reddy Kalluru 
347c78c70faSSudarsana Reddy Kalluru 	/* Reset PTP event detection rules - will be configured in the IOCTL */
348c78c70faSSudarsana Reddy Kalluru 	qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_PARAM_MASK, 0x7FF);
349c78c70faSSudarsana Reddy Kalluru 	qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_RULE_MASK, 0x3FFF);
350c78c70faSSudarsana Reddy Kalluru 	qed_wr(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_PARAM_MASK, 0x7FF);
351c78c70faSSudarsana Reddy Kalluru 	qed_wr(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_RULE_MASK, 0x3FFF);
352c78c70faSSudarsana Reddy Kalluru 
353c78c70faSSudarsana Reddy Kalluru 	qed_wr(p_hwfn, p_ptt, NIG_REG_TX_PTP_EN, 7);
354c78c70faSSudarsana Reddy Kalluru 	qed_wr(p_hwfn, p_ptt, NIG_REG_RX_PTP_EN, 7);
355c78c70faSSudarsana Reddy Kalluru 
356c78c70faSSudarsana Reddy Kalluru 	qed_wr(p_hwfn, p_ptt, NIG_REG_TS_OUTPUT_ENABLE_PDA, 0x1);
357c78c70faSSudarsana Reddy Kalluru 
358c78c70faSSudarsana Reddy Kalluru 	/* Pause free running counter */
3599c79ddaaSMintz, Yuval 	if (QED_IS_BB_B0(p_hwfn->cdev))
360c78c70faSSudarsana Reddy Kalluru 		qed_wr(p_hwfn, p_ptt, NIG_REG_TIMESYNC_GEN_REG_BB, 2);
3619c79ddaaSMintz, Yuval 	if (QED_IS_AH(p_hwfn->cdev))
3629c79ddaaSMintz, Yuval 		qed_wr(p_hwfn, p_ptt, NIG_REG_TSGEN_FREECNT_UPDATE_K2, 2);
363c78c70faSSudarsana Reddy Kalluru 
364c78c70faSSudarsana Reddy Kalluru 	qed_wr(p_hwfn, p_ptt, NIG_REG_TSGEN_FREE_CNT_VALUE_LSB, 0);
365c78c70faSSudarsana Reddy Kalluru 	qed_wr(p_hwfn, p_ptt, NIG_REG_TSGEN_FREE_CNT_VALUE_MSB, 0);
366c78c70faSSudarsana Reddy Kalluru 	/* Resume free running counter */
3679c79ddaaSMintz, Yuval 	if (QED_IS_BB_B0(p_hwfn->cdev))
368c78c70faSSudarsana Reddy Kalluru 		qed_wr(p_hwfn, p_ptt, NIG_REG_TIMESYNC_GEN_REG_BB, 4);
3699c79ddaaSMintz, Yuval 	if (QED_IS_AH(p_hwfn->cdev)) {
3709c79ddaaSMintz, Yuval 		qed_wr(p_hwfn, p_ptt, NIG_REG_TSGEN_FREECNT_UPDATE_K2, 4);
3719c79ddaaSMintz, Yuval 		qed_wr(p_hwfn, p_ptt, NIG_REG_PTP_LATCH_OSTS_PKT_TIME, 1);
3729c79ddaaSMintz, Yuval 	}
373c78c70faSSudarsana Reddy Kalluru 
374c78c70faSSudarsana Reddy Kalluru 	/* Disable drift register */
375c78c70faSSudarsana Reddy Kalluru 	qed_wr(p_hwfn, p_ptt, NIG_REG_TSGEN_DRIFT_CNTR_CONF, 0x0);
376c78c70faSSudarsana Reddy Kalluru 	qed_wr(p_hwfn, p_ptt, NIG_REG_TSGEN_RST_DRIFT_CNTR, 0x0);
377c78c70faSSudarsana Reddy Kalluru 
378c78c70faSSudarsana Reddy Kalluru 	/* Reset possibly old timestamps */
379c78c70faSSudarsana Reddy Kalluru 	qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_HOST_BUF_SEQID,
380c78c70faSSudarsana Reddy Kalluru 	       QED_TIMESTAMP_MASK);
381c78c70faSSudarsana Reddy Kalluru 	qed_wr(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_BUF_SEQID, QED_TIMESTAMP_MASK);
382c78c70faSSudarsana Reddy Kalluru 
383c78c70faSSudarsana Reddy Kalluru 	return 0;
384c78c70faSSudarsana Reddy Kalluru }
385c78c70faSSudarsana Reddy Kalluru 
386c78c70faSSudarsana Reddy Kalluru static int qed_ptp_hw_hwtstamp_tx_on(struct qed_dev *cdev)
387c78c70faSSudarsana Reddy Kalluru {
388c78c70faSSudarsana Reddy Kalluru 	struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
389c78c70faSSudarsana Reddy Kalluru 	struct qed_ptt *p_ptt = p_hwfn->p_ptp_ptt;
390c78c70faSSudarsana Reddy Kalluru 
391c78c70faSSudarsana Reddy Kalluru 	qed_wr(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_PARAM_MASK, 0x6AA);
392c78c70faSSudarsana Reddy Kalluru 	qed_wr(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_RULE_MASK, 0x3EEE);
393c78c70faSSudarsana Reddy Kalluru 
394c78c70faSSudarsana Reddy Kalluru 	return 0;
395c78c70faSSudarsana Reddy Kalluru }
396c78c70faSSudarsana Reddy Kalluru 
397c78c70faSSudarsana Reddy Kalluru static int qed_ptp_hw_disable(struct qed_dev *cdev)
398c78c70faSSudarsana Reddy Kalluru {
399c78c70faSSudarsana Reddy Kalluru 	struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
400c78c70faSSudarsana Reddy Kalluru 	struct qed_ptt *p_ptt = p_hwfn->p_ptp_ptt;
401c78c70faSSudarsana Reddy Kalluru 
402db82f70eSsudarsana.kalluru@cavium.com 	qed_ptp_res_unlock(p_hwfn, p_ptt);
403db82f70eSsudarsana.kalluru@cavium.com 
404c78c70faSSudarsana Reddy Kalluru 	/* Reset PTP event detection rules */
405c78c70faSSudarsana Reddy Kalluru 	qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_PARAM_MASK, 0x7FF);
406c78c70faSSudarsana Reddy Kalluru 	qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_RULE_MASK, 0x3FFF);
407c78c70faSSudarsana Reddy Kalluru 
408c78c70faSSudarsana Reddy Kalluru 	qed_wr(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_PARAM_MASK, 0x7FF);
409c78c70faSSudarsana Reddy Kalluru 	qed_wr(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_RULE_MASK, 0x3FFF);
410c78c70faSSudarsana Reddy Kalluru 
411c78c70faSSudarsana Reddy Kalluru 	/* Disable the PTP feature */
412c78c70faSSudarsana Reddy Kalluru 	qed_wr(p_hwfn, p_ptt, NIG_REG_RX_PTP_EN, 0x0);
413c78c70faSSudarsana Reddy Kalluru 	qed_wr(p_hwfn, p_ptt, NIG_REG_TX_PTP_EN, 0x0);
414c78c70faSSudarsana Reddy Kalluru 
415d179bd16Ssudarsana.kalluru@cavium.com 	qed_ptt_release(p_hwfn, p_ptt);
416d179bd16Ssudarsana.kalluru@cavium.com 	p_hwfn->p_ptp_ptt = NULL;
417d179bd16Ssudarsana.kalluru@cavium.com 
418c78c70faSSudarsana Reddy Kalluru 	return 0;
419c78c70faSSudarsana Reddy Kalluru }
420c78c70faSSudarsana Reddy Kalluru 
421c78c70faSSudarsana Reddy Kalluru const struct qed_eth_ptp_ops qed_ptp_ops_pass = {
422c78c70faSSudarsana Reddy Kalluru 	.hwtstamp_tx_on = qed_ptp_hw_hwtstamp_tx_on,
423c78c70faSSudarsana Reddy Kalluru 	.cfg_rx_filters = qed_ptp_hw_cfg_rx_filters,
424c78c70faSSudarsana Reddy Kalluru 	.read_rx_ts = qed_ptp_hw_read_rx_ts,
425c78c70faSSudarsana Reddy Kalluru 	.read_tx_ts = qed_ptp_hw_read_tx_ts,
426c78c70faSSudarsana Reddy Kalluru 	.read_cc = qed_ptp_hw_read_cc,
427c78c70faSSudarsana Reddy Kalluru 	.adjfreq = qed_ptp_hw_adjfreq,
428c78c70faSSudarsana Reddy Kalluru 	.disable = qed_ptp_hw_disable,
429c78c70faSSudarsana Reddy Kalluru 	.enable = qed_ptp_hw_enable,
430c78c70faSSudarsana Reddy Kalluru };
431