xref: /linux/drivers/net/dsa/mv88e6xxx/port.c (revision 4d5f2ba7)
118abed21SVivien Didelot /*
218abed21SVivien Didelot  * Marvell 88E6xxx Switch Port Registers support
318abed21SVivien Didelot  *
418abed21SVivien Didelot  * Copyright (c) 2008 Marvell Semiconductor
518abed21SVivien Didelot  *
64333d619SVivien Didelot  * Copyright (c) 2016-2017 Savoir-faire Linux Inc.
74333d619SVivien Didelot  *	Vivien Didelot <vivien.didelot@savoirfairelinux.com>
818abed21SVivien Didelot  *
918abed21SVivien Didelot  * This program is free software; you can redistribute it and/or modify
1018abed21SVivien Didelot  * it under the terms of the GNU General Public License as published by
1118abed21SVivien Didelot  * the Free Software Foundation; either version 2 of the License, or
1218abed21SVivien Didelot  * (at your option) any later version.
1318abed21SVivien Didelot  */
1418abed21SVivien Didelot 
15f39908d3SAndrew Lunn #include <linux/phy.h>
16*4d5f2ba7SVivien Didelot 
17*4d5f2ba7SVivien Didelot #include "chip.h"
1818abed21SVivien Didelot #include "port.h"
1918abed21SVivien Didelot 
2018abed21SVivien Didelot int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg,
2118abed21SVivien Didelot 			u16 *val)
2218abed21SVivien Didelot {
2318abed21SVivien Didelot 	int addr = chip->info->port_base_addr + port;
2418abed21SVivien Didelot 
2518abed21SVivien Didelot 	return mv88e6xxx_read(chip, addr, reg, val);
2618abed21SVivien Didelot }
2718abed21SVivien Didelot 
2818abed21SVivien Didelot int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg,
2918abed21SVivien Didelot 			 u16 val)
3018abed21SVivien Didelot {
3118abed21SVivien Didelot 	int addr = chip->info->port_base_addr + port;
3218abed21SVivien Didelot 
3318abed21SVivien Didelot 	return mv88e6xxx_write(chip, addr, reg, val);
3418abed21SVivien Didelot }
35e28def33SVivien Didelot 
3608ef7f10SVivien Didelot /* Offset 0x01: MAC (or PCS or Physical) Control Register
3708ef7f10SVivien Didelot  *
3808ef7f10SVivien Didelot  * Link, Duplex and Flow Control have one force bit, one value bit.
3996a2b40cSVivien Didelot  *
4096a2b40cSVivien Didelot  * For port's MAC speed, ForceSpd (or SpdValue) bits 1:0 program the value.
4196a2b40cSVivien Didelot  * Alternative values require the 200BASE (or AltSpeed) bit 12 set.
4296a2b40cSVivien Didelot  * Newer chips need a ForcedSpd bit 13 set to consider the value.
4308ef7f10SVivien Didelot  */
4408ef7f10SVivien Didelot 
45a0a0f622SVivien Didelot static int mv88e6xxx_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
46a0a0f622SVivien Didelot 					  phy_interface_t mode)
47a0a0f622SVivien Didelot {
48a0a0f622SVivien Didelot 	u16 reg;
49a0a0f622SVivien Didelot 	int err;
50a0a0f622SVivien Didelot 
51a0a0f622SVivien Didelot 	err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, &reg);
52a0a0f622SVivien Didelot 	if (err)
53a0a0f622SVivien Didelot 		return err;
54a0a0f622SVivien Didelot 
55a0a0f622SVivien Didelot 	reg &= ~(PORT_PCS_CTRL_RGMII_DELAY_RXCLK |
56a0a0f622SVivien Didelot 		 PORT_PCS_CTRL_RGMII_DELAY_TXCLK);
57a0a0f622SVivien Didelot 
58a0a0f622SVivien Didelot 	switch (mode) {
59a0a0f622SVivien Didelot 	case PHY_INTERFACE_MODE_RGMII_RXID:
60a0a0f622SVivien Didelot 		reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK;
61a0a0f622SVivien Didelot 		break;
62a0a0f622SVivien Didelot 	case PHY_INTERFACE_MODE_RGMII_TXID:
63a0a0f622SVivien Didelot 		reg |= PORT_PCS_CTRL_RGMII_DELAY_TXCLK;
64a0a0f622SVivien Didelot 		break;
65a0a0f622SVivien Didelot 	case PHY_INTERFACE_MODE_RGMII_ID:
66a0a0f622SVivien Didelot 		reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK |
67a0a0f622SVivien Didelot 			PORT_PCS_CTRL_RGMII_DELAY_TXCLK;
68a0a0f622SVivien Didelot 		break;
69fedf1865SAndrew Lunn 	case PHY_INTERFACE_MODE_RGMII:
70a0a0f622SVivien Didelot 		break;
71fedf1865SAndrew Lunn 	default:
72fedf1865SAndrew Lunn 		return 0;
73a0a0f622SVivien Didelot 	}
74a0a0f622SVivien Didelot 
75a0a0f622SVivien Didelot 	err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
76a0a0f622SVivien Didelot 	if (err)
77a0a0f622SVivien Didelot 		return err;
78a0a0f622SVivien Didelot 
79a0a0f622SVivien Didelot 	netdev_dbg(chip->ds->ports[port].netdev, "delay RXCLK %s, TXCLK %s\n",
80a0a0f622SVivien Didelot 		   reg & PORT_PCS_CTRL_RGMII_DELAY_RXCLK ? "yes" : "no",
81a0a0f622SVivien Didelot 		   reg & PORT_PCS_CTRL_RGMII_DELAY_TXCLK ? "yes" : "no");
82a0a0f622SVivien Didelot 
83a0a0f622SVivien Didelot 	return 0;
84a0a0f622SVivien Didelot }
85a0a0f622SVivien Didelot 
86a0a0f622SVivien Didelot int mv88e6352_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
87a0a0f622SVivien Didelot 				   phy_interface_t mode)
88a0a0f622SVivien Didelot {
89a0a0f622SVivien Didelot 	if (port < 5)
90a0a0f622SVivien Didelot 		return -EOPNOTSUPP;
91a0a0f622SVivien Didelot 
92a0a0f622SVivien Didelot 	return mv88e6xxx_port_set_rgmii_delay(chip, port, mode);
93a0a0f622SVivien Didelot }
94a0a0f622SVivien Didelot 
95a0a0f622SVivien Didelot int mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
96a0a0f622SVivien Didelot 				   phy_interface_t mode)
97a0a0f622SVivien Didelot {
98a0a0f622SVivien Didelot 	if (port != 0)
99a0a0f622SVivien Didelot 		return -EOPNOTSUPP;
100a0a0f622SVivien Didelot 
101a0a0f622SVivien Didelot 	return mv88e6xxx_port_set_rgmii_delay(chip, port, mode);
102a0a0f622SVivien Didelot }
103a0a0f622SVivien Didelot 
10408ef7f10SVivien Didelot int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link)
10508ef7f10SVivien Didelot {
10608ef7f10SVivien Didelot 	u16 reg;
10708ef7f10SVivien Didelot 	int err;
10808ef7f10SVivien Didelot 
10908ef7f10SVivien Didelot 	err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, &reg);
11008ef7f10SVivien Didelot 	if (err)
11108ef7f10SVivien Didelot 		return err;
11208ef7f10SVivien Didelot 
11308ef7f10SVivien Didelot 	reg &= ~(PORT_PCS_CTRL_FORCE_LINK | PORT_PCS_CTRL_LINK_UP);
11408ef7f10SVivien Didelot 
11508ef7f10SVivien Didelot 	switch (link) {
11608ef7f10SVivien Didelot 	case LINK_FORCED_DOWN:
11708ef7f10SVivien Didelot 		reg |= PORT_PCS_CTRL_FORCE_LINK;
11808ef7f10SVivien Didelot 		break;
11908ef7f10SVivien Didelot 	case LINK_FORCED_UP:
12008ef7f10SVivien Didelot 		reg |= PORT_PCS_CTRL_FORCE_LINK | PORT_PCS_CTRL_LINK_UP;
12108ef7f10SVivien Didelot 		break;
12208ef7f10SVivien Didelot 	case LINK_UNFORCED:
12308ef7f10SVivien Didelot 		/* normal link detection */
12408ef7f10SVivien Didelot 		break;
12508ef7f10SVivien Didelot 	default:
12608ef7f10SVivien Didelot 		return -EINVAL;
12708ef7f10SVivien Didelot 	}
12808ef7f10SVivien Didelot 
12908ef7f10SVivien Didelot 	err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
13008ef7f10SVivien Didelot 	if (err)
13108ef7f10SVivien Didelot 		return err;
13208ef7f10SVivien Didelot 
13308ef7f10SVivien Didelot 	netdev_dbg(chip->ds->ports[port].netdev, "%s link %s\n",
13408ef7f10SVivien Didelot 		   reg & PORT_PCS_CTRL_FORCE_LINK ? "Force" : "Unforce",
13508ef7f10SVivien Didelot 		   reg & PORT_PCS_CTRL_LINK_UP ? "up" : "down");
13608ef7f10SVivien Didelot 
13708ef7f10SVivien Didelot 	return 0;
13808ef7f10SVivien Didelot }
13908ef7f10SVivien Didelot 
1407f1ae07bSVivien Didelot int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup)
1417f1ae07bSVivien Didelot {
1427f1ae07bSVivien Didelot 	u16 reg;
1437f1ae07bSVivien Didelot 	int err;
1447f1ae07bSVivien Didelot 
1457f1ae07bSVivien Didelot 	err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, &reg);
1467f1ae07bSVivien Didelot 	if (err)
1477f1ae07bSVivien Didelot 		return err;
1487f1ae07bSVivien Didelot 
1497f1ae07bSVivien Didelot 	reg &= ~(PORT_PCS_CTRL_FORCE_DUPLEX | PORT_PCS_CTRL_DUPLEX_FULL);
1507f1ae07bSVivien Didelot 
1517f1ae07bSVivien Didelot 	switch (dup) {
1527f1ae07bSVivien Didelot 	case DUPLEX_HALF:
1537f1ae07bSVivien Didelot 		reg |= PORT_PCS_CTRL_FORCE_DUPLEX;
1547f1ae07bSVivien Didelot 		break;
1557f1ae07bSVivien Didelot 	case DUPLEX_FULL:
1567f1ae07bSVivien Didelot 		reg |= PORT_PCS_CTRL_FORCE_DUPLEX | PORT_PCS_CTRL_DUPLEX_FULL;
1577f1ae07bSVivien Didelot 		break;
1587f1ae07bSVivien Didelot 	case DUPLEX_UNFORCED:
1597f1ae07bSVivien Didelot 		/* normal duplex detection */
1607f1ae07bSVivien Didelot 		break;
1617f1ae07bSVivien Didelot 	default:
1627f1ae07bSVivien Didelot 		return -EINVAL;
1637f1ae07bSVivien Didelot 	}
1647f1ae07bSVivien Didelot 
1657f1ae07bSVivien Didelot 	err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
1667f1ae07bSVivien Didelot 	if (err)
1677f1ae07bSVivien Didelot 		return err;
1687f1ae07bSVivien Didelot 
1697f1ae07bSVivien Didelot 	netdev_dbg(chip->ds->ports[port].netdev, "%s %s duplex\n",
1707f1ae07bSVivien Didelot 		   reg & PORT_PCS_CTRL_FORCE_DUPLEX ? "Force" : "Unforce",
1717f1ae07bSVivien Didelot 		   reg & PORT_PCS_CTRL_DUPLEX_FULL ? "full" : "half");
1727f1ae07bSVivien Didelot 
1737f1ae07bSVivien Didelot 	return 0;
1747f1ae07bSVivien Didelot }
1757f1ae07bSVivien Didelot 
17696a2b40cSVivien Didelot static int mv88e6xxx_port_set_speed(struct mv88e6xxx_chip *chip, int port,
17796a2b40cSVivien Didelot 				    int speed, bool alt_bit, bool force_bit)
17896a2b40cSVivien Didelot {
17996a2b40cSVivien Didelot 	u16 reg, ctrl;
18096a2b40cSVivien Didelot 	int err;
18196a2b40cSVivien Didelot 
18296a2b40cSVivien Didelot 	switch (speed) {
18396a2b40cSVivien Didelot 	case 10:
18496a2b40cSVivien Didelot 		ctrl = PORT_PCS_CTRL_SPEED_10;
18596a2b40cSVivien Didelot 		break;
18696a2b40cSVivien Didelot 	case 100:
18796a2b40cSVivien Didelot 		ctrl = PORT_PCS_CTRL_SPEED_100;
18896a2b40cSVivien Didelot 		break;
18996a2b40cSVivien Didelot 	case 200:
19096a2b40cSVivien Didelot 		if (alt_bit)
19196a2b40cSVivien Didelot 			ctrl = PORT_PCS_CTRL_SPEED_100 | PORT_PCS_CTRL_ALTSPEED;
19296a2b40cSVivien Didelot 		else
19396a2b40cSVivien Didelot 			ctrl = PORT_PCS_CTRL_SPEED_200;
19496a2b40cSVivien Didelot 		break;
19596a2b40cSVivien Didelot 	case 1000:
19696a2b40cSVivien Didelot 		ctrl = PORT_PCS_CTRL_SPEED_1000;
19796a2b40cSVivien Didelot 		break;
19896a2b40cSVivien Didelot 	case 2500:
199740117a8SAndrew Lunn 		ctrl = PORT_PCS_CTRL_SPEED_10000 | PORT_PCS_CTRL_ALTSPEED;
20096a2b40cSVivien Didelot 		break;
20196a2b40cSVivien Didelot 	case 10000:
20296a2b40cSVivien Didelot 		/* all bits set, fall through... */
20396a2b40cSVivien Didelot 	case SPEED_UNFORCED:
20496a2b40cSVivien Didelot 		ctrl = PORT_PCS_CTRL_SPEED_UNFORCED;
20596a2b40cSVivien Didelot 		break;
20696a2b40cSVivien Didelot 	default:
20796a2b40cSVivien Didelot 		return -EOPNOTSUPP;
20896a2b40cSVivien Didelot 	}
20996a2b40cSVivien Didelot 
21096a2b40cSVivien Didelot 	err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, &reg);
21196a2b40cSVivien Didelot 	if (err)
21296a2b40cSVivien Didelot 		return err;
21396a2b40cSVivien Didelot 
21496a2b40cSVivien Didelot 	reg &= ~PORT_PCS_CTRL_SPEED_MASK;
21596a2b40cSVivien Didelot 	if (alt_bit)
21696a2b40cSVivien Didelot 		reg &= ~PORT_PCS_CTRL_ALTSPEED;
21796a2b40cSVivien Didelot 	if (force_bit) {
21896a2b40cSVivien Didelot 		reg &= ~PORT_PCS_CTRL_FORCE_SPEED;
2190b6e3d03SAndrew Lunn 		if (speed != SPEED_UNFORCED)
22096a2b40cSVivien Didelot 			ctrl |= PORT_PCS_CTRL_FORCE_SPEED;
22196a2b40cSVivien Didelot 	}
22296a2b40cSVivien Didelot 	reg |= ctrl;
22396a2b40cSVivien Didelot 
22496a2b40cSVivien Didelot 	err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
22596a2b40cSVivien Didelot 	if (err)
22696a2b40cSVivien Didelot 		return err;
22796a2b40cSVivien Didelot 
22896a2b40cSVivien Didelot 	if (speed)
22996a2b40cSVivien Didelot 		netdev_dbg(chip->ds->ports[port].netdev,
23096a2b40cSVivien Didelot 			   "Speed set to %d Mbps\n", speed);
23196a2b40cSVivien Didelot 	else
23296a2b40cSVivien Didelot 		netdev_dbg(chip->ds->ports[port].netdev, "Speed unforced\n");
23396a2b40cSVivien Didelot 
23496a2b40cSVivien Didelot 	return 0;
23596a2b40cSVivien Didelot }
23696a2b40cSVivien Didelot 
23796a2b40cSVivien Didelot /* Support 10, 100, 200 Mbps (e.g. 88E6065 family) */
23896a2b40cSVivien Didelot int mv88e6065_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
23996a2b40cSVivien Didelot {
24096a2b40cSVivien Didelot 	if (speed == SPEED_MAX)
24196a2b40cSVivien Didelot 		speed = 200;
24296a2b40cSVivien Didelot 
24396a2b40cSVivien Didelot 	if (speed > 200)
24496a2b40cSVivien Didelot 		return -EOPNOTSUPP;
24596a2b40cSVivien Didelot 
24696a2b40cSVivien Didelot 	/* Setting 200 Mbps on port 0 to 3 selects 100 Mbps */
24796a2b40cSVivien Didelot 	return mv88e6xxx_port_set_speed(chip, port, speed, false, false);
24896a2b40cSVivien Didelot }
24996a2b40cSVivien Didelot 
25096a2b40cSVivien Didelot /* Support 10, 100, 1000 Mbps (e.g. 88E6185 family) */
25196a2b40cSVivien Didelot int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
25296a2b40cSVivien Didelot {
25396a2b40cSVivien Didelot 	if (speed == SPEED_MAX)
25496a2b40cSVivien Didelot 		speed = 1000;
25596a2b40cSVivien Didelot 
25696a2b40cSVivien Didelot 	if (speed == 200 || speed > 1000)
25796a2b40cSVivien Didelot 		return -EOPNOTSUPP;
25896a2b40cSVivien Didelot 
25996a2b40cSVivien Didelot 	return mv88e6xxx_port_set_speed(chip, port, speed, false, false);
26096a2b40cSVivien Didelot }
26196a2b40cSVivien Didelot 
26296a2b40cSVivien Didelot /* Support 10, 100, 200, 1000 Mbps (e.g. 88E6352 family) */
26396a2b40cSVivien Didelot int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
26496a2b40cSVivien Didelot {
26596a2b40cSVivien Didelot 	if (speed == SPEED_MAX)
26696a2b40cSVivien Didelot 		speed = 1000;
26796a2b40cSVivien Didelot 
26896a2b40cSVivien Didelot 	if (speed > 1000)
26996a2b40cSVivien Didelot 		return -EOPNOTSUPP;
27096a2b40cSVivien Didelot 
27196a2b40cSVivien Didelot 	if (speed == 200 && port < 5)
27296a2b40cSVivien Didelot 		return -EOPNOTSUPP;
27396a2b40cSVivien Didelot 
27496a2b40cSVivien Didelot 	return mv88e6xxx_port_set_speed(chip, port, speed, true, false);
27596a2b40cSVivien Didelot }
27696a2b40cSVivien Didelot 
27796a2b40cSVivien Didelot /* Support 10, 100, 200, 1000, 2500 Mbps (e.g. 88E6390) */
27896a2b40cSVivien Didelot int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
27996a2b40cSVivien Didelot {
28096a2b40cSVivien Didelot 	if (speed == SPEED_MAX)
28196a2b40cSVivien Didelot 		speed = port < 9 ? 1000 : 2500;
28296a2b40cSVivien Didelot 
28396a2b40cSVivien Didelot 	if (speed > 2500)
28496a2b40cSVivien Didelot 		return -EOPNOTSUPP;
28596a2b40cSVivien Didelot 
28696a2b40cSVivien Didelot 	if (speed == 200 && port != 0)
28796a2b40cSVivien Didelot 		return -EOPNOTSUPP;
28896a2b40cSVivien Didelot 
28996a2b40cSVivien Didelot 	if (speed == 2500 && port < 9)
29096a2b40cSVivien Didelot 		return -EOPNOTSUPP;
29196a2b40cSVivien Didelot 
29296a2b40cSVivien Didelot 	return mv88e6xxx_port_set_speed(chip, port, speed, true, true);
29396a2b40cSVivien Didelot }
29496a2b40cSVivien Didelot 
29596a2b40cSVivien Didelot /* Support 10, 100, 200, 1000, 2500, 10000 Mbps (e.g. 88E6190X) */
29696a2b40cSVivien Didelot int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
29796a2b40cSVivien Didelot {
29896a2b40cSVivien Didelot 	if (speed == SPEED_MAX)
29996a2b40cSVivien Didelot 		speed = port < 9 ? 1000 : 10000;
30096a2b40cSVivien Didelot 
30196a2b40cSVivien Didelot 	if (speed == 200 && port != 0)
30296a2b40cSVivien Didelot 		return -EOPNOTSUPP;
30396a2b40cSVivien Didelot 
30496a2b40cSVivien Didelot 	if (speed >= 2500 && port < 9)
30596a2b40cSVivien Didelot 		return -EOPNOTSUPP;
30696a2b40cSVivien Didelot 
30796a2b40cSVivien Didelot 	return mv88e6xxx_port_set_speed(chip, port, speed, true, true);
30896a2b40cSVivien Didelot }
30996a2b40cSVivien Didelot 
310f39908d3SAndrew Lunn int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
311f39908d3SAndrew Lunn 			      phy_interface_t mode)
312f39908d3SAndrew Lunn {
313f39908d3SAndrew Lunn 	u16 reg;
314f39908d3SAndrew Lunn 	u16 cmode;
315f39908d3SAndrew Lunn 	int err;
316f39908d3SAndrew Lunn 
317f39908d3SAndrew Lunn 	if (mode == PHY_INTERFACE_MODE_NA)
318f39908d3SAndrew Lunn 		return 0;
319f39908d3SAndrew Lunn 
320f39908d3SAndrew Lunn 	if (port != 9 && port != 10)
321f39908d3SAndrew Lunn 		return -EOPNOTSUPP;
322f39908d3SAndrew Lunn 
323f39908d3SAndrew Lunn 	switch (mode) {
324f39908d3SAndrew Lunn 	case PHY_INTERFACE_MODE_1000BASEX:
325f39908d3SAndrew Lunn 		cmode = PORT_STATUS_CMODE_1000BASE_X;
326f39908d3SAndrew Lunn 		break;
327f39908d3SAndrew Lunn 	case PHY_INTERFACE_MODE_SGMII:
328f39908d3SAndrew Lunn 		cmode = PORT_STATUS_CMODE_SGMII;
329f39908d3SAndrew Lunn 		break;
330f39908d3SAndrew Lunn 	case PHY_INTERFACE_MODE_2500BASEX:
331f39908d3SAndrew Lunn 		cmode = PORT_STATUS_CMODE_2500BASEX;
332f39908d3SAndrew Lunn 		break;
333f39908d3SAndrew Lunn 	case PHY_INTERFACE_MODE_XGMII:
334f39908d3SAndrew Lunn 		cmode = PORT_STATUS_CMODE_XAUI;
335f39908d3SAndrew Lunn 		break;
336f39908d3SAndrew Lunn 	case PHY_INTERFACE_MODE_RXAUI:
337f39908d3SAndrew Lunn 		cmode = PORT_STATUS_CMODE_RXAUI;
338f39908d3SAndrew Lunn 		break;
339f39908d3SAndrew Lunn 	default:
340f39908d3SAndrew Lunn 		cmode = 0;
341f39908d3SAndrew Lunn 	}
342f39908d3SAndrew Lunn 
343f39908d3SAndrew Lunn 	if (cmode) {
344f39908d3SAndrew Lunn 		err = mv88e6xxx_port_read(chip, port, PORT_STATUS, &reg);
345f39908d3SAndrew Lunn 		if (err)
346f39908d3SAndrew Lunn 			return err;
347f39908d3SAndrew Lunn 
348f39908d3SAndrew Lunn 		reg &= ~PORT_STATUS_CMODE_MASK;
349f39908d3SAndrew Lunn 		reg |= cmode;
350f39908d3SAndrew Lunn 
351f39908d3SAndrew Lunn 		err = mv88e6xxx_port_write(chip, port, PORT_STATUS, reg);
352f39908d3SAndrew Lunn 		if (err)
353f39908d3SAndrew Lunn 			return err;
354f39908d3SAndrew Lunn 	}
355f39908d3SAndrew Lunn 
356f39908d3SAndrew Lunn 	return 0;
357f39908d3SAndrew Lunn }
358f39908d3SAndrew Lunn 
359f39908d3SAndrew Lunn int mv88e6xxx_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode)
360f39908d3SAndrew Lunn {
361f39908d3SAndrew Lunn 	int err;
362f39908d3SAndrew Lunn 	u16 reg;
363f39908d3SAndrew Lunn 
364f39908d3SAndrew Lunn 	err = mv88e6xxx_port_read(chip, port, PORT_STATUS, &reg);
365f39908d3SAndrew Lunn 	if (err)
366f39908d3SAndrew Lunn 		return err;
367f39908d3SAndrew Lunn 
368f39908d3SAndrew Lunn 	*cmode = reg & PORT_STATUS_CMODE_MASK;
369f39908d3SAndrew Lunn 
370f39908d3SAndrew Lunn 	return 0;
371f39908d3SAndrew Lunn }
372f39908d3SAndrew Lunn 
373b35d322aSAndrew Lunn /* Offset 0x02: Pause Control
374b35d322aSAndrew Lunn  *
375b35d322aSAndrew Lunn  * Do not limit the period of time that this port can be paused for by
376b35d322aSAndrew Lunn  * the remote end or the period of time that this port can pause the
377b35d322aSAndrew Lunn  * remote end.
378b35d322aSAndrew Lunn  */
379b35d322aSAndrew Lunn int mv88e6097_port_pause_config(struct mv88e6xxx_chip *chip, int port)
380b35d322aSAndrew Lunn {
381b35d322aSAndrew Lunn 	return mv88e6xxx_port_write(chip, port, PORT_PAUSE_CTRL, 0x0000);
382b35d322aSAndrew Lunn }
383b35d322aSAndrew Lunn 
3843ce0e65eSAndrew Lunn int mv88e6390_port_pause_config(struct mv88e6xxx_chip *chip, int port)
3853ce0e65eSAndrew Lunn {
3863ce0e65eSAndrew Lunn 	int err;
3873ce0e65eSAndrew Lunn 
3883ce0e65eSAndrew Lunn 	err = mv88e6xxx_port_write(chip, port, PORT_PAUSE_CTRL,
3893ce0e65eSAndrew Lunn 				   PORT_FLOW_CTRL_LIMIT_IN | 0);
3903ce0e65eSAndrew Lunn 	if (err)
3913ce0e65eSAndrew Lunn 		return err;
3923ce0e65eSAndrew Lunn 
3933ce0e65eSAndrew Lunn 	return mv88e6xxx_port_write(chip, port, PORT_PAUSE_CTRL,
3943ce0e65eSAndrew Lunn 				    PORT_FLOW_CTRL_LIMIT_OUT | 0);
3953ce0e65eSAndrew Lunn }
3963ce0e65eSAndrew Lunn 
397e28def33SVivien Didelot /* Offset 0x04: Port Control Register */
398e28def33SVivien Didelot 
399e28def33SVivien Didelot static const char * const mv88e6xxx_port_state_names[] = {
400e28def33SVivien Didelot 	[PORT_CONTROL_STATE_DISABLED] = "Disabled",
401e28def33SVivien Didelot 	[PORT_CONTROL_STATE_BLOCKING] = "Blocking/Listening",
402e28def33SVivien Didelot 	[PORT_CONTROL_STATE_LEARNING] = "Learning",
403e28def33SVivien Didelot 	[PORT_CONTROL_STATE_FORWARDING] = "Forwarding",
404e28def33SVivien Didelot };
405e28def33SVivien Didelot 
406e28def33SVivien Didelot int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state)
407e28def33SVivien Didelot {
408e28def33SVivien Didelot 	u16 reg;
409e28def33SVivien Didelot 	int err;
410e28def33SVivien Didelot 
411e28def33SVivien Didelot 	err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, &reg);
412e28def33SVivien Didelot 	if (err)
413e28def33SVivien Didelot 		return err;
414e28def33SVivien Didelot 
415e28def33SVivien Didelot 	reg &= ~PORT_CONTROL_STATE_MASK;
416e28def33SVivien Didelot 	reg |= state;
417e28def33SVivien Didelot 
418e28def33SVivien Didelot 	err = mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
419e28def33SVivien Didelot 	if (err)
420e28def33SVivien Didelot 		return err;
421e28def33SVivien Didelot 
422e28def33SVivien Didelot 	netdev_dbg(chip->ds->ports[port].netdev, "PortState set to %s\n",
423e28def33SVivien Didelot 		   mv88e6xxx_port_state_names[state]);
424e28def33SVivien Didelot 
425e28def33SVivien Didelot 	return 0;
426e28def33SVivien Didelot }
4275a7921f4SVivien Didelot 
42856995cbcSAndrew Lunn int mv88e6xxx_port_set_egress_mode(struct mv88e6xxx_chip *chip, int port,
42956995cbcSAndrew Lunn 				   u16 mode)
43056995cbcSAndrew Lunn {
43156995cbcSAndrew Lunn 	int err;
43256995cbcSAndrew Lunn 	u16 reg;
43356995cbcSAndrew Lunn 
43456995cbcSAndrew Lunn 	err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, &reg);
43556995cbcSAndrew Lunn 	if (err)
43656995cbcSAndrew Lunn 		return err;
43756995cbcSAndrew Lunn 
43856995cbcSAndrew Lunn 	reg &= ~PORT_CONTROL_EGRESS_MASK;
43956995cbcSAndrew Lunn 	reg |= mode;
44056995cbcSAndrew Lunn 
44156995cbcSAndrew Lunn 	return mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
44256995cbcSAndrew Lunn }
44356995cbcSAndrew Lunn 
44456995cbcSAndrew Lunn int mv88e6085_port_set_frame_mode(struct mv88e6xxx_chip *chip, int port,
44556995cbcSAndrew Lunn 				  enum mv88e6xxx_frame_mode mode)
44656995cbcSAndrew Lunn {
44756995cbcSAndrew Lunn 	int err;
44856995cbcSAndrew Lunn 	u16 reg;
44956995cbcSAndrew Lunn 
45056995cbcSAndrew Lunn 	err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, &reg);
45156995cbcSAndrew Lunn 	if (err)
45256995cbcSAndrew Lunn 		return err;
45356995cbcSAndrew Lunn 
45456995cbcSAndrew Lunn 	reg &= ~PORT_CONTROL_FRAME_MODE_DSA;
45556995cbcSAndrew Lunn 
45656995cbcSAndrew Lunn 	switch (mode) {
45756995cbcSAndrew Lunn 	case MV88E6XXX_FRAME_MODE_NORMAL:
45856995cbcSAndrew Lunn 		reg |= PORT_CONTROL_FRAME_MODE_NORMAL;
45956995cbcSAndrew Lunn 		break;
46056995cbcSAndrew Lunn 	case MV88E6XXX_FRAME_MODE_DSA:
46156995cbcSAndrew Lunn 		reg |= PORT_CONTROL_FRAME_MODE_DSA;
46256995cbcSAndrew Lunn 		break;
46356995cbcSAndrew Lunn 	default:
46456995cbcSAndrew Lunn 		return -EINVAL;
46556995cbcSAndrew Lunn 	}
46656995cbcSAndrew Lunn 
46756995cbcSAndrew Lunn 	return mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
46856995cbcSAndrew Lunn }
46956995cbcSAndrew Lunn 
47056995cbcSAndrew Lunn int mv88e6351_port_set_frame_mode(struct mv88e6xxx_chip *chip, int port,
47156995cbcSAndrew Lunn 				  enum mv88e6xxx_frame_mode mode)
47256995cbcSAndrew Lunn {
47356995cbcSAndrew Lunn 	int err;
47456995cbcSAndrew Lunn 	u16 reg;
47556995cbcSAndrew Lunn 
47656995cbcSAndrew Lunn 	err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, &reg);
47756995cbcSAndrew Lunn 	if (err)
47856995cbcSAndrew Lunn 		return err;
47956995cbcSAndrew Lunn 
48056995cbcSAndrew Lunn 	reg &= ~PORT_CONTROL_FRAME_MASK;
48156995cbcSAndrew Lunn 
48256995cbcSAndrew Lunn 	switch (mode) {
48356995cbcSAndrew Lunn 	case MV88E6XXX_FRAME_MODE_NORMAL:
48456995cbcSAndrew Lunn 		reg |= PORT_CONTROL_FRAME_MODE_NORMAL;
48556995cbcSAndrew Lunn 		break;
48656995cbcSAndrew Lunn 	case MV88E6XXX_FRAME_MODE_DSA:
48756995cbcSAndrew Lunn 		reg |= PORT_CONTROL_FRAME_MODE_DSA;
48856995cbcSAndrew Lunn 		break;
48956995cbcSAndrew Lunn 	case MV88E6XXX_FRAME_MODE_PROVIDER:
49056995cbcSAndrew Lunn 		reg |= PORT_CONTROL_FRAME_MODE_PROVIDER;
49156995cbcSAndrew Lunn 		break;
49256995cbcSAndrew Lunn 	case MV88E6XXX_FRAME_MODE_ETHERTYPE:
49356995cbcSAndrew Lunn 		reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA;
49456995cbcSAndrew Lunn 		break;
49556995cbcSAndrew Lunn 	default:
49656995cbcSAndrew Lunn 		return -EINVAL;
49756995cbcSAndrew Lunn 	}
49856995cbcSAndrew Lunn 
49956995cbcSAndrew Lunn 	return mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
50056995cbcSAndrew Lunn }
50156995cbcSAndrew Lunn 
502601aeed3SVivien Didelot static int mv88e6185_port_set_forward_unknown(struct mv88e6xxx_chip *chip,
503601aeed3SVivien Didelot 					      int port, bool unicast)
50456995cbcSAndrew Lunn {
50556995cbcSAndrew Lunn 	int err;
50656995cbcSAndrew Lunn 	u16 reg;
50756995cbcSAndrew Lunn 
50856995cbcSAndrew Lunn 	err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, &reg);
50956995cbcSAndrew Lunn 	if (err)
51056995cbcSAndrew Lunn 		return err;
51156995cbcSAndrew Lunn 
512601aeed3SVivien Didelot 	if (unicast)
51356995cbcSAndrew Lunn 		reg |= PORT_CONTROL_FORWARD_UNKNOWN;
51456995cbcSAndrew Lunn 	else
51556995cbcSAndrew Lunn 		reg &= ~PORT_CONTROL_FORWARD_UNKNOWN;
51656995cbcSAndrew Lunn 
51756995cbcSAndrew Lunn 	return mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
51856995cbcSAndrew Lunn }
51956995cbcSAndrew Lunn 
520601aeed3SVivien Didelot int mv88e6352_port_set_egress_floods(struct mv88e6xxx_chip *chip, int port,
521601aeed3SVivien Didelot 				     bool unicast, bool multicast)
52256995cbcSAndrew Lunn {
52356995cbcSAndrew Lunn 	int err;
52456995cbcSAndrew Lunn 	u16 reg;
52556995cbcSAndrew Lunn 
52656995cbcSAndrew Lunn 	err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, &reg);
52756995cbcSAndrew Lunn 	if (err)
52856995cbcSAndrew Lunn 		return err;
52956995cbcSAndrew Lunn 
530601aeed3SVivien Didelot 	reg &= ~PORT_CONTROL_EGRESS_FLOODS_MASK;
531601aeed3SVivien Didelot 
532601aeed3SVivien Didelot 	if (unicast && multicast)
533601aeed3SVivien Didelot 		reg |= PORT_CONTROL_EGRESS_FLOODS_ALL_UNKNOWN_DA;
534601aeed3SVivien Didelot 	else if (unicast)
535601aeed3SVivien Didelot 		reg |= PORT_CONTROL_EGRESS_FLOODS_NO_UNKNOWN_MC_DA;
536601aeed3SVivien Didelot 	else if (multicast)
537601aeed3SVivien Didelot 		reg |= PORT_CONTROL_EGRESS_FLOODS_NO_UNKNOWN_UC_DA;
53856995cbcSAndrew Lunn 	else
539601aeed3SVivien Didelot 		reg |= PORT_CONTROL_EGRESS_FLOODS_NO_UNKNOWN_DA;
54056995cbcSAndrew Lunn 
54156995cbcSAndrew Lunn 	return mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
54256995cbcSAndrew Lunn }
54356995cbcSAndrew Lunn 
544b4e48c50SVivien Didelot /* Offset 0x05: Port Control 1 */
545b4e48c50SVivien Didelot 
546ea698f4fSVivien Didelot int mv88e6xxx_port_set_message_port(struct mv88e6xxx_chip *chip, int port,
547ea698f4fSVivien Didelot 				    bool message_port)
548ea698f4fSVivien Didelot {
549ea698f4fSVivien Didelot 	u16 val;
550ea698f4fSVivien Didelot 	int err;
551ea698f4fSVivien Didelot 
552ea698f4fSVivien Didelot 	err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, &val);
553ea698f4fSVivien Didelot 	if (err)
554ea698f4fSVivien Didelot 		return err;
555ea698f4fSVivien Didelot 
556ea698f4fSVivien Didelot 	if (message_port)
557ea698f4fSVivien Didelot 		val |= PORT_CONTROL_1_MESSAGE_PORT;
558ea698f4fSVivien Didelot 	else
559ea698f4fSVivien Didelot 		val &= ~PORT_CONTROL_1_MESSAGE_PORT;
560ea698f4fSVivien Didelot 
561ea698f4fSVivien Didelot 	return mv88e6xxx_port_write(chip, port, PORT_CONTROL_1, val);
562ea698f4fSVivien Didelot }
563ea698f4fSVivien Didelot 
5645a7921f4SVivien Didelot /* Offset 0x06: Port Based VLAN Map */
5655a7921f4SVivien Didelot 
5665a7921f4SVivien Didelot int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map)
5675a7921f4SVivien Didelot {
5684d294af2SVivien Didelot 	const u16 mask = mv88e6xxx_port_mask(chip);
5695a7921f4SVivien Didelot 	u16 reg;
5705a7921f4SVivien Didelot 	int err;
5715a7921f4SVivien Didelot 
5725a7921f4SVivien Didelot 	err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg);
5735a7921f4SVivien Didelot 	if (err)
5745a7921f4SVivien Didelot 		return err;
5755a7921f4SVivien Didelot 
5765a7921f4SVivien Didelot 	reg &= ~mask;
5775a7921f4SVivien Didelot 	reg |= map & mask;
5785a7921f4SVivien Didelot 
5795a7921f4SVivien Didelot 	err = mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg);
5805a7921f4SVivien Didelot 	if (err)
5815a7921f4SVivien Didelot 		return err;
5825a7921f4SVivien Didelot 
5835a7921f4SVivien Didelot 	netdev_dbg(chip->ds->ports[port].netdev, "VLANTable set to %.3x\n",
5845a7921f4SVivien Didelot 		   map);
5855a7921f4SVivien Didelot 
5865a7921f4SVivien Didelot 	return 0;
5875a7921f4SVivien Didelot }
588b4e48c50SVivien Didelot 
589b4e48c50SVivien Didelot int mv88e6xxx_port_get_fid(struct mv88e6xxx_chip *chip, int port, u16 *fid)
590b4e48c50SVivien Didelot {
591b4e48c50SVivien Didelot 	const u16 upper_mask = (mv88e6xxx_num_databases(chip) - 1) >> 4;
592b4e48c50SVivien Didelot 	u16 reg;
593b4e48c50SVivien Didelot 	int err;
594b4e48c50SVivien Didelot 
595b4e48c50SVivien Didelot 	/* Port's default FID lower 4 bits are located in reg 0x06, offset 12 */
596b4e48c50SVivien Didelot 	err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg);
597b4e48c50SVivien Didelot 	if (err)
598b4e48c50SVivien Didelot 		return err;
599b4e48c50SVivien Didelot 
600b4e48c50SVivien Didelot 	*fid = (reg & 0xf000) >> 12;
601b4e48c50SVivien Didelot 
602b4e48c50SVivien Didelot 	/* Port's default FID upper bits are located in reg 0x05, offset 0 */
603b4e48c50SVivien Didelot 	if (upper_mask) {
604b4e48c50SVivien Didelot 		err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, &reg);
605b4e48c50SVivien Didelot 		if (err)
606b4e48c50SVivien Didelot 			return err;
607b4e48c50SVivien Didelot 
608b4e48c50SVivien Didelot 		*fid |= (reg & upper_mask) << 4;
609b4e48c50SVivien Didelot 	}
610b4e48c50SVivien Didelot 
611b4e48c50SVivien Didelot 	return 0;
612b4e48c50SVivien Didelot }
613b4e48c50SVivien Didelot 
614b4e48c50SVivien Didelot int mv88e6xxx_port_set_fid(struct mv88e6xxx_chip *chip, int port, u16 fid)
615b4e48c50SVivien Didelot {
616b4e48c50SVivien Didelot 	const u16 upper_mask = (mv88e6xxx_num_databases(chip) - 1) >> 4;
617b4e48c50SVivien Didelot 	u16 reg;
618b4e48c50SVivien Didelot 	int err;
619b4e48c50SVivien Didelot 
620b4e48c50SVivien Didelot 	if (fid >= mv88e6xxx_num_databases(chip))
621b4e48c50SVivien Didelot 		return -EINVAL;
622b4e48c50SVivien Didelot 
623b4e48c50SVivien Didelot 	/* Port's default FID lower 4 bits are located in reg 0x06, offset 12 */
624b4e48c50SVivien Didelot 	err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg);
625b4e48c50SVivien Didelot 	if (err)
626b4e48c50SVivien Didelot 		return err;
627b4e48c50SVivien Didelot 
628b4e48c50SVivien Didelot 	reg &= 0x0fff;
629b4e48c50SVivien Didelot 	reg |= (fid & 0x000f) << 12;
630b4e48c50SVivien Didelot 
631b4e48c50SVivien Didelot 	err = mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg);
632b4e48c50SVivien Didelot 	if (err)
633b4e48c50SVivien Didelot 		return err;
634b4e48c50SVivien Didelot 
635b4e48c50SVivien Didelot 	/* Port's default FID upper bits are located in reg 0x05, offset 0 */
636b4e48c50SVivien Didelot 	if (upper_mask) {
637b4e48c50SVivien Didelot 		err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, &reg);
638b4e48c50SVivien Didelot 		if (err)
639b4e48c50SVivien Didelot 			return err;
640b4e48c50SVivien Didelot 
641b4e48c50SVivien Didelot 		reg &= ~upper_mask;
642b4e48c50SVivien Didelot 		reg |= (fid >> 4) & upper_mask;
643b4e48c50SVivien Didelot 
644b4e48c50SVivien Didelot 		err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_1, reg);
645b4e48c50SVivien Didelot 		if (err)
646b4e48c50SVivien Didelot 			return err;
647b4e48c50SVivien Didelot 	}
648b4e48c50SVivien Didelot 
649b4e48c50SVivien Didelot 	netdev_dbg(chip->ds->ports[port].netdev, "FID set to %u\n", fid);
650b4e48c50SVivien Didelot 
651b4e48c50SVivien Didelot 	return 0;
652b4e48c50SVivien Didelot }
65377064f37SVivien Didelot 
65477064f37SVivien Didelot /* Offset 0x07: Default Port VLAN ID & Priority */
65577064f37SVivien Didelot 
65677064f37SVivien Didelot int mv88e6xxx_port_get_pvid(struct mv88e6xxx_chip *chip, int port, u16 *pvid)
65777064f37SVivien Didelot {
65877064f37SVivien Didelot 	u16 reg;
65977064f37SVivien Didelot 	int err;
66077064f37SVivien Didelot 
66177064f37SVivien Didelot 	err = mv88e6xxx_port_read(chip, port, PORT_DEFAULT_VLAN, &reg);
66277064f37SVivien Didelot 	if (err)
66377064f37SVivien Didelot 		return err;
66477064f37SVivien Didelot 
66577064f37SVivien Didelot 	*pvid = reg & PORT_DEFAULT_VLAN_MASK;
66677064f37SVivien Didelot 
66777064f37SVivien Didelot 	return 0;
66877064f37SVivien Didelot }
66977064f37SVivien Didelot 
67077064f37SVivien Didelot int mv88e6xxx_port_set_pvid(struct mv88e6xxx_chip *chip, int port, u16 pvid)
67177064f37SVivien Didelot {
67277064f37SVivien Didelot 	u16 reg;
67377064f37SVivien Didelot 	int err;
67477064f37SVivien Didelot 
67577064f37SVivien Didelot 	err = mv88e6xxx_port_read(chip, port, PORT_DEFAULT_VLAN, &reg);
67677064f37SVivien Didelot 	if (err)
67777064f37SVivien Didelot 		return err;
67877064f37SVivien Didelot 
67977064f37SVivien Didelot 	reg &= ~PORT_DEFAULT_VLAN_MASK;
68077064f37SVivien Didelot 	reg |= pvid & PORT_DEFAULT_VLAN_MASK;
68177064f37SVivien Didelot 
68277064f37SVivien Didelot 	err = mv88e6xxx_port_write(chip, port, PORT_DEFAULT_VLAN, reg);
68377064f37SVivien Didelot 	if (err)
68477064f37SVivien Didelot 		return err;
68577064f37SVivien Didelot 
68677064f37SVivien Didelot 	netdev_dbg(chip->ds->ports[port].netdev, "DefaultVID set to %u\n",
68777064f37SVivien Didelot 		   pvid);
68877064f37SVivien Didelot 
68977064f37SVivien Didelot 	return 0;
69077064f37SVivien Didelot }
691385a0995SVivien Didelot 
692385a0995SVivien Didelot /* Offset 0x08: Port Control 2 Register */
693385a0995SVivien Didelot 
694385a0995SVivien Didelot static const char * const mv88e6xxx_port_8021q_mode_names[] = {
695385a0995SVivien Didelot 	[PORT_CONTROL_2_8021Q_DISABLED] = "Disabled",
696385a0995SVivien Didelot 	[PORT_CONTROL_2_8021Q_FALLBACK] = "Fallback",
697385a0995SVivien Didelot 	[PORT_CONTROL_2_8021Q_CHECK] = "Check",
698385a0995SVivien Didelot 	[PORT_CONTROL_2_8021Q_SECURE] = "Secure",
699385a0995SVivien Didelot };
700385a0995SVivien Didelot 
701601aeed3SVivien Didelot static int mv88e6185_port_set_default_forward(struct mv88e6xxx_chip *chip,
702601aeed3SVivien Didelot 					      int port, bool multicast)
703a23b2961SAndrew Lunn {
704a23b2961SAndrew Lunn 	int err;
705a23b2961SAndrew Lunn 	u16 reg;
706a23b2961SAndrew Lunn 
707a23b2961SAndrew Lunn 	err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, &reg);
708a23b2961SAndrew Lunn 	if (err)
709a23b2961SAndrew Lunn 		return err;
710a23b2961SAndrew Lunn 
711601aeed3SVivien Didelot 	if (multicast)
712601aeed3SVivien Didelot 		reg |= PORT_CONTROL_2_DEFAULT_FORWARD;
713a23b2961SAndrew Lunn 	else
714601aeed3SVivien Didelot 		reg &= ~PORT_CONTROL_2_DEFAULT_FORWARD;
715a23b2961SAndrew Lunn 
716a23b2961SAndrew Lunn 	return mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg);
717a23b2961SAndrew Lunn }
718a23b2961SAndrew Lunn 
719601aeed3SVivien Didelot int mv88e6185_port_set_egress_floods(struct mv88e6xxx_chip *chip, int port,
720601aeed3SVivien Didelot 				     bool unicast, bool multicast)
721601aeed3SVivien Didelot {
722601aeed3SVivien Didelot 	int err;
723601aeed3SVivien Didelot 
724601aeed3SVivien Didelot 	err = mv88e6185_port_set_forward_unknown(chip, port, unicast);
725601aeed3SVivien Didelot 	if (err)
726601aeed3SVivien Didelot 		return err;
727601aeed3SVivien Didelot 
728601aeed3SVivien Didelot 	return mv88e6185_port_set_default_forward(chip, port, multicast);
729601aeed3SVivien Didelot }
730601aeed3SVivien Didelot 
731a23b2961SAndrew Lunn int mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port,
732a23b2961SAndrew Lunn 				     int upstream_port)
733a23b2961SAndrew Lunn {
734a23b2961SAndrew Lunn 	int err;
735a23b2961SAndrew Lunn 	u16 reg;
736a23b2961SAndrew Lunn 
737a23b2961SAndrew Lunn 	err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, &reg);
738a23b2961SAndrew Lunn 	if (err)
739a23b2961SAndrew Lunn 		return err;
740a23b2961SAndrew Lunn 
741a23b2961SAndrew Lunn 	reg &= ~PORT_CONTROL_2_UPSTREAM_MASK;
742a23b2961SAndrew Lunn 	reg |= upstream_port;
743a23b2961SAndrew Lunn 
744a23b2961SAndrew Lunn 	return mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg);
745a23b2961SAndrew Lunn }
746a23b2961SAndrew Lunn 
747385a0995SVivien Didelot int mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port,
748385a0995SVivien Didelot 				  u16 mode)
749385a0995SVivien Didelot {
750385a0995SVivien Didelot 	u16 reg;
751385a0995SVivien Didelot 	int err;
752385a0995SVivien Didelot 
753385a0995SVivien Didelot 	err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, &reg);
754385a0995SVivien Didelot 	if (err)
755385a0995SVivien Didelot 		return err;
756385a0995SVivien Didelot 
757385a0995SVivien Didelot 	reg &= ~PORT_CONTROL_2_8021Q_MASK;
758385a0995SVivien Didelot 	reg |= mode & PORT_CONTROL_2_8021Q_MASK;
759385a0995SVivien Didelot 
760385a0995SVivien Didelot 	err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg);
761385a0995SVivien Didelot 	if (err)
762385a0995SVivien Didelot 		return err;
763385a0995SVivien Didelot 
764385a0995SVivien Didelot 	netdev_dbg(chip->ds->ports[port].netdev, "802.1QMode set to %s\n",
765385a0995SVivien Didelot 		   mv88e6xxx_port_8021q_mode_names[mode]);
766385a0995SVivien Didelot 
767385a0995SVivien Didelot 	return 0;
768385a0995SVivien Didelot }
769ef0a7318SAndrew Lunn 
770a23b2961SAndrew Lunn int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port)
771a23b2961SAndrew Lunn {
772a23b2961SAndrew Lunn 	u16 reg;
773a23b2961SAndrew Lunn 	int err;
774a23b2961SAndrew Lunn 
775a23b2961SAndrew Lunn 	err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, &reg);
776a23b2961SAndrew Lunn 	if (err)
777a23b2961SAndrew Lunn 		return err;
778a23b2961SAndrew Lunn 
779a23b2961SAndrew Lunn 	reg |= PORT_CONTROL_2_MAP_DA;
780a23b2961SAndrew Lunn 
781a23b2961SAndrew Lunn 	return mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg);
782a23b2961SAndrew Lunn }
783a23b2961SAndrew Lunn 
7845f436666SAndrew Lunn int mv88e6165_port_jumbo_config(struct mv88e6xxx_chip *chip, int port)
7855f436666SAndrew Lunn {
7865f436666SAndrew Lunn 	u16 reg;
7875f436666SAndrew Lunn 	int err;
7885f436666SAndrew Lunn 
7895f436666SAndrew Lunn 	err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, &reg);
7905f436666SAndrew Lunn 	if (err)
7915f436666SAndrew Lunn 		return err;
7925f436666SAndrew Lunn 
7935f436666SAndrew Lunn 	reg |= PORT_CONTROL_2_JUMBO_10240;
7945f436666SAndrew Lunn 
7955f436666SAndrew Lunn 	return mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg);
7965f436666SAndrew Lunn }
7975f436666SAndrew Lunn 
798ef70b111SAndrew Lunn /* Offset 0x09: Port Rate Control */
799ef70b111SAndrew Lunn 
800ef70b111SAndrew Lunn int mv88e6095_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port)
801ef70b111SAndrew Lunn {
802ef70b111SAndrew Lunn 	return mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL, 0x0000);
803ef70b111SAndrew Lunn }
804ef70b111SAndrew Lunn 
805ef70b111SAndrew Lunn int mv88e6097_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port)
806ef70b111SAndrew Lunn {
807ef70b111SAndrew Lunn 	return mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL, 0x0001);
808ef70b111SAndrew Lunn }
809ef70b111SAndrew Lunn 
810c8c94891SVivien Didelot /* Offset 0x0C: Port ATU Control */
811c8c94891SVivien Didelot 
812c8c94891SVivien Didelot int mv88e6xxx_port_disable_learn_limit(struct mv88e6xxx_chip *chip, int port)
813c8c94891SVivien Didelot {
814c8c94891SVivien Didelot 	return mv88e6xxx_port_write(chip, port, PORT_ATU_CONTROL, 0);
815c8c94891SVivien Didelot }
816c8c94891SVivien Didelot 
8179dbfb4e1SVivien Didelot /* Offset 0x0D: (Priority) Override Register */
8189dbfb4e1SVivien Didelot 
8199dbfb4e1SVivien Didelot int mv88e6xxx_port_disable_pri_override(struct mv88e6xxx_chip *chip, int port)
8209dbfb4e1SVivien Didelot {
8219dbfb4e1SVivien Didelot 	return mv88e6xxx_port_write(chip, port, PORT_PRI_OVERRIDE, 0);
8229dbfb4e1SVivien Didelot }
8239dbfb4e1SVivien Didelot 
82456995cbcSAndrew Lunn /* Offset 0x0f: Port Ether type */
82556995cbcSAndrew Lunn 
82656995cbcSAndrew Lunn int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port,
82756995cbcSAndrew Lunn 				  u16 etype)
82856995cbcSAndrew Lunn {
82956995cbcSAndrew Lunn 	return mv88e6xxx_port_write(chip, port, PORT_ETH_TYPE, etype);
83056995cbcSAndrew Lunn }
83156995cbcSAndrew Lunn 
832ef0a7318SAndrew Lunn /* Offset 0x18: Port IEEE Priority Remapping Registers [0-3]
833ef0a7318SAndrew Lunn  * Offset 0x19: Port IEEE Priority Remapping Registers [4-7]
834ef0a7318SAndrew Lunn  */
835ef0a7318SAndrew Lunn 
836ef0a7318SAndrew Lunn int mv88e6095_port_tag_remap(struct mv88e6xxx_chip *chip, int port)
837ef0a7318SAndrew Lunn {
838ef0a7318SAndrew Lunn 	int err;
839ef0a7318SAndrew Lunn 
840ef0a7318SAndrew Lunn 	/* Use a direct priority mapping for all IEEE tagged frames */
841ef0a7318SAndrew Lunn 	err = mv88e6xxx_port_write(chip, port, PORT_TAG_REGMAP_0123, 0x3210);
842ef0a7318SAndrew Lunn 	if (err)
843ef0a7318SAndrew Lunn 		return err;
844ef0a7318SAndrew Lunn 
845ef0a7318SAndrew Lunn 	return mv88e6xxx_port_write(chip, port, PORT_TAG_REGMAP_4567, 0x7654);
846ef0a7318SAndrew Lunn }
847ef0a7318SAndrew Lunn 
848ef0a7318SAndrew Lunn static int mv88e6xxx_port_ieeepmt_write(struct mv88e6xxx_chip *chip,
849ef0a7318SAndrew Lunn 					int port, u16 table,
850ef0a7318SAndrew Lunn 					u8 pointer, u16 data)
851ef0a7318SAndrew Lunn {
852ef0a7318SAndrew Lunn 	u16 reg;
853ef0a7318SAndrew Lunn 
854ef0a7318SAndrew Lunn 	reg = PORT_IEEE_PRIO_MAP_TABLE_UPDATE |
855ef0a7318SAndrew Lunn 		table |
856ef0a7318SAndrew Lunn 		(pointer << PORT_IEEE_PRIO_MAP_TABLE_POINTER_SHIFT) |
857ef0a7318SAndrew Lunn 		data;
858ef0a7318SAndrew Lunn 
859ef0a7318SAndrew Lunn 	return mv88e6xxx_port_write(chip, port, PORT_IEEE_PRIO_MAP_TABLE, reg);
860ef0a7318SAndrew Lunn }
861ef0a7318SAndrew Lunn 
862ef0a7318SAndrew Lunn int mv88e6390_port_tag_remap(struct mv88e6xxx_chip *chip, int port)
863ef0a7318SAndrew Lunn {
864ef0a7318SAndrew Lunn 	int err, i;
865ef0a7318SAndrew Lunn 
866ef0a7318SAndrew Lunn 	for (i = 0; i <= 7; i++) {
867ef0a7318SAndrew Lunn 		err = mv88e6xxx_port_ieeepmt_write(
868ef0a7318SAndrew Lunn 			chip, port, PORT_IEEE_PRIO_MAP_TABLE_INGRESS_PCP,
869ef0a7318SAndrew Lunn 			i, (i | i << 4));
870ef0a7318SAndrew Lunn 		if (err)
871ef0a7318SAndrew Lunn 			return err;
872ef0a7318SAndrew Lunn 
873ef0a7318SAndrew Lunn 		err = mv88e6xxx_port_ieeepmt_write(
874ef0a7318SAndrew Lunn 			chip, port, PORT_IEEE_PRIO_MAP_TABLE_EGRESS_GREEN_PCP,
875ef0a7318SAndrew Lunn 			i, i);
876ef0a7318SAndrew Lunn 		if (err)
877ef0a7318SAndrew Lunn 			return err;
878ef0a7318SAndrew Lunn 
879ef0a7318SAndrew Lunn 		err = mv88e6xxx_port_ieeepmt_write(
880ef0a7318SAndrew Lunn 			chip, port, PORT_IEEE_PRIO_MAP_TABLE_EGRESS_YELLOW_PCP,
881ef0a7318SAndrew Lunn 			i, i);
882ef0a7318SAndrew Lunn 		if (err)
883ef0a7318SAndrew Lunn 			return err;
884ef0a7318SAndrew Lunn 
885ef0a7318SAndrew Lunn 		err = mv88e6xxx_port_ieeepmt_write(
886ef0a7318SAndrew Lunn 			chip, port, PORT_IEEE_PRIO_MAP_TABLE_EGRESS_AVB_PCP,
887ef0a7318SAndrew Lunn 			i, i);
888ef0a7318SAndrew Lunn 		if (err)
889ef0a7318SAndrew Lunn 			return err;
890ef0a7318SAndrew Lunn 	}
891ef0a7318SAndrew Lunn 
892ef0a7318SAndrew Lunn 	return 0;
893ef0a7318SAndrew Lunn }
894