blob: 46e73ca0ac4d5426f92a606d644727ef7b176572 [file] [log] [blame]
Vivien Didelot18abed22016-11-04 03:23:26 +01001/*
2 * Marvell 88E6xxx Switch Port Registers support
3 *
4 * Copyright (c) 2008 Marvell Semiconductor
5 *
Vivien Didelot4333d612017-03-28 15:10:36 -04006 * Copyright (c) 2016-2017 Savoir-faire Linux Inc.
7 * Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Vivien Didelot18abed22016-11-04 03:23:26 +01008 *
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
Vivien Didelotf894c292017-06-08 18:34:10 -040015#include <linux/if_bridge.h>
Andrew Lunnf39908d2017-02-04 20:02:50 +010016#include <linux/phy.h>
Vivien Didelot4d5f2ba72017-06-02 17:06:15 -040017
18#include "chip.h"
Vivien Didelot18abed22016-11-04 03:23:26 +010019#include "port.h"
20
21int 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
29int 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}
Vivien Didelote28def332016-11-04 03:23:27 +010036
Vivien Didelot08ef7f12016-11-04 03:23:32 +010037/* Offset 0x01: MAC (or PCS or Physical) Control Register
38 *
39 * Link, Duplex and Flow Control have one force bit, one value bit.
Vivien Didelot96a2b402016-11-04 03:23:35 +010040 *
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.
Vivien Didelot08ef7f12016-11-04 03:23:32 +010044 */
45
Vivien Didelota0a0f622016-11-04 03:23:34 +010046static 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, &reg);
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;
Andrew Lunnfedf1862016-11-10 15:44:00 +010070 case PHY_INTERFACE_MODE_RGMII:
Vivien Didelota0a0f622016-11-04 03:23:34 +010071 break;
Andrew Lunnfedf1862016-11-10 15:44:00 +010072 default:
73 return 0;
Vivien Didelota0a0f622016-11-04 03:23:34 +010074 }
75
76 err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
77 if (err)
78 return err;
79
Vivien Didelot774439e52017-06-08 18:34:08 -040080 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");
Vivien Didelota0a0f622016-11-04 03:23:34 +010083
84 return 0;
85}
86
87int 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
96int 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
Vivien Didelot08ef7f12016-11-04 03:23:32 +0100105int 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, &reg);
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
Vivien Didelot774439e52017-06-08 18:34:08 -0400134 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");
Vivien Didelot08ef7f12016-11-04 03:23:32 +0100137
138 return 0;
139}
140
Vivien Didelot7f1ae072016-11-04 03:23:33 +0100141int 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, &reg);
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
Vivien Didelot774439e52017-06-08 18:34:08 -0400170 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");
Vivien Didelot7f1ae072016-11-04 03:23:33 +0100173
174 return 0;
175}
176
Vivien Didelot96a2b402016-11-04 03:23:35 +0100177static 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:
Andrew Lunn740117a2017-02-02 00:46:16 +0100200 ctrl = PORT_PCS_CTRL_SPEED_10000 | PORT_PCS_CTRL_ALTSPEED;
Vivien Didelot96a2b402016-11-04 03:23:35 +0100201 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, &reg);
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;
Andrew Lunn0b6e3d02016-11-16 04:26:48 +0100220 if (speed != SPEED_UNFORCED)
Vivien Didelot96a2b402016-11-04 03:23:35 +0100221 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)
Vivien Didelot774439e52017-06-08 18:34:08 -0400230 dev_dbg(chip->dev, "p%d: Speed set to %d Mbps\n", port, speed);
Vivien Didelot96a2b402016-11-04 03:23:35 +0100231 else
Vivien Didelot774439e52017-06-08 18:34:08 -0400232 dev_dbg(chip->dev, "p%d: Speed unforced\n", port);
Vivien Didelot96a2b402016-11-04 03:23:35 +0100233
234 return 0;
235}
236
237/* Support 10, 100, 200 Mbps (e.g. 88E6065 family) */
238int 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) */
251int 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) */
263int 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) */
278int 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) */
296int 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
Andrew Lunnf39908d2017-02-04 20:02:50 +0100310int 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:
325 cmode = PORT_STATUS_CMODE_1000BASE_X;
326 break;
327 case PHY_INTERFACE_MODE_SGMII:
328 cmode = PORT_STATUS_CMODE_SGMII;
329 break;
330 case PHY_INTERFACE_MODE_2500BASEX:
331 cmode = PORT_STATUS_CMODE_2500BASEX;
332 break;
333 case PHY_INTERFACE_MODE_XGMII:
334 cmode = PORT_STATUS_CMODE_XAUI;
335 break;
336 case PHY_INTERFACE_MODE_RXAUI:
337 cmode = PORT_STATUS_CMODE_RXAUI;
338 break;
339 default:
340 cmode = 0;
341 }
342
343 if (cmode) {
344 err = mv88e6xxx_port_read(chip, port, PORT_STATUS, &reg);
345 if (err)
346 return err;
347
348 reg &= ~PORT_STATUS_CMODE_MASK;
349 reg |= cmode;
350
351 err = mv88e6xxx_port_write(chip, port, PORT_STATUS, reg);
352 if (err)
353 return err;
354 }
355
356 return 0;
357}
358
359int mv88e6xxx_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode)
360{
361 int err;
362 u16 reg;
363
364 err = mv88e6xxx_port_read(chip, port, PORT_STATUS, &reg);
365 if (err)
366 return err;
367
368 *cmode = reg & PORT_STATUS_CMODE_MASK;
369
370 return 0;
371}
372
Andrew Lunnb35d322a2016-12-03 04:45:19 +0100373/* 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 */
379int mv88e6097_port_pause_config(struct mv88e6xxx_chip *chip, int port)
380{
381 return mv88e6xxx_port_write(chip, port, PORT_PAUSE_CTRL, 0x0000);
382}
383
Andrew Lunn3ce0e652016-12-03 04:45:20 +0100384int mv88e6390_port_pause_config(struct mv88e6xxx_chip *chip, int port)
385{
386 int err;
387
388 err = mv88e6xxx_port_write(chip, port, PORT_PAUSE_CTRL,
389 PORT_FLOW_CTRL_LIMIT_IN | 0);
390 if (err)
391 return err;
392
393 return mv88e6xxx_port_write(chip, port, PORT_PAUSE_CTRL,
394 PORT_FLOW_CTRL_LIMIT_OUT | 0);
395}
396
Vivien Didelote28def332016-11-04 03:23:27 +0100397/* Offset 0x04: Port Control Register */
398
399static const char * const mv88e6xxx_port_state_names[] = {
400 [PORT_CONTROL_STATE_DISABLED] = "Disabled",
401 [PORT_CONTROL_STATE_BLOCKING] = "Blocking/Listening",
402 [PORT_CONTROL_STATE_LEARNING] = "Learning",
403 [PORT_CONTROL_STATE_FORWARDING] = "Forwarding",
404};
405
406int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state)
407{
408 u16 reg;
409 int err;
410
411 err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, &reg);
412 if (err)
413 return err;
414
415 reg &= ~PORT_CONTROL_STATE_MASK;
Vivien Didelotf894c292017-06-08 18:34:10 -0400416
417 switch (state) {
418 case BR_STATE_DISABLED:
419 state = PORT_CONTROL_STATE_DISABLED;
420 break;
421 case BR_STATE_BLOCKING:
422 case BR_STATE_LISTENING:
423 state = PORT_CONTROL_STATE_BLOCKING;
424 break;
425 case BR_STATE_LEARNING:
426 state = PORT_CONTROL_STATE_LEARNING;
427 break;
428 case BR_STATE_FORWARDING:
429 state = PORT_CONTROL_STATE_FORWARDING;
430 break;
431 default:
432 return -EINVAL;
433 }
434
Vivien Didelote28def332016-11-04 03:23:27 +0100435 reg |= state;
436
437 err = mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
438 if (err)
439 return err;
440
Vivien Didelot774439e52017-06-08 18:34:08 -0400441 dev_dbg(chip->dev, "p%d: PortState set to %s\n", port,
442 mv88e6xxx_port_state_names[state]);
Vivien Didelote28def332016-11-04 03:23:27 +0100443
444 return 0;
445}
Vivien Didelot5a7921f2016-11-04 03:23:28 +0100446
Andrew Lunn56995cb2016-12-03 04:35:19 +0100447int mv88e6xxx_port_set_egress_mode(struct mv88e6xxx_chip *chip, int port,
Vivien Didelot31bef4e2017-06-08 18:34:09 -0400448 enum mv88e6xxx_egress_mode mode)
Andrew Lunn56995cb2016-12-03 04:35:19 +0100449{
450 int err;
451 u16 reg;
452
453 err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, &reg);
454 if (err)
455 return err;
456
457 reg &= ~PORT_CONTROL_EGRESS_MASK;
Vivien Didelot31bef4e2017-06-08 18:34:09 -0400458
459 switch (mode) {
460 case MV88E6XXX_EGRESS_MODE_UNMODIFIED:
461 reg |= PORT_CONTROL_EGRESS_UNMODIFIED;
462 break;
463 case MV88E6XXX_EGRESS_MODE_UNTAGGED:
464 reg |= PORT_CONTROL_EGRESS_UNTAGGED;
465 break;
466 case MV88E6XXX_EGRESS_MODE_TAGGED:
467 reg |= PORT_CONTROL_EGRESS_TAGGED;
468 break;
469 case MV88E6XXX_EGRESS_MODE_ETHERTYPE:
470 reg |= PORT_CONTROL_EGRESS_ADD_TAG;
471 break;
472 default:
473 return -EINVAL;
474 }
Andrew Lunn56995cb2016-12-03 04:35:19 +0100475
476 return mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
477}
478
479int mv88e6085_port_set_frame_mode(struct mv88e6xxx_chip *chip, int port,
480 enum mv88e6xxx_frame_mode mode)
481{
482 int err;
483 u16 reg;
484
485 err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, &reg);
486 if (err)
487 return err;
488
Vivien Didelot5461bd42017-06-05 18:17:16 -0400489 reg &= ~PORT_CONTROL_FRAME_MASK;
Andrew Lunn56995cb2016-12-03 04:35:19 +0100490
491 switch (mode) {
492 case MV88E6XXX_FRAME_MODE_NORMAL:
493 reg |= PORT_CONTROL_FRAME_MODE_NORMAL;
494 break;
495 case MV88E6XXX_FRAME_MODE_DSA:
496 reg |= PORT_CONTROL_FRAME_MODE_DSA;
497 break;
498 default:
499 return -EINVAL;
500 }
501
502 return mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
503}
504
505int mv88e6351_port_set_frame_mode(struct mv88e6xxx_chip *chip, int port,
506 enum mv88e6xxx_frame_mode mode)
507{
508 int err;
509 u16 reg;
510
511 err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, &reg);
512 if (err)
513 return err;
514
515 reg &= ~PORT_CONTROL_FRAME_MASK;
516
517 switch (mode) {
518 case MV88E6XXX_FRAME_MODE_NORMAL:
519 reg |= PORT_CONTROL_FRAME_MODE_NORMAL;
520 break;
521 case MV88E6XXX_FRAME_MODE_DSA:
522 reg |= PORT_CONTROL_FRAME_MODE_DSA;
523 break;
524 case MV88E6XXX_FRAME_MODE_PROVIDER:
525 reg |= PORT_CONTROL_FRAME_MODE_PROVIDER;
526 break;
527 case MV88E6XXX_FRAME_MODE_ETHERTYPE:
528 reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA;
529 break;
530 default:
531 return -EINVAL;
532 }
533
534 return mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
535}
536
Vivien Didelot601aeed2017-03-11 16:13:00 -0500537static int mv88e6185_port_set_forward_unknown(struct mv88e6xxx_chip *chip,
538 int port, bool unicast)
Andrew Lunn56995cb2016-12-03 04:35:19 +0100539{
540 int err;
541 u16 reg;
542
543 err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, &reg);
544 if (err)
545 return err;
546
Vivien Didelot601aeed2017-03-11 16:13:00 -0500547 if (unicast)
Andrew Lunn56995cb2016-12-03 04:35:19 +0100548 reg |= PORT_CONTROL_FORWARD_UNKNOWN;
549 else
550 reg &= ~PORT_CONTROL_FORWARD_UNKNOWN;
551
552 return mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
553}
554
Vivien Didelot601aeed2017-03-11 16:13:00 -0500555int mv88e6352_port_set_egress_floods(struct mv88e6xxx_chip *chip, int port,
556 bool unicast, bool multicast)
Andrew Lunn56995cb2016-12-03 04:35:19 +0100557{
558 int err;
559 u16 reg;
560
561 err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, &reg);
562 if (err)
563 return err;
564
Vivien Didelot601aeed2017-03-11 16:13:00 -0500565 reg &= ~PORT_CONTROL_EGRESS_FLOODS_MASK;
566
567 if (unicast && multicast)
568 reg |= PORT_CONTROL_EGRESS_FLOODS_ALL_UNKNOWN_DA;
569 else if (unicast)
570 reg |= PORT_CONTROL_EGRESS_FLOODS_NO_UNKNOWN_MC_DA;
571 else if (multicast)
572 reg |= PORT_CONTROL_EGRESS_FLOODS_NO_UNKNOWN_UC_DA;
Andrew Lunn56995cb2016-12-03 04:35:19 +0100573 else
Vivien Didelot601aeed2017-03-11 16:13:00 -0500574 reg |= PORT_CONTROL_EGRESS_FLOODS_NO_UNKNOWN_DA;
Andrew Lunn56995cb2016-12-03 04:35:19 +0100575
576 return mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
577}
578
Vivien Didelotb4e48c52016-11-04 03:23:29 +0100579/* Offset 0x05: Port Control 1 */
580
Vivien Didelotea698f42017-03-11 16:12:50 -0500581int mv88e6xxx_port_set_message_port(struct mv88e6xxx_chip *chip, int port,
582 bool message_port)
583{
584 u16 val;
585 int err;
586
587 err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, &val);
588 if (err)
589 return err;
590
591 if (message_port)
592 val |= PORT_CONTROL_1_MESSAGE_PORT;
593 else
594 val &= ~PORT_CONTROL_1_MESSAGE_PORT;
595
596 return mv88e6xxx_port_write(chip, port, PORT_CONTROL_1, val);
597}
598
Vivien Didelot5a7921f2016-11-04 03:23:28 +0100599/* Offset 0x06: Port Based VLAN Map */
600
601int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map)
602{
Vivien Didelot4d294af2017-03-11 16:12:47 -0500603 const u16 mask = mv88e6xxx_port_mask(chip);
Vivien Didelot5a7921f2016-11-04 03:23:28 +0100604 u16 reg;
605 int err;
606
607 err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg);
608 if (err)
609 return err;
610
611 reg &= ~mask;
612 reg |= map & mask;
613
614 err = mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg);
615 if (err)
616 return err;
617
Vivien Didelot774439e52017-06-08 18:34:08 -0400618 dev_dbg(chip->dev, "p%d: VLANTable set to %.3x\n", port, map);
Vivien Didelot5a7921f2016-11-04 03:23:28 +0100619
620 return 0;
621}
Vivien Didelotb4e48c52016-11-04 03:23:29 +0100622
623int mv88e6xxx_port_get_fid(struct mv88e6xxx_chip *chip, int port, u16 *fid)
624{
625 const u16 upper_mask = (mv88e6xxx_num_databases(chip) - 1) >> 4;
626 u16 reg;
627 int err;
628
629 /* Port's default FID lower 4 bits are located in reg 0x06, offset 12 */
630 err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg);
631 if (err)
632 return err;
633
634 *fid = (reg & 0xf000) >> 12;
635
636 /* Port's default FID upper bits are located in reg 0x05, offset 0 */
637 if (upper_mask) {
638 err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, &reg);
639 if (err)
640 return err;
641
642 *fid |= (reg & upper_mask) << 4;
643 }
644
645 return 0;
646}
647
648int mv88e6xxx_port_set_fid(struct mv88e6xxx_chip *chip, int port, u16 fid)
649{
650 const u16 upper_mask = (mv88e6xxx_num_databases(chip) - 1) >> 4;
651 u16 reg;
652 int err;
653
654 if (fid >= mv88e6xxx_num_databases(chip))
655 return -EINVAL;
656
657 /* Port's default FID lower 4 bits are located in reg 0x06, offset 12 */
658 err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg);
659 if (err)
660 return err;
661
662 reg &= 0x0fff;
663 reg |= (fid & 0x000f) << 12;
664
665 err = mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg);
666 if (err)
667 return err;
668
669 /* Port's default FID upper bits are located in reg 0x05, offset 0 */
670 if (upper_mask) {
671 err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, &reg);
672 if (err)
673 return err;
674
675 reg &= ~upper_mask;
676 reg |= (fid >> 4) & upper_mask;
677
678 err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_1, reg);
679 if (err)
680 return err;
681 }
682
Vivien Didelot774439e52017-06-08 18:34:08 -0400683 dev_dbg(chip->dev, "p%d: FID set to %u\n", port, fid);
Vivien Didelotb4e48c52016-11-04 03:23:29 +0100684
685 return 0;
686}
Vivien Didelot77064f32016-11-04 03:23:30 +0100687
688/* Offset 0x07: Default Port VLAN ID & Priority */
689
690int mv88e6xxx_port_get_pvid(struct mv88e6xxx_chip *chip, int port, u16 *pvid)
691{
692 u16 reg;
693 int err;
694
695 err = mv88e6xxx_port_read(chip, port, PORT_DEFAULT_VLAN, &reg);
696 if (err)
697 return err;
698
699 *pvid = reg & PORT_DEFAULT_VLAN_MASK;
700
701 return 0;
702}
703
704int mv88e6xxx_port_set_pvid(struct mv88e6xxx_chip *chip, int port, u16 pvid)
705{
706 u16 reg;
707 int err;
708
709 err = mv88e6xxx_port_read(chip, port, PORT_DEFAULT_VLAN, &reg);
710 if (err)
711 return err;
712
713 reg &= ~PORT_DEFAULT_VLAN_MASK;
714 reg |= pvid & PORT_DEFAULT_VLAN_MASK;
715
716 err = mv88e6xxx_port_write(chip, port, PORT_DEFAULT_VLAN, reg);
717 if (err)
718 return err;
719
Vivien Didelot774439e52017-06-08 18:34:08 -0400720 dev_dbg(chip->dev, "p%d: DefaultVID set to %u\n", port, pvid);
Vivien Didelot77064f32016-11-04 03:23:30 +0100721
722 return 0;
723}
Vivien Didelot385a0992016-11-04 03:23:31 +0100724
725/* Offset 0x08: Port Control 2 Register */
726
727static const char * const mv88e6xxx_port_8021q_mode_names[] = {
728 [PORT_CONTROL_2_8021Q_DISABLED] = "Disabled",
729 [PORT_CONTROL_2_8021Q_FALLBACK] = "Fallback",
730 [PORT_CONTROL_2_8021Q_CHECK] = "Check",
731 [PORT_CONTROL_2_8021Q_SECURE] = "Secure",
732};
733
Vivien Didelot601aeed2017-03-11 16:13:00 -0500734static int mv88e6185_port_set_default_forward(struct mv88e6xxx_chip *chip,
735 int port, bool multicast)
Andrew Lunna23b2962017-02-04 20:15:28 +0100736{
737 int err;
738 u16 reg;
739
740 err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, &reg);
741 if (err)
742 return err;
743
Vivien Didelot601aeed2017-03-11 16:13:00 -0500744 if (multicast)
745 reg |= PORT_CONTROL_2_DEFAULT_FORWARD;
Andrew Lunna23b2962017-02-04 20:15:28 +0100746 else
Vivien Didelot601aeed2017-03-11 16:13:00 -0500747 reg &= ~PORT_CONTROL_2_DEFAULT_FORWARD;
Andrew Lunna23b2962017-02-04 20:15:28 +0100748
749 return mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg);
750}
751
Vivien Didelot601aeed2017-03-11 16:13:00 -0500752int mv88e6185_port_set_egress_floods(struct mv88e6xxx_chip *chip, int port,
753 bool unicast, bool multicast)
754{
755 int err;
756
757 err = mv88e6185_port_set_forward_unknown(chip, port, unicast);
758 if (err)
759 return err;
760
761 return mv88e6185_port_set_default_forward(chip, port, multicast);
762}
763
Andrew Lunna23b2962017-02-04 20:15:28 +0100764int mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port,
765 int upstream_port)
766{
767 int err;
768 u16 reg;
769
770 err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, &reg);
771 if (err)
772 return err;
773
774 reg &= ~PORT_CONTROL_2_UPSTREAM_MASK;
775 reg |= upstream_port;
776
777 return mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg);
778}
779
Vivien Didelot385a0992016-11-04 03:23:31 +0100780int mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port,
781 u16 mode)
782{
783 u16 reg;
784 int err;
785
786 err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, &reg);
787 if (err)
788 return err;
789
790 reg &= ~PORT_CONTROL_2_8021Q_MASK;
791 reg |= mode & PORT_CONTROL_2_8021Q_MASK;
792
793 err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg);
794 if (err)
795 return err;
796
Vivien Didelot774439e52017-06-08 18:34:08 -0400797 dev_dbg(chip->dev, "p%d: 802.1QMode set to %s\n", port,
798 mv88e6xxx_port_8021q_mode_names[mode]);
Vivien Didelot385a0992016-11-04 03:23:31 +0100799
800 return 0;
801}
Andrew Lunnef0a7312016-12-03 04:35:16 +0100802
Andrew Lunna23b2962017-02-04 20:15:28 +0100803int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port)
804{
805 u16 reg;
806 int err;
807
808 err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, &reg);
809 if (err)
810 return err;
811
812 reg |= PORT_CONTROL_2_MAP_DA;
813
814 return mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg);
815}
816
Andrew Lunn5f436662016-12-03 04:45:17 +0100817int mv88e6165_port_jumbo_config(struct mv88e6xxx_chip *chip, int port)
818{
819 u16 reg;
820 int err;
821
822 err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, &reg);
823 if (err)
824 return err;
825
826 reg |= PORT_CONTROL_2_JUMBO_10240;
827
828 return mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg);
829}
830
Andrew Lunnef70b112016-12-03 04:45:18 +0100831/* Offset 0x09: Port Rate Control */
832
833int mv88e6095_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port)
834{
835 return mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL, 0x0000);
836}
837
838int mv88e6097_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port)
839{
840 return mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL, 0x0001);
841}
842
Vivien Didelotc8c94892017-03-11 16:13:01 -0500843/* Offset 0x0C: Port ATU Control */
844
845int mv88e6xxx_port_disable_learn_limit(struct mv88e6xxx_chip *chip, int port)
846{
847 return mv88e6xxx_port_write(chip, port, PORT_ATU_CONTROL, 0);
848}
849
Vivien Didelot9dbfb4e2017-03-11 16:13:02 -0500850/* Offset 0x0D: (Priority) Override Register */
851
852int mv88e6xxx_port_disable_pri_override(struct mv88e6xxx_chip *chip, int port)
853{
854 return mv88e6xxx_port_write(chip, port, PORT_PRI_OVERRIDE, 0);
855}
856
Andrew Lunn56995cb2016-12-03 04:35:19 +0100857/* Offset 0x0f: Port Ether type */
858
859int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port,
860 u16 etype)
861{
862 return mv88e6xxx_port_write(chip, port, PORT_ETH_TYPE, etype);
863}
864
Andrew Lunnef0a7312016-12-03 04:35:16 +0100865/* Offset 0x18: Port IEEE Priority Remapping Registers [0-3]
866 * Offset 0x19: Port IEEE Priority Remapping Registers [4-7]
867 */
868
869int mv88e6095_port_tag_remap(struct mv88e6xxx_chip *chip, int port)
870{
871 int err;
872
873 /* Use a direct priority mapping for all IEEE tagged frames */
874 err = mv88e6xxx_port_write(chip, port, PORT_TAG_REGMAP_0123, 0x3210);
875 if (err)
876 return err;
877
878 return mv88e6xxx_port_write(chip, port, PORT_TAG_REGMAP_4567, 0x7654);
879}
880
881static int mv88e6xxx_port_ieeepmt_write(struct mv88e6xxx_chip *chip,
882 int port, u16 table,
883 u8 pointer, u16 data)
884{
885 u16 reg;
886
887 reg = PORT_IEEE_PRIO_MAP_TABLE_UPDATE |
888 table |
889 (pointer << PORT_IEEE_PRIO_MAP_TABLE_POINTER_SHIFT) |
890 data;
891
892 return mv88e6xxx_port_write(chip, port, PORT_IEEE_PRIO_MAP_TABLE, reg);
893}
894
895int mv88e6390_port_tag_remap(struct mv88e6xxx_chip *chip, int port)
896{
897 int err, i;
898
899 for (i = 0; i <= 7; i++) {
900 err = mv88e6xxx_port_ieeepmt_write(
901 chip, port, PORT_IEEE_PRIO_MAP_TABLE_INGRESS_PCP,
902 i, (i | i << 4));
903 if (err)
904 return err;
905
906 err = mv88e6xxx_port_ieeepmt_write(
907 chip, port, PORT_IEEE_PRIO_MAP_TABLE_EGRESS_GREEN_PCP,
908 i, i);
909 if (err)
910 return err;
911
912 err = mv88e6xxx_port_ieeepmt_write(
913 chip, port, PORT_IEEE_PRIO_MAP_TABLE_EGRESS_YELLOW_PCP,
914 i, i);
915 if (err)
916 return err;
917
918 err = mv88e6xxx_port_ieeepmt_write(
919 chip, port, PORT_IEEE_PRIO_MAP_TABLE_EGRESS_AVB_PCP,
920 i, i);
921 if (err)
922 return err;
923 }
924
925 return 0;
926}