1 /*
2 * Marvell 88E6xxx VLAN [Spanning Tree] Translation Unit (VTU [STU]) support
3 *
4 * Copyright (c) 2008 Marvell Semiconductor
5 * Copyright (c) 2015 CMC Electronics, Inc.
6 * Copyright (c) 2017 Savoir-faire Linux, Inc.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14 #include "chip.h"
15 #include "global1.h"
16
17 /* Offset 0x02: VTU FID Register */
18
mv88e6xxx_g1_vtu_fid_read(struct mv88e6xxx_chip * chip,struct mv88e6xxx_vtu_entry * entry)19 static int mv88e6xxx_g1_vtu_fid_read(struct mv88e6xxx_chip *chip,
20 struct mv88e6xxx_vtu_entry *entry)
21 {
22 u16 val;
23 int err;
24
25 err = mv88e6xxx_g1_read(chip, MV88E6352_G1_VTU_FID, &val);
26 if (err)
27 return err;
28
29 entry->fid = val & MV88E6352_G1_VTU_FID_MASK;
30
31 return 0;
32 }
33
mv88e6xxx_g1_vtu_fid_write(struct mv88e6xxx_chip * chip,struct mv88e6xxx_vtu_entry * entry)34 static int mv88e6xxx_g1_vtu_fid_write(struct mv88e6xxx_chip *chip,
35 struct mv88e6xxx_vtu_entry *entry)
36 {
37 u16 val = entry->fid & MV88E6352_G1_VTU_FID_MASK;
38
39 return mv88e6xxx_g1_write(chip, MV88E6352_G1_VTU_FID, val);
40 }
41
42 /* Offset 0x03: VTU SID Register */
43
mv88e6xxx_g1_vtu_sid_read(struct mv88e6xxx_chip * chip,struct mv88e6xxx_vtu_entry * entry)44 static int mv88e6xxx_g1_vtu_sid_read(struct mv88e6xxx_chip *chip,
45 struct mv88e6xxx_vtu_entry *entry)
46 {
47 u16 val;
48 int err;
49
50 err = mv88e6xxx_g1_read(chip, MV88E6352_G1_VTU_SID, &val);
51 if (err)
52 return err;
53
54 entry->sid = val & MV88E6352_G1_VTU_SID_MASK;
55
56 return 0;
57 }
58
mv88e6xxx_g1_vtu_sid_write(struct mv88e6xxx_chip * chip,struct mv88e6xxx_vtu_entry * entry)59 static int mv88e6xxx_g1_vtu_sid_write(struct mv88e6xxx_chip *chip,
60 struct mv88e6xxx_vtu_entry *entry)
61 {
62 u16 val = entry->sid & MV88E6352_G1_VTU_SID_MASK;
63
64 return mv88e6xxx_g1_write(chip, MV88E6352_G1_VTU_SID, val);
65 }
66
67 /* Offset 0x05: VTU Operation Register */
68
mv88e6xxx_g1_vtu_op_wait(struct mv88e6xxx_chip * chip)69 static int mv88e6xxx_g1_vtu_op_wait(struct mv88e6xxx_chip *chip)
70 {
71 return mv88e6xxx_g1_wait(chip, MV88E6XXX_G1_VTU_OP,
72 MV88E6XXX_G1_VTU_OP_BUSY);
73 }
74
mv88e6xxx_g1_vtu_op(struct mv88e6xxx_chip * chip,u16 op)75 static int mv88e6xxx_g1_vtu_op(struct mv88e6xxx_chip *chip, u16 op)
76 {
77 int err;
78
79 err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_VTU_OP,
80 MV88E6XXX_G1_VTU_OP_BUSY | op);
81 if (err)
82 return err;
83
84 return mv88e6xxx_g1_vtu_op_wait(chip);
85 }
86
87 /* Offset 0x06: VTU VID Register */
88
mv88e6xxx_g1_vtu_vid_read(struct mv88e6xxx_chip * chip,struct mv88e6xxx_vtu_entry * entry)89 static int mv88e6xxx_g1_vtu_vid_read(struct mv88e6xxx_chip *chip,
90 struct mv88e6xxx_vtu_entry *entry)
91 {
92 u16 val;
93 int err;
94
95 err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_VID, &val);
96 if (err)
97 return err;
98
99 entry->vid = val & 0xfff;
100
101 if (val & MV88E6390_G1_VTU_VID_PAGE)
102 entry->vid |= 0x1000;
103
104 entry->valid = !!(val & MV88E6XXX_G1_VTU_VID_VALID);
105
106 return 0;
107 }
108
mv88e6xxx_g1_vtu_vid_write(struct mv88e6xxx_chip * chip,struct mv88e6xxx_vtu_entry * entry)109 static int mv88e6xxx_g1_vtu_vid_write(struct mv88e6xxx_chip *chip,
110 struct mv88e6xxx_vtu_entry *entry)
111 {
112 u16 val = entry->vid & 0xfff;
113
114 if (entry->vid & 0x1000)
115 val |= MV88E6390_G1_VTU_VID_PAGE;
116
117 if (entry->valid)
118 val |= MV88E6XXX_G1_VTU_VID_VALID;
119
120 return mv88e6xxx_g1_write(chip, MV88E6XXX_G1_VTU_VID, val);
121 }
122
123 /* Offset 0x07: VTU/STU Data Register 1
124 * Offset 0x08: VTU/STU Data Register 2
125 * Offset 0x09: VTU/STU Data Register 3
126 */
127
mv88e6185_g1_vtu_data_read(struct mv88e6xxx_chip * chip,struct mv88e6xxx_vtu_entry * entry)128 static int mv88e6185_g1_vtu_data_read(struct mv88e6xxx_chip *chip,
129 struct mv88e6xxx_vtu_entry *entry)
130 {
131 u16 regs[3];
132 int i;
133
134 /* Read all 3 VTU/STU Data registers */
135 for (i = 0; i < 3; ++i) {
136 u16 *reg = ®s[i];
137 int err;
138
139 err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA1 + i, reg);
140 if (err)
141 return err;
142 }
143
144 /* Extract MemberTag and PortState data */
145 for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
146 unsigned int member_offset = (i % 4) * 4;
147 unsigned int state_offset = member_offset + 2;
148
149 entry->member[i] = (regs[i / 4] >> member_offset) & 0x3;
150 entry->state[i] = (regs[i / 4] >> state_offset) & 0x3;
151 }
152
153 return 0;
154 }
155
mv88e6185_g1_vtu_data_write(struct mv88e6xxx_chip * chip,struct mv88e6xxx_vtu_entry * entry)156 static int mv88e6185_g1_vtu_data_write(struct mv88e6xxx_chip *chip,
157 struct mv88e6xxx_vtu_entry *entry)
158 {
159 u16 regs[3] = { 0 };
160 int i;
161
162 /* Insert MemberTag and PortState 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 regs[i / 4] |= (entry->member[i] & 0x3) << member_offset;
168 regs[i / 4] |= (entry->state[i] & 0x3) << state_offset;
169 }
170
171 /* Write all 3 VTU/STU Data registers */
172 for (i = 0; i < 3; ++i) {
173 u16 reg = regs[i];
174 int err;
175
176 err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_VTU_DATA1 + i, reg);
177 if (err)
178 return err;
179 }
180
181 return 0;
182 }
183
mv88e6390_g1_vtu_data_read(struct mv88e6xxx_chip * chip,u8 * data)184 static int mv88e6390_g1_vtu_data_read(struct mv88e6xxx_chip *chip, u8 *data)
185 {
186 u16 regs[2];
187 int i;
188
189 /* Read the 2 VTU/STU Data registers */
190 for (i = 0; i < 2; ++i) {
191 u16 *reg = ®s[i];
192 int err;
193
194 err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA1 + i, reg);
195 if (err)
196 return err;
197 }
198
199 /* Extract data */
200 for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
201 unsigned int offset = (i % 8) * 2;
202
203 data[i] = (regs[i / 8] >> offset) & 0x3;
204 }
205
206 return 0;
207 }
208
mv88e6390_g1_vtu_data_write(struct mv88e6xxx_chip * chip,u8 * data)209 static int mv88e6390_g1_vtu_data_write(struct mv88e6xxx_chip *chip, u8 *data)
210 {
211 u16 regs[2] = { 0 };
212 int i;
213
214 /* Insert data */
215 for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
216 unsigned int offset = (i % 8) * 2;
217
218 regs[i / 8] |= (data[i] & 0x3) << offset;
219 }
220
221 /* Write the 2 VTU/STU Data registers */
222 for (i = 0; i < 2; ++i) {
223 u16 reg = regs[i];
224 int err;
225
226 err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_VTU_DATA1 + i, reg);
227 if (err)
228 return err;
229 }
230
231 return 0;
232 }
233
234 /* VLAN Translation Unit Operations */
235
mv88e6xxx_g1_vtu_stu_getnext(struct mv88e6xxx_chip * chip,struct mv88e6xxx_vtu_entry * entry)236 static int mv88e6xxx_g1_vtu_stu_getnext(struct mv88e6xxx_chip *chip,
237 struct mv88e6xxx_vtu_entry *entry)
238 {
239 int err;
240
241 err = mv88e6xxx_g1_vtu_sid_write(chip, entry);
242 if (err)
243 return err;
244
245 err = mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_STU_GET_NEXT);
246 if (err)
247 return err;
248
249 err = mv88e6xxx_g1_vtu_sid_read(chip, entry);
250 if (err)
251 return err;
252
253 return mv88e6xxx_g1_vtu_vid_read(chip, entry);
254 }
255
mv88e6xxx_g1_vtu_stu_get(struct mv88e6xxx_chip * chip,struct mv88e6xxx_vtu_entry * vtu)256 static int mv88e6xxx_g1_vtu_stu_get(struct mv88e6xxx_chip *chip,
257 struct mv88e6xxx_vtu_entry *vtu)
258 {
259 struct mv88e6xxx_vtu_entry stu;
260 int err;
261
262 err = mv88e6xxx_g1_vtu_sid_read(chip, vtu);
263 if (err)
264 return err;
265
266 stu.sid = vtu->sid - 1;
267
268 err = mv88e6xxx_g1_vtu_stu_getnext(chip, &stu);
269 if (err)
270 return err;
271
272 if (stu.sid != vtu->sid || !stu.valid)
273 return -EINVAL;
274
275 return 0;
276 }
277
mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip * chip,struct mv88e6xxx_vtu_entry * entry)278 static int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
279 struct mv88e6xxx_vtu_entry *entry)
280 {
281 int err;
282
283 err = mv88e6xxx_g1_vtu_op_wait(chip);
284 if (err)
285 return err;
286
287 /* To get the next higher active VID, the VTU GetNext operation can be
288 * started again without setting the VID registers since it already
289 * contains the last VID.
290 *
291 * To save a few hardware accesses and abstract this to the caller,
292 * write the VID only once, when the entry is given as invalid.
293 */
294 if (!entry->valid) {
295 err = mv88e6xxx_g1_vtu_vid_write(chip, entry);
296 if (err)
297 return err;
298 }
299
300 err = mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_VTU_GET_NEXT);
301 if (err)
302 return err;
303
304 return mv88e6xxx_g1_vtu_vid_read(chip, entry);
305 }
306
mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip * chip,struct mv88e6xxx_vtu_entry * entry)307 int mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
308 struct mv88e6xxx_vtu_entry *entry)
309 {
310 u16 val;
311 int err;
312
313 err = mv88e6xxx_g1_vtu_getnext(chip, entry);
314 if (err)
315 return err;
316
317 if (entry->valid) {
318 err = mv88e6185_g1_vtu_data_read(chip, entry);
319 if (err)
320 return err;
321
322 /* VTU DBNum[3:0] are located in VTU Operation 3:0
323 * VTU DBNum[7:4] are located in VTU Operation 11:8
324 */
325 err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_OP, &val);
326 if (err)
327 return err;
328
329 entry->fid = val & 0x000f;
330 entry->fid |= (val & 0x0f00) >> 4;
331 }
332
333 return 0;
334 }
335
mv88e6352_g1_vtu_getnext(struct mv88e6xxx_chip * chip,struct mv88e6xxx_vtu_entry * entry)336 int mv88e6352_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
337 struct mv88e6xxx_vtu_entry *entry)
338 {
339 int err;
340
341 /* Fetch VLAN MemberTag data from the VTU */
342 err = mv88e6xxx_g1_vtu_getnext(chip, entry);
343 if (err)
344 return err;
345
346 if (entry->valid) {
347 /* Fetch (and mask) VLAN PortState data from the STU */
348 err = mv88e6xxx_g1_vtu_stu_get(chip, entry);
349 if (err)
350 return err;
351
352 err = mv88e6185_g1_vtu_data_read(chip, entry);
353 if (err)
354 return err;
355
356 err = mv88e6xxx_g1_vtu_fid_read(chip, entry);
357 if (err)
358 return err;
359 }
360
361 return 0;
362 }
363
mv88e6390_g1_vtu_getnext(struct mv88e6xxx_chip * chip,struct mv88e6xxx_vtu_entry * entry)364 int mv88e6390_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
365 struct mv88e6xxx_vtu_entry *entry)
366 {
367 int err;
368
369 /* Fetch VLAN MemberTag data from the VTU */
370 err = mv88e6xxx_g1_vtu_getnext(chip, entry);
371 if (err)
372 return err;
373
374 if (entry->valid) {
375 err = mv88e6390_g1_vtu_data_read(chip, entry->member);
376 if (err)
377 return err;
378
379 /* Fetch VLAN PortState data from the STU */
380 err = mv88e6xxx_g1_vtu_stu_get(chip, entry);
381 if (err)
382 return err;
383
384 err = mv88e6390_g1_vtu_data_read(chip, entry->state);
385 if (err)
386 return err;
387
388 err = mv88e6xxx_g1_vtu_fid_read(chip, entry);
389 if (err)
390 return err;
391 }
392
393 return 0;
394 }
395
mv88e6185_g1_vtu_loadpurge(struct mv88e6xxx_chip * chip,struct mv88e6xxx_vtu_entry * entry)396 int mv88e6185_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,
397 struct mv88e6xxx_vtu_entry *entry)
398 {
399 u16 op = MV88E6XXX_G1_VTU_OP_VTU_LOAD_PURGE;
400 int err;
401
402 err = mv88e6xxx_g1_vtu_op_wait(chip);
403 if (err)
404 return err;
405
406 err = mv88e6xxx_g1_vtu_vid_write(chip, entry);
407 if (err)
408 return err;
409
410 if (entry->valid) {
411 err = mv88e6185_g1_vtu_data_write(chip, entry);
412 if (err)
413 return err;
414
415 /* VTU DBNum[3:0] are located in VTU Operation 3:0
416 * VTU DBNum[7:4] are located in VTU Operation 11:8
417 */
418 op |= entry->fid & 0x000f;
419 op |= (entry->fid & 0x00f0) << 4;
420 }
421
422 return mv88e6xxx_g1_vtu_op(chip, op);
423 }
424
mv88e6352_g1_vtu_loadpurge(struct mv88e6xxx_chip * chip,struct mv88e6xxx_vtu_entry * entry)425 int mv88e6352_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,
426 struct mv88e6xxx_vtu_entry *entry)
427 {
428 int err;
429
430 err = mv88e6xxx_g1_vtu_op_wait(chip);
431 if (err)
432 return err;
433
434 err = mv88e6xxx_g1_vtu_vid_write(chip, entry);
435 if (err)
436 return err;
437
438 if (entry->valid) {
439 /* Write MemberTag and PortState data */
440 err = mv88e6185_g1_vtu_data_write(chip, entry);
441 if (err)
442 return err;
443
444 err = mv88e6xxx_g1_vtu_sid_write(chip, entry);
445 if (err)
446 return err;
447
448 /* Load STU entry */
449 err = mv88e6xxx_g1_vtu_op(chip,
450 MV88E6XXX_G1_VTU_OP_STU_LOAD_PURGE);
451 if (err)
452 return err;
453
454 err = mv88e6xxx_g1_vtu_fid_write(chip, entry);
455 if (err)
456 return err;
457 }
458
459 /* Load/Purge VTU entry */
460 return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_VTU_LOAD_PURGE);
461 }
462
mv88e6390_g1_vtu_loadpurge(struct mv88e6xxx_chip * chip,struct mv88e6xxx_vtu_entry * entry)463 int mv88e6390_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,
464 struct mv88e6xxx_vtu_entry *entry)
465 {
466 int err;
467
468 err = mv88e6xxx_g1_vtu_op_wait(chip);
469 if (err)
470 return err;
471
472 err = mv88e6xxx_g1_vtu_vid_write(chip, entry);
473 if (err)
474 return err;
475
476 if (entry->valid) {
477 /* Write PortState data */
478 err = mv88e6390_g1_vtu_data_write(chip, entry->state);
479 if (err)
480 return err;
481
482 err = mv88e6xxx_g1_vtu_sid_write(chip, entry);
483 if (err)
484 return err;
485
486 /* Load STU entry */
487 err = mv88e6xxx_g1_vtu_op(chip,
488 MV88E6XXX_G1_VTU_OP_STU_LOAD_PURGE);
489 if (err)
490 return err;
491
492 /* Write MemberTag data */
493 err = mv88e6390_g1_vtu_data_write(chip, entry->member);
494 if (err)
495 return err;
496
497 err = mv88e6xxx_g1_vtu_fid_write(chip, entry);
498 if (err)
499 return err;
500 }
501
502 /* Load/Purge VTU entry */
503 return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_VTU_LOAD_PURGE);
504 }
505
mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip * chip)506 int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip)
507 {
508 int err;
509
510 err = mv88e6xxx_g1_vtu_op_wait(chip);
511 if (err)
512 return err;
513
514 return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_FLUSH_ALL);
515 }
516