1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Texas Instruments K3 AM65 Ethernet Switch SubSystem Driver ethtool ops |
3 | * |
4 | * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ |
5 | * |
6 | */ |
7 | |
8 | #include <linux/net_tstamp.h> |
9 | #include <linux/phylink.h> |
10 | #include <linux/platform_device.h> |
11 | #include <linux/pm_runtime.h> |
12 | |
13 | #include "am65-cpsw-nuss.h" |
14 | #include "cpsw_ale.h" |
15 | #include "am65-cpts.h" |
16 | |
17 | #define AM65_CPSW_REGDUMP_VER 0x1 |
18 | |
19 | enum { |
20 | AM65_CPSW_REGDUMP_MOD_NUSS = 1, |
21 | AM65_CPSW_REGDUMP_MOD_RGMII_STATUS = 2, |
22 | AM65_CPSW_REGDUMP_MOD_MDIO = 3, |
23 | AM65_CPSW_REGDUMP_MOD_CPSW = 4, |
24 | AM65_CPSW_REGDUMP_MOD_CPSW_P0 = 5, |
25 | AM65_CPSW_REGDUMP_MOD_CPSW_P1 = 6, |
26 | AM65_CPSW_REGDUMP_MOD_CPSW_CPTS = 7, |
27 | AM65_CPSW_REGDUMP_MOD_CPSW_ALE = 8, |
28 | AM65_CPSW_REGDUMP_MOD_CPSW_ALE_TBL = 9, |
29 | AM65_CPSW_REGDUMP_MOD_LAST, |
30 | }; |
31 | |
32 | /** |
33 | * struct am65_cpsw_regdump_hdr - regdump record header |
34 | * |
35 | * @module_id: CPSW module ID |
36 | * @len: CPSW module registers space length in u32 |
37 | */ |
38 | |
39 | struct am65_cpsw_regdump_hdr { |
40 | u32 module_id; |
41 | u32 len; |
42 | }; |
43 | |
44 | /** |
45 | * struct am65_cpsw_regdump_item - regdump module description |
46 | * |
47 | * @hdr: CPSW module header |
48 | * @start_ofs: CPSW module registers start addr |
49 | * @end_ofs: CPSW module registers end addr |
50 | * |
51 | * Registers dump provided in the format: |
52 | * u32 : module ID |
53 | * u32 : dump length |
54 | * u32[..len]: registers values |
55 | */ |
56 | struct am65_cpsw_regdump_item { |
57 | struct am65_cpsw_regdump_hdr hdr; |
58 | u32 start_ofs; |
59 | u32 end_ofs; |
60 | }; |
61 | |
62 | #define AM65_CPSW_REGDUMP_REC(mod, start, end) { \ |
63 | .hdr.module_id = (mod), \ |
64 | .hdr.len = (end + 4 - start) * 2 + \ |
65 | sizeof(struct am65_cpsw_regdump_hdr), \ |
66 | .start_ofs = (start), \ |
67 | .end_ofs = end, \ |
68 | } |
69 | |
70 | static const struct am65_cpsw_regdump_item am65_cpsw_regdump[] = { |
71 | AM65_CPSW_REGDUMP_REC(AM65_CPSW_REGDUMP_MOD_NUSS, 0x0, 0x1c), |
72 | AM65_CPSW_REGDUMP_REC(AM65_CPSW_REGDUMP_MOD_RGMII_STATUS, 0x30, 0x4c), |
73 | AM65_CPSW_REGDUMP_REC(AM65_CPSW_REGDUMP_MOD_MDIO, 0xf00, 0xffc), |
74 | AM65_CPSW_REGDUMP_REC(AM65_CPSW_REGDUMP_MOD_CPSW, 0x20000, 0x2011c), |
75 | AM65_CPSW_REGDUMP_REC(AM65_CPSW_REGDUMP_MOD_CPSW_P0, 0x21000, 0x21320), |
76 | AM65_CPSW_REGDUMP_REC(AM65_CPSW_REGDUMP_MOD_CPSW_P1, 0x22000, 0x223a4), |
77 | AM65_CPSW_REGDUMP_REC(AM65_CPSW_REGDUMP_MOD_CPSW_CPTS, |
78 | 0x3d000, 0x3d048), |
79 | AM65_CPSW_REGDUMP_REC(AM65_CPSW_REGDUMP_MOD_CPSW_ALE, 0x3e000, 0x3e13c), |
80 | AM65_CPSW_REGDUMP_REC(AM65_CPSW_REGDUMP_MOD_CPSW_ALE_TBL, 0, 0), |
81 | }; |
82 | |
83 | struct am65_cpsw_stats_regs { |
84 | u32 rx_good_frames; |
85 | u32 rx_broadcast_frames; |
86 | u32 rx_multicast_frames; |
87 | u32 rx_pause_frames; /* slave */ |
88 | u32 rx_crc_errors; |
89 | u32 rx_align_code_errors; /* slave */ |
90 | u32 rx_oversized_frames; |
91 | u32 rx_jabber_frames; /* slave */ |
92 | u32 rx_undersized_frames; |
93 | u32 rx_fragments; /* slave */ |
94 | u32 ale_drop; |
95 | u32 ale_overrun_drop; |
96 | u32 rx_octets; |
97 | u32 tx_good_frames; |
98 | u32 tx_broadcast_frames; |
99 | u32 tx_multicast_frames; |
100 | u32 tx_pause_frames; /* slave */ |
101 | u32 tx_deferred_frames; /* slave */ |
102 | u32 tx_collision_frames; /* slave */ |
103 | u32 tx_single_coll_frames; /* slave */ |
104 | u32 tx_mult_coll_frames; /* slave */ |
105 | u32 tx_excessive_collisions; /* slave */ |
106 | u32 tx_late_collisions; /* slave */ |
107 | u32 rx_ipg_error; /* slave 10G only */ |
108 | u32 tx_carrier_sense_errors; /* slave */ |
109 | u32 tx_octets; |
110 | u32 tx_64B_frames; |
111 | u32 tx_65_to_127B_frames; |
112 | u32 tx_128_to_255B_frames; |
113 | u32 tx_256_to_511B_frames; |
114 | u32 tx_512_to_1023B_frames; |
115 | u32 tx_1024B_frames; |
116 | u32 net_octets; |
117 | u32 rx_bottom_fifo_drop; |
118 | u32 rx_port_mask_drop; |
119 | u32 rx_top_fifo_drop; |
120 | u32 ale_rate_limit_drop; |
121 | u32 ale_vid_ingress_drop; |
122 | u32 ale_da_eq_sa_drop; |
123 | u32 ale_block_drop; /* K3 */ |
124 | u32 ale_secure_drop; /* K3 */ |
125 | u32 ale_auth_drop; /* K3 */ |
126 | u32 ale_unknown_ucast; |
127 | u32 ale_unknown_ucast_bytes; |
128 | u32 ale_unknown_mcast; |
129 | u32 ale_unknown_mcast_bytes; |
130 | u32 ale_unknown_bcast; |
131 | u32 ale_unknown_bcast_bytes; |
132 | u32 ale_pol_match; |
133 | u32 ale_pol_match_red; |
134 | u32 ale_pol_match_yellow; |
135 | u32 ale_mcast_sa_drop; /* K3 */ |
136 | u32 ale_dual_vlan_drop; /* K3 */ |
137 | u32 ale_len_err_drop; /* K3 */ |
138 | u32 ale_ip_next_hdr_drop; /* K3 */ |
139 | u32 ale_ipv4_frag_drop; /* K3 */ |
140 | u32 __rsvd_1[24]; |
141 | u32 iet_rx_assembly_err; /* K3 slave */ |
142 | u32 iet_rx_assembly_ok; /* K3 slave */ |
143 | u32 iet_rx_smd_err; /* K3 slave */ |
144 | u32 iet_rx_frag; /* K3 slave */ |
145 | u32 iet_tx_hold; /* K3 slave */ |
146 | u32 iet_tx_frag; /* K3 slave */ |
147 | u32 __rsvd_2[9]; |
148 | u32 tx_mem_protect_err; |
149 | /* following NU only */ |
150 | u32 tx_pri0; |
151 | u32 tx_pri1; |
152 | u32 tx_pri2; |
153 | u32 tx_pri3; |
154 | u32 tx_pri4; |
155 | u32 tx_pri5; |
156 | u32 tx_pri6; |
157 | u32 tx_pri7; |
158 | u32 tx_pri0_bcnt; |
159 | u32 tx_pri1_bcnt; |
160 | u32 tx_pri2_bcnt; |
161 | u32 tx_pri3_bcnt; |
162 | u32 tx_pri4_bcnt; |
163 | u32 tx_pri5_bcnt; |
164 | u32 tx_pri6_bcnt; |
165 | u32 tx_pri7_bcnt; |
166 | u32 tx_pri0_drop; |
167 | u32 tx_pri1_drop; |
168 | u32 tx_pri2_drop; |
169 | u32 tx_pri3_drop; |
170 | u32 tx_pri4_drop; |
171 | u32 tx_pri5_drop; |
172 | u32 tx_pri6_drop; |
173 | u32 tx_pri7_drop; |
174 | u32 tx_pri0_drop_bcnt; |
175 | u32 tx_pri1_drop_bcnt; |
176 | u32 tx_pri2_drop_bcnt; |
177 | u32 tx_pri3_drop_bcnt; |
178 | u32 tx_pri4_drop_bcnt; |
179 | u32 tx_pri5_drop_bcnt; |
180 | u32 tx_pri6_drop_bcnt; |
181 | u32 tx_pri7_drop_bcnt; |
182 | }; |
183 | |
184 | struct am65_cpsw_ethtool_stat { |
185 | char desc[ETH_GSTRING_LEN]; |
186 | int offset; |
187 | }; |
188 | |
189 | #define AM65_CPSW_STATS(prefix, field) \ |
190 | { \ |
191 | #prefix#field, \ |
192 | offsetof(struct am65_cpsw_stats_regs, field) \ |
193 | } |
194 | |
195 | static const struct am65_cpsw_ethtool_stat am65_host_stats[] = { |
196 | AM65_CPSW_STATS(p0_, rx_good_frames), |
197 | AM65_CPSW_STATS(p0_, rx_broadcast_frames), |
198 | AM65_CPSW_STATS(p0_, rx_multicast_frames), |
199 | AM65_CPSW_STATS(p0_, rx_crc_errors), |
200 | AM65_CPSW_STATS(p0_, rx_oversized_frames), |
201 | AM65_CPSW_STATS(p0_, rx_undersized_frames), |
202 | AM65_CPSW_STATS(p0_, ale_drop), |
203 | AM65_CPSW_STATS(p0_, ale_overrun_drop), |
204 | AM65_CPSW_STATS(p0_, rx_octets), |
205 | AM65_CPSW_STATS(p0_, tx_good_frames), |
206 | AM65_CPSW_STATS(p0_, tx_broadcast_frames), |
207 | AM65_CPSW_STATS(p0_, tx_multicast_frames), |
208 | AM65_CPSW_STATS(p0_, tx_octets), |
209 | AM65_CPSW_STATS(p0_, tx_64B_frames), |
210 | AM65_CPSW_STATS(p0_, tx_65_to_127B_frames), |
211 | AM65_CPSW_STATS(p0_, tx_128_to_255B_frames), |
212 | AM65_CPSW_STATS(p0_, tx_256_to_511B_frames), |
213 | AM65_CPSW_STATS(p0_, tx_512_to_1023B_frames), |
214 | AM65_CPSW_STATS(p0_, tx_1024B_frames), |
215 | AM65_CPSW_STATS(p0_, net_octets), |
216 | AM65_CPSW_STATS(p0_, rx_bottom_fifo_drop), |
217 | AM65_CPSW_STATS(p0_, rx_port_mask_drop), |
218 | AM65_CPSW_STATS(p0_, rx_top_fifo_drop), |
219 | AM65_CPSW_STATS(p0_, ale_rate_limit_drop), |
220 | AM65_CPSW_STATS(p0_, ale_vid_ingress_drop), |
221 | AM65_CPSW_STATS(p0_, ale_da_eq_sa_drop), |
222 | AM65_CPSW_STATS(p0_, ale_block_drop), |
223 | AM65_CPSW_STATS(p0_, ale_secure_drop), |
224 | AM65_CPSW_STATS(p0_, ale_auth_drop), |
225 | AM65_CPSW_STATS(p0_, ale_unknown_ucast), |
226 | AM65_CPSW_STATS(p0_, ale_unknown_ucast_bytes), |
227 | AM65_CPSW_STATS(p0_, ale_unknown_mcast), |
228 | AM65_CPSW_STATS(p0_, ale_unknown_mcast_bytes), |
229 | AM65_CPSW_STATS(p0_, ale_unknown_bcast), |
230 | AM65_CPSW_STATS(p0_, ale_unknown_bcast_bytes), |
231 | AM65_CPSW_STATS(p0_, ale_pol_match), |
232 | AM65_CPSW_STATS(p0_, ale_pol_match_red), |
233 | AM65_CPSW_STATS(p0_, ale_pol_match_yellow), |
234 | AM65_CPSW_STATS(p0_, ale_mcast_sa_drop), |
235 | AM65_CPSW_STATS(p0_, ale_dual_vlan_drop), |
236 | AM65_CPSW_STATS(p0_, ale_len_err_drop), |
237 | AM65_CPSW_STATS(p0_, ale_ip_next_hdr_drop), |
238 | AM65_CPSW_STATS(p0_, ale_ipv4_frag_drop), |
239 | AM65_CPSW_STATS(p0_, tx_mem_protect_err), |
240 | AM65_CPSW_STATS(p0_, tx_pri0), |
241 | AM65_CPSW_STATS(p0_, tx_pri1), |
242 | AM65_CPSW_STATS(p0_, tx_pri2), |
243 | AM65_CPSW_STATS(p0_, tx_pri3), |
244 | AM65_CPSW_STATS(p0_, tx_pri4), |
245 | AM65_CPSW_STATS(p0_, tx_pri5), |
246 | AM65_CPSW_STATS(p0_, tx_pri6), |
247 | AM65_CPSW_STATS(p0_, tx_pri7), |
248 | AM65_CPSW_STATS(p0_, tx_pri0_bcnt), |
249 | AM65_CPSW_STATS(p0_, tx_pri1_bcnt), |
250 | AM65_CPSW_STATS(p0_, tx_pri2_bcnt), |
251 | AM65_CPSW_STATS(p0_, tx_pri3_bcnt), |
252 | AM65_CPSW_STATS(p0_, tx_pri4_bcnt), |
253 | AM65_CPSW_STATS(p0_, tx_pri5_bcnt), |
254 | AM65_CPSW_STATS(p0_, tx_pri6_bcnt), |
255 | AM65_CPSW_STATS(p0_, tx_pri7_bcnt), |
256 | AM65_CPSW_STATS(p0_, tx_pri0_drop), |
257 | AM65_CPSW_STATS(p0_, tx_pri1_drop), |
258 | AM65_CPSW_STATS(p0_, tx_pri2_drop), |
259 | AM65_CPSW_STATS(p0_, tx_pri3_drop), |
260 | AM65_CPSW_STATS(p0_, tx_pri4_drop), |
261 | AM65_CPSW_STATS(p0_, tx_pri5_drop), |
262 | AM65_CPSW_STATS(p0_, tx_pri6_drop), |
263 | AM65_CPSW_STATS(p0_, tx_pri7_drop), |
264 | AM65_CPSW_STATS(p0_, tx_pri0_drop_bcnt), |
265 | AM65_CPSW_STATS(p0_, tx_pri1_drop_bcnt), |
266 | AM65_CPSW_STATS(p0_, tx_pri2_drop_bcnt), |
267 | AM65_CPSW_STATS(p0_, tx_pri3_drop_bcnt), |
268 | AM65_CPSW_STATS(p0_, tx_pri4_drop_bcnt), |
269 | AM65_CPSW_STATS(p0_, tx_pri5_drop_bcnt), |
270 | AM65_CPSW_STATS(p0_, tx_pri6_drop_bcnt), |
271 | AM65_CPSW_STATS(p0_, tx_pri7_drop_bcnt), |
272 | }; |
273 | |
274 | static const struct am65_cpsw_ethtool_stat am65_slave_stats[] = { |
275 | AM65_CPSW_STATS(, rx_good_frames), |
276 | AM65_CPSW_STATS(, rx_broadcast_frames), |
277 | AM65_CPSW_STATS(, rx_multicast_frames), |
278 | AM65_CPSW_STATS(, rx_pause_frames), |
279 | AM65_CPSW_STATS(, rx_crc_errors), |
280 | AM65_CPSW_STATS(, rx_align_code_errors), |
281 | AM65_CPSW_STATS(, rx_oversized_frames), |
282 | AM65_CPSW_STATS(, rx_jabber_frames), |
283 | AM65_CPSW_STATS(, rx_undersized_frames), |
284 | AM65_CPSW_STATS(, rx_fragments), |
285 | AM65_CPSW_STATS(, ale_drop), |
286 | AM65_CPSW_STATS(, ale_overrun_drop), |
287 | AM65_CPSW_STATS(, rx_octets), |
288 | AM65_CPSW_STATS(, tx_good_frames), |
289 | AM65_CPSW_STATS(, tx_broadcast_frames), |
290 | AM65_CPSW_STATS(, tx_multicast_frames), |
291 | AM65_CPSW_STATS(, tx_pause_frames), |
292 | AM65_CPSW_STATS(, tx_deferred_frames), |
293 | AM65_CPSW_STATS(, tx_collision_frames), |
294 | AM65_CPSW_STATS(, tx_single_coll_frames), |
295 | AM65_CPSW_STATS(, tx_mult_coll_frames), |
296 | AM65_CPSW_STATS(, tx_excessive_collisions), |
297 | AM65_CPSW_STATS(, tx_late_collisions), |
298 | AM65_CPSW_STATS(, rx_ipg_error), |
299 | AM65_CPSW_STATS(, tx_carrier_sense_errors), |
300 | AM65_CPSW_STATS(, tx_octets), |
301 | AM65_CPSW_STATS(, tx_64B_frames), |
302 | AM65_CPSW_STATS(, tx_65_to_127B_frames), |
303 | AM65_CPSW_STATS(, tx_128_to_255B_frames), |
304 | AM65_CPSW_STATS(, tx_256_to_511B_frames), |
305 | AM65_CPSW_STATS(, tx_512_to_1023B_frames), |
306 | AM65_CPSW_STATS(, tx_1024B_frames), |
307 | AM65_CPSW_STATS(, net_octets), |
308 | AM65_CPSW_STATS(, rx_bottom_fifo_drop), |
309 | AM65_CPSW_STATS(, rx_port_mask_drop), |
310 | AM65_CPSW_STATS(, rx_top_fifo_drop), |
311 | AM65_CPSW_STATS(, ale_rate_limit_drop), |
312 | AM65_CPSW_STATS(, ale_vid_ingress_drop), |
313 | AM65_CPSW_STATS(, ale_da_eq_sa_drop), |
314 | AM65_CPSW_STATS(, ale_block_drop), |
315 | AM65_CPSW_STATS(, ale_secure_drop), |
316 | AM65_CPSW_STATS(, ale_auth_drop), |
317 | AM65_CPSW_STATS(, ale_unknown_ucast), |
318 | AM65_CPSW_STATS(, ale_unknown_ucast_bytes), |
319 | AM65_CPSW_STATS(, ale_unknown_mcast), |
320 | AM65_CPSW_STATS(, ale_unknown_mcast_bytes), |
321 | AM65_CPSW_STATS(, ale_unknown_bcast), |
322 | AM65_CPSW_STATS(, ale_unknown_bcast_bytes), |
323 | AM65_CPSW_STATS(, ale_pol_match), |
324 | AM65_CPSW_STATS(, ale_pol_match_red), |
325 | AM65_CPSW_STATS(, ale_pol_match_yellow), |
326 | AM65_CPSW_STATS(, ale_mcast_sa_drop), |
327 | AM65_CPSW_STATS(, ale_dual_vlan_drop), |
328 | AM65_CPSW_STATS(, ale_len_err_drop), |
329 | AM65_CPSW_STATS(, ale_ip_next_hdr_drop), |
330 | AM65_CPSW_STATS(, ale_ipv4_frag_drop), |
331 | AM65_CPSW_STATS(, iet_rx_assembly_err), |
332 | AM65_CPSW_STATS(, iet_rx_assembly_ok), |
333 | AM65_CPSW_STATS(, iet_rx_smd_err), |
334 | AM65_CPSW_STATS(, iet_rx_frag), |
335 | AM65_CPSW_STATS(, iet_tx_hold), |
336 | AM65_CPSW_STATS(, iet_tx_frag), |
337 | AM65_CPSW_STATS(, tx_mem_protect_err), |
338 | AM65_CPSW_STATS(, tx_pri0), |
339 | AM65_CPSW_STATS(, tx_pri1), |
340 | AM65_CPSW_STATS(, tx_pri2), |
341 | AM65_CPSW_STATS(, tx_pri3), |
342 | AM65_CPSW_STATS(, tx_pri4), |
343 | AM65_CPSW_STATS(, tx_pri5), |
344 | AM65_CPSW_STATS(, tx_pri6), |
345 | AM65_CPSW_STATS(, tx_pri7), |
346 | AM65_CPSW_STATS(, tx_pri0_bcnt), |
347 | AM65_CPSW_STATS(, tx_pri1_bcnt), |
348 | AM65_CPSW_STATS(, tx_pri2_bcnt), |
349 | AM65_CPSW_STATS(, tx_pri3_bcnt), |
350 | AM65_CPSW_STATS(, tx_pri4_bcnt), |
351 | AM65_CPSW_STATS(, tx_pri5_bcnt), |
352 | AM65_CPSW_STATS(, tx_pri6_bcnt), |
353 | AM65_CPSW_STATS(, tx_pri7_bcnt), |
354 | AM65_CPSW_STATS(, tx_pri0_drop), |
355 | AM65_CPSW_STATS(, tx_pri1_drop), |
356 | AM65_CPSW_STATS(, tx_pri2_drop), |
357 | AM65_CPSW_STATS(, tx_pri3_drop), |
358 | AM65_CPSW_STATS(, tx_pri4_drop), |
359 | AM65_CPSW_STATS(, tx_pri5_drop), |
360 | AM65_CPSW_STATS(, tx_pri6_drop), |
361 | AM65_CPSW_STATS(, tx_pri7_drop), |
362 | AM65_CPSW_STATS(, tx_pri0_drop_bcnt), |
363 | AM65_CPSW_STATS(, tx_pri1_drop_bcnt), |
364 | AM65_CPSW_STATS(, tx_pri2_drop_bcnt), |
365 | AM65_CPSW_STATS(, tx_pri3_drop_bcnt), |
366 | AM65_CPSW_STATS(, tx_pri4_drop_bcnt), |
367 | AM65_CPSW_STATS(, tx_pri5_drop_bcnt), |
368 | AM65_CPSW_STATS(, tx_pri6_drop_bcnt), |
369 | AM65_CPSW_STATS(, tx_pri7_drop_bcnt), |
370 | }; |
371 | |
372 | /* Ethtool priv_flags */ |
373 | static const char am65_cpsw_ethtool_priv_flags[][ETH_GSTRING_LEN] = { |
374 | #define AM65_CPSW_PRIV_P0_RX_PTYPE_RROBIN BIT(0) |
375 | "p0-rx-ptype-rrobin" , |
376 | }; |
377 | |
378 | static int am65_cpsw_ethtool_op_begin(struct net_device *ndev) |
379 | { |
380 | struct am65_cpsw_common *common = am65_ndev_to_common(ndev); |
381 | int ret; |
382 | |
383 | ret = pm_runtime_resume_and_get(dev: common->dev); |
384 | if (ret < 0) |
385 | dev_err(common->dev, "ethtool begin failed %d\n" , ret); |
386 | |
387 | return ret; |
388 | } |
389 | |
390 | static void am65_cpsw_ethtool_op_complete(struct net_device *ndev) |
391 | { |
392 | struct am65_cpsw_common *common = am65_ndev_to_common(ndev); |
393 | int ret; |
394 | |
395 | ret = pm_runtime_put(dev: common->dev); |
396 | if (ret < 0 && ret != -EBUSY) |
397 | dev_err(common->dev, "ethtool complete failed %d\n" , ret); |
398 | } |
399 | |
400 | static void am65_cpsw_get_drvinfo(struct net_device *ndev, |
401 | struct ethtool_drvinfo *info) |
402 | { |
403 | struct am65_cpsw_common *common = am65_ndev_to_common(ndev); |
404 | |
405 | strscpy(p: info->driver, q: dev_driver_string(dev: common->dev), |
406 | size: sizeof(info->driver)); |
407 | strscpy(p: info->bus_info, q: dev_name(dev: common->dev), size: sizeof(info->bus_info)); |
408 | } |
409 | |
410 | static u32 am65_cpsw_get_msglevel(struct net_device *ndev) |
411 | { |
412 | struct am65_cpsw_ndev_priv *priv = am65_ndev_to_priv(ndev); |
413 | |
414 | return priv->msg_enable; |
415 | } |
416 | |
417 | static void am65_cpsw_set_msglevel(struct net_device *ndev, u32 value) |
418 | { |
419 | struct am65_cpsw_ndev_priv *priv = am65_ndev_to_priv(ndev); |
420 | |
421 | priv->msg_enable = value; |
422 | } |
423 | |
424 | static void am65_cpsw_get_channels(struct net_device *ndev, |
425 | struct ethtool_channels *ch) |
426 | { |
427 | struct am65_cpsw_common *common = am65_ndev_to_common(ndev); |
428 | |
429 | ch->max_rx = AM65_CPSW_MAX_RX_QUEUES; |
430 | ch->max_tx = AM65_CPSW_MAX_TX_QUEUES; |
431 | ch->rx_count = AM65_CPSW_MAX_RX_QUEUES; |
432 | ch->tx_count = common->tx_ch_num; |
433 | } |
434 | |
435 | static int am65_cpsw_set_channels(struct net_device *ndev, |
436 | struct ethtool_channels *chs) |
437 | { |
438 | struct am65_cpsw_common *common = am65_ndev_to_common(ndev); |
439 | |
440 | if (!chs->rx_count || !chs->tx_count) |
441 | return -EINVAL; |
442 | |
443 | /* Check if interface is up. Can change the num queues when |
444 | * the interface is down. |
445 | */ |
446 | if (common->usage_count) |
447 | return -EBUSY; |
448 | |
449 | am65_cpsw_nuss_remove_tx_chns(common); |
450 | |
451 | return am65_cpsw_nuss_update_tx_chns(common, num_tx: chs->tx_count); |
452 | } |
453 | |
454 | static void |
455 | am65_cpsw_get_ringparam(struct net_device *ndev, |
456 | struct ethtool_ringparam *ering, |
457 | struct kernel_ethtool_ringparam *kernel_ering, |
458 | struct netlink_ext_ack *extack) |
459 | { |
460 | struct am65_cpsw_common *common = am65_ndev_to_common(ndev); |
461 | |
462 | /* not supported */ |
463 | ering->tx_pending = common->tx_chns[0].descs_num; |
464 | ering->rx_pending = common->rx_chns.descs_num; |
465 | } |
466 | |
467 | static void am65_cpsw_get_pauseparam(struct net_device *ndev, |
468 | struct ethtool_pauseparam *pause) |
469 | { |
470 | struct am65_cpsw_slave_data *salve = am65_ndev_to_slave(ndev); |
471 | |
472 | phylink_ethtool_get_pauseparam(salve->phylink, pause); |
473 | } |
474 | |
475 | static int am65_cpsw_set_pauseparam(struct net_device *ndev, |
476 | struct ethtool_pauseparam *pause) |
477 | { |
478 | struct am65_cpsw_slave_data *salve = am65_ndev_to_slave(ndev); |
479 | |
480 | return phylink_ethtool_set_pauseparam(salve->phylink, pause); |
481 | } |
482 | |
483 | static void am65_cpsw_get_wol(struct net_device *ndev, |
484 | struct ethtool_wolinfo *wol) |
485 | { |
486 | struct am65_cpsw_slave_data *salve = am65_ndev_to_slave(ndev); |
487 | |
488 | phylink_ethtool_get_wol(salve->phylink, wol); |
489 | } |
490 | |
491 | static int am65_cpsw_set_wol(struct net_device *ndev, |
492 | struct ethtool_wolinfo *wol) |
493 | { |
494 | struct am65_cpsw_slave_data *salve = am65_ndev_to_slave(ndev); |
495 | |
496 | return phylink_ethtool_set_wol(salve->phylink, wol); |
497 | } |
498 | |
499 | static int am65_cpsw_get_link_ksettings(struct net_device *ndev, |
500 | struct ethtool_link_ksettings *ecmd) |
501 | { |
502 | struct am65_cpsw_slave_data *salve = am65_ndev_to_slave(ndev); |
503 | |
504 | return phylink_ethtool_ksettings_get(salve->phylink, ecmd); |
505 | } |
506 | |
507 | static int |
508 | am65_cpsw_set_link_ksettings(struct net_device *ndev, |
509 | const struct ethtool_link_ksettings *ecmd) |
510 | { |
511 | struct am65_cpsw_slave_data *salve = am65_ndev_to_slave(ndev); |
512 | |
513 | return phylink_ethtool_ksettings_set(salve->phylink, ecmd); |
514 | } |
515 | |
516 | static int am65_cpsw_get_eee(struct net_device *ndev, struct ethtool_eee *edata) |
517 | { |
518 | struct am65_cpsw_slave_data *salve = am65_ndev_to_slave(ndev); |
519 | |
520 | return phylink_ethtool_get_eee(salve->phylink, edata); |
521 | } |
522 | |
523 | static int am65_cpsw_set_eee(struct net_device *ndev, struct ethtool_eee *edata) |
524 | { |
525 | struct am65_cpsw_slave_data *salve = am65_ndev_to_slave(ndev); |
526 | |
527 | return phylink_ethtool_set_eee(salve->phylink, edata); |
528 | } |
529 | |
530 | static int am65_cpsw_nway_reset(struct net_device *ndev) |
531 | { |
532 | struct am65_cpsw_slave_data *salve = am65_ndev_to_slave(ndev); |
533 | |
534 | return phylink_ethtool_nway_reset(salve->phylink); |
535 | } |
536 | |
537 | static int am65_cpsw_get_regs_len(struct net_device *ndev) |
538 | { |
539 | struct am65_cpsw_common *common = am65_ndev_to_common(ndev); |
540 | u32 ale_entries, i, regdump_len = 0; |
541 | |
542 | ale_entries = cpsw_ale_get_num_entries(ale: common->ale); |
543 | for (i = 0; i < ARRAY_SIZE(am65_cpsw_regdump); i++) { |
544 | if (am65_cpsw_regdump[i].hdr.module_id == |
545 | AM65_CPSW_REGDUMP_MOD_CPSW_ALE_TBL) { |
546 | regdump_len += sizeof(struct am65_cpsw_regdump_hdr); |
547 | regdump_len += ale_entries * |
548 | ALE_ENTRY_WORDS * sizeof(u32); |
549 | continue; |
550 | } |
551 | regdump_len += am65_cpsw_regdump[i].hdr.len; |
552 | } |
553 | |
554 | return regdump_len; |
555 | } |
556 | |
557 | static void am65_cpsw_get_regs(struct net_device *ndev, |
558 | struct ethtool_regs *regs, void *p) |
559 | { |
560 | struct am65_cpsw_common *common = am65_ndev_to_common(ndev); |
561 | u32 ale_entries, i, j, pos, *reg = p; |
562 | |
563 | /* update CPSW IP version */ |
564 | regs->version = AM65_CPSW_REGDUMP_VER; |
565 | ale_entries = cpsw_ale_get_num_entries(ale: common->ale); |
566 | |
567 | pos = 0; |
568 | for (i = 0; i < ARRAY_SIZE(am65_cpsw_regdump); i++) { |
569 | reg[pos++] = am65_cpsw_regdump[i].hdr.module_id; |
570 | |
571 | if (am65_cpsw_regdump[i].hdr.module_id == |
572 | AM65_CPSW_REGDUMP_MOD_CPSW_ALE_TBL) { |
573 | u32 ale_tbl_len = ale_entries * |
574 | ALE_ENTRY_WORDS * sizeof(u32) + |
575 | sizeof(struct am65_cpsw_regdump_hdr); |
576 | reg[pos++] = ale_tbl_len; |
577 | cpsw_ale_dump(ale: common->ale, data: ®[pos]); |
578 | pos += ale_tbl_len; |
579 | continue; |
580 | } |
581 | |
582 | reg[pos++] = am65_cpsw_regdump[i].hdr.len; |
583 | |
584 | j = am65_cpsw_regdump[i].start_ofs; |
585 | do { |
586 | reg[pos++] = j; |
587 | reg[pos++] = readl_relaxed(common->ss_base + j); |
588 | j += sizeof(u32); |
589 | } while (j <= am65_cpsw_regdump[i].end_ofs); |
590 | } |
591 | } |
592 | |
593 | static int am65_cpsw_get_sset_count(struct net_device *ndev, int sset) |
594 | { |
595 | switch (sset) { |
596 | case ETH_SS_STATS: |
597 | return ARRAY_SIZE(am65_host_stats) + |
598 | ARRAY_SIZE(am65_slave_stats); |
599 | case ETH_SS_PRIV_FLAGS: |
600 | return ARRAY_SIZE(am65_cpsw_ethtool_priv_flags); |
601 | default: |
602 | return -EOPNOTSUPP; |
603 | } |
604 | } |
605 | |
606 | static void am65_cpsw_get_strings(struct net_device *ndev, |
607 | u32 stringset, u8 *data) |
608 | { |
609 | const struct am65_cpsw_ethtool_stat *hw_stats; |
610 | u32 i, num_stats; |
611 | u8 *p = data; |
612 | |
613 | switch (stringset) { |
614 | case ETH_SS_STATS: |
615 | num_stats = ARRAY_SIZE(am65_host_stats); |
616 | hw_stats = am65_host_stats; |
617 | for (i = 0; i < num_stats; i++) { |
618 | memcpy(p, hw_stats[i].desc, ETH_GSTRING_LEN); |
619 | p += ETH_GSTRING_LEN; |
620 | } |
621 | |
622 | num_stats = ARRAY_SIZE(am65_slave_stats); |
623 | hw_stats = am65_slave_stats; |
624 | for (i = 0; i < num_stats; i++) { |
625 | memcpy(p, hw_stats[i].desc, ETH_GSTRING_LEN); |
626 | p += ETH_GSTRING_LEN; |
627 | } |
628 | break; |
629 | case ETH_SS_PRIV_FLAGS: |
630 | num_stats = ARRAY_SIZE(am65_cpsw_ethtool_priv_flags); |
631 | |
632 | for (i = 0; i < num_stats; i++) { |
633 | memcpy(p, am65_cpsw_ethtool_priv_flags[i], |
634 | ETH_GSTRING_LEN); |
635 | p += ETH_GSTRING_LEN; |
636 | } |
637 | break; |
638 | } |
639 | } |
640 | |
641 | static void am65_cpsw_get_ethtool_stats(struct net_device *ndev, |
642 | struct ethtool_stats *stats, u64 *data) |
643 | { |
644 | struct am65_cpsw_common *common = am65_ndev_to_common(ndev); |
645 | const struct am65_cpsw_ethtool_stat *hw_stats; |
646 | struct am65_cpsw_host *host_p; |
647 | struct am65_cpsw_port *port; |
648 | u32 i, num_stats; |
649 | |
650 | host_p = am65_common_get_host(common); |
651 | port = am65_ndev_to_port(ndev); |
652 | num_stats = ARRAY_SIZE(am65_host_stats); |
653 | hw_stats = am65_host_stats; |
654 | for (i = 0; i < num_stats; i++) |
655 | *data++ = readl_relaxed(host_p->stat_base + |
656 | hw_stats[i].offset); |
657 | |
658 | num_stats = ARRAY_SIZE(am65_slave_stats); |
659 | hw_stats = am65_slave_stats; |
660 | for (i = 0; i < num_stats; i++) |
661 | *data++ = readl_relaxed(port->stat_base + |
662 | hw_stats[i].offset); |
663 | } |
664 | |
665 | static int am65_cpsw_get_ethtool_ts_info(struct net_device *ndev, |
666 | struct ethtool_ts_info *info) |
667 | { |
668 | struct am65_cpsw_common *common = am65_ndev_to_common(ndev); |
669 | |
670 | if (!IS_ENABLED(CONFIG_TI_K3_AM65_CPTS)) |
671 | return ethtool_op_get_ts_info(dev: ndev, eti: info); |
672 | |
673 | info->so_timestamping = |
674 | SOF_TIMESTAMPING_TX_HARDWARE | |
675 | SOF_TIMESTAMPING_TX_SOFTWARE | |
676 | SOF_TIMESTAMPING_RX_HARDWARE | |
677 | SOF_TIMESTAMPING_RX_SOFTWARE | |
678 | SOF_TIMESTAMPING_SOFTWARE | |
679 | SOF_TIMESTAMPING_RAW_HARDWARE; |
680 | info->phc_index = am65_cpts_phc_index(cpts: common->cpts); |
681 | info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON); |
682 | info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | BIT(HWTSTAMP_FILTER_ALL); |
683 | return 0; |
684 | } |
685 | |
686 | static u32 am65_cpsw_get_ethtool_priv_flags(struct net_device *ndev) |
687 | { |
688 | struct am65_cpsw_common *common = am65_ndev_to_common(ndev); |
689 | u32 priv_flags = 0; |
690 | |
691 | if (common->pf_p0_rx_ptype_rrobin) |
692 | priv_flags |= AM65_CPSW_PRIV_P0_RX_PTYPE_RROBIN; |
693 | |
694 | return priv_flags; |
695 | } |
696 | |
697 | static int am65_cpsw_set_ethtool_priv_flags(struct net_device *ndev, u32 flags) |
698 | { |
699 | struct am65_cpsw_common *common = am65_ndev_to_common(ndev); |
700 | int rrobin; |
701 | |
702 | rrobin = !!(flags & AM65_CPSW_PRIV_P0_RX_PTYPE_RROBIN); |
703 | |
704 | if (common->usage_count) |
705 | return -EBUSY; |
706 | |
707 | if (common->est_enabled && rrobin) { |
708 | netdev_err(dev: ndev, |
709 | format: "p0-rx-ptype-rrobin flag conflicts with QOS\n" ); |
710 | return -EINVAL; |
711 | } |
712 | |
713 | common->pf_p0_rx_ptype_rrobin = rrobin; |
714 | |
715 | return 0; |
716 | } |
717 | |
718 | const struct ethtool_ops am65_cpsw_ethtool_ops_slave = { |
719 | .begin = am65_cpsw_ethtool_op_begin, |
720 | .complete = am65_cpsw_ethtool_op_complete, |
721 | .get_drvinfo = am65_cpsw_get_drvinfo, |
722 | .get_msglevel = am65_cpsw_get_msglevel, |
723 | .set_msglevel = am65_cpsw_set_msglevel, |
724 | .get_channels = am65_cpsw_get_channels, |
725 | .set_channels = am65_cpsw_set_channels, |
726 | .get_ringparam = am65_cpsw_get_ringparam, |
727 | .get_regs_len = am65_cpsw_get_regs_len, |
728 | .get_regs = am65_cpsw_get_regs, |
729 | .get_sset_count = am65_cpsw_get_sset_count, |
730 | .get_strings = am65_cpsw_get_strings, |
731 | .get_ethtool_stats = am65_cpsw_get_ethtool_stats, |
732 | .get_ts_info = am65_cpsw_get_ethtool_ts_info, |
733 | .get_priv_flags = am65_cpsw_get_ethtool_priv_flags, |
734 | .set_priv_flags = am65_cpsw_set_ethtool_priv_flags, |
735 | |
736 | .get_link = ethtool_op_get_link, |
737 | .get_link_ksettings = am65_cpsw_get_link_ksettings, |
738 | .set_link_ksettings = am65_cpsw_set_link_ksettings, |
739 | .get_pauseparam = am65_cpsw_get_pauseparam, |
740 | .set_pauseparam = am65_cpsw_set_pauseparam, |
741 | .get_wol = am65_cpsw_get_wol, |
742 | .set_wol = am65_cpsw_set_wol, |
743 | .get_eee = am65_cpsw_get_eee, |
744 | .set_eee = am65_cpsw_set_eee, |
745 | .nway_reset = am65_cpsw_nway_reset, |
746 | }; |
747 | |