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> | |
18 | #include "bnxt_hsi.h" | |
19 | #include "bnxt.h" | |
20 | #include "bnxt_ptp.h" | |
21 | ||
22 | static int bnxt_ptp_settime(struct ptp_clock_info *ptp_info, | |
23 | const struct timespec64 *ts) | |
24 | { | |
25 | struct bnxt_ptp_cfg *ptp = container_of(ptp_info, struct bnxt_ptp_cfg, | |
26 | ptp_info); | |
27 | u64 ns = timespec64_to_ns(ts); | |
28 | ||
29 | spin_lock_bh(&ptp->ptp_lock); | |
30 | timecounter_init(&ptp->tc, &ptp->cc, ns); | |
31 | spin_unlock_bh(&ptp->ptp_lock); | |
32 | return 0; | |
33 | } | |
34 | ||
35 | /* Caller holds ptp_lock */ | |
36 | static u64 bnxt_refclk_read(struct bnxt *bp, struct ptp_system_timestamp *sts) | |
37 | { | |
38 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; | |
39 | u64 ns; | |
40 | ||
41 | ptp_read_system_prets(sts); | |
42 | ns = readl(bp->bar0 + ptp->refclk_mapped_regs[0]); | |
43 | ptp_read_system_postts(sts); | |
44 | ns |= (u64)readl(bp->bar0 + ptp->refclk_mapped_regs[1]) << 32; | |
45 | return ns; | |
46 | } | |
47 | ||
390862f4 PC |
48 | static void bnxt_ptp_get_current_time(struct bnxt *bp) |
49 | { | |
50 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; | |
51 | ||
52 | if (!ptp) | |
53 | return; | |
54 | spin_lock_bh(&ptp->ptp_lock); | |
55 | WRITE_ONCE(ptp->old_time, ptp->current_time); | |
56 | ptp->current_time = bnxt_refclk_read(bp, NULL); | |
57 | spin_unlock_bh(&ptp->ptp_lock); | |
58 | } | |
59 | ||
118612d5 MC |
60 | static int bnxt_ptp_gettimex(struct ptp_clock_info *ptp_info, |
61 | struct timespec64 *ts, | |
62 | struct ptp_system_timestamp *sts) | |
63 | { | |
64 | struct bnxt_ptp_cfg *ptp = container_of(ptp_info, struct bnxt_ptp_cfg, | |
65 | ptp_info); | |
66 | u64 ns, cycles; | |
67 | ||
68 | spin_lock_bh(&ptp->ptp_lock); | |
69 | cycles = bnxt_refclk_read(ptp->bp, sts); | |
70 | ns = timecounter_cyc2time(&ptp->tc, cycles); | |
71 | spin_unlock_bh(&ptp->ptp_lock); | |
72 | *ts = ns_to_timespec64(ns); | |
73 | ||
74 | return 0; | |
75 | } | |
76 | ||
77 | static int bnxt_ptp_adjtime(struct ptp_clock_info *ptp_info, s64 delta) | |
78 | { | |
79 | struct bnxt_ptp_cfg *ptp = container_of(ptp_info, struct bnxt_ptp_cfg, | |
80 | ptp_info); | |
81 | ||
82 | spin_lock_bh(&ptp->ptp_lock); | |
83 | timecounter_adjtime(&ptp->tc, delta); | |
84 | spin_unlock_bh(&ptp->ptp_lock); | |
85 | return 0; | |
86 | } | |
87 | ||
88 | static int bnxt_ptp_adjfreq(struct ptp_clock_info *ptp_info, s32 ppb) | |
89 | { | |
90 | struct bnxt_ptp_cfg *ptp = container_of(ptp_info, struct bnxt_ptp_cfg, | |
91 | ptp_info); | |
92 | struct hwrm_port_mac_cfg_input req = {0}; | |
93 | struct bnxt *bp = ptp->bp; | |
94 | int rc; | |
95 | ||
96 | bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_PORT_MAC_CFG, -1, -1); | |
97 | req.ptp_freq_adj_ppb = cpu_to_le32(ppb); | |
98 | req.enables = cpu_to_le32(PORT_MAC_CFG_REQ_ENABLES_PTP_FREQ_ADJ_PPB); | |
99 | rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); | |
100 | if (rc) | |
101 | netdev_err(ptp->bp->dev, | |
102 | "ptp adjfreq failed. rc = %d\n", rc); | |
103 | return rc; | |
104 | } | |
105 | ||
106 | static int bnxt_ptp_enable(struct ptp_clock_info *ptp, | |
107 | struct ptp_clock_request *rq, int on) | |
108 | { | |
109 | return -EOPNOTSUPP; | |
110 | } | |
111 | ||
112 | static int bnxt_hwrm_ptp_cfg(struct bnxt *bp) | |
113 | { | |
114 | struct hwrm_port_mac_cfg_input req = {0}; | |
115 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; | |
116 | u32 flags = 0; | |
117 | ||
118 | bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_PORT_MAC_CFG, -1, -1); | |
119 | if (ptp->rx_filter) | |
120 | flags |= PORT_MAC_CFG_REQ_FLAGS_PTP_RX_TS_CAPTURE_ENABLE; | |
121 | else | |
122 | flags |= PORT_MAC_CFG_REQ_FLAGS_PTP_RX_TS_CAPTURE_DISABLE; | |
123 | if (ptp->tx_tstamp_en) | |
124 | flags |= PORT_MAC_CFG_REQ_FLAGS_PTP_TX_TS_CAPTURE_ENABLE; | |
125 | else | |
126 | flags |= PORT_MAC_CFG_REQ_FLAGS_PTP_TX_TS_CAPTURE_DISABLE; | |
127 | req.flags = cpu_to_le32(flags); | |
128 | req.enables = cpu_to_le32(PORT_MAC_CFG_REQ_ENABLES_RX_TS_CAPTURE_PTP_MSG_TYPE); | |
129 | req.rx_ts_capture_ptp_msg_type = cpu_to_le16(ptp->rxctl); | |
130 | ||
131 | return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); | |
132 | } | |
133 | ||
134 | int bnxt_hwtstamp_set(struct net_device *dev, struct ifreq *ifr) | |
135 | { | |
136 | struct bnxt *bp = netdev_priv(dev); | |
137 | struct hwtstamp_config stmpconf; | |
138 | struct bnxt_ptp_cfg *ptp; | |
139 | u16 old_rxctl; | |
140 | int old_rx_filter, rc; | |
141 | u8 old_tx_tstamp_en; | |
142 | ||
143 | ptp = bp->ptp_cfg; | |
144 | if (!ptp) | |
145 | return -EOPNOTSUPP; | |
146 | ||
147 | if (copy_from_user(&stmpconf, ifr->ifr_data, sizeof(stmpconf))) | |
148 | return -EFAULT; | |
149 | ||
150 | if (stmpconf.flags) | |
151 | return -EINVAL; | |
152 | ||
153 | if (stmpconf.tx_type != HWTSTAMP_TX_ON && | |
154 | stmpconf.tx_type != HWTSTAMP_TX_OFF) | |
155 | return -ERANGE; | |
156 | ||
157 | old_rx_filter = ptp->rx_filter; | |
158 | old_rxctl = ptp->rxctl; | |
159 | old_tx_tstamp_en = ptp->tx_tstamp_en; | |
160 | switch (stmpconf.rx_filter) { | |
161 | case HWTSTAMP_FILTER_NONE: | |
162 | ptp->rxctl = 0; | |
163 | ptp->rx_filter = HWTSTAMP_FILTER_NONE; | |
164 | break; | |
165 | case HWTSTAMP_FILTER_PTP_V2_EVENT: | |
166 | case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: | |
167 | case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: | |
168 | ptp->rxctl = BNXT_PTP_MSG_EVENTS; | |
169 | ptp->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; | |
170 | break; | |
171 | case HWTSTAMP_FILTER_PTP_V2_SYNC: | |
172 | case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: | |
173 | case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: | |
174 | ptp->rxctl = BNXT_PTP_MSG_SYNC; | |
175 | ptp->rx_filter = HWTSTAMP_FILTER_PTP_V2_SYNC; | |
176 | break; | |
177 | case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: | |
178 | case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: | |
179 | case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: | |
180 | ptp->rxctl = BNXT_PTP_MSG_DELAY_REQ; | |
181 | ptp->rx_filter = HWTSTAMP_FILTER_PTP_V2_DELAY_REQ; | |
182 | break; | |
183 | default: | |
184 | return -ERANGE; | |
185 | } | |
186 | ||
187 | if (stmpconf.tx_type == HWTSTAMP_TX_ON) | |
188 | ptp->tx_tstamp_en = 1; | |
189 | else | |
190 | ptp->tx_tstamp_en = 0; | |
191 | ||
192 | rc = bnxt_hwrm_ptp_cfg(bp); | |
193 | if (rc) | |
194 | goto ts_set_err; | |
195 | ||
196 | stmpconf.rx_filter = ptp->rx_filter; | |
197 | return copy_to_user(ifr->ifr_data, &stmpconf, sizeof(stmpconf)) ? | |
198 | -EFAULT : 0; | |
199 | ||
200 | ts_set_err: | |
201 | ptp->rx_filter = old_rx_filter; | |
202 | ptp->rxctl = old_rxctl; | |
203 | ptp->tx_tstamp_en = old_tx_tstamp_en; | |
204 | return rc; | |
205 | } | |
206 | ||
207 | int bnxt_hwtstamp_get(struct net_device *dev, struct ifreq *ifr) | |
208 | { | |
209 | struct bnxt *bp = netdev_priv(dev); | |
210 | struct hwtstamp_config stmpconf; | |
211 | struct bnxt_ptp_cfg *ptp; | |
212 | ||
213 | ptp = bp->ptp_cfg; | |
214 | if (!ptp) | |
215 | return -EOPNOTSUPP; | |
216 | ||
217 | stmpconf.flags = 0; | |
218 | stmpconf.tx_type = ptp->tx_tstamp_en ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF; | |
219 | ||
220 | stmpconf.rx_filter = ptp->rx_filter; | |
221 | return copy_to_user(ifr->ifr_data, &stmpconf, sizeof(stmpconf)) ? | |
222 | -EFAULT : 0; | |
223 | } | |
224 | ||
225 | static int bnxt_map_regs(struct bnxt *bp, u32 *reg_arr, int count, int reg_win) | |
226 | { | |
227 | u32 reg_base = *reg_arr & BNXT_GRC_BASE_MASK; | |
228 | u32 win_off; | |
229 | int i; | |
230 | ||
231 | for (i = 0; i < count; i++) { | |
232 | if ((reg_arr[i] & BNXT_GRC_BASE_MASK) != reg_base) | |
233 | return -ERANGE; | |
234 | } | |
235 | win_off = BNXT_GRCPF_REG_WINDOW_BASE_OUT + (reg_win - 1) * 4; | |
236 | writel(reg_base, bp->bar0 + win_off); | |
237 | return 0; | |
238 | } | |
239 | ||
240 | static int bnxt_map_ptp_regs(struct bnxt *bp) | |
241 | { | |
242 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; | |
243 | u32 *reg_arr; | |
244 | int rc, i; | |
245 | ||
246 | reg_arr = ptp->refclk_regs; | |
247 | if (bp->flags & BNXT_FLAG_CHIP_P5) { | |
248 | rc = bnxt_map_regs(bp, reg_arr, 2, BNXT_PTP_GRC_WIN); | |
249 | if (rc) | |
250 | return rc; | |
251 | for (i = 0; i < 2; i++) | |
252 | ptp->refclk_mapped_regs[i] = BNXT_PTP_GRC_WIN_BASE + | |
253 | (ptp->refclk_regs[i] & BNXT_GRC_OFFSET_MASK); | |
254 | return 0; | |
255 | } | |
256 | return -ENODEV; | |
257 | } | |
258 | ||
259 | static void bnxt_unmap_ptp_regs(struct bnxt *bp) | |
260 | { | |
261 | writel(0, bp->bar0 + BNXT_GRCPF_REG_WINDOW_BASE_OUT + | |
262 | (BNXT_PTP_GRC_WIN - 1) * 4); | |
263 | } | |
264 | ||
265 | static u64 bnxt_cc_read(const struct cyclecounter *cc) | |
266 | { | |
267 | struct bnxt_ptp_cfg *ptp = container_of(cc, struct bnxt_ptp_cfg, cc); | |
268 | ||
269 | return bnxt_refclk_read(ptp->bp, NULL); | |
270 | } | |
271 | ||
390862f4 PC |
272 | static long bnxt_ptp_ts_aux_work(struct ptp_clock_info *ptp_info) |
273 | { | |
274 | struct bnxt_ptp_cfg *ptp = container_of(ptp_info, struct bnxt_ptp_cfg, | |
275 | ptp_info); | |
276 | struct bnxt *bp = ptp->bp; | |
277 | ||
278 | bnxt_ptp_get_current_time(bp); | |
279 | return HZ; | |
280 | } | |
281 | ||
282 | void bnxt_ptp_start(struct bnxt *bp) | |
283 | { | |
284 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; | |
285 | ||
286 | if (!ptp) | |
287 | return; | |
288 | ||
289 | if (bp->flags & BNXT_FLAG_CHIP_P5) { | |
290 | spin_lock_bh(&ptp->ptp_lock); | |
291 | ptp->current_time = bnxt_refclk_read(bp, NULL); | |
292 | WRITE_ONCE(ptp->old_time, ptp->current_time); | |
293 | spin_unlock_bh(&ptp->ptp_lock); | |
294 | ptp_schedule_worker(ptp->ptp_clock, 0); | |
295 | } | |
296 | } | |
297 | ||
118612d5 MC |
298 | static const struct ptp_clock_info bnxt_ptp_caps = { |
299 | .owner = THIS_MODULE, | |
300 | .name = "bnxt clock", | |
301 | .max_adj = BNXT_MAX_PHC_DRIFT, | |
302 | .n_alarm = 0, | |
303 | .n_ext_ts = 0, | |
304 | .n_per_out = 0, | |
305 | .n_pins = 0, | |
306 | .pps = 0, | |
307 | .adjfreq = bnxt_ptp_adjfreq, | |
308 | .adjtime = bnxt_ptp_adjtime, | |
390862f4 | 309 | .do_aux_work = bnxt_ptp_ts_aux_work, |
118612d5 MC |
310 | .gettimex64 = bnxt_ptp_gettimex, |
311 | .settime64 = bnxt_ptp_settime, | |
312 | .enable = bnxt_ptp_enable, | |
313 | }; | |
314 | ||
315 | int bnxt_ptp_init(struct bnxt *bp) | |
316 | { | |
317 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; | |
318 | int rc; | |
319 | ||
320 | if (!ptp) | |
321 | return 0; | |
322 | ||
323 | rc = bnxt_map_ptp_regs(bp); | |
324 | if (rc) | |
325 | return rc; | |
326 | ||
327 | atomic_set(&ptp->tx_avail, BNXT_MAX_TX_TS); | |
328 | spin_lock_init(&ptp->ptp_lock); | |
329 | ||
330 | memset(&ptp->cc, 0, sizeof(ptp->cc)); | |
331 | ptp->cc.read = bnxt_cc_read; | |
332 | ptp->cc.mask = CYCLECOUNTER_MASK(48); | |
333 | ptp->cc.shift = 0; | |
334 | ptp->cc.mult = 1; | |
335 | ||
336 | timecounter_init(&ptp->tc, &ptp->cc, ktime_to_ns(ktime_get_real())); | |
337 | ||
338 | ptp->ptp_info = bnxt_ptp_caps; | |
339 | ptp->ptp_clock = ptp_clock_register(&ptp->ptp_info, &bp->pdev->dev); | |
340 | if (IS_ERR(ptp->ptp_clock)) { | |
341 | int err = PTR_ERR(ptp->ptp_clock); | |
342 | ||
343 | ptp->ptp_clock = NULL; | |
344 | bnxt_unmap_ptp_regs(bp); | |
345 | return err; | |
346 | } | |
347 | ||
348 | return 0; | |
349 | } | |
350 | ||
351 | void bnxt_ptp_clear(struct bnxt *bp) | |
352 | { | |
353 | struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; | |
354 | ||
355 | if (!ptp) | |
356 | return; | |
357 | ||
358 | if (ptp->ptp_clock) | |
359 | ptp_clock_unregister(ptp->ptp_clock); | |
360 | ||
361 | ptp->ptp_clock = NULL; | |
362 | bnxt_unmap_ptp_regs(bp); | |
363 | } |