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