blob: 6315774d72b3304d5ccd1729cb150c183bbac19e [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 Didelotddcbabf2017-06-17 23:07:14 -040015#include <linux/bitfield.h>
Vivien Didelotf894c292017-06-08 18:34:10 -040016#include <linux/if_bridge.h>
Andrew Lunnf39908d2017-02-04 20:02:50 +010017#include <linux/phy.h>
Vivien Didelot4d5f2ba72017-06-02 17:06:15 -040018
19#include "chip.h"
Vivien Didelot18abed22016-11-04 03:23:26 +010020#include "port.h"
21
22int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg,
23 u16 *val)
24{
25 int addr = chip->info->port_base_addr + port;
26
27 return mv88e6xxx_read(chip, addr, reg, val);
28}
29
30int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg,
31 u16 val)
32{
33 int addr = chip->info->port_base_addr + port;
34
35 return mv88e6xxx_write(chip, addr, reg, val);
36}
Vivien Didelote28def332016-11-04 03:23:27 +010037
Vivien Didelot08ef7f12016-11-04 03:23:32 +010038/* Offset 0x01: MAC (or PCS or Physical) Control Register
39 *
40 * Link, Duplex and Flow Control have one force bit, one value bit.
Vivien Didelot96a2b402016-11-04 03:23:35 +010041 *
42 * For port's MAC speed, ForceSpd (or SpdValue) bits 1:0 program the value.
43 * Alternative values require the 200BASE (or AltSpeed) bit 12 set.
44 * Newer chips need a ForcedSpd bit 13 set to consider the value.
Vivien Didelot08ef7f12016-11-04 03:23:32 +010045 */
46
Vivien Didelota0a0f622016-11-04 03:23:34 +010047static int mv88e6xxx_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
48 phy_interface_t mode)
49{
50 u16 reg;
51 int err;
52
Vivien Didelot5ee55572017-06-12 12:37:34 -040053 err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, &reg);
Vivien Didelota0a0f622016-11-04 03:23:34 +010054 if (err)
55 return err;
56
Vivien Didelot5ee55572017-06-12 12:37:34 -040057 reg &= ~(MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK |
58 MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK);
Vivien Didelota0a0f622016-11-04 03:23:34 +010059
60 switch (mode) {
61 case PHY_INTERFACE_MODE_RGMII_RXID:
Vivien Didelot5ee55572017-06-12 12:37:34 -040062 reg |= MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK;
Vivien Didelota0a0f622016-11-04 03:23:34 +010063 break;
64 case PHY_INTERFACE_MODE_RGMII_TXID:
Vivien Didelot5ee55572017-06-12 12:37:34 -040065 reg |= MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK;
Vivien Didelota0a0f622016-11-04 03:23:34 +010066 break;
67 case PHY_INTERFACE_MODE_RGMII_ID:
Vivien Didelot5ee55572017-06-12 12:37:34 -040068 reg |= MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK |
69 MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK;
Vivien Didelota0a0f622016-11-04 03:23:34 +010070 break;
Andrew Lunnfedf1862016-11-10 15:44:00 +010071 case PHY_INTERFACE_MODE_RGMII:
Vivien Didelota0a0f622016-11-04 03:23:34 +010072 break;
Andrew Lunnfedf1862016-11-10 15:44:00 +010073 default:
74 return 0;
Vivien Didelota0a0f622016-11-04 03:23:34 +010075 }
76
Vivien Didelot5ee55572017-06-12 12:37:34 -040077 err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
Vivien Didelota0a0f622016-11-04 03:23:34 +010078 if (err)
79 return err;
80
Vivien Didelot774439e52017-06-08 18:34:08 -040081 dev_dbg(chip->dev, "p%d: delay RXCLK %s, TXCLK %s\n", port,
Vivien Didelot5ee55572017-06-12 12:37:34 -040082 reg & MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK ? "yes" : "no",
83 reg & MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK ? "yes" : "no");
Vivien Didelota0a0f622016-11-04 03:23:34 +010084
85 return 0;
86}
87
88int mv88e6352_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
89 phy_interface_t mode)
90{
91 if (port < 5)
92 return -EOPNOTSUPP;
93
94 return mv88e6xxx_port_set_rgmii_delay(chip, port, mode);
95}
96
97int mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
98 phy_interface_t mode)
99{
100 if (port != 0)
101 return -EOPNOTSUPP;
102
103 return mv88e6xxx_port_set_rgmii_delay(chip, port, mode);
104}
105
Vivien Didelot08ef7f12016-11-04 03:23:32 +0100106int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link)
107{
108 u16 reg;
109 int err;
110
Vivien Didelot5ee55572017-06-12 12:37:34 -0400111 err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, &reg);
Vivien Didelot08ef7f12016-11-04 03:23:32 +0100112 if (err)
113 return err;
114
Vivien Didelot5ee55572017-06-12 12:37:34 -0400115 reg &= ~(MV88E6XXX_PORT_MAC_CTL_FORCE_LINK |
116 MV88E6XXX_PORT_MAC_CTL_LINK_UP);
Vivien Didelot08ef7f12016-11-04 03:23:32 +0100117
118 switch (link) {
119 case LINK_FORCED_DOWN:
Vivien Didelot5ee55572017-06-12 12:37:34 -0400120 reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_LINK;
Vivien Didelot08ef7f12016-11-04 03:23:32 +0100121 break;
122 case LINK_FORCED_UP:
Vivien Didelot5ee55572017-06-12 12:37:34 -0400123 reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_LINK |
124 MV88E6XXX_PORT_MAC_CTL_LINK_UP;
Vivien Didelot08ef7f12016-11-04 03:23:32 +0100125 break;
126 case LINK_UNFORCED:
127 /* normal link detection */
128 break;
129 default:
130 return -EINVAL;
131 }
132
Vivien Didelot5ee55572017-06-12 12:37:34 -0400133 err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
Vivien Didelot08ef7f12016-11-04 03:23:32 +0100134 if (err)
135 return err;
136
Vivien Didelot774439e52017-06-08 18:34:08 -0400137 dev_dbg(chip->dev, "p%d: %s link %s\n", port,
Vivien Didelot5ee55572017-06-12 12:37:34 -0400138 reg & MV88E6XXX_PORT_MAC_CTL_FORCE_LINK ? "Force" : "Unforce",
139 reg & MV88E6XXX_PORT_MAC_CTL_LINK_UP ? "up" : "down");
Vivien Didelot08ef7f12016-11-04 03:23:32 +0100140
141 return 0;
142}
143
Vivien Didelot7f1ae072016-11-04 03:23:33 +0100144int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup)
145{
146 u16 reg;
147 int err;
148
Vivien Didelot5ee55572017-06-12 12:37:34 -0400149 err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, &reg);
Vivien Didelot7f1ae072016-11-04 03:23:33 +0100150 if (err)
151 return err;
152
Vivien Didelot5ee55572017-06-12 12:37:34 -0400153 reg &= ~(MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX |
154 MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL);
Vivien Didelot7f1ae072016-11-04 03:23:33 +0100155
156 switch (dup) {
157 case DUPLEX_HALF:
Vivien Didelot5ee55572017-06-12 12:37:34 -0400158 reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX;
Vivien Didelot7f1ae072016-11-04 03:23:33 +0100159 break;
160 case DUPLEX_FULL:
Vivien Didelot5ee55572017-06-12 12:37:34 -0400161 reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX |
162 MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL;
Vivien Didelot7f1ae072016-11-04 03:23:33 +0100163 break;
164 case DUPLEX_UNFORCED:
165 /* normal duplex detection */
166 break;
167 default:
168 return -EINVAL;
169 }
170
Vivien Didelot5ee55572017-06-12 12:37:34 -0400171 err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
Vivien Didelot7f1ae072016-11-04 03:23:33 +0100172 if (err)
173 return err;
174
Vivien Didelot774439e52017-06-08 18:34:08 -0400175 dev_dbg(chip->dev, "p%d: %s %s duplex\n", port,
Vivien Didelot5ee55572017-06-12 12:37:34 -0400176 reg & MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX ? "Force" : "Unforce",
177 reg & MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL ? "full" : "half");
Vivien Didelot7f1ae072016-11-04 03:23:33 +0100178
179 return 0;
180}
181
Vivien Didelot96a2b402016-11-04 03:23:35 +0100182static int mv88e6xxx_port_set_speed(struct mv88e6xxx_chip *chip, int port,
183 int speed, bool alt_bit, bool force_bit)
184{
185 u16 reg, ctrl;
186 int err;
187
188 switch (speed) {
189 case 10:
Vivien Didelot5ee55572017-06-12 12:37:34 -0400190 ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_10;
Vivien Didelot96a2b402016-11-04 03:23:35 +0100191 break;
192 case 100:
Vivien Didelot5ee55572017-06-12 12:37:34 -0400193 ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_100;
Vivien Didelot96a2b402016-11-04 03:23:35 +0100194 break;
195 case 200:
196 if (alt_bit)
Vivien Didelot5ee55572017-06-12 12:37:34 -0400197 ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_100 |
198 MV88E6390_PORT_MAC_CTL_ALTSPEED;
Vivien Didelot96a2b402016-11-04 03:23:35 +0100199 else
Vivien Didelot5ee55572017-06-12 12:37:34 -0400200 ctrl = MV88E6065_PORT_MAC_CTL_SPEED_200;
Vivien Didelot96a2b402016-11-04 03:23:35 +0100201 break;
202 case 1000:
Vivien Didelot5ee55572017-06-12 12:37:34 -0400203 ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_1000;
Vivien Didelot96a2b402016-11-04 03:23:35 +0100204 break;
205 case 2500:
Vivien Didelot5ee55572017-06-12 12:37:34 -0400206 ctrl = MV88E6390_PORT_MAC_CTL_SPEED_10000 |
207 MV88E6390_PORT_MAC_CTL_ALTSPEED;
Vivien Didelot96a2b402016-11-04 03:23:35 +0100208 break;
209 case 10000:
210 /* all bits set, fall through... */
211 case SPEED_UNFORCED:
Vivien Didelot5ee55572017-06-12 12:37:34 -0400212 ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_UNFORCED;
Vivien Didelot96a2b402016-11-04 03:23:35 +0100213 break;
214 default:
215 return -EOPNOTSUPP;
216 }
217
Vivien Didelot5ee55572017-06-12 12:37:34 -0400218 err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, &reg);
Vivien Didelot96a2b402016-11-04 03:23:35 +0100219 if (err)
220 return err;
221
Vivien Didelot5ee55572017-06-12 12:37:34 -0400222 reg &= ~MV88E6XXX_PORT_MAC_CTL_SPEED_MASK;
Vivien Didelot96a2b402016-11-04 03:23:35 +0100223 if (alt_bit)
Vivien Didelot5ee55572017-06-12 12:37:34 -0400224 reg &= ~MV88E6390_PORT_MAC_CTL_ALTSPEED;
Vivien Didelot96a2b402016-11-04 03:23:35 +0100225 if (force_bit) {
Vivien Didelot5ee55572017-06-12 12:37:34 -0400226 reg &= ~MV88E6390_PORT_MAC_CTL_FORCE_SPEED;
Andrew Lunn0b6e3d02016-11-16 04:26:48 +0100227 if (speed != SPEED_UNFORCED)
Vivien Didelot5ee55572017-06-12 12:37:34 -0400228 ctrl |= MV88E6390_PORT_MAC_CTL_FORCE_SPEED;
Vivien Didelot96a2b402016-11-04 03:23:35 +0100229 }
230 reg |= ctrl;
231
Vivien Didelot5ee55572017-06-12 12:37:34 -0400232 err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
Vivien Didelot96a2b402016-11-04 03:23:35 +0100233 if (err)
234 return err;
235
236 if (speed)
Vivien Didelot774439e52017-06-08 18:34:08 -0400237 dev_dbg(chip->dev, "p%d: Speed set to %d Mbps\n", port, speed);
Vivien Didelot96a2b402016-11-04 03:23:35 +0100238 else
Vivien Didelot774439e52017-06-08 18:34:08 -0400239 dev_dbg(chip->dev, "p%d: Speed unforced\n", port);
Vivien Didelot96a2b402016-11-04 03:23:35 +0100240
241 return 0;
242}
243
244/* Support 10, 100, 200 Mbps (e.g. 88E6065 family) */
245int mv88e6065_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
246{
247 if (speed == SPEED_MAX)
248 speed = 200;
249
250 if (speed > 200)
251 return -EOPNOTSUPP;
252
253 /* Setting 200 Mbps on port 0 to 3 selects 100 Mbps */
254 return mv88e6xxx_port_set_speed(chip, port, speed, false, false);
255}
256
257/* Support 10, 100, 1000 Mbps (e.g. 88E6185 family) */
258int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
259{
260 if (speed == SPEED_MAX)
261 speed = 1000;
262
263 if (speed == 200 || speed > 1000)
264 return -EOPNOTSUPP;
265
266 return mv88e6xxx_port_set_speed(chip, port, speed, false, false);
267}
268
269/* Support 10, 100, 200, 1000 Mbps (e.g. 88E6352 family) */
270int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
271{
272 if (speed == SPEED_MAX)
273 speed = 1000;
274
275 if (speed > 1000)
276 return -EOPNOTSUPP;
277
278 if (speed == 200 && port < 5)
279 return -EOPNOTSUPP;
280
281 return mv88e6xxx_port_set_speed(chip, port, speed, true, false);
282}
283
284/* Support 10, 100, 200, 1000, 2500 Mbps (e.g. 88E6390) */
285int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
286{
287 if (speed == SPEED_MAX)
288 speed = port < 9 ? 1000 : 2500;
289
290 if (speed > 2500)
291 return -EOPNOTSUPP;
292
293 if (speed == 200 && port != 0)
294 return -EOPNOTSUPP;
295
296 if (speed == 2500 && port < 9)
297 return -EOPNOTSUPP;
298
299 return mv88e6xxx_port_set_speed(chip, port, speed, true, true);
300}
301
302/* Support 10, 100, 200, 1000, 2500, 10000 Mbps (e.g. 88E6190X) */
303int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
304{
305 if (speed == SPEED_MAX)
306 speed = port < 9 ? 1000 : 10000;
307
308 if (speed == 200 && port != 0)
309 return -EOPNOTSUPP;
310
311 if (speed >= 2500 && port < 9)
312 return -EOPNOTSUPP;
313
314 return mv88e6xxx_port_set_speed(chip, port, speed, true, true);
315}
316
Andrew Lunnf39908d2017-02-04 20:02:50 +0100317int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
318 phy_interface_t mode)
319{
320 u16 reg;
321 u16 cmode;
322 int err;
323
324 if (mode == PHY_INTERFACE_MODE_NA)
325 return 0;
326
327 if (port != 9 && port != 10)
328 return -EOPNOTSUPP;
329
330 switch (mode) {
331 case PHY_INTERFACE_MODE_1000BASEX:
Vivien Didelot5f83dc92017-06-12 12:37:33 -0400332 cmode = MV88E6XXX_PORT_STS_CMODE_1000BASE_X;
Andrew Lunnf39908d2017-02-04 20:02:50 +0100333 break;
334 case PHY_INTERFACE_MODE_SGMII:
Vivien Didelot5f83dc92017-06-12 12:37:33 -0400335 cmode = MV88E6XXX_PORT_STS_CMODE_SGMII;
Andrew Lunnf39908d2017-02-04 20:02:50 +0100336 break;
337 case PHY_INTERFACE_MODE_2500BASEX:
Vivien Didelot5f83dc92017-06-12 12:37:33 -0400338 cmode = MV88E6XXX_PORT_STS_CMODE_2500BASEX;
Andrew Lunnf39908d2017-02-04 20:02:50 +0100339 break;
340 case PHY_INTERFACE_MODE_XGMII:
Russell King2e51a8d2017-12-12 09:29:46 +0000341 case PHY_INTERFACE_MODE_XAUI:
Vivien Didelot5f83dc92017-06-12 12:37:33 -0400342 cmode = MV88E6XXX_PORT_STS_CMODE_XAUI;
Andrew Lunnf39908d2017-02-04 20:02:50 +0100343 break;
344 case PHY_INTERFACE_MODE_RXAUI:
Vivien Didelot5f83dc92017-06-12 12:37:33 -0400345 cmode = MV88E6XXX_PORT_STS_CMODE_RXAUI;
Andrew Lunnf39908d2017-02-04 20:02:50 +0100346 break;
347 default:
348 cmode = 0;
349 }
350
351 if (cmode) {
Vivien Didelot5f83dc92017-06-12 12:37:33 -0400352 err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &reg);
Andrew Lunnf39908d2017-02-04 20:02:50 +0100353 if (err)
354 return err;
355
Vivien Didelot5f83dc92017-06-12 12:37:33 -0400356 reg &= ~MV88E6XXX_PORT_STS_CMODE_MASK;
Andrew Lunnf39908d2017-02-04 20:02:50 +0100357 reg |= cmode;
358
Vivien Didelot5f83dc92017-06-12 12:37:33 -0400359 err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_STS, reg);
Andrew Lunnf39908d2017-02-04 20:02:50 +0100360 if (err)
361 return err;
362 }
363
364 return 0;
365}
366
367int mv88e6xxx_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode)
368{
369 int err;
370 u16 reg;
371
Vivien Didelot5f83dc92017-06-12 12:37:33 -0400372 err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &reg);
Andrew Lunnf39908d2017-02-04 20:02:50 +0100373 if (err)
374 return err;
375
Vivien Didelot5f83dc92017-06-12 12:37:33 -0400376 *cmode = reg & MV88E6XXX_PORT_STS_CMODE_MASK;
Andrew Lunnf39908d2017-02-04 20:02:50 +0100377
378 return 0;
379}
380
Vivien Didelot6c96bbf2017-06-12 12:37:35 -0400381/* Offset 0x02: Jamming Control
Andrew Lunnb35d322a2016-12-03 04:45:19 +0100382 *
383 * Do not limit the period of time that this port can be paused for by
384 * the remote end or the period of time that this port can pause the
385 * remote end.
386 */
Vivien Didelot08984322017-06-08 18:34:12 -0400387int mv88e6097_port_pause_limit(struct mv88e6xxx_chip *chip, int port, u8 in,
388 u8 out)
Andrew Lunnb35d322a2016-12-03 04:45:19 +0100389{
Vivien Didelot6c96bbf2017-06-12 12:37:35 -0400390 return mv88e6xxx_port_write(chip, port, MV88E6097_PORT_JAM_CTL,
391 out << 8 | in);
Andrew Lunnb35d322a2016-12-03 04:45:19 +0100392}
393
Vivien Didelot08984322017-06-08 18:34:12 -0400394int mv88e6390_port_pause_limit(struct mv88e6xxx_chip *chip, int port, u8 in,
395 u8 out)
Andrew Lunn3ce0e652016-12-03 04:45:20 +0100396{
397 int err;
398
Vivien Didelot6c96bbf2017-06-12 12:37:35 -0400399 err = mv88e6xxx_port_write(chip, port, MV88E6390_PORT_FLOW_CTL,
400 MV88E6390_PORT_FLOW_CTL_UPDATE |
401 MV88E6390_PORT_FLOW_CTL_LIMIT_IN | in);
Andrew Lunn3ce0e652016-12-03 04:45:20 +0100402 if (err)
403 return err;
404
Vivien Didelot6c96bbf2017-06-12 12:37:35 -0400405 return mv88e6xxx_port_write(chip, port, MV88E6390_PORT_FLOW_CTL,
406 MV88E6390_PORT_FLOW_CTL_UPDATE |
407 MV88E6390_PORT_FLOW_CTL_LIMIT_OUT | out);
Andrew Lunn3ce0e652016-12-03 04:45:20 +0100408}
409
Vivien Didelote28def332016-11-04 03:23:27 +0100410/* Offset 0x04: Port Control Register */
411
412static const char * const mv88e6xxx_port_state_names[] = {
Vivien Didelota89b433be2017-06-12 12:37:37 -0400413 [MV88E6XXX_PORT_CTL0_STATE_DISABLED] = "Disabled",
414 [MV88E6XXX_PORT_CTL0_STATE_BLOCKING] = "Blocking/Listening",
415 [MV88E6XXX_PORT_CTL0_STATE_LEARNING] = "Learning",
416 [MV88E6XXX_PORT_CTL0_STATE_FORWARDING] = "Forwarding",
Vivien Didelote28def332016-11-04 03:23:27 +0100417};
418
419int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state)
420{
421 u16 reg;
422 int err;
423
Vivien Didelota89b433be2017-06-12 12:37:37 -0400424 err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, &reg);
Vivien Didelote28def332016-11-04 03:23:27 +0100425 if (err)
426 return err;
427
Vivien Didelota89b433be2017-06-12 12:37:37 -0400428 reg &= ~MV88E6XXX_PORT_CTL0_STATE_MASK;
Vivien Didelotf894c292017-06-08 18:34:10 -0400429
430 switch (state) {
431 case BR_STATE_DISABLED:
Vivien Didelota89b433be2017-06-12 12:37:37 -0400432 state = MV88E6XXX_PORT_CTL0_STATE_DISABLED;
Vivien Didelotf894c292017-06-08 18:34:10 -0400433 break;
434 case BR_STATE_BLOCKING:
435 case BR_STATE_LISTENING:
Vivien Didelota89b433be2017-06-12 12:37:37 -0400436 state = MV88E6XXX_PORT_CTL0_STATE_BLOCKING;
Vivien Didelotf894c292017-06-08 18:34:10 -0400437 break;
438 case BR_STATE_LEARNING:
Vivien Didelota89b433be2017-06-12 12:37:37 -0400439 state = MV88E6XXX_PORT_CTL0_STATE_LEARNING;
Vivien Didelotf894c292017-06-08 18:34:10 -0400440 break;
441 case BR_STATE_FORWARDING:
Vivien Didelota89b433be2017-06-12 12:37:37 -0400442 state = MV88E6XXX_PORT_CTL0_STATE_FORWARDING;
Vivien Didelotf894c292017-06-08 18:34:10 -0400443 break;
444 default:
445 return -EINVAL;
446 }
447
Vivien Didelote28def332016-11-04 03:23:27 +0100448 reg |= state;
449
Vivien Didelota89b433be2017-06-12 12:37:37 -0400450 err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg);
Vivien Didelote28def332016-11-04 03:23:27 +0100451 if (err)
452 return err;
453
Vivien Didelot774439e52017-06-08 18:34:08 -0400454 dev_dbg(chip->dev, "p%d: PortState set to %s\n", port,
455 mv88e6xxx_port_state_names[state]);
Vivien Didelote28def332016-11-04 03:23:27 +0100456
457 return 0;
458}
Vivien Didelot5a7921f2016-11-04 03:23:28 +0100459
Andrew Lunn56995cb2016-12-03 04:35:19 +0100460int mv88e6xxx_port_set_egress_mode(struct mv88e6xxx_chip *chip, int port,
Vivien Didelot31bef4e2017-06-08 18:34:09 -0400461 enum mv88e6xxx_egress_mode mode)
Andrew Lunn56995cb2016-12-03 04:35:19 +0100462{
463 int err;
464 u16 reg;
465
Vivien Didelota89b433be2017-06-12 12:37:37 -0400466 err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, &reg);
Andrew Lunn56995cb2016-12-03 04:35:19 +0100467 if (err)
468 return err;
469
Vivien Didelota89b433be2017-06-12 12:37:37 -0400470 reg &= ~MV88E6XXX_PORT_CTL0_EGRESS_MODE_MASK;
Vivien Didelot31bef4e2017-06-08 18:34:09 -0400471
472 switch (mode) {
473 case MV88E6XXX_EGRESS_MODE_UNMODIFIED:
Vivien Didelota89b433be2017-06-12 12:37:37 -0400474 reg |= MV88E6XXX_PORT_CTL0_EGRESS_MODE_UNMODIFIED;
Vivien Didelot31bef4e2017-06-08 18:34:09 -0400475 break;
476 case MV88E6XXX_EGRESS_MODE_UNTAGGED:
Vivien Didelota89b433be2017-06-12 12:37:37 -0400477 reg |= MV88E6XXX_PORT_CTL0_EGRESS_MODE_UNTAGGED;
Vivien Didelot31bef4e2017-06-08 18:34:09 -0400478 break;
479 case MV88E6XXX_EGRESS_MODE_TAGGED:
Vivien Didelota89b433be2017-06-12 12:37:37 -0400480 reg |= MV88E6XXX_PORT_CTL0_EGRESS_MODE_TAGGED;
Vivien Didelot31bef4e2017-06-08 18:34:09 -0400481 break;
482 case MV88E6XXX_EGRESS_MODE_ETHERTYPE:
Vivien Didelota89b433be2017-06-12 12:37:37 -0400483 reg |= MV88E6XXX_PORT_CTL0_EGRESS_MODE_ETHER_TYPE_DSA;
Vivien Didelot31bef4e2017-06-08 18:34:09 -0400484 break;
485 default:
486 return -EINVAL;
487 }
Andrew Lunn56995cb2016-12-03 04:35:19 +0100488
Vivien Didelota89b433be2017-06-12 12:37:37 -0400489 return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg);
Andrew Lunn56995cb2016-12-03 04:35:19 +0100490}
491
492int mv88e6085_port_set_frame_mode(struct mv88e6xxx_chip *chip, int port,
493 enum mv88e6xxx_frame_mode mode)
494{
495 int err;
496 u16 reg;
497
Vivien Didelota89b433be2017-06-12 12:37:37 -0400498 err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, &reg);
Andrew Lunn56995cb2016-12-03 04:35:19 +0100499 if (err)
500 return err;
501
Vivien Didelota89b433be2017-06-12 12:37:37 -0400502 reg &= ~MV88E6XXX_PORT_CTL0_FRAME_MODE_MASK;
Andrew Lunn56995cb2016-12-03 04:35:19 +0100503
504 switch (mode) {
505 case MV88E6XXX_FRAME_MODE_NORMAL:
Vivien Didelota89b433be2017-06-12 12:37:37 -0400506 reg |= MV88E6XXX_PORT_CTL0_FRAME_MODE_NORMAL;
Andrew Lunn56995cb2016-12-03 04:35:19 +0100507 break;
508 case MV88E6XXX_FRAME_MODE_DSA:
Vivien Didelota89b433be2017-06-12 12:37:37 -0400509 reg |= MV88E6XXX_PORT_CTL0_FRAME_MODE_DSA;
Andrew Lunn56995cb2016-12-03 04:35:19 +0100510 break;
511 default:
512 return -EINVAL;
513 }
514
Vivien Didelota89b433be2017-06-12 12:37:37 -0400515 return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg);
Andrew Lunn56995cb2016-12-03 04:35:19 +0100516}
517
518int mv88e6351_port_set_frame_mode(struct mv88e6xxx_chip *chip, int port,
519 enum mv88e6xxx_frame_mode mode)
520{
521 int err;
522 u16 reg;
523
Vivien Didelota89b433be2017-06-12 12:37:37 -0400524 err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, &reg);
Andrew Lunn56995cb2016-12-03 04:35:19 +0100525 if (err)
526 return err;
527
Vivien Didelota89b433be2017-06-12 12:37:37 -0400528 reg &= ~MV88E6XXX_PORT_CTL0_FRAME_MODE_MASK;
Andrew Lunn56995cb2016-12-03 04:35:19 +0100529
530 switch (mode) {
531 case MV88E6XXX_FRAME_MODE_NORMAL:
Vivien Didelota89b433be2017-06-12 12:37:37 -0400532 reg |= MV88E6XXX_PORT_CTL0_FRAME_MODE_NORMAL;
Andrew Lunn56995cb2016-12-03 04:35:19 +0100533 break;
534 case MV88E6XXX_FRAME_MODE_DSA:
Vivien Didelota89b433be2017-06-12 12:37:37 -0400535 reg |= MV88E6XXX_PORT_CTL0_FRAME_MODE_DSA;
Andrew Lunn56995cb2016-12-03 04:35:19 +0100536 break;
537 case MV88E6XXX_FRAME_MODE_PROVIDER:
Vivien Didelota89b433be2017-06-12 12:37:37 -0400538 reg |= MV88E6XXX_PORT_CTL0_FRAME_MODE_PROVIDER;
Andrew Lunn56995cb2016-12-03 04:35:19 +0100539 break;
540 case MV88E6XXX_FRAME_MODE_ETHERTYPE:
Vivien Didelota89b433be2017-06-12 12:37:37 -0400541 reg |= MV88E6XXX_PORT_CTL0_FRAME_MODE_ETHER_TYPE_DSA;
Andrew Lunn56995cb2016-12-03 04:35:19 +0100542 break;
543 default:
544 return -EINVAL;
545 }
546
Vivien Didelota89b433be2017-06-12 12:37:37 -0400547 return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg);
Andrew Lunn56995cb2016-12-03 04:35:19 +0100548}
549
Vivien Didelot601aeed2017-03-11 16:13:00 -0500550static int mv88e6185_port_set_forward_unknown(struct mv88e6xxx_chip *chip,
551 int port, bool unicast)
Andrew Lunn56995cb2016-12-03 04:35:19 +0100552{
553 int err;
554 u16 reg;
555
Vivien Didelota89b433be2017-06-12 12:37:37 -0400556 err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, &reg);
Andrew Lunn56995cb2016-12-03 04:35:19 +0100557 if (err)
558 return err;
559
Vivien Didelot601aeed2017-03-11 16:13:00 -0500560 if (unicast)
Vivien Didelota89b433be2017-06-12 12:37:37 -0400561 reg |= MV88E6185_PORT_CTL0_FORWARD_UNKNOWN;
Andrew Lunn56995cb2016-12-03 04:35:19 +0100562 else
Vivien Didelota89b433be2017-06-12 12:37:37 -0400563 reg &= ~MV88E6185_PORT_CTL0_FORWARD_UNKNOWN;
Andrew Lunn56995cb2016-12-03 04:35:19 +0100564
Vivien Didelota89b433be2017-06-12 12:37:37 -0400565 return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg);
Andrew Lunn56995cb2016-12-03 04:35:19 +0100566}
567
Vivien Didelot601aeed2017-03-11 16:13:00 -0500568int mv88e6352_port_set_egress_floods(struct mv88e6xxx_chip *chip, int port,
569 bool unicast, bool multicast)
Andrew Lunn56995cb2016-12-03 04:35:19 +0100570{
571 int err;
572 u16 reg;
573
Vivien Didelota89b433be2017-06-12 12:37:37 -0400574 err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, &reg);
Andrew Lunn56995cb2016-12-03 04:35:19 +0100575 if (err)
576 return err;
577
Vivien Didelota89b433be2017-06-12 12:37:37 -0400578 reg &= ~MV88E6352_PORT_CTL0_EGRESS_FLOODS_MASK;
Vivien Didelot601aeed2017-03-11 16:13:00 -0500579
580 if (unicast && multicast)
Vivien Didelota89b433be2017-06-12 12:37:37 -0400581 reg |= MV88E6352_PORT_CTL0_EGRESS_FLOODS_ALL_UNKNOWN_DA;
Vivien Didelot601aeed2017-03-11 16:13:00 -0500582 else if (unicast)
Vivien Didelota89b433be2017-06-12 12:37:37 -0400583 reg |= MV88E6352_PORT_CTL0_EGRESS_FLOODS_NO_UNKNOWN_MC_DA;
Vivien Didelot601aeed2017-03-11 16:13:00 -0500584 else if (multicast)
Vivien Didelota89b433be2017-06-12 12:37:37 -0400585 reg |= MV88E6352_PORT_CTL0_EGRESS_FLOODS_NO_UNKNOWN_UC_DA;
Andrew Lunn56995cb2016-12-03 04:35:19 +0100586 else
Vivien Didelota89b433be2017-06-12 12:37:37 -0400587 reg |= MV88E6352_PORT_CTL0_EGRESS_FLOODS_NO_UNKNOWN_DA;
Andrew Lunn56995cb2016-12-03 04:35:19 +0100588
Vivien Didelota89b433be2017-06-12 12:37:37 -0400589 return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg);
Andrew Lunn56995cb2016-12-03 04:35:19 +0100590}
591
Vivien Didelotb4e48c52016-11-04 03:23:29 +0100592/* Offset 0x05: Port Control 1 */
593
Vivien Didelotea698f42017-03-11 16:12:50 -0500594int mv88e6xxx_port_set_message_port(struct mv88e6xxx_chip *chip, int port,
595 bool message_port)
596{
597 u16 val;
598 int err;
599
Vivien Didelotcd985bb2017-06-12 12:37:38 -0400600 err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL1, &val);
Vivien Didelotea698f42017-03-11 16:12:50 -0500601 if (err)
602 return err;
603
604 if (message_port)
Vivien Didelotcd985bb2017-06-12 12:37:38 -0400605 val |= MV88E6XXX_PORT_CTL1_MESSAGE_PORT;
Vivien Didelotea698f42017-03-11 16:12:50 -0500606 else
Vivien Didelotcd985bb2017-06-12 12:37:38 -0400607 val &= ~MV88E6XXX_PORT_CTL1_MESSAGE_PORT;
Vivien Didelotea698f42017-03-11 16:12:50 -0500608
Vivien Didelotcd985bb2017-06-12 12:37:38 -0400609 return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL1, val);
Vivien Didelotea698f42017-03-11 16:12:50 -0500610}
611
Vivien Didelot5a7921f2016-11-04 03:23:28 +0100612/* Offset 0x06: Port Based VLAN Map */
613
614int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map)
615{
Vivien Didelot4d294af2017-03-11 16:12:47 -0500616 const u16 mask = mv88e6xxx_port_mask(chip);
Vivien Didelot5a7921f2016-11-04 03:23:28 +0100617 u16 reg;
618 int err;
619
Vivien Didelot7e5cc5f2017-06-12 12:37:39 -0400620 err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_BASE_VLAN, &reg);
Vivien Didelot5a7921f2016-11-04 03:23:28 +0100621 if (err)
622 return err;
623
624 reg &= ~mask;
625 reg |= map & mask;
626
Vivien Didelot7e5cc5f2017-06-12 12:37:39 -0400627 err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_BASE_VLAN, reg);
Vivien Didelot5a7921f2016-11-04 03:23:28 +0100628 if (err)
629 return err;
630
Vivien Didelot774439e52017-06-08 18:34:08 -0400631 dev_dbg(chip->dev, "p%d: VLANTable set to %.3x\n", port, map);
Vivien Didelot5a7921f2016-11-04 03:23:28 +0100632
633 return 0;
634}
Vivien Didelotb4e48c52016-11-04 03:23:29 +0100635
636int mv88e6xxx_port_get_fid(struct mv88e6xxx_chip *chip, int port, u16 *fid)
637{
638 const u16 upper_mask = (mv88e6xxx_num_databases(chip) - 1) >> 4;
639 u16 reg;
640 int err;
641
642 /* Port's default FID lower 4 bits are located in reg 0x06, offset 12 */
Vivien Didelot7e5cc5f2017-06-12 12:37:39 -0400643 err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_BASE_VLAN, &reg);
Vivien Didelotb4e48c52016-11-04 03:23:29 +0100644 if (err)
645 return err;
646
647 *fid = (reg & 0xf000) >> 12;
648
649 /* Port's default FID upper bits are located in reg 0x05, offset 0 */
650 if (upper_mask) {
Vivien Didelotcd985bb2017-06-12 12:37:38 -0400651 err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL1,
652 &reg);
Vivien Didelotb4e48c52016-11-04 03:23:29 +0100653 if (err)
654 return err;
655
656 *fid |= (reg & upper_mask) << 4;
657 }
658
659 return 0;
660}
661
662int mv88e6xxx_port_set_fid(struct mv88e6xxx_chip *chip, int port, u16 fid)
663{
664 const u16 upper_mask = (mv88e6xxx_num_databases(chip) - 1) >> 4;
665 u16 reg;
666 int err;
667
668 if (fid >= mv88e6xxx_num_databases(chip))
669 return -EINVAL;
670
671 /* Port's default FID lower 4 bits are located in reg 0x06, offset 12 */
Vivien Didelot7e5cc5f2017-06-12 12:37:39 -0400672 err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_BASE_VLAN, &reg);
Vivien Didelotb4e48c52016-11-04 03:23:29 +0100673 if (err)
674 return err;
675
676 reg &= 0x0fff;
677 reg |= (fid & 0x000f) << 12;
678
Vivien Didelot7e5cc5f2017-06-12 12:37:39 -0400679 err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_BASE_VLAN, reg);
Vivien Didelotb4e48c52016-11-04 03:23:29 +0100680 if (err)
681 return err;
682
683 /* Port's default FID upper bits are located in reg 0x05, offset 0 */
684 if (upper_mask) {
Vivien Didelotcd985bb2017-06-12 12:37:38 -0400685 err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL1,
686 &reg);
Vivien Didelotb4e48c52016-11-04 03:23:29 +0100687 if (err)
688 return err;
689
690 reg &= ~upper_mask;
691 reg |= (fid >> 4) & upper_mask;
692
Vivien Didelotcd985bb2017-06-12 12:37:38 -0400693 err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL1,
694 reg);
Vivien Didelotb4e48c52016-11-04 03:23:29 +0100695 if (err)
696 return err;
697 }
698
Vivien Didelot774439e52017-06-08 18:34:08 -0400699 dev_dbg(chip->dev, "p%d: FID set to %u\n", port, fid);
Vivien Didelotb4e48c52016-11-04 03:23:29 +0100700
701 return 0;
702}
Vivien Didelot77064f32016-11-04 03:23:30 +0100703
704/* Offset 0x07: Default Port VLAN ID & Priority */
705
706int mv88e6xxx_port_get_pvid(struct mv88e6xxx_chip *chip, int port, u16 *pvid)
707{
708 u16 reg;
709 int err;
710
Vivien Didelotb7929fb2017-06-12 12:37:40 -0400711 err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_DEFAULT_VLAN,
712 &reg);
Vivien Didelot77064f32016-11-04 03:23:30 +0100713 if (err)
714 return err;
715
Vivien Didelotb7929fb2017-06-12 12:37:40 -0400716 *pvid = reg & MV88E6XXX_PORT_DEFAULT_VLAN_MASK;
Vivien Didelot77064f32016-11-04 03:23:30 +0100717
718 return 0;
719}
720
721int mv88e6xxx_port_set_pvid(struct mv88e6xxx_chip *chip, int port, u16 pvid)
722{
723 u16 reg;
724 int err;
725
Vivien Didelotb7929fb2017-06-12 12:37:40 -0400726 err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_DEFAULT_VLAN,
727 &reg);
Vivien Didelot77064f32016-11-04 03:23:30 +0100728 if (err)
729 return err;
730
Vivien Didelotb7929fb2017-06-12 12:37:40 -0400731 reg &= ~MV88E6XXX_PORT_DEFAULT_VLAN_MASK;
732 reg |= pvid & MV88E6XXX_PORT_DEFAULT_VLAN_MASK;
Vivien Didelot77064f32016-11-04 03:23:30 +0100733
Vivien Didelotb7929fb2017-06-12 12:37:40 -0400734 err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_DEFAULT_VLAN,
735 reg);
Vivien Didelot77064f32016-11-04 03:23:30 +0100736 if (err)
737 return err;
738
Vivien Didelot774439e52017-06-08 18:34:08 -0400739 dev_dbg(chip->dev, "p%d: DefaultVID set to %u\n", port, pvid);
Vivien Didelot77064f32016-11-04 03:23:30 +0100740
741 return 0;
742}
Vivien Didelot385a0992016-11-04 03:23:31 +0100743
744/* Offset 0x08: Port Control 2 Register */
745
746static const char * const mv88e6xxx_port_8021q_mode_names[] = {
Vivien Didelot81c6edb2017-06-12 12:37:41 -0400747 [MV88E6XXX_PORT_CTL2_8021Q_MODE_DISABLED] = "Disabled",
748 [MV88E6XXX_PORT_CTL2_8021Q_MODE_FALLBACK] = "Fallback",
749 [MV88E6XXX_PORT_CTL2_8021Q_MODE_CHECK] = "Check",
750 [MV88E6XXX_PORT_CTL2_8021Q_MODE_SECURE] = "Secure",
Vivien Didelot385a0992016-11-04 03:23:31 +0100751};
752
Vivien Didelot601aeed2017-03-11 16:13:00 -0500753static int mv88e6185_port_set_default_forward(struct mv88e6xxx_chip *chip,
754 int port, bool multicast)
Andrew Lunna23b2962017-02-04 20:15:28 +0100755{
756 int err;
757 u16 reg;
758
Vivien Didelot81c6edb2017-06-12 12:37:41 -0400759 err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, &reg);
Andrew Lunna23b2962017-02-04 20:15:28 +0100760 if (err)
761 return err;
762
Vivien Didelot601aeed2017-03-11 16:13:00 -0500763 if (multicast)
Vivien Didelot81c6edb2017-06-12 12:37:41 -0400764 reg |= MV88E6XXX_PORT_CTL2_DEFAULT_FORWARD;
Andrew Lunna23b2962017-02-04 20:15:28 +0100765 else
Vivien Didelot81c6edb2017-06-12 12:37:41 -0400766 reg &= ~MV88E6XXX_PORT_CTL2_DEFAULT_FORWARD;
Andrew Lunna23b2962017-02-04 20:15:28 +0100767
Vivien Didelot81c6edb2017-06-12 12:37:41 -0400768 return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, reg);
Andrew Lunna23b2962017-02-04 20:15:28 +0100769}
770
Vivien Didelot601aeed2017-03-11 16:13:00 -0500771int mv88e6185_port_set_egress_floods(struct mv88e6xxx_chip *chip, int port,
772 bool unicast, bool multicast)
773{
774 int err;
775
776 err = mv88e6185_port_set_forward_unknown(chip, port, unicast);
777 if (err)
778 return err;
779
780 return mv88e6185_port_set_default_forward(chip, port, multicast);
781}
782
Andrew Lunna23b2962017-02-04 20:15:28 +0100783int mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port,
784 int upstream_port)
785{
786 int err;
787 u16 reg;
788
Vivien Didelot81c6edb2017-06-12 12:37:41 -0400789 err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, &reg);
Andrew Lunna23b2962017-02-04 20:15:28 +0100790 if (err)
791 return err;
792
Vivien Didelot81c6edb2017-06-12 12:37:41 -0400793 reg &= ~MV88E6095_PORT_CTL2_CPU_PORT_MASK;
Andrew Lunna23b2962017-02-04 20:15:28 +0100794 reg |= upstream_port;
795
Vivien Didelot81c6edb2017-06-12 12:37:41 -0400796 return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, reg);
Andrew Lunna23b2962017-02-04 20:15:28 +0100797}
798
Vivien Didelot385a0992016-11-04 03:23:31 +0100799int mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port,
800 u16 mode)
801{
802 u16 reg;
803 int err;
804
Vivien Didelot81c6edb2017-06-12 12:37:41 -0400805 err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, &reg);
Vivien Didelot385a0992016-11-04 03:23:31 +0100806 if (err)
807 return err;
808
Vivien Didelot81c6edb2017-06-12 12:37:41 -0400809 reg &= ~MV88E6XXX_PORT_CTL2_8021Q_MODE_MASK;
810 reg |= mode & MV88E6XXX_PORT_CTL2_8021Q_MODE_MASK;
Vivien Didelot385a0992016-11-04 03:23:31 +0100811
Vivien Didelot81c6edb2017-06-12 12:37:41 -0400812 err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, reg);
Vivien Didelot385a0992016-11-04 03:23:31 +0100813 if (err)
814 return err;
815
Vivien Didelot774439e52017-06-08 18:34:08 -0400816 dev_dbg(chip->dev, "p%d: 802.1QMode set to %s\n", port,
817 mv88e6xxx_port_8021q_mode_names[mode]);
Vivien Didelot385a0992016-11-04 03:23:31 +0100818
819 return 0;
820}
Andrew Lunnef0a7312016-12-03 04:35:16 +0100821
Andrew Lunna23b2962017-02-04 20:15:28 +0100822int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port)
823{
824 u16 reg;
825 int err;
826
Vivien Didelot81c6edb2017-06-12 12:37:41 -0400827 err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, &reg);
Andrew Lunna23b2962017-02-04 20:15:28 +0100828 if (err)
829 return err;
830
Vivien Didelot81c6edb2017-06-12 12:37:41 -0400831 reg |= MV88E6XXX_PORT_CTL2_MAP_DA;
Andrew Lunna23b2962017-02-04 20:15:28 +0100832
Vivien Didelot81c6edb2017-06-12 12:37:41 -0400833 return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, reg);
Andrew Lunna23b2962017-02-04 20:15:28 +0100834}
835
Vivien Didelotcd782652017-06-08 18:34:13 -0400836int mv88e6165_port_set_jumbo_size(struct mv88e6xxx_chip *chip, int port,
837 size_t size)
Andrew Lunn5f436662016-12-03 04:45:17 +0100838{
839 u16 reg;
840 int err;
841
Vivien Didelot81c6edb2017-06-12 12:37:41 -0400842 err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, &reg);
Andrew Lunn5f436662016-12-03 04:45:17 +0100843 if (err)
844 return err;
845
Vivien Didelot81c6edb2017-06-12 12:37:41 -0400846 reg &= ~MV88E6XXX_PORT_CTL2_JUMBO_MODE_MASK;
Vivien Didelotcd782652017-06-08 18:34:13 -0400847
848 if (size <= 1522)
Vivien Didelot81c6edb2017-06-12 12:37:41 -0400849 reg |= MV88E6XXX_PORT_CTL2_JUMBO_MODE_1522;
Vivien Didelotcd782652017-06-08 18:34:13 -0400850 else if (size <= 2048)
Vivien Didelot81c6edb2017-06-12 12:37:41 -0400851 reg |= MV88E6XXX_PORT_CTL2_JUMBO_MODE_2048;
Vivien Didelotcd782652017-06-08 18:34:13 -0400852 else if (size <= 10240)
Vivien Didelot81c6edb2017-06-12 12:37:41 -0400853 reg |= MV88E6XXX_PORT_CTL2_JUMBO_MODE_10240;
Vivien Didelotcd782652017-06-08 18:34:13 -0400854 else
855 return -ERANGE;
Andrew Lunn5f436662016-12-03 04:45:17 +0100856
Vivien Didelot81c6edb2017-06-12 12:37:41 -0400857 return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, reg);
Andrew Lunn5f436662016-12-03 04:45:17 +0100858}
859
Andrew Lunnef70b112016-12-03 04:45:18 +0100860/* Offset 0x09: Port Rate Control */
861
862int mv88e6095_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port)
863{
Vivien Didelot2cb8cb12017-06-12 12:37:42 -0400864 return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_EGRESS_RATE_CTL1,
865 0x0000);
Andrew Lunnef70b112016-12-03 04:45:18 +0100866}
867
868int mv88e6097_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port)
869{
Vivien Didelot2cb8cb12017-06-12 12:37:42 -0400870 return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_EGRESS_RATE_CTL1,
871 0x0001);
Andrew Lunnef70b112016-12-03 04:45:18 +0100872}
873
Vivien Didelotc8c94892017-03-11 16:13:01 -0500874/* Offset 0x0C: Port ATU Control */
875
876int mv88e6xxx_port_disable_learn_limit(struct mv88e6xxx_chip *chip, int port)
877{
Vivien Didelotb8109592017-06-12 12:37:45 -0400878 return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_ATU_CTL, 0);
Vivien Didelotc8c94892017-03-11 16:13:01 -0500879}
880
Vivien Didelot9dbfb4e2017-03-11 16:13:02 -0500881/* Offset 0x0D: (Priority) Override Register */
882
883int mv88e6xxx_port_disable_pri_override(struct mv88e6xxx_chip *chip, int port)
884{
Vivien Didelotb8109592017-06-12 12:37:45 -0400885 return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_PRI_OVERRIDE, 0);
Vivien Didelot9dbfb4e2017-03-11 16:13:02 -0500886}
887
Andrew Lunn56995cb2016-12-03 04:35:19 +0100888/* Offset 0x0f: Port Ether type */
889
890int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port,
891 u16 etype)
892{
Vivien Didelotb8109592017-06-12 12:37:45 -0400893 return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_ETH_TYPE, etype);
Andrew Lunn56995cb2016-12-03 04:35:19 +0100894}
895
Andrew Lunnef0a7312016-12-03 04:35:16 +0100896/* Offset 0x18: Port IEEE Priority Remapping Registers [0-3]
897 * Offset 0x19: Port IEEE Priority Remapping Registers [4-7]
898 */
899
900int mv88e6095_port_tag_remap(struct mv88e6xxx_chip *chip, int port)
901{
902 int err;
903
904 /* Use a direct priority mapping for all IEEE tagged frames */
Vivien Didelot8009df92017-06-12 12:37:44 -0400905 err = mv88e6xxx_port_write(chip, port,
906 MV88E6095_PORT_IEEE_PRIO_REMAP_0123,
907 0x3210);
Andrew Lunnef0a7312016-12-03 04:35:16 +0100908 if (err)
909 return err;
910
Vivien Didelot8009df92017-06-12 12:37:44 -0400911 return mv88e6xxx_port_write(chip, port,
912 MV88E6095_PORT_IEEE_PRIO_REMAP_4567,
913 0x7654);
Andrew Lunnef0a7312016-12-03 04:35:16 +0100914}
915
916static int mv88e6xxx_port_ieeepmt_write(struct mv88e6xxx_chip *chip,
Vivien Didelotddcbabf2017-06-17 23:07:14 -0400917 int port, u16 table, u8 ptr, u16 data)
Andrew Lunnef0a7312016-12-03 04:35:16 +0100918{
919 u16 reg;
920
Vivien Didelotddcbabf2017-06-17 23:07:14 -0400921 reg = MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_UPDATE | table |
922 (ptr << __bf_shf(MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_PTR_MASK)) |
923 (data & MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_DATA_MASK);
Andrew Lunnef0a7312016-12-03 04:35:16 +0100924
Vivien Didelot8009df92017-06-12 12:37:44 -0400925 return mv88e6xxx_port_write(chip, port,
926 MV88E6390_PORT_IEEE_PRIO_MAP_TABLE, reg);
Andrew Lunnef0a7312016-12-03 04:35:16 +0100927}
928
929int mv88e6390_port_tag_remap(struct mv88e6xxx_chip *chip, int port)
930{
931 int err, i;
Vivien Didelot8009df92017-06-12 12:37:44 -0400932 u16 table;
Andrew Lunnef0a7312016-12-03 04:35:16 +0100933
934 for (i = 0; i <= 7; i++) {
Vivien Didelot8009df92017-06-12 12:37:44 -0400935 table = MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_INGRESS_PCP;
936 err = mv88e6xxx_port_ieeepmt_write(chip, port, table, i,
937 (i | i << 4));
Andrew Lunnef0a7312016-12-03 04:35:16 +0100938 if (err)
939 return err;
940
Vivien Didelot8009df92017-06-12 12:37:44 -0400941 table = MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_EGRESS_GREEN_PCP;
942 err = mv88e6xxx_port_ieeepmt_write(chip, port, table, i, i);
Andrew Lunnef0a7312016-12-03 04:35:16 +0100943 if (err)
944 return err;
945
Vivien Didelot8009df92017-06-12 12:37:44 -0400946 table = MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_EGRESS_YELLOW_PCP;
947 err = mv88e6xxx_port_ieeepmt_write(chip, port, table, i, i);
Andrew Lunnef0a7312016-12-03 04:35:16 +0100948 if (err)
949 return err;
950
Vivien Didelot8009df92017-06-12 12:37:44 -0400951 table = MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_EGRESS_AVB_PCP;
952 err = mv88e6xxx_port_ieeepmt_write(chip, port, table, i, i);
Andrew Lunnef0a7312016-12-03 04:35:16 +0100953 if (err)
954 return err;
955 }
956
957 return 0;
958}