qed_ptp.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  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. /* 16 nano second time quantas to wait before making a Drift adjustment */
  40. #define QED_DRIFT_CNTR_TIME_QUANTA_SHIFT 0
  41. /* Nano seconds to add/subtract when making a Drift adjustment */
  42. #define QED_DRIFT_CNTR_ADJUSTMENT_SHIFT 28
  43. /* Add/subtract the Adjustment_Value when making a Drift adjustment */
  44. #define QED_DRIFT_CNTR_DIRECTION_SHIFT 31
  45. #define QED_TIMESTAMP_MASK BIT(16)
  46. /* Read Rx timestamp */
  47. static int qed_ptp_hw_read_rx_ts(struct qed_dev *cdev, u64 *timestamp)
  48. {
  49. struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
  50. struct qed_ptt *p_ptt = p_hwfn->p_ptp_ptt;
  51. u32 val;
  52. *timestamp = 0;
  53. val = qed_rd(p_hwfn, p_ptt, NIG_REG_LLH_PTP_HOST_BUF_SEQID);
  54. if (!(val & QED_TIMESTAMP_MASK)) {
  55. DP_INFO(p_hwfn, "Invalid Rx timestamp, buf_seqid = %d\n", val);
  56. return -EINVAL;
  57. }
  58. val = qed_rd(p_hwfn, p_ptt, NIG_REG_LLH_PTP_HOST_BUF_TS_LSB);
  59. *timestamp = qed_rd(p_hwfn, p_ptt, NIG_REG_LLH_PTP_HOST_BUF_TS_MSB);
  60. *timestamp <<= 32;
  61. *timestamp |= val;
  62. /* Reset timestamp register to allow new timestamp */
  63. qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_HOST_BUF_SEQID,
  64. QED_TIMESTAMP_MASK);
  65. return 0;
  66. }
  67. /* Read Tx timestamp */
  68. static int qed_ptp_hw_read_tx_ts(struct qed_dev *cdev, u64 *timestamp)
  69. {
  70. struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
  71. struct qed_ptt *p_ptt = p_hwfn->p_ptp_ptt;
  72. u32 val;
  73. *timestamp = 0;
  74. val = qed_rd(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_BUF_SEQID);
  75. if (!(val & QED_TIMESTAMP_MASK)) {
  76. DP_INFO(p_hwfn, "Invalid Tx timestamp, buf_seqid = %d\n", val);
  77. return -EINVAL;
  78. }
  79. val = qed_rd(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_BUF_TS_LSB);
  80. *timestamp = qed_rd(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_BUF_TS_MSB);
  81. *timestamp <<= 32;
  82. *timestamp |= val;
  83. /* Reset timestamp register to allow new timestamp */
  84. qed_wr(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_BUF_SEQID, QED_TIMESTAMP_MASK);
  85. return 0;
  86. }
  87. /* Read Phy Hardware Clock */
  88. static int qed_ptp_hw_read_cc(struct qed_dev *cdev, u64 *phc_cycles)
  89. {
  90. struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
  91. struct qed_ptt *p_ptt = p_hwfn->p_ptp_ptt;
  92. u32 temp = 0;
  93. temp = qed_rd(p_hwfn, p_ptt, NIG_REG_TSGEN_SYNC_TIME_LSB);
  94. *phc_cycles = qed_rd(p_hwfn, p_ptt, NIG_REG_TSGEN_SYNC_TIME_MSB);
  95. *phc_cycles <<= 32;
  96. *phc_cycles |= temp;
  97. return 0;
  98. }
  99. /* Filter PTP protocol packets that need to be timestamped */
  100. static int qed_ptp_hw_cfg_rx_filters(struct qed_dev *cdev,
  101. enum qed_ptp_filter_type type)
  102. {
  103. struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
  104. struct qed_ptt *p_ptt = p_hwfn->p_ptp_ptt;
  105. u32 rule_mask, parm_mask;
  106. switch (type) {
  107. case QED_PTP_FILTER_L2_IPV4_IPV6:
  108. parm_mask = 0x6AA;
  109. rule_mask = 0x3EEE;
  110. break;
  111. case QED_PTP_FILTER_L2:
  112. parm_mask = 0x6BF;
  113. rule_mask = 0x3EFF;
  114. break;
  115. case QED_PTP_FILTER_IPV4_IPV6:
  116. parm_mask = 0x7EA;
  117. rule_mask = 0x3FFE;
  118. break;
  119. case QED_PTP_FILTER_IPV4:
  120. parm_mask = 0x7EE;
  121. rule_mask = 0x3FFE;
  122. break;
  123. default:
  124. DP_INFO(p_hwfn, "Invalid PTP filter type %d\n", type);
  125. return -EINVAL;
  126. }
  127. qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_PARAM_MASK, parm_mask);
  128. qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_RULE_MASK, rule_mask);
  129. qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_TO_HOST, 0x1);
  130. /* Reset possibly old timestamps */
  131. qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_HOST_BUF_SEQID,
  132. QED_TIMESTAMP_MASK);
  133. return 0;
  134. }
  135. /* Adjust the HW clock by a rate given in parts-per-billion (ppb) units.
  136. * FW/HW accepts the adjustment value in terms of 3 parameters:
  137. * Drift period - adjustment happens once in certain number of nano seconds.
  138. * Drift value - time is adjusted by a certain value, for example by 5 ns.
  139. * Drift direction - add or subtract the adjustment value.
  140. * The routine translates ppb into the adjustment triplet in an optimal manner.
  141. */
  142. static int qed_ptp_hw_adjfreq(struct qed_dev *cdev, s32 ppb)
  143. {
  144. s64 best_val = 0, val, best_period = 0, period, approx_dev, dif, dif2;
  145. struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
  146. struct qed_ptt *p_ptt = p_hwfn->p_ptp_ptt;
  147. u32 drift_ctr_cfg = 0, drift_state;
  148. int drift_dir = 1;
  149. if (ppb < 0) {
  150. ppb = -ppb;
  151. drift_dir = 0;
  152. }
  153. if (ppb > 1) {
  154. s64 best_dif = ppb, best_approx_dev = 1;
  155. /* Adjustment value is up to +/-7ns, find an optimal value in
  156. * this range.
  157. */
  158. for (val = 7; val > 0; val--) {
  159. period = div_s64(val * 1000000000, ppb);
  160. period -= 8;
  161. period >>= 4;
  162. if (period < 1)
  163. period = 1;
  164. if (period > 0xFFFFFFE)
  165. period = 0xFFFFFFE;
  166. /* Check both rounding ends for approximate error */
  167. approx_dev = period * 16 + 8;
  168. dif = ppb * approx_dev - val * 1000000000;
  169. dif2 = dif + 16 * ppb;
  170. if (dif < 0)
  171. dif = -dif;
  172. if (dif2 < 0)
  173. dif2 = -dif2;
  174. /* Determine which end gives better approximation */
  175. if (dif * (approx_dev + 16) > dif2 * approx_dev) {
  176. period++;
  177. approx_dev += 16;
  178. dif = dif2;
  179. }
  180. /* Track best approximation found so far */
  181. if (best_dif * approx_dev > dif * best_approx_dev) {
  182. best_dif = dif;
  183. best_val = val;
  184. best_period = period;
  185. best_approx_dev = approx_dev;
  186. }
  187. }
  188. } else if (ppb == 1) {
  189. /* This is a special case as its the only value which wouldn't
  190. * fit in a s64 variable. In order to prevent castings simple
  191. * handle it seperately.
  192. */
  193. best_val = 4;
  194. best_period = 0xee6b27f;
  195. } else {
  196. best_val = 0;
  197. best_period = 0xFFFFFFF;
  198. }
  199. drift_ctr_cfg = (best_period << QED_DRIFT_CNTR_TIME_QUANTA_SHIFT) |
  200. (((int)best_val) << QED_DRIFT_CNTR_ADJUSTMENT_SHIFT) |
  201. (((int)drift_dir) << QED_DRIFT_CNTR_DIRECTION_SHIFT);
  202. qed_wr(p_hwfn, p_ptt, NIG_REG_TSGEN_RST_DRIFT_CNTR, 0x1);
  203. drift_state = qed_rd(p_hwfn, p_ptt, NIG_REG_TSGEN_RST_DRIFT_CNTR);
  204. if (drift_state & 1) {
  205. qed_wr(p_hwfn, p_ptt, NIG_REG_TSGEN_DRIFT_CNTR_CONF,
  206. drift_ctr_cfg);
  207. } else {
  208. DP_INFO(p_hwfn, "Drift counter is not reset\n");
  209. return -EINVAL;
  210. }
  211. qed_wr(p_hwfn, p_ptt, NIG_REG_TSGEN_RST_DRIFT_CNTR, 0x0);
  212. return 0;
  213. }
  214. static int qed_ptp_hw_enable(struct qed_dev *cdev)
  215. {
  216. struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
  217. struct qed_ptt *p_ptt = p_hwfn->p_ptp_ptt;
  218. /* Reset PTP event detection rules - will be configured in the IOCTL */
  219. qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_PARAM_MASK, 0x7FF);
  220. qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_RULE_MASK, 0x3FFF);
  221. qed_wr(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_PARAM_MASK, 0x7FF);
  222. qed_wr(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_RULE_MASK, 0x3FFF);
  223. qed_wr(p_hwfn, p_ptt, NIG_REG_TX_PTP_EN, 7);
  224. qed_wr(p_hwfn, p_ptt, NIG_REG_RX_PTP_EN, 7);
  225. qed_wr(p_hwfn, p_ptt, NIG_REG_TS_OUTPUT_ENABLE_PDA, 0x1);
  226. /* Pause free running counter */
  227. qed_wr(p_hwfn, p_ptt, NIG_REG_TIMESYNC_GEN_REG_BB, 2);
  228. qed_wr(p_hwfn, p_ptt, NIG_REG_TSGEN_FREE_CNT_VALUE_LSB, 0);
  229. qed_wr(p_hwfn, p_ptt, NIG_REG_TSGEN_FREE_CNT_VALUE_MSB, 0);
  230. /* Resume free running counter */
  231. qed_wr(p_hwfn, p_ptt, NIG_REG_TIMESYNC_GEN_REG_BB, 4);
  232. /* Disable drift register */
  233. qed_wr(p_hwfn, p_ptt, NIG_REG_TSGEN_DRIFT_CNTR_CONF, 0x0);
  234. qed_wr(p_hwfn, p_ptt, NIG_REG_TSGEN_RST_DRIFT_CNTR, 0x0);
  235. /* Reset possibly old timestamps */
  236. qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_HOST_BUF_SEQID,
  237. QED_TIMESTAMP_MASK);
  238. qed_wr(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_BUF_SEQID, QED_TIMESTAMP_MASK);
  239. return 0;
  240. }
  241. static int qed_ptp_hw_hwtstamp_tx_on(struct qed_dev *cdev)
  242. {
  243. struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
  244. struct qed_ptt *p_ptt = p_hwfn->p_ptp_ptt;
  245. qed_wr(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_PARAM_MASK, 0x6AA);
  246. qed_wr(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_RULE_MASK, 0x3EEE);
  247. return 0;
  248. }
  249. static int qed_ptp_hw_disable(struct qed_dev *cdev)
  250. {
  251. struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
  252. struct qed_ptt *p_ptt = p_hwfn->p_ptp_ptt;
  253. /* Reset PTP event detection rules */
  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. /* Disable the PTP feature */
  259. qed_wr(p_hwfn, p_ptt, NIG_REG_RX_PTP_EN, 0x0);
  260. qed_wr(p_hwfn, p_ptt, NIG_REG_TX_PTP_EN, 0x0);
  261. return 0;
  262. }
  263. const struct qed_eth_ptp_ops qed_ptp_ops_pass = {
  264. .hwtstamp_tx_on = qed_ptp_hw_hwtstamp_tx_on,
  265. .cfg_rx_filters = qed_ptp_hw_cfg_rx_filters,
  266. .read_rx_ts = qed_ptp_hw_read_rx_ts,
  267. .read_tx_ts = qed_ptp_hw_read_tx_ts,
  268. .read_cc = qed_ptp_hw_read_cc,
  269. .adjfreq = qed_ptp_hw_adjfreq,
  270. .disable = qed_ptp_hw_disable,
  271. .enable = qed_ptp_hw_enable,
  272. };