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