blob: bf593c7aaa9b1045ccd0217e4bdbbcd0787da21c [file] [log] [blame]
Vivien Didelot332aa5c2017-05-01 14:05:12 -04001/*
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
Vivien Didelot4d5f2ba72017-06-02 17:06:15 -040014#include "chip.h"
Vivien Didelot332aa5c2017-05-01 14:05:12 -040015#include "global1.h"
16
Vivien Didelot8ee51f62017-05-01 14:05:14 -040017/* Offset 0x02: VTU FID Register */
18
Vivien Didelotbf7d71c2017-05-01 14:05:24 -040019static int mv88e6xxx_g1_vtu_fid_read(struct mv88e6xxx_chip *chip,
20 struct mv88e6xxx_vtu_entry *entry)
Vivien Didelot8ee51f62017-05-01 14:05:14 -040021{
22 u16 val;
23 int err;
24
25 err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_FID, &val);
26 if (err)
27 return err;
28
29 entry->fid = val & GLOBAL_VTU_FID_MASK;
30
31 return 0;
32}
33
Vivien Didelotbf7d71c2017-05-01 14:05:24 -040034static int mv88e6xxx_g1_vtu_fid_write(struct mv88e6xxx_chip *chip,
35 struct mv88e6xxx_vtu_entry *entry)
Vivien Didelot8ee51f62017-05-01 14:05:14 -040036{
37 u16 val = entry->fid & GLOBAL_VTU_FID_MASK;
38
39 return mv88e6xxx_g1_write(chip, GLOBAL_VTU_FID, val);
40}
41
Vivien Didelotd2ca1ea2017-05-01 14:05:15 -040042/* Offset 0x03: VTU SID Register */
43
Vivien Didelotbf7d71c2017-05-01 14:05:24 -040044static int mv88e6xxx_g1_vtu_sid_read(struct mv88e6xxx_chip *chip,
45 struct mv88e6xxx_vtu_entry *entry)
Vivien Didelotd2ca1ea2017-05-01 14:05:15 -040046{
47 u16 val;
48 int err;
49
50 err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_SID, &val);
51 if (err)
52 return err;
53
54 entry->sid = val & GLOBAL_VTU_SID_MASK;
55
56 return 0;
57}
58
Vivien Didelotbf7d71c2017-05-01 14:05:24 -040059static int mv88e6xxx_g1_vtu_sid_write(struct mv88e6xxx_chip *chip,
60 struct mv88e6xxx_vtu_entry *entry)
Vivien Didelotd2ca1ea2017-05-01 14:05:15 -040061{
62 u16 val = entry->sid & GLOBAL_VTU_SID_MASK;
63
64 return mv88e6xxx_g1_write(chip, GLOBAL_VTU_SID, val);
65}
66
Vivien Didelot332aa5c2017-05-01 14:05:12 -040067/* Offset 0x05: VTU Operation Register */
68
Vivien Didelotbf7d71c2017-05-01 14:05:24 -040069static int mv88e6xxx_g1_vtu_op_wait(struct mv88e6xxx_chip *chip)
Vivien Didelot332aa5c2017-05-01 14:05:12 -040070{
71 return mv88e6xxx_g1_wait(chip, GLOBAL_VTU_OP, GLOBAL_VTU_OP_BUSY);
72}
73
Vivien Didelotbf7d71c2017-05-01 14:05:24 -040074static int mv88e6xxx_g1_vtu_op(struct mv88e6xxx_chip *chip, u16 op)
Vivien Didelot332aa5c2017-05-01 14:05:12 -040075{
76 int err;
77
78 err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_OP, op);
79 if (err)
80 return err;
81
82 return mv88e6xxx_g1_vtu_op_wait(chip);
83}
Vivien Didelotb486d7c2017-05-01 14:05:13 -040084
Vivien Didelot3afb4bd2017-05-01 14:05:16 -040085/* Offset 0x06: VTU VID Register */
86
Vivien Didelotbf7d71c2017-05-01 14:05:24 -040087static int mv88e6xxx_g1_vtu_vid_read(struct mv88e6xxx_chip *chip,
88 struct mv88e6xxx_vtu_entry *entry)
Vivien Didelot3afb4bd2017-05-01 14:05:16 -040089{
90 u16 val;
91 int err;
92
93 err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_VID, &val);
94 if (err)
95 return err;
96
97 entry->vid = val & 0xfff;
Vivien Didelot1ac75862017-05-01 14:05:26 -040098
99 if (val & GLOBAL_VTU_VID_PAGE)
100 entry->vid |= 0x1000;
101
Vivien Didelot3afb4bd2017-05-01 14:05:16 -0400102 entry->valid = !!(val & GLOBAL_VTU_VID_VALID);
103
104 return 0;
105}
106
Vivien Didelotbf7d71c2017-05-01 14:05:24 -0400107static int mv88e6xxx_g1_vtu_vid_write(struct mv88e6xxx_chip *chip,
108 struct mv88e6xxx_vtu_entry *entry)
Vivien Didelot3afb4bd2017-05-01 14:05:16 -0400109{
110 u16 val = entry->vid & 0xfff;
111
Vivien Didelot1ac75862017-05-01 14:05:26 -0400112 if (entry->vid & 0x1000)
113 val |= GLOBAL_VTU_VID_PAGE;
114
Vivien Didelot3afb4bd2017-05-01 14:05:16 -0400115 if (entry->valid)
116 val |= GLOBAL_VTU_VID_VALID;
117
118 return mv88e6xxx_g1_write(chip, GLOBAL_VTU_VID, val);
119}
120
Vivien Didelotc499a642017-05-01 14:05:18 -0400121/* Offset 0x07: VTU/STU Data Register 1
122 * Offset 0x08: VTU/STU Data Register 2
123 * Offset 0x09: VTU/STU Data Register 3
124 */
125
Vivien Didelotbf7d71c2017-05-01 14:05:24 -0400126static int mv88e6185_g1_vtu_data_read(struct mv88e6xxx_chip *chip,
127 struct mv88e6xxx_vtu_entry *entry)
Vivien Didelotc499a642017-05-01 14:05:18 -0400128{
129 u16 regs[3];
130 int i;
131
132 /* Read all 3 VTU/STU Data registers */
133 for (i = 0; i < 3; ++i) {
134 u16 *reg = &regs[i];
135 int err;
136
137 err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_DATA_0_3 + i, reg);
138 if (err)
139 return err;
140 }
141
142 /* Extract MemberTag and PortState data */
143 for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
144 unsigned int member_offset = (i % 4) * 4;
145 unsigned int state_offset = member_offset + 2;
146
147 entry->member[i] = (regs[i / 4] >> member_offset) & 0x3;
148 entry->state[i] = (regs[i / 4] >> state_offset) & 0x3;
149 }
150
151 return 0;
152}
153
Vivien Didelotbf7d71c2017-05-01 14:05:24 -0400154static int mv88e6185_g1_vtu_data_write(struct mv88e6xxx_chip *chip,
155 struct mv88e6xxx_vtu_entry *entry)
Vivien Didelotc499a642017-05-01 14:05:18 -0400156{
157 u16 regs[3] = { 0 };
158 int i;
159
160 /* Insert MemberTag and PortState data */
161 for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
162 unsigned int member_offset = (i % 4) * 4;
163 unsigned int state_offset = member_offset + 2;
164
165 regs[i / 4] |= (entry->member[i] & 0x3) << member_offset;
166 regs[i / 4] |= (entry->state[i] & 0x3) << state_offset;
167 }
168
169 /* Write all 3 VTU/STU Data registers */
170 for (i = 0; i < 3; ++i) {
171 u16 reg = regs[i];
172 int err;
173
174 err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_DATA_0_3 + i, reg);
175 if (err)
176 return err;
177 }
178
179 return 0;
180}
181
Vivien Didelot931d1822017-05-01 14:05:27 -0400182static int mv88e6390_g1_vtu_data_read(struct mv88e6xxx_chip *chip, u8 *data)
183{
184 u16 regs[2];
185 int i;
186
187 /* Read the 2 VTU/STU Data registers */
188 for (i = 0; i < 2; ++i) {
189 u16 *reg = &regs[i];
190 int err;
191
192 err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_DATA_0_3 + i, reg);
193 if (err)
194 return err;
195 }
196
197 /* Extract data */
198 for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
199 unsigned int offset = (i % 8) * 2;
200
201 data[i] = (regs[i / 8] >> offset) & 0x3;
202 }
203
204 return 0;
205}
206
207static int mv88e6390_g1_vtu_data_write(struct mv88e6xxx_chip *chip, u8 *data)
208{
209 u16 regs[2] = { 0 };
210 int i;
211
212 /* Insert data */
213 for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
214 unsigned int offset = (i % 8) * 2;
215
216 regs[i / 8] |= (data[i] & 0x3) << offset;
217 }
218
219 /* Write the 2 VTU/STU Data registers */
220 for (i = 0; i < 2; ++i) {
221 u16 reg = regs[i];
222 int err;
223
224 err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_DATA_0_3 + i, reg);
225 if (err)
226 return err;
227 }
228
229 return 0;
230}
231
Vivien Didelotb486d7c2017-05-01 14:05:13 -0400232/* VLAN Translation Unit Operations */
233
Vivien Didelotbf7d71c2017-05-01 14:05:24 -0400234static int mv88e6xxx_g1_vtu_stu_getnext(struct mv88e6xxx_chip *chip,
235 struct mv88e6xxx_vtu_entry *entry)
Vivien Didelot66a8e1f2017-05-01 14:05:19 -0400236{
237 int err;
238
239 err = mv88e6xxx_g1_vtu_sid_write(chip, entry);
240 if (err)
241 return err;
242
243 err = mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_STU_GET_NEXT);
244 if (err)
245 return err;
246
247 err = mv88e6xxx_g1_vtu_sid_read(chip, entry);
248 if (err)
249 return err;
250
251 return mv88e6xxx_g1_vtu_vid_read(chip, entry);
252}
253
Vivien Didelotbf7d71c2017-05-01 14:05:24 -0400254static int mv88e6xxx_g1_vtu_stu_get(struct mv88e6xxx_chip *chip,
255 struct mv88e6xxx_vtu_entry *vtu)
Vivien Didelotef6fcea2017-05-01 14:05:20 -0400256{
257 struct mv88e6xxx_vtu_entry stu;
258 int err;
259
260 err = mv88e6xxx_g1_vtu_sid_read(chip, vtu);
261 if (err)
262 return err;
263
264 stu.sid = vtu->sid - 1;
265
266 err = mv88e6xxx_g1_vtu_stu_getnext(chip, &stu);
267 if (err)
268 return err;
269
270 if (stu.sid != vtu->sid || !stu.valid)
271 return -EINVAL;
272
273 return 0;
274}
275
Vivien Didelotbf7d71c2017-05-01 14:05:24 -0400276static int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
277 struct mv88e6xxx_vtu_entry *entry)
Vivien Didelotf169e5e2017-05-01 14:05:17 -0400278{
279 int err;
280
281 err = mv88e6xxx_g1_vtu_op_wait(chip);
282 if (err)
283 return err;
284
285 /* To get the next higher active VID, the VTU GetNext operation can be
286 * started again without setting the VID registers since it already
287 * contains the last VID.
288 *
289 * To save a few hardware accesses and abstract this to the caller,
290 * write the VID only once, when the entry is given as invalid.
291 */
292 if (!entry->valid) {
293 err = mv88e6xxx_g1_vtu_vid_write(chip, entry);
294 if (err)
295 return err;
296 }
297
298 err = mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_VTU_GET_NEXT);
299 if (err)
300 return err;
301
302 return mv88e6xxx_g1_vtu_vid_read(chip, entry);
303}
304
Vivien Didelotf1394b72017-05-01 14:05:22 -0400305int mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
306 struct mv88e6xxx_vtu_entry *entry)
307{
308 u16 val;
309 int err;
310
311 err = mv88e6xxx_g1_vtu_getnext(chip, entry);
312 if (err)
313 return err;
314
315 if (entry->valid) {
316 err = mv88e6185_g1_vtu_data_read(chip, entry);
317 if (err)
318 return err;
319
320 /* VTU DBNum[3:0] are located in VTU Operation 3:0
321 * VTU DBNum[7:4] are located in VTU Operation 11:8
322 */
323 err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_OP, &val);
324 if (err)
325 return err;
326
327 entry->fid = val & 0x000f;
328 entry->fid |= (val & 0x0f00) >> 4;
329 }
330
331 return 0;
332}
333
334int mv88e6352_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
335 struct mv88e6xxx_vtu_entry *entry)
336{
337 int err;
338
339 /* Fetch VLAN MemberTag data from the VTU */
340 err = mv88e6xxx_g1_vtu_getnext(chip, entry);
341 if (err)
342 return err;
343
344 if (entry->valid) {
345 /* Fetch (and mask) VLAN PortState data from the STU */
346 err = mv88e6xxx_g1_vtu_stu_get(chip, entry);
347 if (err)
348 return err;
349
350 err = mv88e6185_g1_vtu_data_read(chip, entry);
351 if (err)
352 return err;
353
354 err = mv88e6xxx_g1_vtu_fid_read(chip, entry);
355 if (err)
356 return err;
357 }
358
359 return 0;
360}
361
Vivien Didelot931d1822017-05-01 14:05:27 -0400362int mv88e6390_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
363 struct mv88e6xxx_vtu_entry *entry)
364{
365 int err;
366
367 /* Fetch VLAN MemberTag data from the VTU */
368 err = mv88e6xxx_g1_vtu_getnext(chip, entry);
369 if (err)
370 return err;
371
372 if (entry->valid) {
373 err = mv88e6390_g1_vtu_data_read(chip, entry->member);
374 if (err)
375 return err;
376
377 /* Fetch VLAN PortState data from the STU */
378 err = mv88e6xxx_g1_vtu_stu_get(chip, entry);
379 if (err)
380 return err;
381
382 err = mv88e6390_g1_vtu_data_read(chip, entry->state);
383 if (err)
384 return err;
385
386 err = mv88e6xxx_g1_vtu_fid_read(chip, entry);
387 if (err)
388 return err;
389 }
390
391 return 0;
392}
393
Vivien Didelot0ad5daf2017-05-01 14:05:23 -0400394int mv88e6185_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,
395 struct mv88e6xxx_vtu_entry *entry)
396{
397 u16 op = GLOBAL_VTU_OP_VTU_LOAD_PURGE;
398 int err;
399
400 err = mv88e6xxx_g1_vtu_op_wait(chip);
401 if (err)
402 return err;
403
404 err = mv88e6xxx_g1_vtu_vid_write(chip, entry);
405 if (err)
406 return err;
407
408 if (entry->valid) {
409 err = mv88e6185_g1_vtu_data_write(chip, entry);
410 if (err)
411 return err;
412
413 /* VTU DBNum[3:0] are located in VTU Operation 3:0
414 * VTU DBNum[7:4] are located in VTU Operation 11:8
415 */
416 op |= entry->fid & 0x000f;
417 op |= (entry->fid & 0x00f0) << 8;
418 }
419
420 return mv88e6xxx_g1_vtu_op(chip, op);
421}
422
423int mv88e6352_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,
424 struct mv88e6xxx_vtu_entry *entry)
425{
426 int err;
427
428 err = mv88e6xxx_g1_vtu_op_wait(chip);
429 if (err)
430 return err;
431
432 err = mv88e6xxx_g1_vtu_vid_write(chip, entry);
433 if (err)
434 return err;
435
436 if (entry->valid) {
437 /* Write MemberTag and PortState data */
438 err = mv88e6185_g1_vtu_data_write(chip, entry);
439 if (err)
440 return err;
441
442 err = mv88e6xxx_g1_vtu_sid_write(chip, entry);
443 if (err)
444 return err;
445
446 /* Load STU entry */
447 err = mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_STU_LOAD_PURGE);
448 if (err)
449 return err;
450
451 err = mv88e6xxx_g1_vtu_fid_write(chip, entry);
452 if (err)
453 return err;
454 }
455
456 /* Load/Purge VTU entry */
457 return mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_VTU_LOAD_PURGE);
458}
459
Vivien Didelot931d1822017-05-01 14:05:27 -0400460int mv88e6390_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,
461 struct mv88e6xxx_vtu_entry *entry)
462{
463 int err;
464
465 err = mv88e6xxx_g1_vtu_op_wait(chip);
466 if (err)
467 return err;
468
469 err = mv88e6xxx_g1_vtu_vid_write(chip, entry);
470 if (err)
471 return err;
472
473 if (entry->valid) {
474 /* Write PortState data */
475 err = mv88e6390_g1_vtu_data_write(chip, entry->state);
476 if (err)
477 return err;
478
479 err = mv88e6xxx_g1_vtu_sid_write(chip, entry);
480 if (err)
481 return err;
482
483 /* Load STU entry */
484 err = mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_STU_LOAD_PURGE);
485 if (err)
486 return err;
487
488 /* Write MemberTag data */
489 err = mv88e6390_g1_vtu_data_write(chip, entry->member);
490 if (err)
491 return err;
492
493 err = mv88e6xxx_g1_vtu_fid_write(chip, entry);
494 if (err)
495 return err;
496 }
497
498 /* Load/Purge VTU entry */
499 return mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_VTU_LOAD_PURGE);
500}
501
Vivien Didelotb486d7c2017-05-01 14:05:13 -0400502int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip)
503{
504 int err;
505
506 err = mv88e6xxx_g1_vtu_op_wait(chip);
507 if (err)
508 return err;
509
510 return mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_FLUSH_ALL);
511}