Commit | Line | Data |
---|---|---|
118612d5 MC |
1 | /* Broadcom NetXtreme-C/E network driver. |
2 | * | |
3 | * Copyright (c) 2021 Broadcom Inc. | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation. | |
8 | */ | |
9 | #include <linux/kernel.h> | |
10 | #include <linux/errno.h> | |
11 | #include <linux/pci.h> | |
12 | #include <linux/netdevice.h> | |
13 | #include <linux/etherdevice.h> | |
14 | #include <linux/ptp_clock_kernel.h> | |
15 | #include <linux/net_tstamp.h> | |
16 | #include <linux/timecounter.h> | |
17 | #include <linux/timekeeping.h> | |
83bb623c | 18 | #include <linux/ptp_classify.h> |
118612d5 MC |
19 | #include "bnxt_hsi.h" |
20 | #include "bnxt.h" | |
21 | #include "bnxt_ptp.h" | |
22 | ||
83bb623c PC |
23 | int bnxt_ptp_parse(struct sk_buff *skb, u16 *seq_id) |
24 | { | |
25 | unsigned int ptp_class; | |
26 | struct ptp_header *hdr; | |
27 | ||
28 | ptp_class = ptp_classify_raw(skb); | |
29 | ||
30 | switch (ptp_class & PTP_CLASS_VMASK) { | |
31 | case PTP_CLASS_V1: | |
32 | case PTP_CLASS_V2: | |
33 | hdr = ptp_parse_header(skb, ptp_class); | |
34 | if (!hdr) | |
35 | return -EINVAL; | |
36 | ||
37 | *seq_id = ntohs(hdr->sequence_id); | |
38 | return 0; | |
39 | default: | |
40 | return -ERANGE; | |
41 | } | |
42 | } | |
43 | ||
118612d5 MC |
44 | static int bnxt_ptp_settime(struct ptp_clock_info *ptp_info, |
45 | const struct timespec64 *ts) | |
46 | { | |
47 | struct bnxt_ptp_cfg *ptp = container_of(ptp_info, struct bnxt_ptp_cfg, | |
48 | ptp_info); | |
49 | u64 ns = timespec64_to_ns(ts); | |
50 | ||
51 | spin_lock_bh(&ptp->ptp_lock); | |
52 | timecounter_init(&ptp->tc, &ptp->cc, ns); | |
53 | spin_unlock_bh(&ptp->ptp_lock); | |
54 | return 0; | |
55 | } | |
56 | ||
57 | /* Caller holds ptp_lock */ | |
58 | static u64 bnxt_refclk_read(struct bnxt *bp, struct ptp_system_timestamp *sts) | |
59 | { | |
60 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; | |
61 | u64 ns; | |
62 | ||
63 | ptp_read_system_prets(sts); | |
64 | ns = readl(bp->bar0 + ptp->refclk_mapped_regs[0]); | |
65 | ptp_read_system_postts(sts); | |
66 | ns |= (u64)readl(bp->bar0 + ptp->refclk_mapped_regs[1]) << 32; | |
67 | return ns; | |
68 | } | |
69 | ||
390862f4 PC |
70 | static void bnxt_ptp_get_current_time(struct bnxt *bp) |
71 | { | |
72 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; | |
73 | ||
74 | if (!ptp) | |
75 | return; | |
76 | spin_lock_bh(&ptp->ptp_lock); | |
77 | WRITE_ONCE(ptp->old_time, ptp->current_time); | |
78 | ptp->current_time = bnxt_refclk_read(bp, NULL); | |
79 | spin_unlock_bh(&ptp->ptp_lock); | |
80 | } | |
81 | ||
83bb623c PC |
82 | static int bnxt_hwrm_port_ts_query(struct bnxt *bp, u32 flags, u64 *ts) |
83 | { | |
84 | struct hwrm_port_ts_query_output *resp = bp->hwrm_cmd_resp_addr; | |
85 | struct hwrm_port_ts_query_input req = {0}; | |
86 | int rc; | |
87 | ||
88 | bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_PORT_TS_QUERY, -1, -1); | |
89 | req.flags = cpu_to_le32(flags); | |
90 | if ((flags & PORT_TS_QUERY_REQ_FLAGS_PATH) == | |
91 | PORT_TS_QUERY_REQ_FLAGS_PATH_TX) { | |
92 | req.enables = cpu_to_le16(BNXT_PTP_QTS_TX_ENABLES); | |
93 | req.ptp_seq_id = cpu_to_le32(bp->ptp_cfg->tx_seqid); | |
94 | req.ts_req_timeout = cpu_to_le16(BNXT_PTP_QTS_TIMEOUT); | |
95 | } | |
96 | mutex_lock(&bp->hwrm_cmd_lock); | |
97 | rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); | |
98 | if (!rc) | |
99 | *ts = le64_to_cpu(resp->ptp_msg_ts); | |
100 | mutex_unlock(&bp->hwrm_cmd_lock); | |
101 | return rc; | |
102 | } | |
103 | ||
118612d5 MC |
104 | static int bnxt_ptp_gettimex(struct ptp_clock_info *ptp_info, |
105 | struct timespec64 *ts, | |
106 | struct ptp_system_timestamp *sts) | |
107 | { | |
108 | struct bnxt_ptp_cfg *ptp = container_of(ptp_info, struct bnxt_ptp_cfg, | |
109 | ptp_info); | |
110 | u64 ns, cycles; | |
111 | ||
112 | spin_lock_bh(&ptp->ptp_lock); | |
113 | cycles = bnxt_refclk_read(ptp->bp, sts); | |
114 | ns = timecounter_cyc2time(&ptp->tc, cycles); | |
115 | spin_unlock_bh(&ptp->ptp_lock); | |
116 | *ts = ns_to_timespec64(ns); | |
117 | ||
118 | return 0; | |
119 | } | |
120 | ||
121 | static int bnxt_ptp_adjtime(struct ptp_clock_info *ptp_info, s64 delta) | |
122 | { | |
123 | struct bnxt_ptp_cfg *ptp = container_of(ptp_info, struct bnxt_ptp_cfg, | |
124 | ptp_info); | |
125 | ||
126 | spin_lock_bh(&ptp->ptp_lock); | |
127 | timecounter_adjtime(&ptp->tc, delta); | |
128 | spin_unlock_bh(&ptp->ptp_lock); | |
129 | return 0; | |
130 | } | |
131 | ||
132 | static int bnxt_ptp_adjfreq(struct ptp_clock_info *ptp_info, s32 ppb) | |
133 | { | |
134 | struct bnxt_ptp_cfg *ptp = container_of(ptp_info, struct bnxt_ptp_cfg, | |
135 | ptp_info); | |
136 | struct hwrm_port_mac_cfg_input req = {0}; | |
137 | struct bnxt *bp = ptp->bp; | |
138 | int rc; | |
139 | ||
140 | bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_PORT_MAC_CFG, -1, -1); | |
141 | req.ptp_freq_adj_ppb = cpu_to_le32(ppb); | |
142 | req.enables = cpu_to_le32(PORT_MAC_CFG_REQ_ENABLES_PTP_FREQ_ADJ_PPB); | |
143 | rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); | |
144 | if (rc) | |
145 | netdev_err(ptp->bp->dev, | |
146 | "ptp adjfreq failed. rc = %d\n", rc); | |
147 | return rc; | |
148 | } | |
149 | ||
150 | static int bnxt_ptp_enable(struct ptp_clock_info *ptp, | |
151 | struct ptp_clock_request *rq, int on) | |
152 | { | |
153 | return -EOPNOTSUPP; | |
154 | } | |
155 | ||
156 | static int bnxt_hwrm_ptp_cfg(struct bnxt *bp) | |
157 | { | |
158 | struct hwrm_port_mac_cfg_input req = {0}; | |
159 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; | |
160 | u32 flags = 0; | |
161 | ||
162 | bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_PORT_MAC_CFG, -1, -1); | |
163 | if (ptp->rx_filter) | |
164 | flags |= PORT_MAC_CFG_REQ_FLAGS_PTP_RX_TS_CAPTURE_ENABLE; | |
165 | else | |
166 | flags |= PORT_MAC_CFG_REQ_FLAGS_PTP_RX_TS_CAPTURE_DISABLE; | |
167 | if (ptp->tx_tstamp_en) | |
168 | flags |= PORT_MAC_CFG_REQ_FLAGS_PTP_TX_TS_CAPTURE_ENABLE; | |
169 | else | |
170 | flags |= PORT_MAC_CFG_REQ_FLAGS_PTP_TX_TS_CAPTURE_DISABLE; | |
171 | req.flags = cpu_to_le32(flags); | |
172 | req.enables = cpu_to_le32(PORT_MAC_CFG_REQ_ENABLES_RX_TS_CAPTURE_PTP_MSG_TYPE); | |
173 | req.rx_ts_capture_ptp_msg_type = cpu_to_le16(ptp->rxctl); | |
174 | ||
175 | return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); | |
176 | } | |
177 | ||
178 | int bnxt_hwtstamp_set(struct net_device *dev, struct ifreq *ifr) | |
179 | { | |
180 | struct bnxt *bp = netdev_priv(dev); | |
181 | struct hwtstamp_config stmpconf; | |
182 | struct bnxt_ptp_cfg *ptp; | |
183 | u16 old_rxctl; | |
184 | int old_rx_filter, rc; | |
185 | u8 old_tx_tstamp_en; | |
186 | ||
187 | ptp = bp->ptp_cfg; | |
188 | if (!ptp) | |
189 | return -EOPNOTSUPP; | |
190 | ||
191 | if (copy_from_user(&stmpconf, ifr->ifr_data, sizeof(stmpconf))) | |
192 | return -EFAULT; | |
193 | ||
194 | if (stmpconf.flags) | |
195 | return -EINVAL; | |
196 | ||
197 | if (stmpconf.tx_type != HWTSTAMP_TX_ON && | |
198 | stmpconf.tx_type != HWTSTAMP_TX_OFF) | |
199 | return -ERANGE; | |
200 | ||
201 | old_rx_filter = ptp->rx_filter; | |
202 | old_rxctl = ptp->rxctl; | |
203 | old_tx_tstamp_en = ptp->tx_tstamp_en; | |
204 | switch (stmpconf.rx_filter) { | |
205 | case HWTSTAMP_FILTER_NONE: | |
206 | ptp->rxctl = 0; | |
207 | ptp->rx_filter = HWTSTAMP_FILTER_NONE; | |
208 | break; | |
209 | case HWTSTAMP_FILTER_PTP_V2_EVENT: | |
210 | case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: | |
211 | case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: | |
212 | ptp->rxctl = BNXT_PTP_MSG_EVENTS; | |
213 | ptp->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; | |
214 | break; | |
215 | case HWTSTAMP_FILTER_PTP_V2_SYNC: | |
216 | case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: | |
217 | case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: | |
218 | ptp->rxctl = BNXT_PTP_MSG_SYNC; | |
219 | ptp->rx_filter = HWTSTAMP_FILTER_PTP_V2_SYNC; | |
220 | break; | |
221 | case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: | |
222 | case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: | |
223 | case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: | |
224 | ptp->rxctl = BNXT_PTP_MSG_DELAY_REQ; | |
225 | ptp->rx_filter = HWTSTAMP_FILTER_PTP_V2_DELAY_REQ; | |
226 | break; | |
227 | default: | |
228 | return -ERANGE; | |
229 | } | |
230 | ||
231 | if (stmpconf.tx_type == HWTSTAMP_TX_ON) | |
232 | ptp->tx_tstamp_en = 1; | |
233 | else | |
234 | ptp->tx_tstamp_en = 0; | |
235 | ||
236 | rc = bnxt_hwrm_ptp_cfg(bp); | |
237 | if (rc) | |
238 | goto ts_set_err; | |
239 | ||
240 | stmpconf.rx_filter = ptp->rx_filter; | |
241 | return copy_to_user(ifr->ifr_data, &stmpconf, sizeof(stmpconf)) ? | |
242 | -EFAULT : 0; | |
243 | ||
244 | ts_set_err: | |
245 | ptp->rx_filter = old_rx_filter; | |
246 | ptp->rxctl = old_rxctl; | |
247 | ptp->tx_tstamp_en = old_tx_tstamp_en; | |
248 | return rc; | |
249 | } | |
250 | ||
251 | int bnxt_hwtstamp_get(struct net_device *dev, struct ifreq *ifr) | |
252 | { | |
253 | struct bnxt *bp = netdev_priv(dev); | |
254 | struct hwtstamp_config stmpconf; | |
255 | struct bnxt_ptp_cfg *ptp; | |
256 | ||
257 | ptp = bp->ptp_cfg; | |
258 | if (!ptp) | |
259 | return -EOPNOTSUPP; | |
260 | ||
261 | stmpconf.flags = 0; | |
262 | stmpconf.tx_type = ptp->tx_tstamp_en ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF; | |
263 | ||
264 | stmpconf.rx_filter = ptp->rx_filter; | |
265 | return copy_to_user(ifr->ifr_data, &stmpconf, sizeof(stmpconf)) ? | |
266 | -EFAULT : 0; | |
267 | } | |
268 | ||
269 | static int bnxt_map_regs(struct bnxt *bp, u32 *reg_arr, int count, int reg_win) | |
270 | { | |
271 | u32 reg_base = *reg_arr & BNXT_GRC_BASE_MASK; | |
272 | u32 win_off; | |
273 | int i; | |
274 | ||
275 | for (i = 0; i < count; i++) { | |
276 | if ((reg_arr[i] & BNXT_GRC_BASE_MASK) != reg_base) | |
277 | return -ERANGE; | |
278 | } | |
279 | win_off = BNXT_GRCPF_REG_WINDOW_BASE_OUT + (reg_win - 1) * 4; | |
280 | writel(reg_base, bp->bar0 + win_off); | |
281 | return 0; | |
282 | } | |
283 | ||
284 | static int bnxt_map_ptp_regs(struct bnxt *bp) | |
285 | { | |
286 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; | |
287 | u32 *reg_arr; | |
288 | int rc, i; | |
289 | ||
290 | reg_arr = ptp->refclk_regs; | |
291 | if (bp->flags & BNXT_FLAG_CHIP_P5) { | |
292 | rc = bnxt_map_regs(bp, reg_arr, 2, BNXT_PTP_GRC_WIN); | |
293 | if (rc) | |
294 | return rc; | |
295 | for (i = 0; i < 2; i++) | |
296 | ptp->refclk_mapped_regs[i] = BNXT_PTP_GRC_WIN_BASE + | |
297 | (ptp->refclk_regs[i] & BNXT_GRC_OFFSET_MASK); | |
298 | return 0; | |
299 | } | |
300 | return -ENODEV; | |
301 | } | |
302 | ||
303 | static void bnxt_unmap_ptp_regs(struct bnxt *bp) | |
304 | { | |
305 | writel(0, bp->bar0 + BNXT_GRCPF_REG_WINDOW_BASE_OUT + | |
306 | (BNXT_PTP_GRC_WIN - 1) * 4); | |
307 | } | |
308 | ||
309 | static u64 bnxt_cc_read(const struct cyclecounter *cc) | |
310 | { | |
311 | struct bnxt_ptp_cfg *ptp = container_of(cc, struct bnxt_ptp_cfg, cc); | |
312 | ||
313 | return bnxt_refclk_read(ptp->bp, NULL); | |
314 | } | |
315 | ||
83bb623c PC |
316 | static void bnxt_stamp_tx_skb(struct bnxt *bp, struct sk_buff *skb) |
317 | { | |
318 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; | |
319 | struct skb_shared_hwtstamps timestamp; | |
320 | u64 ts = 0, ns = 0; | |
321 | int rc; | |
322 | ||
323 | rc = bnxt_hwrm_port_ts_query(bp, PORT_TS_QUERY_REQ_FLAGS_PATH_TX, &ts); | |
324 | if (!rc) { | |
325 | memset(×tamp, 0, sizeof(timestamp)); | |
326 | spin_lock_bh(&ptp->ptp_lock); | |
327 | ns = timecounter_cyc2time(&ptp->tc, ts); | |
328 | spin_unlock_bh(&ptp->ptp_lock); | |
329 | timestamp.hwtstamp = ns_to_ktime(ns); | |
330 | skb_tstamp_tx(ptp->tx_skb, ×tamp); | |
331 | } else { | |
332 | netdev_err(bp->dev, "TS query for TX timer failed rc = %x\n", | |
333 | rc); | |
334 | } | |
335 | ||
336 | dev_kfree_skb_any(ptp->tx_skb); | |
337 | ptp->tx_skb = NULL; | |
338 | atomic_inc(&ptp->tx_avail); | |
339 | } | |
340 | ||
390862f4 PC |
341 | static long bnxt_ptp_ts_aux_work(struct ptp_clock_info *ptp_info) |
342 | { | |
343 | struct bnxt_ptp_cfg *ptp = container_of(ptp_info, struct bnxt_ptp_cfg, | |
344 | ptp_info); | |
83bb623c | 345 | unsigned long now = jiffies; |
390862f4 PC |
346 | struct bnxt *bp = ptp->bp; |
347 | ||
83bb623c PC |
348 | if (ptp->tx_skb) |
349 | bnxt_stamp_tx_skb(bp, ptp->tx_skb); | |
350 | ||
351 | if (!time_after_eq(now, ptp->next_period)) | |
352 | return ptp->next_period - now; | |
353 | ||
390862f4 | 354 | bnxt_ptp_get_current_time(bp); |
83bb623c | 355 | ptp->next_period = now + HZ; |
89bc7f45 MC |
356 | if (time_after_eq(now, ptp->next_overflow_check)) { |
357 | spin_lock_bh(&ptp->ptp_lock); | |
358 | timecounter_read(&ptp->tc); | |
359 | spin_unlock_bh(&ptp->ptp_lock); | |
360 | ptp->next_overflow_check = now + BNXT_PHC_OVERFLOW_PERIOD; | |
361 | } | |
390862f4 PC |
362 | return HZ; |
363 | } | |
364 | ||
83bb623c PC |
365 | int bnxt_get_tx_ts_p5(struct bnxt *bp, struct sk_buff *skb) |
366 | { | |
367 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; | |
368 | ||
369 | if (ptp->tx_skb) { | |
370 | netdev_err(bp->dev, "deferring skb:one SKB is still outstanding\n"); | |
371 | return -EBUSY; | |
372 | } | |
373 | ptp->tx_skb = skb; | |
374 | ptp_schedule_worker(ptp->ptp_clock, 0); | |
375 | return 0; | |
376 | } | |
377 | ||
7f5515d1 PC |
378 | int bnxt_get_rx_ts_p5(struct bnxt *bp, u64 *ts, u32 pkt_ts) |
379 | { | |
380 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; | |
381 | u64 time; | |
382 | ||
383 | if (!ptp) | |
384 | return -ENODEV; | |
385 | ||
386 | BNXT_READ_TIME64(ptp, time, ptp->old_time); | |
387 | *ts = (time & BNXT_HI_TIMER_MASK) | pkt_ts; | |
388 | if (pkt_ts < (time & BNXT_LO_TIMER_MASK)) | |
389 | *ts += BNXT_LO_TIMER_MASK + 1; | |
390 | ||
391 | return 0; | |
392 | } | |
393 | ||
118612d5 MC |
394 | static const struct ptp_clock_info bnxt_ptp_caps = { |
395 | .owner = THIS_MODULE, | |
396 | .name = "bnxt clock", | |
397 | .max_adj = BNXT_MAX_PHC_DRIFT, | |
398 | .n_alarm = 0, | |
399 | .n_ext_ts = 0, | |
400 | .n_per_out = 0, | |
401 | .n_pins = 0, | |
402 | .pps = 0, | |
403 | .adjfreq = bnxt_ptp_adjfreq, | |
404 | .adjtime = bnxt_ptp_adjtime, | |
390862f4 | 405 | .do_aux_work = bnxt_ptp_ts_aux_work, |
118612d5 MC |
406 | .gettimex64 = bnxt_ptp_gettimex, |
407 | .settime64 = bnxt_ptp_settime, | |
408 | .enable = bnxt_ptp_enable, | |
409 | }; | |
410 | ||
411 | int bnxt_ptp_init(struct bnxt *bp) | |
412 | { | |
413 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; | |
414 | int rc; | |
415 | ||
416 | if (!ptp) | |
417 | return 0; | |
418 | ||
419 | rc = bnxt_map_ptp_regs(bp); | |
420 | if (rc) | |
421 | return rc; | |
422 | ||
423 | atomic_set(&ptp->tx_avail, BNXT_MAX_TX_TS); | |
424 | spin_lock_init(&ptp->ptp_lock); | |
425 | ||
426 | memset(&ptp->cc, 0, sizeof(ptp->cc)); | |
427 | ptp->cc.read = bnxt_cc_read; | |
428 | ptp->cc.mask = CYCLECOUNTER_MASK(48); | |
429 | ptp->cc.shift = 0; | |
430 | ptp->cc.mult = 1; | |
431 | ||
89bc7f45 | 432 | ptp->next_overflow_check = jiffies + BNXT_PHC_OVERFLOW_PERIOD; |
118612d5 MC |
433 | timecounter_init(&ptp->tc, &ptp->cc, ktime_to_ns(ktime_get_real())); |
434 | ||
435 | ptp->ptp_info = bnxt_ptp_caps; | |
436 | ptp->ptp_clock = ptp_clock_register(&ptp->ptp_info, &bp->pdev->dev); | |
437 | if (IS_ERR(ptp->ptp_clock)) { | |
438 | int err = PTR_ERR(ptp->ptp_clock); | |
439 | ||
440 | ptp->ptp_clock = NULL; | |
441 | bnxt_unmap_ptp_regs(bp); | |
442 | return err; | |
443 | } | |
d7859afb MC |
444 | if (bp->flags & BNXT_FLAG_CHIP_P5) { |
445 | spin_lock_bh(&ptp->ptp_lock); | |
446 | ptp->current_time = bnxt_refclk_read(bp, NULL); | |
447 | WRITE_ONCE(ptp->old_time, ptp->current_time); | |
448 | spin_unlock_bh(&ptp->ptp_lock); | |
449 | ptp_schedule_worker(ptp->ptp_clock, 0); | |
450 | } | |
118612d5 MC |
451 | return 0; |
452 | } | |
453 | ||
454 | void bnxt_ptp_clear(struct bnxt *bp) | |
455 | { | |
456 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; | |
457 | ||
458 | if (!ptp) | |
459 | return; | |
460 | ||
461 | if (ptp->ptp_clock) | |
462 | ptp_clock_unregister(ptp->ptp_clock); | |
463 | ||
464 | ptp->ptp_clock = NULL; | |
83bb623c PC |
465 | if (ptp->tx_skb) { |
466 | dev_kfree_skb_any(ptp->tx_skb); | |
467 | ptp->tx_skb = NULL; | |
468 | } | |
118612d5 MC |
469 | bnxt_unmap_ptp_regs(bp); |
470 | } |