]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blob - drivers/net/dsa/mv88e6xxx/global1_vtu.c
net: dsa: mv88e6xxx: move STU GetNext operation
[mirror_ubuntu-artful-kernel.git] / drivers / net / dsa / mv88e6xxx / global1_vtu.c
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
17 /* Offset 0x02: VTU FID Register */
18
19 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, 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
34 int mv88e6xxx_g1_vtu_fid_write(struct mv88e6xxx_chip *chip,
35 struct mv88e6xxx_vtu_entry *entry)
36 {
37 u16 val = entry->fid & GLOBAL_VTU_FID_MASK;
38
39 return mv88e6xxx_g1_write(chip, GLOBAL_VTU_FID, val);
40 }
41
42 /* Offset 0x03: VTU SID Register */
43
44 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, 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
59 int mv88e6xxx_g1_vtu_sid_write(struct mv88e6xxx_chip *chip,
60 struct mv88e6xxx_vtu_entry *entry)
61 {
62 u16 val = entry->sid & GLOBAL_VTU_SID_MASK;
63
64 return mv88e6xxx_g1_write(chip, GLOBAL_VTU_SID, val);
65 }
66
67 /* Offset 0x05: VTU Operation Register */
68
69 int mv88e6xxx_g1_vtu_op_wait(struct mv88e6xxx_chip *chip)
70 {
71 return mv88e6xxx_g1_wait(chip, GLOBAL_VTU_OP, GLOBAL_VTU_OP_BUSY);
72 }
73
74 int mv88e6xxx_g1_vtu_op(struct mv88e6xxx_chip *chip, u16 op)
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 }
84
85 /* Offset 0x06: VTU VID Register */
86
87 int mv88e6xxx_g1_vtu_vid_read(struct mv88e6xxx_chip *chip,
88 struct mv88e6xxx_vtu_entry *entry)
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
103 int mv88e6xxx_g1_vtu_vid_write(struct mv88e6xxx_chip *chip,
104 struct mv88e6xxx_vtu_entry *entry)
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
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
119 int mv88e6185_g1_vtu_data_read(struct mv88e6xxx_chip *chip,
120 struct mv88e6xxx_vtu_entry *entry)
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
147 int mv88e6185_g1_vtu_data_write(struct mv88e6xxx_chip *chip,
148 struct mv88e6xxx_vtu_entry *entry)
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
175 /* VLAN Translation Unit Operations */
176
177 int mv88e6xxx_g1_vtu_stu_getnext(struct mv88e6xxx_chip *chip,
178 struct mv88e6xxx_vtu_entry *entry)
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
197 int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
198 struct mv88e6xxx_vtu_entry *entry)
199 {
200 int err;
201
202 err = mv88e6xxx_g1_vtu_op_wait(chip);
203 if (err)
204 return err;
205
206 /* To get the next higher active VID, the VTU GetNext operation can be
207 * started again without setting the VID registers since it already
208 * contains the last VID.
209 *
210 * To save a few hardware accesses and abstract this to the caller,
211 * write the VID only once, when the entry is given as invalid.
212 */
213 if (!entry->valid) {
214 err = mv88e6xxx_g1_vtu_vid_write(chip, entry);
215 if (err)
216 return err;
217 }
218
219 err = mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_VTU_GET_NEXT);
220 if (err)
221 return err;
222
223 return mv88e6xxx_g1_vtu_vid_read(chip, entry);
224 }
225
226 int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip)
227 {
228 int err;
229
230 err = mv88e6xxx_g1_vtu_op_wait(chip);
231 if (err)
232 return err;
233
234 return mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_FLUSH_ALL);
235 }