1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Marvell 88E6xxx VLAN [Spanning Tree] Translation Unit (VTU [STU]) support |
4 | * |
5 | * Copyright (c) 2008 Marvell Semiconductor |
6 | * Copyright (c) 2015 CMC Electronics, Inc. |
7 | * Copyright (c) 2017 Savoir-faire Linux, Inc. |
8 | */ |
9 | |
10 | #include <linux/bitfield.h> |
11 | #include <linux/interrupt.h> |
12 | #include <linux/irqdomain.h> |
13 | |
14 | #include "chip.h" |
15 | #include "global1.h" |
16 | #include "trace.h" |
17 | |
18 | /* Offset 0x02: VTU FID Register */ |
19 | |
20 | static int mv88e6xxx_g1_vtu_fid_read(struct mv88e6xxx_chip *chip, |
21 | struct mv88e6xxx_vtu_entry *entry) |
22 | { |
23 | u16 val; |
24 | int err; |
25 | |
26 | err = mv88e6xxx_g1_read(chip, MV88E6352_G1_VTU_FID, val: &val); |
27 | if (err) |
28 | return err; |
29 | |
30 | entry->fid = val & MV88E6352_G1_VTU_FID_MASK; |
31 | entry->policy = !!(val & MV88E6352_G1_VTU_FID_VID_POLICY); |
32 | return 0; |
33 | } |
34 | |
35 | static int mv88e6xxx_g1_vtu_fid_write(struct mv88e6xxx_chip *chip, |
36 | struct mv88e6xxx_vtu_entry *entry) |
37 | { |
38 | u16 val = entry->fid & MV88E6352_G1_VTU_FID_MASK; |
39 | |
40 | if (entry->policy) |
41 | val |= MV88E6352_G1_VTU_FID_VID_POLICY; |
42 | |
43 | return mv88e6xxx_g1_write(chip, MV88E6352_G1_VTU_FID, val); |
44 | } |
45 | |
46 | /* Offset 0x03: VTU SID Register */ |
47 | |
48 | static int mv88e6xxx_g1_vtu_sid_read(struct mv88e6xxx_chip *chip, u8 *sid) |
49 | { |
50 | u16 val; |
51 | int err; |
52 | |
53 | err = mv88e6xxx_g1_read(chip, MV88E6352_G1_VTU_SID, val: &val); |
54 | if (err) |
55 | return err; |
56 | |
57 | *sid = val & MV88E6352_G1_VTU_SID_MASK; |
58 | |
59 | return 0; |
60 | } |
61 | |
62 | static int mv88e6xxx_g1_vtu_sid_write(struct mv88e6xxx_chip *chip, u8 sid) |
63 | { |
64 | u16 val = sid & MV88E6352_G1_VTU_SID_MASK; |
65 | |
66 | return mv88e6xxx_g1_write(chip, MV88E6352_G1_VTU_SID, val); |
67 | } |
68 | |
69 | /* Offset 0x05: VTU Operation Register */ |
70 | |
71 | static int mv88e6xxx_g1_vtu_op_wait(struct mv88e6xxx_chip *chip) |
72 | { |
73 | int bit = __bf_shf(MV88E6XXX_G1_VTU_OP_BUSY); |
74 | |
75 | return mv88e6xxx_g1_wait_bit(chip, MV88E6XXX_G1_VTU_OP, bit, val: 0); |
76 | } |
77 | |
78 | static int mv88e6xxx_g1_vtu_op(struct mv88e6xxx_chip *chip, u16 op) |
79 | { |
80 | int err; |
81 | |
82 | err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_VTU_OP, |
83 | MV88E6XXX_G1_VTU_OP_BUSY | op); |
84 | if (err) |
85 | return err; |
86 | |
87 | return mv88e6xxx_g1_vtu_op_wait(chip); |
88 | } |
89 | |
90 | /* Offset 0x06: VTU VID Register */ |
91 | |
92 | static int mv88e6xxx_g1_vtu_vid_read(struct mv88e6xxx_chip *chip, |
93 | bool *valid, u16 *vid) |
94 | { |
95 | u16 val; |
96 | int err; |
97 | |
98 | err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_VID, val: &val); |
99 | if (err) |
100 | return err; |
101 | |
102 | if (vid) { |
103 | *vid = val & 0xfff; |
104 | |
105 | if (val & MV88E6390_G1_VTU_VID_PAGE) |
106 | *vid |= 0x1000; |
107 | } |
108 | |
109 | if (valid) |
110 | *valid = !!(val & MV88E6XXX_G1_VTU_VID_VALID); |
111 | |
112 | return 0; |
113 | } |
114 | |
115 | static int mv88e6xxx_g1_vtu_vid_write(struct mv88e6xxx_chip *chip, |
116 | bool valid, u16 vid) |
117 | { |
118 | u16 val = vid & 0xfff; |
119 | |
120 | if (vid & 0x1000) |
121 | val |= MV88E6390_G1_VTU_VID_PAGE; |
122 | |
123 | if (valid) |
124 | val |= MV88E6XXX_G1_VTU_VID_VALID; |
125 | |
126 | return mv88e6xxx_g1_write(chip, MV88E6XXX_G1_VTU_VID, val); |
127 | } |
128 | |
129 | /* Offset 0x07: VTU/STU Data Register 1 |
130 | * Offset 0x08: VTU/STU Data Register 2 |
131 | * Offset 0x09: VTU/STU Data Register 3 |
132 | */ |
133 | static int mv88e6185_g1_vtu_stu_data_read(struct mv88e6xxx_chip *chip, |
134 | u16 *regs) |
135 | { |
136 | int i; |
137 | |
138 | /* Read all 3 VTU/STU Data registers */ |
139 | for (i = 0; i < 3; ++i) { |
140 | u16 *reg = ®s[i]; |
141 | int err; |
142 | |
143 | err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA1 + i, val: reg); |
144 | if (err) |
145 | return err; |
146 | } |
147 | |
148 | return 0; |
149 | } |
150 | |
151 | static int mv88e6185_g1_vtu_data_read(struct mv88e6xxx_chip *chip, |
152 | u8 *member, u8 *state) |
153 | { |
154 | u16 regs[3]; |
155 | int err; |
156 | int i; |
157 | |
158 | err = mv88e6185_g1_vtu_stu_data_read(chip, regs); |
159 | if (err) |
160 | return err; |
161 | |
162 | /* Extract MemberTag data */ |
163 | for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { |
164 | unsigned int member_offset = (i % 4) * 4; |
165 | unsigned int state_offset = member_offset + 2; |
166 | |
167 | if (member) |
168 | member[i] = (regs[i / 4] >> member_offset) & 0x3; |
169 | |
170 | if (state) |
171 | state[i] = (regs[i / 4] >> state_offset) & 0x3; |
172 | } |
173 | |
174 | return 0; |
175 | } |
176 | |
177 | static int mv88e6185_g1_vtu_data_write(struct mv88e6xxx_chip *chip, |
178 | u8 *member, u8 *state) |
179 | { |
180 | u16 regs[3] = { 0 }; |
181 | int i; |
182 | |
183 | /* Insert MemberTag and PortState data */ |
184 | for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { |
185 | unsigned int member_offset = (i % 4) * 4; |
186 | unsigned int state_offset = member_offset + 2; |
187 | |
188 | if (member) |
189 | regs[i / 4] |= (member[i] & 0x3) << member_offset; |
190 | |
191 | if (state) |
192 | regs[i / 4] |= (state[i] & 0x3) << state_offset; |
193 | } |
194 | |
195 | /* Write all 3 VTU/STU Data registers */ |
196 | for (i = 0; i < 3; ++i) { |
197 | u16 reg = regs[i]; |
198 | int err; |
199 | |
200 | err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_VTU_DATA1 + i, val: reg); |
201 | if (err) |
202 | return err; |
203 | } |
204 | |
205 | return 0; |
206 | } |
207 | |
208 | static int mv88e6390_g1_vtu_data_read(struct mv88e6xxx_chip *chip, u8 *data) |
209 | { |
210 | u16 regs[2]; |
211 | int i; |
212 | |
213 | /* Read the 2 VTU/STU Data registers */ |
214 | for (i = 0; i < 2; ++i) { |
215 | u16 *reg = ®s[i]; |
216 | int err; |
217 | |
218 | err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA1 + i, val: reg); |
219 | if (err) |
220 | return err; |
221 | } |
222 | |
223 | /* Extract data */ |
224 | for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { |
225 | unsigned int offset = (i % 8) * 2; |
226 | |
227 | data[i] = (regs[i / 8] >> offset) & 0x3; |
228 | } |
229 | |
230 | return 0; |
231 | } |
232 | |
233 | static int mv88e6390_g1_vtu_data_write(struct mv88e6xxx_chip *chip, u8 *data) |
234 | { |
235 | u16 regs[2] = { 0 }; |
236 | int i; |
237 | |
238 | /* Insert data */ |
239 | for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { |
240 | unsigned int offset = (i % 8) * 2; |
241 | |
242 | regs[i / 8] |= (data[i] & 0x3) << offset; |
243 | } |
244 | |
245 | /* Write the 2 VTU/STU Data registers */ |
246 | for (i = 0; i < 2; ++i) { |
247 | u16 reg = regs[i]; |
248 | int err; |
249 | |
250 | err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_VTU_DATA1 + i, val: reg); |
251 | if (err) |
252 | return err; |
253 | } |
254 | |
255 | return 0; |
256 | } |
257 | |
258 | /* VLAN Translation Unit Operations */ |
259 | |
260 | int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip, |
261 | struct mv88e6xxx_vtu_entry *entry) |
262 | { |
263 | int err; |
264 | |
265 | err = mv88e6xxx_g1_vtu_op_wait(chip); |
266 | if (err) |
267 | return err; |
268 | |
269 | /* To get the next higher active VID, the VTU GetNext operation can be |
270 | * started again without setting the VID registers since it already |
271 | * contains the last VID. |
272 | * |
273 | * To save a few hardware accesses and abstract this to the caller, |
274 | * write the VID only once, when the entry is given as invalid. |
275 | */ |
276 | if (!entry->valid) { |
277 | err = mv88e6xxx_g1_vtu_vid_write(chip, valid: false, vid: entry->vid); |
278 | if (err) |
279 | return err; |
280 | } |
281 | |
282 | err = mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_VTU_GET_NEXT); |
283 | if (err) |
284 | return err; |
285 | |
286 | return mv88e6xxx_g1_vtu_vid_read(chip, valid: &entry->valid, vid: &entry->vid); |
287 | } |
288 | |
289 | int mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip *chip, |
290 | struct mv88e6xxx_vtu_entry *entry) |
291 | { |
292 | u16 val; |
293 | int err; |
294 | |
295 | err = mv88e6xxx_g1_vtu_getnext(chip, entry); |
296 | if (err) |
297 | return err; |
298 | |
299 | if (entry->valid) { |
300 | err = mv88e6185_g1_vtu_data_read(chip, member: entry->member, state: entry->state); |
301 | if (err) |
302 | return err; |
303 | |
304 | /* VTU DBNum[3:0] are located in VTU Operation 3:0 |
305 | * VTU DBNum[7:4] ([5:4] for 6250) are located in VTU Operation 11:8 (9:8) |
306 | */ |
307 | err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_OP, val: &val); |
308 | if (err) |
309 | return err; |
310 | |
311 | entry->fid = val & 0x000f; |
312 | entry->fid |= (val & 0x0f00) >> 4; |
313 | entry->fid &= mv88e6xxx_num_databases(chip) - 1; |
314 | } |
315 | |
316 | return 0; |
317 | } |
318 | |
319 | int mv88e6352_g1_vtu_getnext(struct mv88e6xxx_chip *chip, |
320 | struct mv88e6xxx_vtu_entry *entry) |
321 | { |
322 | int err; |
323 | |
324 | /* Fetch VLAN MemberTag data from the VTU */ |
325 | err = mv88e6xxx_g1_vtu_getnext(chip, entry); |
326 | if (err) |
327 | return err; |
328 | |
329 | if (entry->valid) { |
330 | err = mv88e6185_g1_vtu_data_read(chip, member: entry->member, NULL); |
331 | if (err) |
332 | return err; |
333 | |
334 | err = mv88e6xxx_g1_vtu_fid_read(chip, entry); |
335 | if (err) |
336 | return err; |
337 | |
338 | err = mv88e6xxx_g1_vtu_sid_read(chip, sid: &entry->sid); |
339 | if (err) |
340 | return err; |
341 | } |
342 | |
343 | return 0; |
344 | } |
345 | |
346 | int mv88e6390_g1_vtu_getnext(struct mv88e6xxx_chip *chip, |
347 | struct mv88e6xxx_vtu_entry *entry) |
348 | { |
349 | int err; |
350 | |
351 | /* Fetch VLAN MemberTag data from the VTU */ |
352 | err = mv88e6xxx_g1_vtu_getnext(chip, entry); |
353 | if (err) |
354 | return err; |
355 | |
356 | if (entry->valid) { |
357 | err = mv88e6390_g1_vtu_data_read(chip, data: entry->member); |
358 | if (err) |
359 | return err; |
360 | |
361 | err = mv88e6xxx_g1_vtu_fid_read(chip, entry); |
362 | if (err) |
363 | return err; |
364 | |
365 | err = mv88e6xxx_g1_vtu_sid_read(chip, sid: &entry->sid); |
366 | if (err) |
367 | return err; |
368 | } |
369 | |
370 | return 0; |
371 | } |
372 | |
373 | int mv88e6185_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, |
374 | struct mv88e6xxx_vtu_entry *entry) |
375 | { |
376 | u16 op = MV88E6XXX_G1_VTU_OP_VTU_LOAD_PURGE; |
377 | int err; |
378 | |
379 | err = mv88e6xxx_g1_vtu_op_wait(chip); |
380 | if (err) |
381 | return err; |
382 | |
383 | err = mv88e6xxx_g1_vtu_vid_write(chip, valid: entry->valid, vid: entry->vid); |
384 | if (err) |
385 | return err; |
386 | |
387 | if (entry->valid) { |
388 | err = mv88e6185_g1_vtu_data_write(chip, member: entry->member, state: entry->state); |
389 | if (err) |
390 | return err; |
391 | |
392 | /* VTU DBNum[3:0] are located in VTU Operation 3:0 |
393 | * VTU DBNum[7:4] are located in VTU Operation 11:8 |
394 | * |
395 | * For the 6250/6220, the latter are really [5:4] and |
396 | * 9:8, but in those cases bits 7:6 of entry->fid are |
397 | * 0 since they have num_databases = 64. |
398 | */ |
399 | op |= entry->fid & 0x000f; |
400 | op |= (entry->fid & 0x00f0) << 4; |
401 | } |
402 | |
403 | return mv88e6xxx_g1_vtu_op(chip, op); |
404 | } |
405 | |
406 | int mv88e6352_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, |
407 | struct mv88e6xxx_vtu_entry *entry) |
408 | { |
409 | int err; |
410 | |
411 | err = mv88e6xxx_g1_vtu_op_wait(chip); |
412 | if (err) |
413 | return err; |
414 | |
415 | err = mv88e6xxx_g1_vtu_vid_write(chip, valid: entry->valid, vid: entry->vid); |
416 | if (err) |
417 | return err; |
418 | |
419 | if (entry->valid) { |
420 | /* Write MemberTag data */ |
421 | err = mv88e6185_g1_vtu_data_write(chip, member: entry->member, NULL); |
422 | if (err) |
423 | return err; |
424 | |
425 | err = mv88e6xxx_g1_vtu_fid_write(chip, entry); |
426 | if (err) |
427 | return err; |
428 | |
429 | err = mv88e6xxx_g1_vtu_sid_write(chip, sid: entry->sid); |
430 | if (err) |
431 | return err; |
432 | } |
433 | |
434 | /* Load/Purge VTU entry */ |
435 | return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_VTU_LOAD_PURGE); |
436 | } |
437 | |
438 | int mv88e6390_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, |
439 | struct mv88e6xxx_vtu_entry *entry) |
440 | { |
441 | int err; |
442 | |
443 | err = mv88e6xxx_g1_vtu_op_wait(chip); |
444 | if (err) |
445 | return err; |
446 | |
447 | err = mv88e6xxx_g1_vtu_vid_write(chip, valid: entry->valid, vid: entry->vid); |
448 | if (err) |
449 | return err; |
450 | |
451 | if (entry->valid) { |
452 | /* Write MemberTag data */ |
453 | err = mv88e6390_g1_vtu_data_write(chip, data: entry->member); |
454 | if (err) |
455 | return err; |
456 | |
457 | err = mv88e6xxx_g1_vtu_fid_write(chip, entry); |
458 | if (err) |
459 | return err; |
460 | |
461 | err = mv88e6xxx_g1_vtu_sid_write(chip, sid: entry->sid); |
462 | if (err) |
463 | return err; |
464 | } |
465 | |
466 | /* Load/Purge VTU entry */ |
467 | return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_VTU_LOAD_PURGE); |
468 | } |
469 | |
470 | int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip) |
471 | { |
472 | int err; |
473 | |
474 | err = mv88e6xxx_g1_vtu_op_wait(chip); |
475 | if (err) |
476 | return err; |
477 | |
478 | return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_FLUSH_ALL); |
479 | } |
480 | |
481 | /* Spanning Tree Unit Operations */ |
482 | |
483 | int mv88e6xxx_g1_stu_getnext(struct mv88e6xxx_chip *chip, |
484 | struct mv88e6xxx_stu_entry *entry) |
485 | { |
486 | int err; |
487 | |
488 | err = mv88e6xxx_g1_vtu_op_wait(chip); |
489 | if (err) |
490 | return err; |
491 | |
492 | /* To get the next higher active SID, the STU GetNext operation can be |
493 | * started again without setting the SID registers since it already |
494 | * contains the last SID. |
495 | * |
496 | * To save a few hardware accesses and abstract this to the caller, |
497 | * write the SID only once, when the entry is given as invalid. |
498 | */ |
499 | if (!entry->valid) { |
500 | err = mv88e6xxx_g1_vtu_sid_write(chip, sid: entry->sid); |
501 | if (err) |
502 | return err; |
503 | } |
504 | |
505 | err = mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_STU_GET_NEXT); |
506 | if (err) |
507 | return err; |
508 | |
509 | err = mv88e6xxx_g1_vtu_vid_read(chip, valid: &entry->valid, NULL); |
510 | if (err) |
511 | return err; |
512 | |
513 | if (entry->valid) { |
514 | err = mv88e6xxx_g1_vtu_sid_read(chip, sid: &entry->sid); |
515 | if (err) |
516 | return err; |
517 | } |
518 | |
519 | return 0; |
520 | } |
521 | |
522 | int mv88e6352_g1_stu_getnext(struct mv88e6xxx_chip *chip, |
523 | struct mv88e6xxx_stu_entry *entry) |
524 | { |
525 | int err; |
526 | |
527 | err = mv88e6xxx_g1_stu_getnext(chip, entry); |
528 | if (err) |
529 | return err; |
530 | |
531 | if (!entry->valid) |
532 | return 0; |
533 | |
534 | return mv88e6185_g1_vtu_data_read(chip, NULL, state: entry->state); |
535 | } |
536 | |
537 | int mv88e6390_g1_stu_getnext(struct mv88e6xxx_chip *chip, |
538 | struct mv88e6xxx_stu_entry *entry) |
539 | { |
540 | int err; |
541 | |
542 | err = mv88e6xxx_g1_stu_getnext(chip, entry); |
543 | if (err) |
544 | return err; |
545 | |
546 | if (!entry->valid) |
547 | return 0; |
548 | |
549 | return mv88e6390_g1_vtu_data_read(chip, data: entry->state); |
550 | } |
551 | |
552 | int mv88e6352_g1_stu_loadpurge(struct mv88e6xxx_chip *chip, |
553 | struct mv88e6xxx_stu_entry *entry) |
554 | { |
555 | int err; |
556 | |
557 | err = mv88e6xxx_g1_vtu_op_wait(chip); |
558 | if (err) |
559 | return err; |
560 | |
561 | err = mv88e6xxx_g1_vtu_vid_write(chip, valid: entry->valid, vid: 0); |
562 | if (err) |
563 | return err; |
564 | |
565 | err = mv88e6xxx_g1_vtu_sid_write(chip, sid: entry->sid); |
566 | if (err) |
567 | return err; |
568 | |
569 | if (entry->valid) { |
570 | err = mv88e6185_g1_vtu_data_write(chip, NULL, state: entry->state); |
571 | if (err) |
572 | return err; |
573 | } |
574 | |
575 | /* Load/Purge STU entry */ |
576 | return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_STU_LOAD_PURGE); |
577 | } |
578 | |
579 | int mv88e6390_g1_stu_loadpurge(struct mv88e6xxx_chip *chip, |
580 | struct mv88e6xxx_stu_entry *entry) |
581 | { |
582 | int err; |
583 | |
584 | err = mv88e6xxx_g1_vtu_op_wait(chip); |
585 | if (err) |
586 | return err; |
587 | |
588 | err = mv88e6xxx_g1_vtu_vid_write(chip, valid: entry->valid, vid: 0); |
589 | if (err) |
590 | return err; |
591 | |
592 | err = mv88e6xxx_g1_vtu_sid_write(chip, sid: entry->sid); |
593 | if (err) |
594 | return err; |
595 | |
596 | if (entry->valid) { |
597 | err = mv88e6390_g1_vtu_data_write(chip, data: entry->state); |
598 | if (err) |
599 | return err; |
600 | } |
601 | |
602 | /* Load/Purge STU entry */ |
603 | return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_STU_LOAD_PURGE); |
604 | } |
605 | |
606 | /* VTU Violation Management */ |
607 | |
608 | static irqreturn_t mv88e6xxx_g1_vtu_prob_irq_thread_fn(int irq, void *dev_id) |
609 | { |
610 | struct mv88e6xxx_chip *chip = dev_id; |
611 | u16 val, vid; |
612 | int spid; |
613 | int err; |
614 | |
615 | mv88e6xxx_reg_lock(chip); |
616 | |
617 | err = mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_GET_CLR_VIOLATION); |
618 | if (err) |
619 | goto out; |
620 | |
621 | err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_OP, val: &val); |
622 | if (err) |
623 | goto out; |
624 | |
625 | err = mv88e6xxx_g1_vtu_vid_read(chip, NULL, vid: &vid); |
626 | if (err) |
627 | goto out; |
628 | |
629 | spid = val & MV88E6XXX_G1_VTU_OP_SPID_MASK; |
630 | |
631 | if (val & MV88E6XXX_G1_VTU_OP_MEMBER_VIOLATION) { |
632 | trace_mv88e6xxx_vtu_member_violation(dev: chip->dev, spid, vid); |
633 | chip->ports[spid].vtu_member_violation++; |
634 | } |
635 | |
636 | if (val & MV88E6XXX_G1_VTU_OP_MISS_VIOLATION) { |
637 | trace_mv88e6xxx_vtu_miss_violation(dev: chip->dev, spid, vid); |
638 | chip->ports[spid].vtu_miss_violation++; |
639 | } |
640 | |
641 | mv88e6xxx_reg_unlock(chip); |
642 | |
643 | return IRQ_HANDLED; |
644 | |
645 | out: |
646 | mv88e6xxx_reg_unlock(chip); |
647 | |
648 | dev_err(chip->dev, "VTU problem: error %d while handling interrupt\n" , |
649 | err); |
650 | |
651 | return IRQ_HANDLED; |
652 | } |
653 | |
654 | int mv88e6xxx_g1_vtu_prob_irq_setup(struct mv88e6xxx_chip *chip) |
655 | { |
656 | int err; |
657 | |
658 | chip->vtu_prob_irq = irq_find_mapping(domain: chip->g1_irq.domain, |
659 | MV88E6XXX_G1_STS_IRQ_VTU_PROB); |
660 | if (chip->vtu_prob_irq < 0) |
661 | return chip->vtu_prob_irq; |
662 | |
663 | snprintf(buf: chip->vtu_prob_irq_name, size: sizeof(chip->vtu_prob_irq_name), |
664 | fmt: "mv88e6xxx-%s-g1-vtu-prob" , dev_name(dev: chip->dev)); |
665 | |
666 | err = request_threaded_irq(irq: chip->vtu_prob_irq, NULL, |
667 | thread_fn: mv88e6xxx_g1_vtu_prob_irq_thread_fn, |
668 | IRQF_ONESHOT, name: chip->vtu_prob_irq_name, |
669 | dev: chip); |
670 | if (err) |
671 | irq_dispose_mapping(virq: chip->vtu_prob_irq); |
672 | |
673 | return err; |
674 | } |
675 | |
676 | void mv88e6xxx_g1_vtu_prob_irq_free(struct mv88e6xxx_chip *chip) |
677 | { |
678 | free_irq(chip->vtu_prob_irq, chip); |
679 | irq_dispose_mapping(virq: chip->vtu_prob_irq); |
680 | } |
681 | |