Commit | Line | Data |
---|---|---|
18abed21 VD |
1 | /* |
2 | * Marvell 88E6xxx Switch Port Registers support | |
3 | * | |
4 | * Copyright (c) 2008 Marvell Semiconductor | |
5 | * | |
4333d619 VD |
6 | * Copyright (c) 2016-2017 Savoir-faire Linux Inc. |
7 | * Vivien Didelot <vivien.didelot@savoirfairelinux.com> | |
18abed21 VD |
8 | * |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation; either version 2 of the License, or | |
12 | * (at your option) any later version. | |
13 | */ | |
14 | ||
f894c29c | 15 | #include <linux/if_bridge.h> |
f39908d3 | 16 | #include <linux/phy.h> |
4d5f2ba7 VD |
17 | |
18 | #include "chip.h" | |
18abed21 VD |
19 | #include "port.h" |
20 | ||
21 | int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg, | |
22 | u16 *val) | |
23 | { | |
24 | int addr = chip->info->port_base_addr + port; | |
25 | ||
26 | return mv88e6xxx_read(chip, addr, reg, val); | |
27 | } | |
28 | ||
29 | int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, | |
30 | u16 val) | |
31 | { | |
32 | int addr = chip->info->port_base_addr + port; | |
33 | ||
34 | return mv88e6xxx_write(chip, addr, reg, val); | |
35 | } | |
e28def33 | 36 | |
08ef7f10 VD |
37 | /* Offset 0x01: MAC (or PCS or Physical) Control Register |
38 | * | |
39 | * Link, Duplex and Flow Control have one force bit, one value bit. | |
96a2b40c VD |
40 | * |
41 | * For port's MAC speed, ForceSpd (or SpdValue) bits 1:0 program the value. | |
42 | * Alternative values require the 200BASE (or AltSpeed) bit 12 set. | |
43 | * Newer chips need a ForcedSpd bit 13 set to consider the value. | |
08ef7f10 VD |
44 | */ |
45 | ||
a0a0f622 VD |
46 | static int mv88e6xxx_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, |
47 | phy_interface_t mode) | |
48 | { | |
49 | u16 reg; | |
50 | int err; | |
51 | ||
52 | err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, ®); | |
53 | if (err) | |
54 | return err; | |
55 | ||
56 | reg &= ~(PORT_PCS_CTRL_RGMII_DELAY_RXCLK | | |
57 | PORT_PCS_CTRL_RGMII_DELAY_TXCLK); | |
58 | ||
59 | switch (mode) { | |
60 | case PHY_INTERFACE_MODE_RGMII_RXID: | |
61 | reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK; | |
62 | break; | |
63 | case PHY_INTERFACE_MODE_RGMII_TXID: | |
64 | reg |= PORT_PCS_CTRL_RGMII_DELAY_TXCLK; | |
65 | break; | |
66 | case PHY_INTERFACE_MODE_RGMII_ID: | |
67 | reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK | | |
68 | PORT_PCS_CTRL_RGMII_DELAY_TXCLK; | |
69 | break; | |
fedf1865 | 70 | case PHY_INTERFACE_MODE_RGMII: |
a0a0f622 | 71 | break; |
fedf1865 AL |
72 | default: |
73 | return 0; | |
a0a0f622 VD |
74 | } |
75 | ||
76 | err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg); | |
77 | if (err) | |
78 | return err; | |
79 | ||
774439e5 VD |
80 | dev_dbg(chip->dev, "p%d: delay RXCLK %s, TXCLK %s\n", port, |
81 | reg & PORT_PCS_CTRL_RGMII_DELAY_RXCLK ? "yes" : "no", | |
82 | reg & PORT_PCS_CTRL_RGMII_DELAY_TXCLK ? "yes" : "no"); | |
a0a0f622 VD |
83 | |
84 | return 0; | |
85 | } | |
86 | ||
87 | int mv88e6352_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, | |
88 | phy_interface_t mode) | |
89 | { | |
90 | if (port < 5) | |
91 | return -EOPNOTSUPP; | |
92 | ||
93 | return mv88e6xxx_port_set_rgmii_delay(chip, port, mode); | |
94 | } | |
95 | ||
96 | int mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, | |
97 | phy_interface_t mode) | |
98 | { | |
99 | if (port != 0) | |
100 | return -EOPNOTSUPP; | |
101 | ||
102 | return mv88e6xxx_port_set_rgmii_delay(chip, port, mode); | |
103 | } | |
104 | ||
08ef7f10 VD |
105 | int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link) |
106 | { | |
107 | u16 reg; | |
108 | int err; | |
109 | ||
110 | err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, ®); | |
111 | if (err) | |
112 | return err; | |
113 | ||
114 | reg &= ~(PORT_PCS_CTRL_FORCE_LINK | PORT_PCS_CTRL_LINK_UP); | |
115 | ||
116 | switch (link) { | |
117 | case LINK_FORCED_DOWN: | |
118 | reg |= PORT_PCS_CTRL_FORCE_LINK; | |
119 | break; | |
120 | case LINK_FORCED_UP: | |
121 | reg |= PORT_PCS_CTRL_FORCE_LINK | PORT_PCS_CTRL_LINK_UP; | |
122 | break; | |
123 | case LINK_UNFORCED: | |
124 | /* normal link detection */ | |
125 | break; | |
126 | default: | |
127 | return -EINVAL; | |
128 | } | |
129 | ||
130 | err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg); | |
131 | if (err) | |
132 | return err; | |
133 | ||
774439e5 VD |
134 | dev_dbg(chip->dev, "p%d: %s link %s\n", port, |
135 | reg & PORT_PCS_CTRL_FORCE_LINK ? "Force" : "Unforce", | |
136 | reg & PORT_PCS_CTRL_LINK_UP ? "up" : "down"); | |
08ef7f10 VD |
137 | |
138 | return 0; | |
139 | } | |
140 | ||
7f1ae07b VD |
141 | int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup) |
142 | { | |
143 | u16 reg; | |
144 | int err; | |
145 | ||
146 | err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, ®); | |
147 | if (err) | |
148 | return err; | |
149 | ||
150 | reg &= ~(PORT_PCS_CTRL_FORCE_DUPLEX | PORT_PCS_CTRL_DUPLEX_FULL); | |
151 | ||
152 | switch (dup) { | |
153 | case DUPLEX_HALF: | |
154 | reg |= PORT_PCS_CTRL_FORCE_DUPLEX; | |
155 | break; | |
156 | case DUPLEX_FULL: | |
157 | reg |= PORT_PCS_CTRL_FORCE_DUPLEX | PORT_PCS_CTRL_DUPLEX_FULL; | |
158 | break; | |
159 | case DUPLEX_UNFORCED: | |
160 | /* normal duplex detection */ | |
161 | break; | |
162 | default: | |
163 | return -EINVAL; | |
164 | } | |
165 | ||
166 | err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg); | |
167 | if (err) | |
168 | return err; | |
169 | ||
774439e5 VD |
170 | dev_dbg(chip->dev, "p%d: %s %s duplex\n", port, |
171 | reg & PORT_PCS_CTRL_FORCE_DUPLEX ? "Force" : "Unforce", | |
172 | reg & PORT_PCS_CTRL_DUPLEX_FULL ? "full" : "half"); | |
7f1ae07b VD |
173 | |
174 | return 0; | |
175 | } | |
176 | ||
96a2b40c VD |
177 | static int mv88e6xxx_port_set_speed(struct mv88e6xxx_chip *chip, int port, |
178 | int speed, bool alt_bit, bool force_bit) | |
179 | { | |
180 | u16 reg, ctrl; | |
181 | int err; | |
182 | ||
183 | switch (speed) { | |
184 | case 10: | |
185 | ctrl = PORT_PCS_CTRL_SPEED_10; | |
186 | break; | |
187 | case 100: | |
188 | ctrl = PORT_PCS_CTRL_SPEED_100; | |
189 | break; | |
190 | case 200: | |
191 | if (alt_bit) | |
192 | ctrl = PORT_PCS_CTRL_SPEED_100 | PORT_PCS_CTRL_ALTSPEED; | |
193 | else | |
194 | ctrl = PORT_PCS_CTRL_SPEED_200; | |
195 | break; | |
196 | case 1000: | |
197 | ctrl = PORT_PCS_CTRL_SPEED_1000; | |
198 | break; | |
199 | case 2500: | |
740117a8 | 200 | ctrl = PORT_PCS_CTRL_SPEED_10000 | PORT_PCS_CTRL_ALTSPEED; |
96a2b40c VD |
201 | break; |
202 | case 10000: | |
203 | /* all bits set, fall through... */ | |
204 | case SPEED_UNFORCED: | |
205 | ctrl = PORT_PCS_CTRL_SPEED_UNFORCED; | |
206 | break; | |
207 | default: | |
208 | return -EOPNOTSUPP; | |
209 | } | |
210 | ||
211 | err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, ®); | |
212 | if (err) | |
213 | return err; | |
214 | ||
215 | reg &= ~PORT_PCS_CTRL_SPEED_MASK; | |
216 | if (alt_bit) | |
217 | reg &= ~PORT_PCS_CTRL_ALTSPEED; | |
218 | if (force_bit) { | |
219 | reg &= ~PORT_PCS_CTRL_FORCE_SPEED; | |
0b6e3d03 | 220 | if (speed != SPEED_UNFORCED) |
96a2b40c VD |
221 | ctrl |= PORT_PCS_CTRL_FORCE_SPEED; |
222 | } | |
223 | reg |= ctrl; | |
224 | ||
225 | err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg); | |
226 | if (err) | |
227 | return err; | |
228 | ||
229 | if (speed) | |
774439e5 | 230 | dev_dbg(chip->dev, "p%d: Speed set to %d Mbps\n", port, speed); |
96a2b40c | 231 | else |
774439e5 | 232 | dev_dbg(chip->dev, "p%d: Speed unforced\n", port); |
96a2b40c VD |
233 | |
234 | return 0; | |
235 | } | |
236 | ||
237 | /* Support 10, 100, 200 Mbps (e.g. 88E6065 family) */ | |
238 | int mv88e6065_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) | |
239 | { | |
240 | if (speed == SPEED_MAX) | |
241 | speed = 200; | |
242 | ||
243 | if (speed > 200) | |
244 | return -EOPNOTSUPP; | |
245 | ||
246 | /* Setting 200 Mbps on port 0 to 3 selects 100 Mbps */ | |
247 | return mv88e6xxx_port_set_speed(chip, port, speed, false, false); | |
248 | } | |
249 | ||
250 | /* Support 10, 100, 1000 Mbps (e.g. 88E6185 family) */ | |
251 | int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) | |
252 | { | |
253 | if (speed == SPEED_MAX) | |
254 | speed = 1000; | |
255 | ||
256 | if (speed == 200 || speed > 1000) | |
257 | return -EOPNOTSUPP; | |
258 | ||
259 | return mv88e6xxx_port_set_speed(chip, port, speed, false, false); | |
260 | } | |
261 | ||
262 | /* Support 10, 100, 200, 1000 Mbps (e.g. 88E6352 family) */ | |
263 | int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) | |
264 | { | |
265 | if (speed == SPEED_MAX) | |
266 | speed = 1000; | |
267 | ||
268 | if (speed > 1000) | |
269 | return -EOPNOTSUPP; | |
270 | ||
271 | if (speed == 200 && port < 5) | |
272 | return -EOPNOTSUPP; | |
273 | ||
274 | return mv88e6xxx_port_set_speed(chip, port, speed, true, false); | |
275 | } | |
276 | ||
277 | /* Support 10, 100, 200, 1000, 2500 Mbps (e.g. 88E6390) */ | |
278 | int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) | |
279 | { | |
280 | if (speed == SPEED_MAX) | |
281 | speed = port < 9 ? 1000 : 2500; | |
282 | ||
283 | if (speed > 2500) | |
284 | return -EOPNOTSUPP; | |
285 | ||
286 | if (speed == 200 && port != 0) | |
287 | return -EOPNOTSUPP; | |
288 | ||
289 | if (speed == 2500 && port < 9) | |
290 | return -EOPNOTSUPP; | |
291 | ||
292 | return mv88e6xxx_port_set_speed(chip, port, speed, true, true); | |
293 | } | |
294 | ||
295 | /* Support 10, 100, 200, 1000, 2500, 10000 Mbps (e.g. 88E6190X) */ | |
296 | int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) | |
297 | { | |
298 | if (speed == SPEED_MAX) | |
299 | speed = port < 9 ? 1000 : 10000; | |
300 | ||
301 | if (speed == 200 && port != 0) | |
302 | return -EOPNOTSUPP; | |
303 | ||
304 | if (speed >= 2500 && port < 9) | |
305 | return -EOPNOTSUPP; | |
306 | ||
307 | return mv88e6xxx_port_set_speed(chip, port, speed, true, true); | |
308 | } | |
309 | ||
f39908d3 AL |
310 | int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, |
311 | phy_interface_t mode) | |
312 | { | |
313 | u16 reg; | |
314 | u16 cmode; | |
315 | int err; | |
316 | ||
317 | if (mode == PHY_INTERFACE_MODE_NA) | |
318 | return 0; | |
319 | ||
320 | if (port != 9 && port != 10) | |
321 | return -EOPNOTSUPP; | |
322 | ||
323 | switch (mode) { | |
324 | case PHY_INTERFACE_MODE_1000BASEX: | |
5f83dc93 | 325 | cmode = MV88E6XXX_PORT_STS_CMODE_1000BASE_X; |
f39908d3 AL |
326 | break; |
327 | case PHY_INTERFACE_MODE_SGMII: | |
5f83dc93 | 328 | cmode = MV88E6XXX_PORT_STS_CMODE_SGMII; |
f39908d3 AL |
329 | break; |
330 | case PHY_INTERFACE_MODE_2500BASEX: | |
5f83dc93 | 331 | cmode = MV88E6XXX_PORT_STS_CMODE_2500BASEX; |
f39908d3 AL |
332 | break; |
333 | case PHY_INTERFACE_MODE_XGMII: | |
5f83dc93 | 334 | cmode = MV88E6XXX_PORT_STS_CMODE_XAUI; |
f39908d3 AL |
335 | break; |
336 | case PHY_INTERFACE_MODE_RXAUI: | |
5f83dc93 | 337 | cmode = MV88E6XXX_PORT_STS_CMODE_RXAUI; |
f39908d3 AL |
338 | break; |
339 | default: | |
340 | cmode = 0; | |
341 | } | |
342 | ||
343 | if (cmode) { | |
5f83dc93 | 344 | err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®); |
f39908d3 AL |
345 | if (err) |
346 | return err; | |
347 | ||
5f83dc93 | 348 | reg &= ~MV88E6XXX_PORT_STS_CMODE_MASK; |
f39908d3 AL |
349 | reg |= cmode; |
350 | ||
5f83dc93 | 351 | err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_STS, reg); |
f39908d3 AL |
352 | if (err) |
353 | return err; | |
354 | } | |
355 | ||
356 | return 0; | |
357 | } | |
358 | ||
359 | int mv88e6xxx_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode) | |
360 | { | |
361 | int err; | |
362 | u16 reg; | |
363 | ||
5f83dc93 | 364 | err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®); |
f39908d3 AL |
365 | if (err) |
366 | return err; | |
367 | ||
5f83dc93 | 368 | *cmode = reg & MV88E6XXX_PORT_STS_CMODE_MASK; |
f39908d3 AL |
369 | |
370 | return 0; | |
371 | } | |
372 | ||
b35d322a AL |
373 | /* Offset 0x02: Pause Control |
374 | * | |
375 | * Do not limit the period of time that this port can be paused for by | |
376 | * the remote end or the period of time that this port can pause the | |
377 | * remote end. | |
378 | */ | |
0898432c VD |
379 | int mv88e6097_port_pause_limit(struct mv88e6xxx_chip *chip, int port, u8 in, |
380 | u8 out) | |
b35d322a | 381 | { |
0898432c | 382 | return mv88e6xxx_port_write(chip, port, PORT_PAUSE_CTRL, out << 8 | in); |
b35d322a AL |
383 | } |
384 | ||
0898432c VD |
385 | int mv88e6390_port_pause_limit(struct mv88e6xxx_chip *chip, int port, u8 in, |
386 | u8 out) | |
3ce0e65e AL |
387 | { |
388 | int err; | |
389 | ||
390 | err = mv88e6xxx_port_write(chip, port, PORT_PAUSE_CTRL, | |
0898432c | 391 | PORT_FLOW_CTRL_LIMIT_IN | in); |
3ce0e65e AL |
392 | if (err) |
393 | return err; | |
394 | ||
395 | return mv88e6xxx_port_write(chip, port, PORT_PAUSE_CTRL, | |
0898432c | 396 | PORT_FLOW_CTRL_LIMIT_OUT | out); |
3ce0e65e AL |
397 | } |
398 | ||
e28def33 VD |
399 | /* Offset 0x04: Port Control Register */ |
400 | ||
401 | static const char * const mv88e6xxx_port_state_names[] = { | |
402 | [PORT_CONTROL_STATE_DISABLED] = "Disabled", | |
403 | [PORT_CONTROL_STATE_BLOCKING] = "Blocking/Listening", | |
404 | [PORT_CONTROL_STATE_LEARNING] = "Learning", | |
405 | [PORT_CONTROL_STATE_FORWARDING] = "Forwarding", | |
406 | }; | |
407 | ||
408 | int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state) | |
409 | { | |
410 | u16 reg; | |
411 | int err; | |
412 | ||
413 | err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, ®); | |
414 | if (err) | |
415 | return err; | |
416 | ||
417 | reg &= ~PORT_CONTROL_STATE_MASK; | |
f894c29c VD |
418 | |
419 | switch (state) { | |
420 | case BR_STATE_DISABLED: | |
421 | state = PORT_CONTROL_STATE_DISABLED; | |
422 | break; | |
423 | case BR_STATE_BLOCKING: | |
424 | case BR_STATE_LISTENING: | |
425 | state = PORT_CONTROL_STATE_BLOCKING; | |
426 | break; | |
427 | case BR_STATE_LEARNING: | |
428 | state = PORT_CONTROL_STATE_LEARNING; | |
429 | break; | |
430 | case BR_STATE_FORWARDING: | |
431 | state = PORT_CONTROL_STATE_FORWARDING; | |
432 | break; | |
433 | default: | |
434 | return -EINVAL; | |
435 | } | |
436 | ||
e28def33 VD |
437 | reg |= state; |
438 | ||
439 | err = mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg); | |
440 | if (err) | |
441 | return err; | |
442 | ||
774439e5 VD |
443 | dev_dbg(chip->dev, "p%d: PortState set to %s\n", port, |
444 | mv88e6xxx_port_state_names[state]); | |
e28def33 VD |
445 | |
446 | return 0; | |
447 | } | |
5a7921f4 | 448 | |
56995cbc | 449 | int mv88e6xxx_port_set_egress_mode(struct mv88e6xxx_chip *chip, int port, |
31bef4e9 | 450 | enum mv88e6xxx_egress_mode mode) |
56995cbc AL |
451 | { |
452 | int err; | |
453 | u16 reg; | |
454 | ||
455 | err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, ®); | |
456 | if (err) | |
457 | return err; | |
458 | ||
459 | reg &= ~PORT_CONTROL_EGRESS_MASK; | |
31bef4e9 VD |
460 | |
461 | switch (mode) { | |
462 | case MV88E6XXX_EGRESS_MODE_UNMODIFIED: | |
463 | reg |= PORT_CONTROL_EGRESS_UNMODIFIED; | |
464 | break; | |
465 | case MV88E6XXX_EGRESS_MODE_UNTAGGED: | |
466 | reg |= PORT_CONTROL_EGRESS_UNTAGGED; | |
467 | break; | |
468 | case MV88E6XXX_EGRESS_MODE_TAGGED: | |
469 | reg |= PORT_CONTROL_EGRESS_TAGGED; | |
470 | break; | |
471 | case MV88E6XXX_EGRESS_MODE_ETHERTYPE: | |
472 | reg |= PORT_CONTROL_EGRESS_ADD_TAG; | |
473 | break; | |
474 | default: | |
475 | return -EINVAL; | |
476 | } | |
56995cbc AL |
477 | |
478 | return mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg); | |
479 | } | |
480 | ||
481 | int mv88e6085_port_set_frame_mode(struct mv88e6xxx_chip *chip, int port, | |
482 | enum mv88e6xxx_frame_mode mode) | |
483 | { | |
484 | int err; | |
485 | u16 reg; | |
486 | ||
487 | err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, ®); | |
488 | if (err) | |
489 | return err; | |
490 | ||
5461bd41 | 491 | reg &= ~PORT_CONTROL_FRAME_MASK; |
56995cbc AL |
492 | |
493 | switch (mode) { | |
494 | case MV88E6XXX_FRAME_MODE_NORMAL: | |
495 | reg |= PORT_CONTROL_FRAME_MODE_NORMAL; | |
496 | break; | |
497 | case MV88E6XXX_FRAME_MODE_DSA: | |
498 | reg |= PORT_CONTROL_FRAME_MODE_DSA; | |
499 | break; | |
500 | default: | |
501 | return -EINVAL; | |
502 | } | |
503 | ||
504 | return mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg); | |
505 | } | |
506 | ||
507 | int mv88e6351_port_set_frame_mode(struct mv88e6xxx_chip *chip, int port, | |
508 | enum mv88e6xxx_frame_mode mode) | |
509 | { | |
510 | int err; | |
511 | u16 reg; | |
512 | ||
513 | err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, ®); | |
514 | if (err) | |
515 | return err; | |
516 | ||
517 | reg &= ~PORT_CONTROL_FRAME_MASK; | |
518 | ||
519 | switch (mode) { | |
520 | case MV88E6XXX_FRAME_MODE_NORMAL: | |
521 | reg |= PORT_CONTROL_FRAME_MODE_NORMAL; | |
522 | break; | |
523 | case MV88E6XXX_FRAME_MODE_DSA: | |
524 | reg |= PORT_CONTROL_FRAME_MODE_DSA; | |
525 | break; | |
526 | case MV88E6XXX_FRAME_MODE_PROVIDER: | |
527 | reg |= PORT_CONTROL_FRAME_MODE_PROVIDER; | |
528 | break; | |
529 | case MV88E6XXX_FRAME_MODE_ETHERTYPE: | |
530 | reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA; | |
531 | break; | |
532 | default: | |
533 | return -EINVAL; | |
534 | } | |
535 | ||
536 | return mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg); | |
537 | } | |
538 | ||
601aeed3 VD |
539 | static int mv88e6185_port_set_forward_unknown(struct mv88e6xxx_chip *chip, |
540 | int port, bool unicast) | |
56995cbc AL |
541 | { |
542 | int err; | |
543 | u16 reg; | |
544 | ||
545 | err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, ®); | |
546 | if (err) | |
547 | return err; | |
548 | ||
601aeed3 | 549 | if (unicast) |
56995cbc AL |
550 | reg |= PORT_CONTROL_FORWARD_UNKNOWN; |
551 | else | |
552 | reg &= ~PORT_CONTROL_FORWARD_UNKNOWN; | |
553 | ||
554 | return mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg); | |
555 | } | |
556 | ||
601aeed3 VD |
557 | int mv88e6352_port_set_egress_floods(struct mv88e6xxx_chip *chip, int port, |
558 | bool unicast, bool multicast) | |
56995cbc AL |
559 | { |
560 | int err; | |
561 | u16 reg; | |
562 | ||
563 | err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, ®); | |
564 | if (err) | |
565 | return err; | |
566 | ||
601aeed3 VD |
567 | reg &= ~PORT_CONTROL_EGRESS_FLOODS_MASK; |
568 | ||
569 | if (unicast && multicast) | |
570 | reg |= PORT_CONTROL_EGRESS_FLOODS_ALL_UNKNOWN_DA; | |
571 | else if (unicast) | |
572 | reg |= PORT_CONTROL_EGRESS_FLOODS_NO_UNKNOWN_MC_DA; | |
573 | else if (multicast) | |
574 | reg |= PORT_CONTROL_EGRESS_FLOODS_NO_UNKNOWN_UC_DA; | |
56995cbc | 575 | else |
601aeed3 | 576 | reg |= PORT_CONTROL_EGRESS_FLOODS_NO_UNKNOWN_DA; |
56995cbc AL |
577 | |
578 | return mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg); | |
579 | } | |
580 | ||
b4e48c50 VD |
581 | /* Offset 0x05: Port Control 1 */ |
582 | ||
ea698f4f VD |
583 | int mv88e6xxx_port_set_message_port(struct mv88e6xxx_chip *chip, int port, |
584 | bool message_port) | |
585 | { | |
586 | u16 val; | |
587 | int err; | |
588 | ||
589 | err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, &val); | |
590 | if (err) | |
591 | return err; | |
592 | ||
593 | if (message_port) | |
594 | val |= PORT_CONTROL_1_MESSAGE_PORT; | |
595 | else | |
596 | val &= ~PORT_CONTROL_1_MESSAGE_PORT; | |
597 | ||
598 | return mv88e6xxx_port_write(chip, port, PORT_CONTROL_1, val); | |
599 | } | |
600 | ||
5a7921f4 VD |
601 | /* Offset 0x06: Port Based VLAN Map */ |
602 | ||
603 | int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map) | |
604 | { | |
4d294af2 | 605 | const u16 mask = mv88e6xxx_port_mask(chip); |
5a7921f4 VD |
606 | u16 reg; |
607 | int err; | |
608 | ||
609 | err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, ®); | |
610 | if (err) | |
611 | return err; | |
612 | ||
613 | reg &= ~mask; | |
614 | reg |= map & mask; | |
615 | ||
616 | err = mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg); | |
617 | if (err) | |
618 | return err; | |
619 | ||
774439e5 | 620 | dev_dbg(chip->dev, "p%d: VLANTable set to %.3x\n", port, map); |
5a7921f4 VD |
621 | |
622 | return 0; | |
623 | } | |
b4e48c50 VD |
624 | |
625 | int mv88e6xxx_port_get_fid(struct mv88e6xxx_chip *chip, int port, u16 *fid) | |
626 | { | |
627 | const u16 upper_mask = (mv88e6xxx_num_databases(chip) - 1) >> 4; | |
628 | u16 reg; | |
629 | int err; | |
630 | ||
631 | /* Port's default FID lower 4 bits are located in reg 0x06, offset 12 */ | |
632 | err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, ®); | |
633 | if (err) | |
634 | return err; | |
635 | ||
636 | *fid = (reg & 0xf000) >> 12; | |
637 | ||
638 | /* Port's default FID upper bits are located in reg 0x05, offset 0 */ | |
639 | if (upper_mask) { | |
640 | err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, ®); | |
641 | if (err) | |
642 | return err; | |
643 | ||
644 | *fid |= (reg & upper_mask) << 4; | |
645 | } | |
646 | ||
647 | return 0; | |
648 | } | |
649 | ||
650 | int mv88e6xxx_port_set_fid(struct mv88e6xxx_chip *chip, int port, u16 fid) | |
651 | { | |
652 | const u16 upper_mask = (mv88e6xxx_num_databases(chip) - 1) >> 4; | |
653 | u16 reg; | |
654 | int err; | |
655 | ||
656 | if (fid >= mv88e6xxx_num_databases(chip)) | |
657 | return -EINVAL; | |
658 | ||
659 | /* Port's default FID lower 4 bits are located in reg 0x06, offset 12 */ | |
660 | err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, ®); | |
661 | if (err) | |
662 | return err; | |
663 | ||
664 | reg &= 0x0fff; | |
665 | reg |= (fid & 0x000f) << 12; | |
666 | ||
667 | err = mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg); | |
668 | if (err) | |
669 | return err; | |
670 | ||
671 | /* Port's default FID upper bits are located in reg 0x05, offset 0 */ | |
672 | if (upper_mask) { | |
673 | err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, ®); | |
674 | if (err) | |
675 | return err; | |
676 | ||
677 | reg &= ~upper_mask; | |
678 | reg |= (fid >> 4) & upper_mask; | |
679 | ||
680 | err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_1, reg); | |
681 | if (err) | |
682 | return err; | |
683 | } | |
684 | ||
774439e5 | 685 | dev_dbg(chip->dev, "p%d: FID set to %u\n", port, fid); |
b4e48c50 VD |
686 | |
687 | return 0; | |
688 | } | |
77064f37 VD |
689 | |
690 | /* Offset 0x07: Default Port VLAN ID & Priority */ | |
691 | ||
692 | int mv88e6xxx_port_get_pvid(struct mv88e6xxx_chip *chip, int port, u16 *pvid) | |
693 | { | |
694 | u16 reg; | |
695 | int err; | |
696 | ||
697 | err = mv88e6xxx_port_read(chip, port, PORT_DEFAULT_VLAN, ®); | |
698 | if (err) | |
699 | return err; | |
700 | ||
701 | *pvid = reg & PORT_DEFAULT_VLAN_MASK; | |
702 | ||
703 | return 0; | |
704 | } | |
705 | ||
706 | int mv88e6xxx_port_set_pvid(struct mv88e6xxx_chip *chip, int port, u16 pvid) | |
707 | { | |
708 | u16 reg; | |
709 | int err; | |
710 | ||
711 | err = mv88e6xxx_port_read(chip, port, PORT_DEFAULT_VLAN, ®); | |
712 | if (err) | |
713 | return err; | |
714 | ||
715 | reg &= ~PORT_DEFAULT_VLAN_MASK; | |
716 | reg |= pvid & PORT_DEFAULT_VLAN_MASK; | |
717 | ||
718 | err = mv88e6xxx_port_write(chip, port, PORT_DEFAULT_VLAN, reg); | |
719 | if (err) | |
720 | return err; | |
721 | ||
774439e5 | 722 | dev_dbg(chip->dev, "p%d: DefaultVID set to %u\n", port, pvid); |
77064f37 VD |
723 | |
724 | return 0; | |
725 | } | |
385a0995 VD |
726 | |
727 | /* Offset 0x08: Port Control 2 Register */ | |
728 | ||
729 | static const char * const mv88e6xxx_port_8021q_mode_names[] = { | |
730 | [PORT_CONTROL_2_8021Q_DISABLED] = "Disabled", | |
731 | [PORT_CONTROL_2_8021Q_FALLBACK] = "Fallback", | |
732 | [PORT_CONTROL_2_8021Q_CHECK] = "Check", | |
733 | [PORT_CONTROL_2_8021Q_SECURE] = "Secure", | |
734 | }; | |
735 | ||
601aeed3 VD |
736 | static int mv88e6185_port_set_default_forward(struct mv88e6xxx_chip *chip, |
737 | int port, bool multicast) | |
a23b2961 AL |
738 | { |
739 | int err; | |
740 | u16 reg; | |
741 | ||
742 | err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, ®); | |
743 | if (err) | |
744 | return err; | |
745 | ||
601aeed3 VD |
746 | if (multicast) |
747 | reg |= PORT_CONTROL_2_DEFAULT_FORWARD; | |
a23b2961 | 748 | else |
601aeed3 | 749 | reg &= ~PORT_CONTROL_2_DEFAULT_FORWARD; |
a23b2961 AL |
750 | |
751 | return mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg); | |
752 | } | |
753 | ||
601aeed3 VD |
754 | int mv88e6185_port_set_egress_floods(struct mv88e6xxx_chip *chip, int port, |
755 | bool unicast, bool multicast) | |
756 | { | |
757 | int err; | |
758 | ||
759 | err = mv88e6185_port_set_forward_unknown(chip, port, unicast); | |
760 | if (err) | |
761 | return err; | |
762 | ||
763 | return mv88e6185_port_set_default_forward(chip, port, multicast); | |
764 | } | |
765 | ||
a23b2961 AL |
766 | int mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port, |
767 | int upstream_port) | |
768 | { | |
769 | int err; | |
770 | u16 reg; | |
771 | ||
772 | err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, ®); | |
773 | if (err) | |
774 | return err; | |
775 | ||
776 | reg &= ~PORT_CONTROL_2_UPSTREAM_MASK; | |
777 | reg |= upstream_port; | |
778 | ||
779 | return mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg); | |
780 | } | |
781 | ||
385a0995 VD |
782 | int mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port, |
783 | u16 mode) | |
784 | { | |
785 | u16 reg; | |
786 | int err; | |
787 | ||
788 | err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, ®); | |
789 | if (err) | |
790 | return err; | |
791 | ||
792 | reg &= ~PORT_CONTROL_2_8021Q_MASK; | |
793 | reg |= mode & PORT_CONTROL_2_8021Q_MASK; | |
794 | ||
795 | err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg); | |
796 | if (err) | |
797 | return err; | |
798 | ||
774439e5 VD |
799 | dev_dbg(chip->dev, "p%d: 802.1QMode set to %s\n", port, |
800 | mv88e6xxx_port_8021q_mode_names[mode]); | |
385a0995 VD |
801 | |
802 | return 0; | |
803 | } | |
ef0a7318 | 804 | |
a23b2961 AL |
805 | int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port) |
806 | { | |
807 | u16 reg; | |
808 | int err; | |
809 | ||
810 | err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, ®); | |
811 | if (err) | |
812 | return err; | |
813 | ||
814 | reg |= PORT_CONTROL_2_MAP_DA; | |
815 | ||
816 | return mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg); | |
817 | } | |
818 | ||
cd782656 VD |
819 | int mv88e6165_port_set_jumbo_size(struct mv88e6xxx_chip *chip, int port, |
820 | size_t size) | |
5f436666 AL |
821 | { |
822 | u16 reg; | |
823 | int err; | |
824 | ||
825 | err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, ®); | |
826 | if (err) | |
827 | return err; | |
828 | ||
cd782656 VD |
829 | reg &= ~PORT_CONTROL_2_JUMBO_MASK; |
830 | ||
831 | if (size <= 1522) | |
832 | reg |= PORT_CONTROL_2_JUMBO_1522; | |
833 | else if (size <= 2048) | |
834 | reg |= PORT_CONTROL_2_JUMBO_2048; | |
835 | else if (size <= 10240) | |
836 | reg |= PORT_CONTROL_2_JUMBO_10240; | |
837 | else | |
838 | return -ERANGE; | |
5f436666 AL |
839 | |
840 | return mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg); | |
841 | } | |
842 | ||
ef70b111 AL |
843 | /* Offset 0x09: Port Rate Control */ |
844 | ||
845 | int mv88e6095_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port) | |
846 | { | |
847 | return mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL, 0x0000); | |
848 | } | |
849 | ||
850 | int mv88e6097_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port) | |
851 | { | |
852 | return mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL, 0x0001); | |
853 | } | |
854 | ||
c8c94891 VD |
855 | /* Offset 0x0C: Port ATU Control */ |
856 | ||
857 | int mv88e6xxx_port_disable_learn_limit(struct mv88e6xxx_chip *chip, int port) | |
858 | { | |
859 | return mv88e6xxx_port_write(chip, port, PORT_ATU_CONTROL, 0); | |
860 | } | |
861 | ||
9dbfb4e1 VD |
862 | /* Offset 0x0D: (Priority) Override Register */ |
863 | ||
864 | int mv88e6xxx_port_disable_pri_override(struct mv88e6xxx_chip *chip, int port) | |
865 | { | |
866 | return mv88e6xxx_port_write(chip, port, PORT_PRI_OVERRIDE, 0); | |
867 | } | |
868 | ||
56995cbc AL |
869 | /* Offset 0x0f: Port Ether type */ |
870 | ||
871 | int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port, | |
872 | u16 etype) | |
873 | { | |
874 | return mv88e6xxx_port_write(chip, port, PORT_ETH_TYPE, etype); | |
875 | } | |
876 | ||
ef0a7318 AL |
877 | /* Offset 0x18: Port IEEE Priority Remapping Registers [0-3] |
878 | * Offset 0x19: Port IEEE Priority Remapping Registers [4-7] | |
879 | */ | |
880 | ||
881 | int mv88e6095_port_tag_remap(struct mv88e6xxx_chip *chip, int port) | |
882 | { | |
883 | int err; | |
884 | ||
885 | /* Use a direct priority mapping for all IEEE tagged frames */ | |
886 | err = mv88e6xxx_port_write(chip, port, PORT_TAG_REGMAP_0123, 0x3210); | |
887 | if (err) | |
888 | return err; | |
889 | ||
890 | return mv88e6xxx_port_write(chip, port, PORT_TAG_REGMAP_4567, 0x7654); | |
891 | } | |
892 | ||
893 | static int mv88e6xxx_port_ieeepmt_write(struct mv88e6xxx_chip *chip, | |
894 | int port, u16 table, | |
895 | u8 pointer, u16 data) | |
896 | { | |
897 | u16 reg; | |
898 | ||
899 | reg = PORT_IEEE_PRIO_MAP_TABLE_UPDATE | | |
900 | table | | |
901 | (pointer << PORT_IEEE_PRIO_MAP_TABLE_POINTER_SHIFT) | | |
902 | data; | |
903 | ||
904 | return mv88e6xxx_port_write(chip, port, PORT_IEEE_PRIO_MAP_TABLE, reg); | |
905 | } | |
906 | ||
907 | int mv88e6390_port_tag_remap(struct mv88e6xxx_chip *chip, int port) | |
908 | { | |
909 | int err, i; | |
910 | ||
911 | for (i = 0; i <= 7; i++) { | |
912 | err = mv88e6xxx_port_ieeepmt_write( | |
913 | chip, port, PORT_IEEE_PRIO_MAP_TABLE_INGRESS_PCP, | |
914 | i, (i | i << 4)); | |
915 | if (err) | |
916 | return err; | |
917 | ||
918 | err = mv88e6xxx_port_ieeepmt_write( | |
919 | chip, port, PORT_IEEE_PRIO_MAP_TABLE_EGRESS_GREEN_PCP, | |
920 | i, i); | |
921 | if (err) | |
922 | return err; | |
923 | ||
924 | err = mv88e6xxx_port_ieeepmt_write( | |
925 | chip, port, PORT_IEEE_PRIO_MAP_TABLE_EGRESS_YELLOW_PCP, | |
926 | i, i); | |
927 | if (err) | |
928 | return err; | |
929 | ||
930 | err = mv88e6xxx_port_ieeepmt_write( | |
931 | chip, port, PORT_IEEE_PRIO_MAP_TABLE_EGRESS_AVB_PCP, | |
932 | i, i); | |
933 | if (err) | |
934 | return err; | |
935 | } | |
936 | ||
937 | return 0; | |
938 | } |