]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - drivers/net/dsa/mv88e6xxx/global1_vtu.c
net: dsa: mv88e6xxx: make VTU helpers static
[mirror_ubuntu-artful-kernel.git] / drivers / net / dsa / mv88e6xxx / global1_vtu.c
CommitLineData
332aa5cc
VD
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 "mv88e6xxx.h"
15#include "global1.h"
16
8ee51f6b
VD
17/* Offset 0x02: VTU FID Register */
18
bf7d71c0
VD
19static int mv88e6xxx_g1_vtu_fid_read(struct mv88e6xxx_chip *chip,
20 struct mv88e6xxx_vtu_entry *entry)
8ee51f6b
VD
21{
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
bf7d71c0
VD
34static int mv88e6xxx_g1_vtu_fid_write(struct mv88e6xxx_chip *chip,
35 struct mv88e6xxx_vtu_entry *entry)
8ee51f6b
VD
36{
37 u16 val = entry->fid & GLOBAL_VTU_FID_MASK;
38
39 return mv88e6xxx_g1_write(chip, GLOBAL_VTU_FID, val);
40}
41
d2ca1ea1
VD
42/* Offset 0x03: VTU SID Register */
43
bf7d71c0
VD
44static int mv88e6xxx_g1_vtu_sid_read(struct mv88e6xxx_chip *chip,
45 struct mv88e6xxx_vtu_entry *entry)
d2ca1ea1
VD
46{
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
bf7d71c0
VD
59static int mv88e6xxx_g1_vtu_sid_write(struct mv88e6xxx_chip *chip,
60 struct mv88e6xxx_vtu_entry *entry)
d2ca1ea1
VD
61{
62 u16 val = entry->sid & GLOBAL_VTU_SID_MASK;
63
64 return mv88e6xxx_g1_write(chip, GLOBAL_VTU_SID, val);
65}
66
332aa5cc
VD
67/* Offset 0x05: VTU Operation Register */
68
bf7d71c0 69static int mv88e6xxx_g1_vtu_op_wait(struct mv88e6xxx_chip *chip)
332aa5cc
VD
70{
71 return mv88e6xxx_g1_wait(chip, GLOBAL_VTU_OP, GLOBAL_VTU_OP_BUSY);
72}
73
bf7d71c0 74static int mv88e6xxx_g1_vtu_op(struct mv88e6xxx_chip *chip, u16 op)
332aa5cc
VD
75{
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}
b486d7c9 84
3afb4bde
VD
85/* Offset 0x06: VTU VID Register */
86
bf7d71c0
VD
87static int mv88e6xxx_g1_vtu_vid_read(struct mv88e6xxx_chip *chip,
88 struct mv88e6xxx_vtu_entry *entry)
3afb4bde
VD
89{
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;
98 entry->valid = !!(val & GLOBAL_VTU_VID_VALID);
99
100 return 0;
101}
102
bf7d71c0
VD
103static int mv88e6xxx_g1_vtu_vid_write(struct mv88e6xxx_chip *chip,
104 struct mv88e6xxx_vtu_entry *entry)
3afb4bde
VD
105{
106 u16 val = entry->vid & 0xfff;
107
108 if (entry->valid)
109 val |= GLOBAL_VTU_VID_VALID;
110
111 return mv88e6xxx_g1_write(chip, GLOBAL_VTU_VID, val);
112}
113
c499a64f
VD
114/* Offset 0x07: VTU/STU Data Register 1
115 * Offset 0x08: VTU/STU Data Register 2
116 * Offset 0x09: VTU/STU Data Register 3
117 */
118
bf7d71c0
VD
119static int mv88e6185_g1_vtu_data_read(struct mv88e6xxx_chip *chip,
120 struct mv88e6xxx_vtu_entry *entry)
c499a64f
VD
121{
122 u16 regs[3];
123 int i;
124
125 /* Read all 3 VTU/STU Data registers */
126 for (i = 0; i < 3; ++i) {
127 u16 *reg = &regs[i];
128 int err;
129
130 err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_DATA_0_3 + i, reg);
131 if (err)
132 return err;
133 }
134
135 /* Extract MemberTag and PortState data */
136 for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
137 unsigned int member_offset = (i % 4) * 4;
138 unsigned int state_offset = member_offset + 2;
139
140 entry->member[i] = (regs[i / 4] >> member_offset) & 0x3;
141 entry->state[i] = (regs[i / 4] >> state_offset) & 0x3;
142 }
143
144 return 0;
145}
146
bf7d71c0
VD
147static int mv88e6185_g1_vtu_data_write(struct mv88e6xxx_chip *chip,
148 struct mv88e6xxx_vtu_entry *entry)
c499a64f
VD
149{
150 u16 regs[3] = { 0 };
151 int i;
152
153 /* Insert MemberTag and PortState data */
154 for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
155 unsigned int member_offset = (i % 4) * 4;
156 unsigned int state_offset = member_offset + 2;
157
158 regs[i / 4] |= (entry->member[i] & 0x3) << member_offset;
159 regs[i / 4] |= (entry->state[i] & 0x3) << state_offset;
160 }
161
162 /* Write all 3 VTU/STU Data registers */
163 for (i = 0; i < 3; ++i) {
164 u16 reg = regs[i];
165 int err;
166
167 err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_DATA_0_3 + i, reg);
168 if (err)
169 return err;
170 }
171
172 return 0;
173}
174
b486d7c9
VD
175/* VLAN Translation Unit Operations */
176
bf7d71c0
VD
177static int mv88e6xxx_g1_vtu_stu_getnext(struct mv88e6xxx_chip *chip,
178 struct mv88e6xxx_vtu_entry *entry)
66a8e1f9
VD
179{
180 int err;
181
182 err = mv88e6xxx_g1_vtu_sid_write(chip, entry);
183 if (err)
184 return err;
185
186 err = mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_STU_GET_NEXT);
187 if (err)
188 return err;
189
190 err = mv88e6xxx_g1_vtu_sid_read(chip, entry);
191 if (err)
192 return err;
193
194 return mv88e6xxx_g1_vtu_vid_read(chip, entry);
195}
196
bf7d71c0
VD
197static int mv88e6xxx_g1_vtu_stu_get(struct mv88e6xxx_chip *chip,
198 struct mv88e6xxx_vtu_entry *vtu)
ef6fcea3
VD
199{
200 struct mv88e6xxx_vtu_entry stu;
201 int err;
202
203 err = mv88e6xxx_g1_vtu_sid_read(chip, vtu);
204 if (err)
205 return err;
206
207 stu.sid = vtu->sid - 1;
208
209 err = mv88e6xxx_g1_vtu_stu_getnext(chip, &stu);
210 if (err)
211 return err;
212
213 if (stu.sid != vtu->sid || !stu.valid)
214 return -EINVAL;
215
216 return 0;
217}
218
bf7d71c0
VD
219static int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
220 struct mv88e6xxx_vtu_entry *entry)
f169e5ee
VD
221{
222 int err;
223
224 err = mv88e6xxx_g1_vtu_op_wait(chip);
225 if (err)
226 return err;
227
228 /* To get the next higher active VID, the VTU GetNext operation can be
229 * started again without setting the VID registers since it already
230 * contains the last VID.
231 *
232 * To save a few hardware accesses and abstract this to the caller,
233 * write the VID only once, when the entry is given as invalid.
234 */
235 if (!entry->valid) {
236 err = mv88e6xxx_g1_vtu_vid_write(chip, entry);
237 if (err)
238 return err;
239 }
240
241 err = mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_VTU_GET_NEXT);
242 if (err)
243 return err;
244
245 return mv88e6xxx_g1_vtu_vid_read(chip, entry);
246}
247
f1394b78
VD
248int mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
249 struct mv88e6xxx_vtu_entry *entry)
250{
251 u16 val;
252 int err;
253
254 err = mv88e6xxx_g1_vtu_getnext(chip, entry);
255 if (err)
256 return err;
257
258 if (entry->valid) {
259 err = mv88e6185_g1_vtu_data_read(chip, entry);
260 if (err)
261 return err;
262
263 /* VTU DBNum[3:0] are located in VTU Operation 3:0
264 * VTU DBNum[7:4] are located in VTU Operation 11:8
265 */
266 err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_OP, &val);
267 if (err)
268 return err;
269
270 entry->fid = val & 0x000f;
271 entry->fid |= (val & 0x0f00) >> 4;
272 }
273
274 return 0;
275}
276
277int mv88e6352_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
278 struct mv88e6xxx_vtu_entry *entry)
279{
280 int err;
281
282 /* Fetch VLAN MemberTag data from the VTU */
283 err = mv88e6xxx_g1_vtu_getnext(chip, entry);
284 if (err)
285 return err;
286
287 if (entry->valid) {
288 /* Fetch (and mask) VLAN PortState data from the STU */
289 err = mv88e6xxx_g1_vtu_stu_get(chip, entry);
290 if (err)
291 return err;
292
293 err = mv88e6185_g1_vtu_data_read(chip, entry);
294 if (err)
295 return err;
296
297 err = mv88e6xxx_g1_vtu_fid_read(chip, entry);
298 if (err)
299 return err;
300 }
301
302 return 0;
303}
304
0ad5daf6
VD
305int mv88e6185_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,
306 struct mv88e6xxx_vtu_entry *entry)
307{
308 u16 op = GLOBAL_VTU_OP_VTU_LOAD_PURGE;
309 int err;
310
311 err = mv88e6xxx_g1_vtu_op_wait(chip);
312 if (err)
313 return err;
314
315 err = mv88e6xxx_g1_vtu_vid_write(chip, entry);
316 if (err)
317 return err;
318
319 if (entry->valid) {
320 err = mv88e6185_g1_vtu_data_write(chip, entry);
321 if (err)
322 return err;
323
324 /* VTU DBNum[3:0] are located in VTU Operation 3:0
325 * VTU DBNum[7:4] are located in VTU Operation 11:8
326 */
327 op |= entry->fid & 0x000f;
328 op |= (entry->fid & 0x00f0) << 8;
329 }
330
331 return mv88e6xxx_g1_vtu_op(chip, op);
332}
333
334int mv88e6352_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,
335 struct mv88e6xxx_vtu_entry *entry)
336{
337 int err;
338
339 err = mv88e6xxx_g1_vtu_op_wait(chip);
340 if (err)
341 return err;
342
343 err = mv88e6xxx_g1_vtu_vid_write(chip, entry);
344 if (err)
345 return err;
346
347 if (entry->valid) {
348 /* Write MemberTag and PortState data */
349 err = mv88e6185_g1_vtu_data_write(chip, entry);
350 if (err)
351 return err;
352
353 err = mv88e6xxx_g1_vtu_sid_write(chip, entry);
354 if (err)
355 return err;
356
357 /* Load STU entry */
358 err = mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_STU_LOAD_PURGE);
359 if (err)
360 return err;
361
362 err = mv88e6xxx_g1_vtu_fid_write(chip, entry);
363 if (err)
364 return err;
365 }
366
367 /* Load/Purge VTU entry */
368 return mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_VTU_LOAD_PURGE);
369}
370
b486d7c9
VD
371int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip)
372{
373 int err;
374
375 err = mv88e6xxx_g1_vtu_op_wait(chip);
376 if (err)
377 return err;
378
379 return mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_FLUSH_ALL);
380}