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(¶ms, NULL, resource, true); 75db82f70eSsudarsana.kalluru@cavium.com 76db82f70eSsudarsana.kalluru@cavium.com rc = qed_mcp_resc_lock(p_hwfn, p_ptt, ¶ms); 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, ¶ms, resource, true); 107db82f70eSsudarsana.kalluru@cavium.com 108db82f70eSsudarsana.kalluru@cavium.com rc = qed_mcp_resc_unlock(p_hwfn, p_ptt, ¶ms); 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