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