162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Marvell 88E6xxx Switch Port Registers support
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2008 Marvell Semiconductor
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (c) 2016-2017 Savoir-faire Linux Inc.
862306a36Sopenharmony_ci *	Vivien Didelot <vivien.didelot@savoirfairelinux.com>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/bitfield.h>
1262306a36Sopenharmony_ci#include <linux/if_bridge.h>
1362306a36Sopenharmony_ci#include <linux/phy.h>
1462306a36Sopenharmony_ci#include <linux/phylink.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "chip.h"
1762306a36Sopenharmony_ci#include "global2.h"
1862306a36Sopenharmony_ci#include "port.h"
1962306a36Sopenharmony_ci#include "serdes.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ciint mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg,
2262306a36Sopenharmony_ci			u16 *val)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	int addr = chip->info->port_base_addr + port;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	return mv88e6xxx_read(chip, addr, reg, val);
2762306a36Sopenharmony_ci}
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ciint mv88e6xxx_port_wait_bit(struct mv88e6xxx_chip *chip, int port, int reg,
3062306a36Sopenharmony_ci			    int bit, int val)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	int addr = chip->info->port_base_addr + port;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	return mv88e6xxx_wait_bit(chip, addr, reg, bit, val);
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ciint mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg,
3862306a36Sopenharmony_ci			 u16 val)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	int addr = chip->info->port_base_addr + port;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	return mv88e6xxx_write(chip, addr, reg, val);
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/* Offset 0x00: MAC (or PCS or Physical) Status Register
4662306a36Sopenharmony_ci *
4762306a36Sopenharmony_ci * For most devices, this is read only. However the 6185 has the MyPause
4862306a36Sopenharmony_ci * bit read/write.
4962306a36Sopenharmony_ci */
5062306a36Sopenharmony_ciint mv88e6185_port_set_pause(struct mv88e6xxx_chip *chip, int port,
5162306a36Sopenharmony_ci			     int pause)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	u16 reg;
5462306a36Sopenharmony_ci	int err;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &reg);
5762306a36Sopenharmony_ci	if (err)
5862306a36Sopenharmony_ci		return err;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	if (pause)
6162306a36Sopenharmony_ci		reg |= MV88E6XXX_PORT_STS_MY_PAUSE;
6262306a36Sopenharmony_ci	else
6362306a36Sopenharmony_ci		reg &= ~MV88E6XXX_PORT_STS_MY_PAUSE;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_STS, reg);
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci/* Offset 0x01: MAC (or PCS or Physical) Control Register
6962306a36Sopenharmony_ci *
7062306a36Sopenharmony_ci * Link, Duplex and Flow Control have one force bit, one value bit.
7162306a36Sopenharmony_ci *
7262306a36Sopenharmony_ci * For port's MAC speed, ForceSpd (or SpdValue) bits 1:0 program the value.
7362306a36Sopenharmony_ci * Alternative values require the 200BASE (or AltSpeed) bit 12 set.
7462306a36Sopenharmony_ci * Newer chips need a ForcedSpd bit 13 set to consider the value.
7562306a36Sopenharmony_ci */
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic int mv88e6xxx_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
7862306a36Sopenharmony_ci					  phy_interface_t mode)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	u16 reg;
8162306a36Sopenharmony_ci	int err;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, &reg);
8462306a36Sopenharmony_ci	if (err)
8562306a36Sopenharmony_ci		return err;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	reg &= ~(MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK |
8862306a36Sopenharmony_ci		 MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	switch (mode) {
9162306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII_RXID:
9262306a36Sopenharmony_ci		reg |= MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK;
9362306a36Sopenharmony_ci		break;
9462306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII_TXID:
9562306a36Sopenharmony_ci		reg |= MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK;
9662306a36Sopenharmony_ci		break;
9762306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII_ID:
9862306a36Sopenharmony_ci		reg |= MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK |
9962306a36Sopenharmony_ci			MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK;
10062306a36Sopenharmony_ci		break;
10162306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII:
10262306a36Sopenharmony_ci		break;
10362306a36Sopenharmony_ci	default:
10462306a36Sopenharmony_ci		return 0;
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
10862306a36Sopenharmony_ci	if (err)
10962306a36Sopenharmony_ci		return err;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	dev_dbg(chip->dev, "p%d: delay RXCLK %s, TXCLK %s\n", port,
11262306a36Sopenharmony_ci		reg & MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK ? "yes" : "no",
11362306a36Sopenharmony_ci		reg & MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK ? "yes" : "no");
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	return 0;
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ciint mv88e6352_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
11962306a36Sopenharmony_ci				   phy_interface_t mode)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	if (port < 5)
12262306a36Sopenharmony_ci		return -EOPNOTSUPP;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	return mv88e6xxx_port_set_rgmii_delay(chip, port, mode);
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ciint mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
12862306a36Sopenharmony_ci				   phy_interface_t mode)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	if (port != 0)
13162306a36Sopenharmony_ci		return -EOPNOTSUPP;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	return mv88e6xxx_port_set_rgmii_delay(chip, port, mode);
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ciint mv88e6320_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
13762306a36Sopenharmony_ci				   phy_interface_t mode)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	if (port != 2 && port != 5 && port != 6)
14062306a36Sopenharmony_ci		return -EOPNOTSUPP;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	return mv88e6xxx_port_set_rgmii_delay(chip, port, mode);
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ciint mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	u16 reg;
14862306a36Sopenharmony_ci	int err;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, &reg);
15162306a36Sopenharmony_ci	if (err)
15262306a36Sopenharmony_ci		return err;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	reg &= ~(MV88E6XXX_PORT_MAC_CTL_FORCE_LINK |
15562306a36Sopenharmony_ci		 MV88E6XXX_PORT_MAC_CTL_LINK_UP);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	switch (link) {
15862306a36Sopenharmony_ci	case LINK_FORCED_DOWN:
15962306a36Sopenharmony_ci		reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_LINK;
16062306a36Sopenharmony_ci		break;
16162306a36Sopenharmony_ci	case LINK_FORCED_UP:
16262306a36Sopenharmony_ci		reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_LINK |
16362306a36Sopenharmony_ci			MV88E6XXX_PORT_MAC_CTL_LINK_UP;
16462306a36Sopenharmony_ci		break;
16562306a36Sopenharmony_ci	case LINK_UNFORCED:
16662306a36Sopenharmony_ci		/* normal link detection */
16762306a36Sopenharmony_ci		break;
16862306a36Sopenharmony_ci	default:
16962306a36Sopenharmony_ci		return -EINVAL;
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
17362306a36Sopenharmony_ci	if (err)
17462306a36Sopenharmony_ci		return err;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	dev_dbg(chip->dev, "p%d: %s link %s\n", port,
17762306a36Sopenharmony_ci		reg & MV88E6XXX_PORT_MAC_CTL_FORCE_LINK ? "Force" : "Unforce",
17862306a36Sopenharmony_ci		reg & MV88E6XXX_PORT_MAC_CTL_LINK_UP ? "up" : "down");
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	return 0;
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ciint mv88e6xxx_port_sync_link(struct mv88e6xxx_chip *chip, int port, unsigned int mode, bool isup)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	const struct mv88e6xxx_ops *ops = chip->info->ops;
18662306a36Sopenharmony_ci	int err = 0;
18762306a36Sopenharmony_ci	int link;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	if (isup)
19062306a36Sopenharmony_ci		link = LINK_FORCED_UP;
19162306a36Sopenharmony_ci	else
19262306a36Sopenharmony_ci		link = LINK_FORCED_DOWN;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	if (ops->port_set_link)
19562306a36Sopenharmony_ci		err = ops->port_set_link(chip, port, link);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	return err;
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ciint mv88e6185_port_sync_link(struct mv88e6xxx_chip *chip, int port, unsigned int mode, bool isup)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	const struct mv88e6xxx_ops *ops = chip->info->ops;
20362306a36Sopenharmony_ci	int err = 0;
20462306a36Sopenharmony_ci	int link;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	if (mode == MLO_AN_INBAND)
20762306a36Sopenharmony_ci		link = LINK_UNFORCED;
20862306a36Sopenharmony_ci	else if (isup)
20962306a36Sopenharmony_ci		link = LINK_FORCED_UP;
21062306a36Sopenharmony_ci	else
21162306a36Sopenharmony_ci		link = LINK_FORCED_DOWN;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	if (ops->port_set_link)
21462306a36Sopenharmony_ci		err = ops->port_set_link(chip, port, link);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	return err;
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic int mv88e6xxx_port_set_speed_duplex(struct mv88e6xxx_chip *chip,
22062306a36Sopenharmony_ci					   int port, int speed, bool alt_bit,
22162306a36Sopenharmony_ci					   bool force_bit, int duplex)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	u16 reg, ctrl;
22462306a36Sopenharmony_ci	int err;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	switch (speed) {
22762306a36Sopenharmony_ci	case 10:
22862306a36Sopenharmony_ci		ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_10;
22962306a36Sopenharmony_ci		break;
23062306a36Sopenharmony_ci	case 100:
23162306a36Sopenharmony_ci		ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_100;
23262306a36Sopenharmony_ci		break;
23362306a36Sopenharmony_ci	case 200:
23462306a36Sopenharmony_ci		if (alt_bit)
23562306a36Sopenharmony_ci			ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_100 |
23662306a36Sopenharmony_ci				MV88E6390_PORT_MAC_CTL_ALTSPEED;
23762306a36Sopenharmony_ci		else
23862306a36Sopenharmony_ci			ctrl = MV88E6065_PORT_MAC_CTL_SPEED_200;
23962306a36Sopenharmony_ci		break;
24062306a36Sopenharmony_ci	case 1000:
24162306a36Sopenharmony_ci		ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_1000;
24262306a36Sopenharmony_ci		break;
24362306a36Sopenharmony_ci	case 2500:
24462306a36Sopenharmony_ci		if (alt_bit)
24562306a36Sopenharmony_ci			ctrl = MV88E6390_PORT_MAC_CTL_SPEED_10000 |
24662306a36Sopenharmony_ci				MV88E6390_PORT_MAC_CTL_ALTSPEED;
24762306a36Sopenharmony_ci		else
24862306a36Sopenharmony_ci			ctrl = MV88E6390_PORT_MAC_CTL_SPEED_10000;
24962306a36Sopenharmony_ci		break;
25062306a36Sopenharmony_ci	case 10000:
25162306a36Sopenharmony_ci		/* all bits set, fall through... */
25262306a36Sopenharmony_ci	case SPEED_UNFORCED:
25362306a36Sopenharmony_ci		ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_UNFORCED;
25462306a36Sopenharmony_ci		break;
25562306a36Sopenharmony_ci	default:
25662306a36Sopenharmony_ci		return -EOPNOTSUPP;
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	switch (duplex) {
26062306a36Sopenharmony_ci	case DUPLEX_HALF:
26162306a36Sopenharmony_ci		ctrl |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX;
26262306a36Sopenharmony_ci		break;
26362306a36Sopenharmony_ci	case DUPLEX_FULL:
26462306a36Sopenharmony_ci		ctrl |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX |
26562306a36Sopenharmony_ci			MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL;
26662306a36Sopenharmony_ci		break;
26762306a36Sopenharmony_ci	case DUPLEX_UNFORCED:
26862306a36Sopenharmony_ci		/* normal duplex detection */
26962306a36Sopenharmony_ci		break;
27062306a36Sopenharmony_ci	default:
27162306a36Sopenharmony_ci		return -EOPNOTSUPP;
27262306a36Sopenharmony_ci	}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, &reg);
27562306a36Sopenharmony_ci	if (err)
27662306a36Sopenharmony_ci		return err;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	reg &= ~(MV88E6XXX_PORT_MAC_CTL_SPEED_MASK |
27962306a36Sopenharmony_ci		 MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX |
28062306a36Sopenharmony_ci		 MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	if (alt_bit)
28362306a36Sopenharmony_ci		reg &= ~MV88E6390_PORT_MAC_CTL_ALTSPEED;
28462306a36Sopenharmony_ci	if (force_bit) {
28562306a36Sopenharmony_ci		reg &= ~MV88E6390_PORT_MAC_CTL_FORCE_SPEED;
28662306a36Sopenharmony_ci		if (speed != SPEED_UNFORCED)
28762306a36Sopenharmony_ci			ctrl |= MV88E6390_PORT_MAC_CTL_FORCE_SPEED;
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci	reg |= ctrl;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
29262306a36Sopenharmony_ci	if (err)
29362306a36Sopenharmony_ci		return err;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	if (speed != SPEED_UNFORCED)
29662306a36Sopenharmony_ci		dev_dbg(chip->dev, "p%d: Speed set to %d Mbps\n", port, speed);
29762306a36Sopenharmony_ci	else
29862306a36Sopenharmony_ci		dev_dbg(chip->dev, "p%d: Speed unforced\n", port);
29962306a36Sopenharmony_ci	dev_dbg(chip->dev, "p%d: %s %s duplex\n", port,
30062306a36Sopenharmony_ci		reg & MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX ? "Force" : "Unforce",
30162306a36Sopenharmony_ci		reg & MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL ? "full" : "half");
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	return 0;
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci/* Support 10, 100, 1000 Mbps (e.g. 88E6185 family) */
30762306a36Sopenharmony_ciint mv88e6185_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
30862306a36Sopenharmony_ci				    int speed, int duplex)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	if (speed == 200 || speed > 1000)
31162306a36Sopenharmony_ci		return -EOPNOTSUPP;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	return mv88e6xxx_port_set_speed_duplex(chip, port, speed, false, false,
31462306a36Sopenharmony_ci					       duplex);
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci/* Support 10, 100 Mbps (e.g. 88E6250 family) */
31862306a36Sopenharmony_ciint mv88e6250_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
31962306a36Sopenharmony_ci				    int speed, int duplex)
32062306a36Sopenharmony_ci{
32162306a36Sopenharmony_ci	if (speed > 100)
32262306a36Sopenharmony_ci		return -EOPNOTSUPP;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	return mv88e6xxx_port_set_speed_duplex(chip, port, speed, false, false,
32562306a36Sopenharmony_ci					       duplex);
32662306a36Sopenharmony_ci}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci/* Support 10, 100, 200, 1000, 2500 Mbps (e.g. 88E6341) */
32962306a36Sopenharmony_ciint mv88e6341_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
33062306a36Sopenharmony_ci				    int speed, int duplex)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	if (speed > 2500)
33362306a36Sopenharmony_ci		return -EOPNOTSUPP;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	if (speed == 200 && port != 0)
33662306a36Sopenharmony_ci		return -EOPNOTSUPP;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	if (speed == 2500 && port < 5)
33962306a36Sopenharmony_ci		return -EOPNOTSUPP;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	return mv88e6xxx_port_set_speed_duplex(chip, port, speed, !port, true,
34262306a36Sopenharmony_ci					       duplex);
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ciphy_interface_t mv88e6341_port_max_speed_mode(struct mv88e6xxx_chip *chip,
34662306a36Sopenharmony_ci					      int port)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	if (port == 5)
34962306a36Sopenharmony_ci		return PHY_INTERFACE_MODE_2500BASEX;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	return PHY_INTERFACE_MODE_NA;
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci/* Support 10, 100, 200, 1000 Mbps (e.g. 88E6352 family) */
35562306a36Sopenharmony_ciint mv88e6352_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
35662306a36Sopenharmony_ci				    int speed, int duplex)
35762306a36Sopenharmony_ci{
35862306a36Sopenharmony_ci	if (speed > 1000)
35962306a36Sopenharmony_ci		return -EOPNOTSUPP;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	if (speed == 200 && port < 5)
36262306a36Sopenharmony_ci		return -EOPNOTSUPP;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	return mv88e6xxx_port_set_speed_duplex(chip, port, speed, true, false,
36562306a36Sopenharmony_ci					       duplex);
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci/* Support 10, 100, 200, 1000, 2500 Mbps (e.g. 88E6390) */
36962306a36Sopenharmony_ciint mv88e6390_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
37062306a36Sopenharmony_ci				    int speed, int duplex)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	if (speed > 2500)
37362306a36Sopenharmony_ci		return -EOPNOTSUPP;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	if (speed == 200 && port != 0)
37662306a36Sopenharmony_ci		return -EOPNOTSUPP;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	if (speed == 2500 && port < 9)
37962306a36Sopenharmony_ci		return -EOPNOTSUPP;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	return mv88e6xxx_port_set_speed_duplex(chip, port, speed, true, true,
38262306a36Sopenharmony_ci					       duplex);
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ciphy_interface_t mv88e6390_port_max_speed_mode(struct mv88e6xxx_chip *chip,
38662306a36Sopenharmony_ci					      int port)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	if (port == 9 || port == 10)
38962306a36Sopenharmony_ci		return PHY_INTERFACE_MODE_2500BASEX;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	return PHY_INTERFACE_MODE_NA;
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci/* Support 10, 100, 200, 1000, 2500, 10000 Mbps (e.g. 88E6190X) */
39562306a36Sopenharmony_ciint mv88e6390x_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
39662306a36Sopenharmony_ci				     int speed, int duplex)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	if (speed == 200 && port != 0)
39962306a36Sopenharmony_ci		return -EOPNOTSUPP;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	if (speed >= 2500 && port < 9)
40262306a36Sopenharmony_ci		return -EOPNOTSUPP;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	return mv88e6xxx_port_set_speed_duplex(chip, port, speed, true, true,
40562306a36Sopenharmony_ci					       duplex);
40662306a36Sopenharmony_ci}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ciphy_interface_t mv88e6390x_port_max_speed_mode(struct mv88e6xxx_chip *chip,
40962306a36Sopenharmony_ci					       int port)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	if (port == 9 || port == 10)
41262306a36Sopenharmony_ci		return PHY_INTERFACE_MODE_XAUI;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	return PHY_INTERFACE_MODE_NA;
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci/* Support 10, 100, 200, 1000, 2500, 5000, 10000 Mbps (e.g. 88E6393X)
41862306a36Sopenharmony_ci * Function mv88e6xxx_port_set_speed_duplex() can't be used as the register
41962306a36Sopenharmony_ci * values for speeds 2500 & 5000 conflict.
42062306a36Sopenharmony_ci */
42162306a36Sopenharmony_ciint mv88e6393x_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
42262306a36Sopenharmony_ci				     int speed, int duplex)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	u16 reg, ctrl;
42562306a36Sopenharmony_ci	int err;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	if (chip->info->prod_num == MV88E6XXX_PORT_SWITCH_ID_PROD_6361 &&
42862306a36Sopenharmony_ci	    speed > 2500)
42962306a36Sopenharmony_ci		return -EOPNOTSUPP;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	if (speed == 200 && port != 0)
43262306a36Sopenharmony_ci		return -EOPNOTSUPP;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	if (speed >= 2500 && port > 0 && port < 9)
43562306a36Sopenharmony_ci		return -EOPNOTSUPP;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	switch (speed) {
43862306a36Sopenharmony_ci	case 10:
43962306a36Sopenharmony_ci		ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_10;
44062306a36Sopenharmony_ci		break;
44162306a36Sopenharmony_ci	case 100:
44262306a36Sopenharmony_ci		ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_100;
44362306a36Sopenharmony_ci		break;
44462306a36Sopenharmony_ci	case 200:
44562306a36Sopenharmony_ci		ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_100 |
44662306a36Sopenharmony_ci			MV88E6390_PORT_MAC_CTL_ALTSPEED;
44762306a36Sopenharmony_ci		break;
44862306a36Sopenharmony_ci	case 1000:
44962306a36Sopenharmony_ci		ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_1000;
45062306a36Sopenharmony_ci		break;
45162306a36Sopenharmony_ci	case 2500:
45262306a36Sopenharmony_ci		ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_1000 |
45362306a36Sopenharmony_ci			MV88E6390_PORT_MAC_CTL_ALTSPEED;
45462306a36Sopenharmony_ci		break;
45562306a36Sopenharmony_ci	case 5000:
45662306a36Sopenharmony_ci		ctrl = MV88E6390_PORT_MAC_CTL_SPEED_10000 |
45762306a36Sopenharmony_ci			MV88E6390_PORT_MAC_CTL_ALTSPEED;
45862306a36Sopenharmony_ci		break;
45962306a36Sopenharmony_ci	case 10000:
46062306a36Sopenharmony_ci	case SPEED_UNFORCED:
46162306a36Sopenharmony_ci		ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_UNFORCED;
46262306a36Sopenharmony_ci		break;
46362306a36Sopenharmony_ci	default:
46462306a36Sopenharmony_ci		return -EOPNOTSUPP;
46562306a36Sopenharmony_ci	}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	switch (duplex) {
46862306a36Sopenharmony_ci	case DUPLEX_HALF:
46962306a36Sopenharmony_ci		ctrl |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX;
47062306a36Sopenharmony_ci		break;
47162306a36Sopenharmony_ci	case DUPLEX_FULL:
47262306a36Sopenharmony_ci		ctrl |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX |
47362306a36Sopenharmony_ci			MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL;
47462306a36Sopenharmony_ci		break;
47562306a36Sopenharmony_ci	case DUPLEX_UNFORCED:
47662306a36Sopenharmony_ci		/* normal duplex detection */
47762306a36Sopenharmony_ci		break;
47862306a36Sopenharmony_ci	default:
47962306a36Sopenharmony_ci		return -EOPNOTSUPP;
48062306a36Sopenharmony_ci	}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, &reg);
48362306a36Sopenharmony_ci	if (err)
48462306a36Sopenharmony_ci		return err;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	reg &= ~(MV88E6XXX_PORT_MAC_CTL_SPEED_MASK |
48762306a36Sopenharmony_ci		 MV88E6390_PORT_MAC_CTL_ALTSPEED |
48862306a36Sopenharmony_ci		 MV88E6390_PORT_MAC_CTL_FORCE_SPEED);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	if (speed != SPEED_UNFORCED)
49162306a36Sopenharmony_ci		reg |= MV88E6390_PORT_MAC_CTL_FORCE_SPEED;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	reg |= ctrl;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
49662306a36Sopenharmony_ci	if (err)
49762306a36Sopenharmony_ci		return err;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	if (speed != SPEED_UNFORCED)
50062306a36Sopenharmony_ci		dev_dbg(chip->dev, "p%d: Speed set to %d Mbps\n", port, speed);
50162306a36Sopenharmony_ci	else
50262306a36Sopenharmony_ci		dev_dbg(chip->dev, "p%d: Speed unforced\n", port);
50362306a36Sopenharmony_ci	dev_dbg(chip->dev, "p%d: %s %s duplex\n", port,
50462306a36Sopenharmony_ci		reg & MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX ? "Force" : "Unforce",
50562306a36Sopenharmony_ci		reg & MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL ? "full" : "half");
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	return 0;
50862306a36Sopenharmony_ci}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ciphy_interface_t mv88e6393x_port_max_speed_mode(struct mv88e6xxx_chip *chip,
51162306a36Sopenharmony_ci					       int port)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	if (port != 0 && port != 9 && port != 10)
51562306a36Sopenharmony_ci		return PHY_INTERFACE_MODE_NA;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	if (chip->info->prod_num == MV88E6XXX_PORT_SWITCH_ID_PROD_6361)
51862306a36Sopenharmony_ci		return PHY_INTERFACE_MODE_2500BASEX;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	return PHY_INTERFACE_MODE_10GBASER;
52162306a36Sopenharmony_ci}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_cistatic int mv88e6xxx_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
52462306a36Sopenharmony_ci				    phy_interface_t mode, bool force)
52562306a36Sopenharmony_ci{
52662306a36Sopenharmony_ci	u16 cmode;
52762306a36Sopenharmony_ci	u16 reg;
52862306a36Sopenharmony_ci	int err;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	/* Default to a slow mode, so freeing up SERDES interfaces for
53162306a36Sopenharmony_ci	 * other ports which might use them for SFPs.
53262306a36Sopenharmony_ci	 */
53362306a36Sopenharmony_ci	if (mode == PHY_INTERFACE_MODE_NA)
53462306a36Sopenharmony_ci		mode = PHY_INTERFACE_MODE_1000BASEX;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	switch (mode) {
53762306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RMII:
53862306a36Sopenharmony_ci		cmode = MV88E6XXX_PORT_STS_CMODE_RMII;
53962306a36Sopenharmony_ci		break;
54062306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII:
54162306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII_ID:
54262306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII_RXID:
54362306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII_TXID:
54462306a36Sopenharmony_ci		cmode = MV88E6XXX_PORT_STS_CMODE_RGMII;
54562306a36Sopenharmony_ci		break;
54662306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_1000BASEX:
54762306a36Sopenharmony_ci		cmode = MV88E6XXX_PORT_STS_CMODE_1000BASEX;
54862306a36Sopenharmony_ci		break;
54962306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_SGMII:
55062306a36Sopenharmony_ci		cmode = MV88E6XXX_PORT_STS_CMODE_SGMII;
55162306a36Sopenharmony_ci		break;
55262306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_2500BASEX:
55362306a36Sopenharmony_ci		cmode = MV88E6XXX_PORT_STS_CMODE_2500BASEX;
55462306a36Sopenharmony_ci		break;
55562306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_5GBASER:
55662306a36Sopenharmony_ci		cmode = MV88E6393X_PORT_STS_CMODE_5GBASER;
55762306a36Sopenharmony_ci		break;
55862306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_XGMII:
55962306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_XAUI:
56062306a36Sopenharmony_ci		cmode = MV88E6XXX_PORT_STS_CMODE_XAUI;
56162306a36Sopenharmony_ci		break;
56262306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RXAUI:
56362306a36Sopenharmony_ci		cmode = MV88E6XXX_PORT_STS_CMODE_RXAUI;
56462306a36Sopenharmony_ci		break;
56562306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_10GBASER:
56662306a36Sopenharmony_ci		cmode = MV88E6393X_PORT_STS_CMODE_10GBASER;
56762306a36Sopenharmony_ci		break;
56862306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_USXGMII:
56962306a36Sopenharmony_ci		cmode = MV88E6393X_PORT_STS_CMODE_USXGMII;
57062306a36Sopenharmony_ci		break;
57162306a36Sopenharmony_ci	default:
57262306a36Sopenharmony_ci		cmode = 0;
57362306a36Sopenharmony_ci	}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	/* cmode doesn't change, nothing to do for us unless forced */
57662306a36Sopenharmony_ci	if (cmode == chip->ports[port].cmode && !force)
57762306a36Sopenharmony_ci		return 0;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	chip->ports[port].cmode = 0;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	if (cmode) {
58262306a36Sopenharmony_ci		err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &reg);
58362306a36Sopenharmony_ci		if (err)
58462306a36Sopenharmony_ci			return err;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci		reg &= ~MV88E6XXX_PORT_STS_CMODE_MASK;
58762306a36Sopenharmony_ci		reg |= cmode;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci		err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_STS, reg);
59062306a36Sopenharmony_ci		if (err)
59162306a36Sopenharmony_ci			return err;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci		chip->ports[port].cmode = cmode;
59462306a36Sopenharmony_ci	}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	return 0;
59762306a36Sopenharmony_ci}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ciint mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
60062306a36Sopenharmony_ci			      phy_interface_t mode)
60162306a36Sopenharmony_ci{
60262306a36Sopenharmony_ci	if (port != 9 && port != 10)
60362306a36Sopenharmony_ci		return -EOPNOTSUPP;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	return mv88e6xxx_port_set_cmode(chip, port, mode, false);
60662306a36Sopenharmony_ci}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ciint mv88e6390_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
60962306a36Sopenharmony_ci			     phy_interface_t mode)
61062306a36Sopenharmony_ci{
61162306a36Sopenharmony_ci	if (port != 9 && port != 10)
61262306a36Sopenharmony_ci		return -EOPNOTSUPP;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	switch (mode) {
61562306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_NA:
61662306a36Sopenharmony_ci		return 0;
61762306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_XGMII:
61862306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_XAUI:
61962306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RXAUI:
62062306a36Sopenharmony_ci		return -EINVAL;
62162306a36Sopenharmony_ci	default:
62262306a36Sopenharmony_ci		break;
62362306a36Sopenharmony_ci	}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	return mv88e6xxx_port_set_cmode(chip, port, mode, false);
62662306a36Sopenharmony_ci}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ciint mv88e6393x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
62962306a36Sopenharmony_ci			      phy_interface_t mode)
63062306a36Sopenharmony_ci{
63162306a36Sopenharmony_ci	int err;
63262306a36Sopenharmony_ci	u16 reg;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	if (port != 0 && port != 9 && port != 10)
63562306a36Sopenharmony_ci		return -EOPNOTSUPP;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	if (port == 9 || port == 10) {
63862306a36Sopenharmony_ci		switch (mode) {
63962306a36Sopenharmony_ci		case PHY_INTERFACE_MODE_RMII:
64062306a36Sopenharmony_ci		case PHY_INTERFACE_MODE_RGMII:
64162306a36Sopenharmony_ci		case PHY_INTERFACE_MODE_RGMII_ID:
64262306a36Sopenharmony_ci		case PHY_INTERFACE_MODE_RGMII_RXID:
64362306a36Sopenharmony_ci		case PHY_INTERFACE_MODE_RGMII_TXID:
64462306a36Sopenharmony_ci			return -EINVAL;
64562306a36Sopenharmony_ci		default:
64662306a36Sopenharmony_ci			break;
64762306a36Sopenharmony_ci		}
64862306a36Sopenharmony_ci	}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	/* mv88e6393x errata 4.5: EEE should be disabled on SERDES ports */
65162306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, &reg);
65262306a36Sopenharmony_ci	if (err)
65362306a36Sopenharmony_ci		return err;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	reg &= ~MV88E6XXX_PORT_MAC_CTL_EEE;
65662306a36Sopenharmony_ci	reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_EEE;
65762306a36Sopenharmony_ci	err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
65862306a36Sopenharmony_ci	if (err)
65962306a36Sopenharmony_ci		return err;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	return mv88e6xxx_port_set_cmode(chip, port, mode, false);
66262306a36Sopenharmony_ci}
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_cistatic int mv88e6341_port_set_cmode_writable(struct mv88e6xxx_chip *chip,
66562306a36Sopenharmony_ci					     int port)
66662306a36Sopenharmony_ci{
66762306a36Sopenharmony_ci	int err, addr;
66862306a36Sopenharmony_ci	u16 reg, bits;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	if (port != 5)
67162306a36Sopenharmony_ci		return -EOPNOTSUPP;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	addr = chip->info->port_base_addr + port;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	err = mv88e6xxx_port_hidden_read(chip, 0x7, addr, 0, &reg);
67662306a36Sopenharmony_ci	if (err)
67762306a36Sopenharmony_ci		return err;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	bits = MV88E6341_PORT_RESERVED_1A_FORCE_CMODE |
68062306a36Sopenharmony_ci	       MV88E6341_PORT_RESERVED_1A_SGMII_AN;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	if ((reg & bits) == bits)
68362306a36Sopenharmony_ci		return 0;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	reg |= bits;
68662306a36Sopenharmony_ci	return mv88e6xxx_port_hidden_write(chip, 0x7, addr, 0, reg);
68762306a36Sopenharmony_ci}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ciint mv88e6341_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
69062306a36Sopenharmony_ci			     phy_interface_t mode)
69162306a36Sopenharmony_ci{
69262306a36Sopenharmony_ci	int err;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	if (port != 5)
69562306a36Sopenharmony_ci		return -EOPNOTSUPP;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	switch (mode) {
69862306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_NA:
69962306a36Sopenharmony_ci		return 0;
70062306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_XGMII:
70162306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_XAUI:
70262306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RXAUI:
70362306a36Sopenharmony_ci		return -EINVAL;
70462306a36Sopenharmony_ci	default:
70562306a36Sopenharmony_ci		break;
70662306a36Sopenharmony_ci	}
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	err = mv88e6341_port_set_cmode_writable(chip, port);
70962306a36Sopenharmony_ci	if (err)
71062306a36Sopenharmony_ci		return err;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	return mv88e6xxx_port_set_cmode(chip, port, mode, true);
71362306a36Sopenharmony_ci}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ciint mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode)
71662306a36Sopenharmony_ci{
71762306a36Sopenharmony_ci	int err;
71862306a36Sopenharmony_ci	u16 reg;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &reg);
72162306a36Sopenharmony_ci	if (err)
72262306a36Sopenharmony_ci		return err;
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	*cmode = reg & MV88E6185_PORT_STS_CMODE_MASK;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	return 0;
72762306a36Sopenharmony_ci}
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ciint mv88e6352_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode)
73062306a36Sopenharmony_ci{
73162306a36Sopenharmony_ci	int err;
73262306a36Sopenharmony_ci	u16 reg;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &reg);
73562306a36Sopenharmony_ci	if (err)
73662306a36Sopenharmony_ci		return err;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	*cmode = reg & MV88E6XXX_PORT_STS_CMODE_MASK;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	return 0;
74162306a36Sopenharmony_ci}
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci/* Offset 0x02: Jamming Control
74462306a36Sopenharmony_ci *
74562306a36Sopenharmony_ci * Do not limit the period of time that this port can be paused for by
74662306a36Sopenharmony_ci * the remote end or the period of time that this port can pause the
74762306a36Sopenharmony_ci * remote end.
74862306a36Sopenharmony_ci */
74962306a36Sopenharmony_ciint mv88e6097_port_pause_limit(struct mv88e6xxx_chip *chip, int port, u8 in,
75062306a36Sopenharmony_ci			       u8 out)
75162306a36Sopenharmony_ci{
75262306a36Sopenharmony_ci	return mv88e6xxx_port_write(chip, port, MV88E6097_PORT_JAM_CTL,
75362306a36Sopenharmony_ci				    out << 8 | in);
75462306a36Sopenharmony_ci}
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ciint mv88e6390_port_pause_limit(struct mv88e6xxx_chip *chip, int port, u8 in,
75762306a36Sopenharmony_ci			       u8 out)
75862306a36Sopenharmony_ci{
75962306a36Sopenharmony_ci	int err;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	err = mv88e6xxx_port_write(chip, port, MV88E6390_PORT_FLOW_CTL,
76262306a36Sopenharmony_ci				   MV88E6390_PORT_FLOW_CTL_UPDATE |
76362306a36Sopenharmony_ci				   MV88E6390_PORT_FLOW_CTL_LIMIT_IN | in);
76462306a36Sopenharmony_ci	if (err)
76562306a36Sopenharmony_ci		return err;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	return mv88e6xxx_port_write(chip, port, MV88E6390_PORT_FLOW_CTL,
76862306a36Sopenharmony_ci				    MV88E6390_PORT_FLOW_CTL_UPDATE |
76962306a36Sopenharmony_ci				    MV88E6390_PORT_FLOW_CTL_LIMIT_OUT | out);
77062306a36Sopenharmony_ci}
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci/* Offset 0x04: Port Control Register */
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_cistatic const char * const mv88e6xxx_port_state_names[] = {
77562306a36Sopenharmony_ci	[MV88E6XXX_PORT_CTL0_STATE_DISABLED] = "Disabled",
77662306a36Sopenharmony_ci	[MV88E6XXX_PORT_CTL0_STATE_BLOCKING] = "Blocking/Listening",
77762306a36Sopenharmony_ci	[MV88E6XXX_PORT_CTL0_STATE_LEARNING] = "Learning",
77862306a36Sopenharmony_ci	[MV88E6XXX_PORT_CTL0_STATE_FORWARDING] = "Forwarding",
77962306a36Sopenharmony_ci};
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ciint mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state)
78262306a36Sopenharmony_ci{
78362306a36Sopenharmony_ci	u16 reg;
78462306a36Sopenharmony_ci	int err;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, &reg);
78762306a36Sopenharmony_ci	if (err)
78862306a36Sopenharmony_ci		return err;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	reg &= ~MV88E6XXX_PORT_CTL0_STATE_MASK;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	switch (state) {
79362306a36Sopenharmony_ci	case BR_STATE_DISABLED:
79462306a36Sopenharmony_ci		state = MV88E6XXX_PORT_CTL0_STATE_DISABLED;
79562306a36Sopenharmony_ci		break;
79662306a36Sopenharmony_ci	case BR_STATE_BLOCKING:
79762306a36Sopenharmony_ci	case BR_STATE_LISTENING:
79862306a36Sopenharmony_ci		state = MV88E6XXX_PORT_CTL0_STATE_BLOCKING;
79962306a36Sopenharmony_ci		break;
80062306a36Sopenharmony_ci	case BR_STATE_LEARNING:
80162306a36Sopenharmony_ci		state = MV88E6XXX_PORT_CTL0_STATE_LEARNING;
80262306a36Sopenharmony_ci		break;
80362306a36Sopenharmony_ci	case BR_STATE_FORWARDING:
80462306a36Sopenharmony_ci		state = MV88E6XXX_PORT_CTL0_STATE_FORWARDING;
80562306a36Sopenharmony_ci		break;
80662306a36Sopenharmony_ci	default:
80762306a36Sopenharmony_ci		return -EINVAL;
80862306a36Sopenharmony_ci	}
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	reg |= state;
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg);
81362306a36Sopenharmony_ci	if (err)
81462306a36Sopenharmony_ci		return err;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	dev_dbg(chip->dev, "p%d: PortState set to %s\n", port,
81762306a36Sopenharmony_ci		mv88e6xxx_port_state_names[state]);
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	return 0;
82062306a36Sopenharmony_ci}
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ciint mv88e6xxx_port_set_egress_mode(struct mv88e6xxx_chip *chip, int port,
82362306a36Sopenharmony_ci				   enum mv88e6xxx_egress_mode mode)
82462306a36Sopenharmony_ci{
82562306a36Sopenharmony_ci	int err;
82662306a36Sopenharmony_ci	u16 reg;
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, &reg);
82962306a36Sopenharmony_ci	if (err)
83062306a36Sopenharmony_ci		return err;
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	reg &= ~MV88E6XXX_PORT_CTL0_EGRESS_MODE_MASK;
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	switch (mode) {
83562306a36Sopenharmony_ci	case MV88E6XXX_EGRESS_MODE_UNMODIFIED:
83662306a36Sopenharmony_ci		reg |= MV88E6XXX_PORT_CTL0_EGRESS_MODE_UNMODIFIED;
83762306a36Sopenharmony_ci		break;
83862306a36Sopenharmony_ci	case MV88E6XXX_EGRESS_MODE_UNTAGGED:
83962306a36Sopenharmony_ci		reg |= MV88E6XXX_PORT_CTL0_EGRESS_MODE_UNTAGGED;
84062306a36Sopenharmony_ci		break;
84162306a36Sopenharmony_ci	case MV88E6XXX_EGRESS_MODE_TAGGED:
84262306a36Sopenharmony_ci		reg |= MV88E6XXX_PORT_CTL0_EGRESS_MODE_TAGGED;
84362306a36Sopenharmony_ci		break;
84462306a36Sopenharmony_ci	case MV88E6XXX_EGRESS_MODE_ETHERTYPE:
84562306a36Sopenharmony_ci		reg |= MV88E6XXX_PORT_CTL0_EGRESS_MODE_ETHER_TYPE_DSA;
84662306a36Sopenharmony_ci		break;
84762306a36Sopenharmony_ci	default:
84862306a36Sopenharmony_ci		return -EINVAL;
84962306a36Sopenharmony_ci	}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg);
85262306a36Sopenharmony_ci}
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ciint mv88e6085_port_set_frame_mode(struct mv88e6xxx_chip *chip, int port,
85562306a36Sopenharmony_ci				  enum mv88e6xxx_frame_mode mode)
85662306a36Sopenharmony_ci{
85762306a36Sopenharmony_ci	int err;
85862306a36Sopenharmony_ci	u16 reg;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, &reg);
86162306a36Sopenharmony_ci	if (err)
86262306a36Sopenharmony_ci		return err;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	reg &= ~MV88E6XXX_PORT_CTL0_FRAME_MODE_MASK;
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	switch (mode) {
86762306a36Sopenharmony_ci	case MV88E6XXX_FRAME_MODE_NORMAL:
86862306a36Sopenharmony_ci		reg |= MV88E6XXX_PORT_CTL0_FRAME_MODE_NORMAL;
86962306a36Sopenharmony_ci		break;
87062306a36Sopenharmony_ci	case MV88E6XXX_FRAME_MODE_DSA:
87162306a36Sopenharmony_ci		reg |= MV88E6XXX_PORT_CTL0_FRAME_MODE_DSA;
87262306a36Sopenharmony_ci		break;
87362306a36Sopenharmony_ci	default:
87462306a36Sopenharmony_ci		return -EINVAL;
87562306a36Sopenharmony_ci	}
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg);
87862306a36Sopenharmony_ci}
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ciint mv88e6351_port_set_frame_mode(struct mv88e6xxx_chip *chip, int port,
88162306a36Sopenharmony_ci				  enum mv88e6xxx_frame_mode mode)
88262306a36Sopenharmony_ci{
88362306a36Sopenharmony_ci	int err;
88462306a36Sopenharmony_ci	u16 reg;
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, &reg);
88762306a36Sopenharmony_ci	if (err)
88862306a36Sopenharmony_ci		return err;
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	reg &= ~MV88E6XXX_PORT_CTL0_FRAME_MODE_MASK;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	switch (mode) {
89362306a36Sopenharmony_ci	case MV88E6XXX_FRAME_MODE_NORMAL:
89462306a36Sopenharmony_ci		reg |= MV88E6XXX_PORT_CTL0_FRAME_MODE_NORMAL;
89562306a36Sopenharmony_ci		break;
89662306a36Sopenharmony_ci	case MV88E6XXX_FRAME_MODE_DSA:
89762306a36Sopenharmony_ci		reg |= MV88E6XXX_PORT_CTL0_FRAME_MODE_DSA;
89862306a36Sopenharmony_ci		break;
89962306a36Sopenharmony_ci	case MV88E6XXX_FRAME_MODE_PROVIDER:
90062306a36Sopenharmony_ci		reg |= MV88E6XXX_PORT_CTL0_FRAME_MODE_PROVIDER;
90162306a36Sopenharmony_ci		break;
90262306a36Sopenharmony_ci	case MV88E6XXX_FRAME_MODE_ETHERTYPE:
90362306a36Sopenharmony_ci		reg |= MV88E6XXX_PORT_CTL0_FRAME_MODE_ETHER_TYPE_DSA;
90462306a36Sopenharmony_ci		break;
90562306a36Sopenharmony_ci	default:
90662306a36Sopenharmony_ci		return -EINVAL;
90762306a36Sopenharmony_ci	}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg);
91062306a36Sopenharmony_ci}
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ciint mv88e6185_port_set_forward_unknown(struct mv88e6xxx_chip *chip,
91362306a36Sopenharmony_ci				       int port, bool unicast)
91462306a36Sopenharmony_ci{
91562306a36Sopenharmony_ci	int err;
91662306a36Sopenharmony_ci	u16 reg;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, &reg);
91962306a36Sopenharmony_ci	if (err)
92062306a36Sopenharmony_ci		return err;
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	if (unicast)
92362306a36Sopenharmony_ci		reg |= MV88E6185_PORT_CTL0_FORWARD_UNKNOWN;
92462306a36Sopenharmony_ci	else
92562306a36Sopenharmony_ci		reg &= ~MV88E6185_PORT_CTL0_FORWARD_UNKNOWN;
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg);
92862306a36Sopenharmony_ci}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ciint mv88e6352_port_set_ucast_flood(struct mv88e6xxx_chip *chip, int port,
93162306a36Sopenharmony_ci				   bool unicast)
93262306a36Sopenharmony_ci{
93362306a36Sopenharmony_ci	int err;
93462306a36Sopenharmony_ci	u16 reg;
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, &reg);
93762306a36Sopenharmony_ci	if (err)
93862306a36Sopenharmony_ci		return err;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	if (unicast)
94162306a36Sopenharmony_ci		reg |= MV88E6352_PORT_CTL0_EGRESS_FLOODS_UC;
94262306a36Sopenharmony_ci	else
94362306a36Sopenharmony_ci		reg &= ~MV88E6352_PORT_CTL0_EGRESS_FLOODS_UC;
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg);
94662306a36Sopenharmony_ci}
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ciint mv88e6352_port_set_mcast_flood(struct mv88e6xxx_chip *chip, int port,
94962306a36Sopenharmony_ci				   bool multicast)
95062306a36Sopenharmony_ci{
95162306a36Sopenharmony_ci	int err;
95262306a36Sopenharmony_ci	u16 reg;
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, &reg);
95562306a36Sopenharmony_ci	if (err)
95662306a36Sopenharmony_ci		return err;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	if (multicast)
95962306a36Sopenharmony_ci		reg |= MV88E6352_PORT_CTL0_EGRESS_FLOODS_MC;
96062306a36Sopenharmony_ci	else
96162306a36Sopenharmony_ci		reg &= ~MV88E6352_PORT_CTL0_EGRESS_FLOODS_MC;
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg);
96462306a36Sopenharmony_ci}
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci/* Offset 0x05: Port Control 1 */
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ciint mv88e6xxx_port_set_message_port(struct mv88e6xxx_chip *chip, int port,
96962306a36Sopenharmony_ci				    bool message_port)
97062306a36Sopenharmony_ci{
97162306a36Sopenharmony_ci	u16 val;
97262306a36Sopenharmony_ci	int err;
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL1, &val);
97562306a36Sopenharmony_ci	if (err)
97662306a36Sopenharmony_ci		return err;
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	if (message_port)
97962306a36Sopenharmony_ci		val |= MV88E6XXX_PORT_CTL1_MESSAGE_PORT;
98062306a36Sopenharmony_ci	else
98162306a36Sopenharmony_ci		val &= ~MV88E6XXX_PORT_CTL1_MESSAGE_PORT;
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL1, val);
98462306a36Sopenharmony_ci}
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ciint mv88e6xxx_port_set_trunk(struct mv88e6xxx_chip *chip, int port,
98762306a36Sopenharmony_ci			     bool trunk, u8 id)
98862306a36Sopenharmony_ci{
98962306a36Sopenharmony_ci	u16 val;
99062306a36Sopenharmony_ci	int err;
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL1, &val);
99362306a36Sopenharmony_ci	if (err)
99462306a36Sopenharmony_ci		return err;
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	val &= ~MV88E6XXX_PORT_CTL1_TRUNK_ID_MASK;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	if (trunk)
99962306a36Sopenharmony_ci		val |= MV88E6XXX_PORT_CTL1_TRUNK_PORT |
100062306a36Sopenharmony_ci			(id << MV88E6XXX_PORT_CTL1_TRUNK_ID_SHIFT);
100162306a36Sopenharmony_ci	else
100262306a36Sopenharmony_ci		val &= ~MV88E6XXX_PORT_CTL1_TRUNK_PORT;
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL1, val);
100562306a36Sopenharmony_ci}
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci/* Offset 0x06: Port Based VLAN Map */
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ciint mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map)
101062306a36Sopenharmony_ci{
101162306a36Sopenharmony_ci	const u16 mask = mv88e6xxx_port_mask(chip);
101262306a36Sopenharmony_ci	u16 reg;
101362306a36Sopenharmony_ci	int err;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_BASE_VLAN, &reg);
101662306a36Sopenharmony_ci	if (err)
101762306a36Sopenharmony_ci		return err;
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	reg &= ~mask;
102062306a36Sopenharmony_ci	reg |= map & mask;
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_BASE_VLAN, reg);
102362306a36Sopenharmony_ci	if (err)
102462306a36Sopenharmony_ci		return err;
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	dev_dbg(chip->dev, "p%d: VLANTable set to %.3x\n", port, map);
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	return 0;
102962306a36Sopenharmony_ci}
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ciint mv88e6xxx_port_get_fid(struct mv88e6xxx_chip *chip, int port, u16 *fid)
103262306a36Sopenharmony_ci{
103362306a36Sopenharmony_ci	const u16 upper_mask = (mv88e6xxx_num_databases(chip) - 1) >> 4;
103462306a36Sopenharmony_ci	u16 reg;
103562306a36Sopenharmony_ci	int err;
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	/* Port's default FID lower 4 bits are located in reg 0x06, offset 12 */
103862306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_BASE_VLAN, &reg);
103962306a36Sopenharmony_ci	if (err)
104062306a36Sopenharmony_ci		return err;
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	*fid = (reg & 0xf000) >> 12;
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	/* Port's default FID upper bits are located in reg 0x05, offset 0 */
104562306a36Sopenharmony_ci	if (upper_mask) {
104662306a36Sopenharmony_ci		err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL1,
104762306a36Sopenharmony_ci					  &reg);
104862306a36Sopenharmony_ci		if (err)
104962306a36Sopenharmony_ci			return err;
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci		*fid |= (reg & upper_mask) << 4;
105262306a36Sopenharmony_ci	}
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	return 0;
105562306a36Sopenharmony_ci}
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ciint mv88e6xxx_port_set_fid(struct mv88e6xxx_chip *chip, int port, u16 fid)
105862306a36Sopenharmony_ci{
105962306a36Sopenharmony_ci	const u16 upper_mask = (mv88e6xxx_num_databases(chip) - 1) >> 4;
106062306a36Sopenharmony_ci	u16 reg;
106162306a36Sopenharmony_ci	int err;
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	if (fid >= mv88e6xxx_num_databases(chip))
106462306a36Sopenharmony_ci		return -EINVAL;
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	/* Port's default FID lower 4 bits are located in reg 0x06, offset 12 */
106762306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_BASE_VLAN, &reg);
106862306a36Sopenharmony_ci	if (err)
106962306a36Sopenharmony_ci		return err;
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	reg &= 0x0fff;
107262306a36Sopenharmony_ci	reg |= (fid & 0x000f) << 12;
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_BASE_VLAN, reg);
107562306a36Sopenharmony_ci	if (err)
107662306a36Sopenharmony_ci		return err;
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	/* Port's default FID upper bits are located in reg 0x05, offset 0 */
107962306a36Sopenharmony_ci	if (upper_mask) {
108062306a36Sopenharmony_ci		err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL1,
108162306a36Sopenharmony_ci					  &reg);
108262306a36Sopenharmony_ci		if (err)
108362306a36Sopenharmony_ci			return err;
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci		reg &= ~upper_mask;
108662306a36Sopenharmony_ci		reg |= (fid >> 4) & upper_mask;
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci		err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL1,
108962306a36Sopenharmony_ci					   reg);
109062306a36Sopenharmony_ci		if (err)
109162306a36Sopenharmony_ci			return err;
109262306a36Sopenharmony_ci	}
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	dev_dbg(chip->dev, "p%d: FID set to %u\n", port, fid);
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	return 0;
109762306a36Sopenharmony_ci}
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci/* Offset 0x07: Default Port VLAN ID & Priority */
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ciint mv88e6xxx_port_get_pvid(struct mv88e6xxx_chip *chip, int port, u16 *pvid)
110262306a36Sopenharmony_ci{
110362306a36Sopenharmony_ci	u16 reg;
110462306a36Sopenharmony_ci	int err;
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_DEFAULT_VLAN,
110762306a36Sopenharmony_ci				  &reg);
110862306a36Sopenharmony_ci	if (err)
110962306a36Sopenharmony_ci		return err;
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	*pvid = reg & MV88E6XXX_PORT_DEFAULT_VLAN_MASK;
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	return 0;
111462306a36Sopenharmony_ci}
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ciint mv88e6xxx_port_set_pvid(struct mv88e6xxx_chip *chip, int port, u16 pvid)
111762306a36Sopenharmony_ci{
111862306a36Sopenharmony_ci	u16 reg;
111962306a36Sopenharmony_ci	int err;
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_DEFAULT_VLAN,
112262306a36Sopenharmony_ci				  &reg);
112362306a36Sopenharmony_ci	if (err)
112462306a36Sopenharmony_ci		return err;
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	reg &= ~MV88E6XXX_PORT_DEFAULT_VLAN_MASK;
112762306a36Sopenharmony_ci	reg |= pvid & MV88E6XXX_PORT_DEFAULT_VLAN_MASK;
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_DEFAULT_VLAN,
113062306a36Sopenharmony_ci				   reg);
113162306a36Sopenharmony_ci	if (err)
113262306a36Sopenharmony_ci		return err;
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	dev_dbg(chip->dev, "p%d: DefaultVID set to %u\n", port, pvid);
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	return 0;
113762306a36Sopenharmony_ci}
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci/* Offset 0x08: Port Control 2 Register */
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_cistatic const char * const mv88e6xxx_port_8021q_mode_names[] = {
114262306a36Sopenharmony_ci	[MV88E6XXX_PORT_CTL2_8021Q_MODE_DISABLED] = "Disabled",
114362306a36Sopenharmony_ci	[MV88E6XXX_PORT_CTL2_8021Q_MODE_FALLBACK] = "Fallback",
114462306a36Sopenharmony_ci	[MV88E6XXX_PORT_CTL2_8021Q_MODE_CHECK] = "Check",
114562306a36Sopenharmony_ci	[MV88E6XXX_PORT_CTL2_8021Q_MODE_SECURE] = "Secure",
114662306a36Sopenharmony_ci};
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ciint mv88e6185_port_set_default_forward(struct mv88e6xxx_chip *chip,
114962306a36Sopenharmony_ci				       int port, bool multicast)
115062306a36Sopenharmony_ci{
115162306a36Sopenharmony_ci	int err;
115262306a36Sopenharmony_ci	u16 reg;
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, &reg);
115562306a36Sopenharmony_ci	if (err)
115662306a36Sopenharmony_ci		return err;
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	if (multicast)
115962306a36Sopenharmony_ci		reg |= MV88E6XXX_PORT_CTL2_DEFAULT_FORWARD;
116062306a36Sopenharmony_ci	else
116162306a36Sopenharmony_ci		reg &= ~MV88E6XXX_PORT_CTL2_DEFAULT_FORWARD;
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, reg);
116462306a36Sopenharmony_ci}
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ciint mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port,
116762306a36Sopenharmony_ci				     int upstream_port)
116862306a36Sopenharmony_ci{
116962306a36Sopenharmony_ci	int err;
117062306a36Sopenharmony_ci	u16 reg;
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, &reg);
117362306a36Sopenharmony_ci	if (err)
117462306a36Sopenharmony_ci		return err;
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	reg &= ~MV88E6095_PORT_CTL2_CPU_PORT_MASK;
117762306a36Sopenharmony_ci	reg |= upstream_port;
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, reg);
118062306a36Sopenharmony_ci}
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ciint mv88e6xxx_port_set_mirror(struct mv88e6xxx_chip *chip, int port,
118362306a36Sopenharmony_ci			      enum mv88e6xxx_egress_direction direction,
118462306a36Sopenharmony_ci			      bool mirror)
118562306a36Sopenharmony_ci{
118662306a36Sopenharmony_ci	bool *mirror_port;
118762306a36Sopenharmony_ci	u16 reg;
118862306a36Sopenharmony_ci	u16 bit;
118962306a36Sopenharmony_ci	int err;
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, &reg);
119262306a36Sopenharmony_ci	if (err)
119362306a36Sopenharmony_ci		return err;
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci	switch (direction) {
119662306a36Sopenharmony_ci	case MV88E6XXX_EGRESS_DIR_INGRESS:
119762306a36Sopenharmony_ci		bit = MV88E6XXX_PORT_CTL2_INGRESS_MONITOR;
119862306a36Sopenharmony_ci		mirror_port = &chip->ports[port].mirror_ingress;
119962306a36Sopenharmony_ci		break;
120062306a36Sopenharmony_ci	case MV88E6XXX_EGRESS_DIR_EGRESS:
120162306a36Sopenharmony_ci		bit = MV88E6XXX_PORT_CTL2_EGRESS_MONITOR;
120262306a36Sopenharmony_ci		mirror_port = &chip->ports[port].mirror_egress;
120362306a36Sopenharmony_ci		break;
120462306a36Sopenharmony_ci	default:
120562306a36Sopenharmony_ci		return -EINVAL;
120662306a36Sopenharmony_ci	}
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	reg &= ~bit;
120962306a36Sopenharmony_ci	if (mirror)
121062306a36Sopenharmony_ci		reg |= bit;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, reg);
121362306a36Sopenharmony_ci	if (!err)
121462306a36Sopenharmony_ci		*mirror_port = mirror;
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	return err;
121762306a36Sopenharmony_ci}
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ciint mv88e6xxx_port_set_lock(struct mv88e6xxx_chip *chip, int port,
122062306a36Sopenharmony_ci			    bool locked)
122162306a36Sopenharmony_ci{
122262306a36Sopenharmony_ci	u16 reg;
122362306a36Sopenharmony_ci	int err;
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, &reg);
122662306a36Sopenharmony_ci	if (err)
122762306a36Sopenharmony_ci		return err;
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	reg &= ~MV88E6XXX_PORT_CTL0_SA_FILT_MASK;
123062306a36Sopenharmony_ci	if (locked)
123162306a36Sopenharmony_ci		reg |= MV88E6XXX_PORT_CTL0_SA_FILT_DROP_ON_LOCK;
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci	err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg);
123462306a36Sopenharmony_ci	if (err)
123562306a36Sopenharmony_ci		return err;
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_ASSOC_VECTOR, &reg);
123862306a36Sopenharmony_ci	if (err)
123962306a36Sopenharmony_ci		return err;
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	reg &= ~MV88E6XXX_PORT_ASSOC_VECTOR_LOCKED_PORT;
124262306a36Sopenharmony_ci	if (locked)
124362306a36Sopenharmony_ci		reg |= MV88E6XXX_PORT_ASSOC_VECTOR_LOCKED_PORT;
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_ASSOC_VECTOR, reg);
124662306a36Sopenharmony_ci}
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ciint mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port,
124962306a36Sopenharmony_ci				  u16 mode)
125062306a36Sopenharmony_ci{
125162306a36Sopenharmony_ci	u16 reg;
125262306a36Sopenharmony_ci	int err;
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, &reg);
125562306a36Sopenharmony_ci	if (err)
125662306a36Sopenharmony_ci		return err;
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	reg &= ~MV88E6XXX_PORT_CTL2_8021Q_MODE_MASK;
125962306a36Sopenharmony_ci	reg |= mode & MV88E6XXX_PORT_CTL2_8021Q_MODE_MASK;
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, reg);
126262306a36Sopenharmony_ci	if (err)
126362306a36Sopenharmony_ci		return err;
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci	dev_dbg(chip->dev, "p%d: 802.1QMode set to %s\n", port,
126662306a36Sopenharmony_ci		mv88e6xxx_port_8021q_mode_names[mode]);
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	return 0;
126962306a36Sopenharmony_ci}
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ciint mv88e6xxx_port_drop_untagged(struct mv88e6xxx_chip *chip, int port,
127262306a36Sopenharmony_ci				 bool drop_untagged)
127362306a36Sopenharmony_ci{
127462306a36Sopenharmony_ci	u16 old, new;
127562306a36Sopenharmony_ci	int err;
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, &old);
127862306a36Sopenharmony_ci	if (err)
127962306a36Sopenharmony_ci		return err;
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	if (drop_untagged)
128262306a36Sopenharmony_ci		new = old | MV88E6XXX_PORT_CTL2_DISCARD_UNTAGGED;
128362306a36Sopenharmony_ci	else
128462306a36Sopenharmony_ci		new = old & ~MV88E6XXX_PORT_CTL2_DISCARD_UNTAGGED;
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	if (new == old)
128762306a36Sopenharmony_ci		return 0;
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, new);
129062306a36Sopenharmony_ci}
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ciint mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port, bool map)
129362306a36Sopenharmony_ci{
129462306a36Sopenharmony_ci	u16 reg;
129562306a36Sopenharmony_ci	int err;
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, &reg);
129862306a36Sopenharmony_ci	if (err)
129962306a36Sopenharmony_ci		return err;
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	if (map)
130262306a36Sopenharmony_ci		reg |= MV88E6XXX_PORT_CTL2_MAP_DA;
130362306a36Sopenharmony_ci	else
130462306a36Sopenharmony_ci		reg &= ~MV88E6XXX_PORT_CTL2_MAP_DA;
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, reg);
130762306a36Sopenharmony_ci}
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ciint mv88e6165_port_set_jumbo_size(struct mv88e6xxx_chip *chip, int port,
131062306a36Sopenharmony_ci				  size_t size)
131162306a36Sopenharmony_ci{
131262306a36Sopenharmony_ci	u16 reg;
131362306a36Sopenharmony_ci	int err;
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	size += VLAN_ETH_HLEN + ETH_FCS_LEN;
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, &reg);
131862306a36Sopenharmony_ci	if (err)
131962306a36Sopenharmony_ci		return err;
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	reg &= ~MV88E6XXX_PORT_CTL2_JUMBO_MODE_MASK;
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci	if (size <= 1522)
132462306a36Sopenharmony_ci		reg |= MV88E6XXX_PORT_CTL2_JUMBO_MODE_1522;
132562306a36Sopenharmony_ci	else if (size <= 2048)
132662306a36Sopenharmony_ci		reg |= MV88E6XXX_PORT_CTL2_JUMBO_MODE_2048;
132762306a36Sopenharmony_ci	else if (size <= 10240)
132862306a36Sopenharmony_ci		reg |= MV88E6XXX_PORT_CTL2_JUMBO_MODE_10240;
132962306a36Sopenharmony_ci	else
133062306a36Sopenharmony_ci		return -ERANGE;
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, reg);
133362306a36Sopenharmony_ci}
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci/* Offset 0x09: Port Rate Control */
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ciint mv88e6095_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port)
133862306a36Sopenharmony_ci{
133962306a36Sopenharmony_ci	return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_EGRESS_RATE_CTL1,
134062306a36Sopenharmony_ci				    0x0000);
134162306a36Sopenharmony_ci}
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ciint mv88e6097_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port)
134462306a36Sopenharmony_ci{
134562306a36Sopenharmony_ci	return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_EGRESS_RATE_CTL1,
134662306a36Sopenharmony_ci				    0x0001);
134762306a36Sopenharmony_ci}
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci/* Offset 0x0B: Port Association Vector */
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ciint mv88e6xxx_port_set_assoc_vector(struct mv88e6xxx_chip *chip, int port,
135262306a36Sopenharmony_ci				    u16 pav)
135362306a36Sopenharmony_ci{
135462306a36Sopenharmony_ci	u16 reg, mask;
135562306a36Sopenharmony_ci	int err;
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_ASSOC_VECTOR,
135862306a36Sopenharmony_ci				  &reg);
135962306a36Sopenharmony_ci	if (err)
136062306a36Sopenharmony_ci		return err;
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci	mask = mv88e6xxx_port_mask(chip);
136362306a36Sopenharmony_ci	reg &= ~mask;
136462306a36Sopenharmony_ci	reg |= pav & mask;
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_ASSOC_VECTOR,
136762306a36Sopenharmony_ci				    reg);
136862306a36Sopenharmony_ci}
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci/* Offset 0x0C: Port ATU Control */
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ciint mv88e6xxx_port_disable_learn_limit(struct mv88e6xxx_chip *chip, int port)
137362306a36Sopenharmony_ci{
137462306a36Sopenharmony_ci	return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_ATU_CTL, 0);
137562306a36Sopenharmony_ci}
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci/* Offset 0x0D: (Priority) Override Register */
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ciint mv88e6xxx_port_disable_pri_override(struct mv88e6xxx_chip *chip, int port)
138062306a36Sopenharmony_ci{
138162306a36Sopenharmony_ci	return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_PRI_OVERRIDE, 0);
138262306a36Sopenharmony_ci}
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci/* Offset 0x0E: Policy & MGMT Control Register for FAMILY 6191X 6193X 6393X */
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_cistatic int mv88e6393x_port_policy_read(struct mv88e6xxx_chip *chip, int port,
138762306a36Sopenharmony_ci				       u16 pointer, u8 *data)
138862306a36Sopenharmony_ci{
138962306a36Sopenharmony_ci	u16 reg;
139062306a36Sopenharmony_ci	int err;
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	err = mv88e6xxx_port_write(chip, port, MV88E6393X_PORT_POLICY_MGMT_CTL,
139362306a36Sopenharmony_ci				   pointer);
139462306a36Sopenharmony_ci	if (err)
139562306a36Sopenharmony_ci		return err;
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6393X_PORT_POLICY_MGMT_CTL,
139862306a36Sopenharmony_ci				  &reg);
139962306a36Sopenharmony_ci	if (err)
140062306a36Sopenharmony_ci		return err;
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci	*data = reg;
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	return 0;
140562306a36Sopenharmony_ci}
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_cistatic int mv88e6393x_port_policy_write(struct mv88e6xxx_chip *chip, int port,
140862306a36Sopenharmony_ci					u16 pointer, u8 data)
140962306a36Sopenharmony_ci{
141062306a36Sopenharmony_ci	u16 reg;
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci	reg = MV88E6393X_PORT_POLICY_MGMT_CTL_UPDATE | pointer | data;
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	return mv88e6xxx_port_write(chip, port, MV88E6393X_PORT_POLICY_MGMT_CTL,
141562306a36Sopenharmony_ci				    reg);
141662306a36Sopenharmony_ci}
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_cistatic int mv88e6393x_port_policy_write_all(struct mv88e6xxx_chip *chip,
141962306a36Sopenharmony_ci					    u16 pointer, u8 data)
142062306a36Sopenharmony_ci{
142162306a36Sopenharmony_ci	int err, port;
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci	for (port = 0; port < mv88e6xxx_num_ports(chip); port++) {
142462306a36Sopenharmony_ci		if (dsa_is_unused_port(chip->ds, port))
142562306a36Sopenharmony_ci			continue;
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci		err = mv88e6393x_port_policy_write(chip, port, pointer, data);
142862306a36Sopenharmony_ci		if (err)
142962306a36Sopenharmony_ci			return err;
143062306a36Sopenharmony_ci	}
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	return 0;
143362306a36Sopenharmony_ci}
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ciint mv88e6393x_set_egress_port(struct mv88e6xxx_chip *chip,
143662306a36Sopenharmony_ci			       enum mv88e6xxx_egress_direction direction,
143762306a36Sopenharmony_ci			       int port)
143862306a36Sopenharmony_ci{
143962306a36Sopenharmony_ci	u16 ptr;
144062306a36Sopenharmony_ci	int err;
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	switch (direction) {
144362306a36Sopenharmony_ci	case MV88E6XXX_EGRESS_DIR_INGRESS:
144462306a36Sopenharmony_ci		ptr = MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_INGRESS_DEST;
144562306a36Sopenharmony_ci		err = mv88e6393x_port_policy_write_all(chip, ptr, port);
144662306a36Sopenharmony_ci		if (err)
144762306a36Sopenharmony_ci			return err;
144862306a36Sopenharmony_ci		break;
144962306a36Sopenharmony_ci	case MV88E6XXX_EGRESS_DIR_EGRESS:
145062306a36Sopenharmony_ci		ptr = MV88E6393X_G2_EGRESS_MONITOR_DEST;
145162306a36Sopenharmony_ci		err = mv88e6xxx_g2_write(chip, ptr, port);
145262306a36Sopenharmony_ci		if (err)
145362306a36Sopenharmony_ci			return err;
145462306a36Sopenharmony_ci		break;
145562306a36Sopenharmony_ci	}
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	return 0;
145862306a36Sopenharmony_ci}
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ciint mv88e6393x_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port,
146162306a36Sopenharmony_ci				      int upstream_port)
146262306a36Sopenharmony_ci{
146362306a36Sopenharmony_ci	u16 ptr = MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_CPU_DEST;
146462306a36Sopenharmony_ci	u8 data = MV88E6393X_PORT_POLICY_MGMT_CTL_CPU_DEST_MGMTPRI |
146562306a36Sopenharmony_ci		  upstream_port;
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci	return mv88e6393x_port_policy_write(chip, port, ptr, data);
146862306a36Sopenharmony_ci}
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ciint mv88e6393x_port_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip)
147162306a36Sopenharmony_ci{
147262306a36Sopenharmony_ci	u16 ptr;
147362306a36Sopenharmony_ci	int err;
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci	/* Consider the frames with reserved multicast destination
147662306a36Sopenharmony_ci	 * addresses matching 01:80:c2:00:00:00 and
147762306a36Sopenharmony_ci	 * 01:80:c2:00:00:02 as MGMT.
147862306a36Sopenharmony_ci	 */
147962306a36Sopenharmony_ci	ptr = MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_01C280000000XLO;
148062306a36Sopenharmony_ci	err = mv88e6393x_port_policy_write_all(chip, ptr, 0xff);
148162306a36Sopenharmony_ci	if (err)
148262306a36Sopenharmony_ci		return err;
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci	ptr = MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_01C280000000XHI;
148562306a36Sopenharmony_ci	err = mv88e6393x_port_policy_write_all(chip, ptr, 0xff);
148662306a36Sopenharmony_ci	if (err)
148762306a36Sopenharmony_ci		return err;
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci	ptr = MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_01C280000002XLO;
149062306a36Sopenharmony_ci	err = mv88e6393x_port_policy_write_all(chip, ptr, 0xff);
149162306a36Sopenharmony_ci	if (err)
149262306a36Sopenharmony_ci		return err;
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci	ptr = MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_01C280000002XHI;
149562306a36Sopenharmony_ci	err = mv88e6393x_port_policy_write_all(chip, ptr, 0xff);
149662306a36Sopenharmony_ci	if (err)
149762306a36Sopenharmony_ci		return err;
149862306a36Sopenharmony_ci
149962306a36Sopenharmony_ci	return 0;
150062306a36Sopenharmony_ci}
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_ci/* Offset 0x10 & 0x11: EPC */
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_cistatic int mv88e6393x_port_epc_wait_ready(struct mv88e6xxx_chip *chip, int port)
150562306a36Sopenharmony_ci{
150662306a36Sopenharmony_ci	int bit = __bf_shf(MV88E6393X_PORT_EPC_CMD_BUSY);
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci	return mv88e6xxx_port_wait_bit(chip, port, MV88E6393X_PORT_EPC_CMD, bit, 0);
150962306a36Sopenharmony_ci}
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci/* Port Ether type for 6393X family */
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ciint mv88e6393x_port_set_ether_type(struct mv88e6xxx_chip *chip, int port,
151462306a36Sopenharmony_ci				   u16 etype)
151562306a36Sopenharmony_ci{
151662306a36Sopenharmony_ci	u16 val;
151762306a36Sopenharmony_ci	int err;
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci	err = mv88e6393x_port_epc_wait_ready(chip, port);
152062306a36Sopenharmony_ci	if (err)
152162306a36Sopenharmony_ci		return err;
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	err = mv88e6xxx_port_write(chip, port, MV88E6393X_PORT_EPC_DATA, etype);
152462306a36Sopenharmony_ci	if (err)
152562306a36Sopenharmony_ci		return err;
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_ci	val = MV88E6393X_PORT_EPC_CMD_BUSY |
152862306a36Sopenharmony_ci	      MV88E6393X_PORT_EPC_CMD_WRITE |
152962306a36Sopenharmony_ci	      MV88E6393X_PORT_EPC_INDEX_PORT_ETYPE;
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci	return mv88e6xxx_port_write(chip, port, MV88E6393X_PORT_EPC_CMD, val);
153262306a36Sopenharmony_ci}
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci/* Offset 0x0f: Port Ether type */
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ciint mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port,
153762306a36Sopenharmony_ci				  u16 etype)
153862306a36Sopenharmony_ci{
153962306a36Sopenharmony_ci	return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_ETH_TYPE, etype);
154062306a36Sopenharmony_ci}
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci/* Offset 0x18: Port IEEE Priority Remapping Registers [0-3]
154362306a36Sopenharmony_ci * Offset 0x19: Port IEEE Priority Remapping Registers [4-7]
154462306a36Sopenharmony_ci */
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ciint mv88e6095_port_tag_remap(struct mv88e6xxx_chip *chip, int port)
154762306a36Sopenharmony_ci{
154862306a36Sopenharmony_ci	int err;
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci	/* Use a direct priority mapping for all IEEE tagged frames */
155162306a36Sopenharmony_ci	err = mv88e6xxx_port_write(chip, port,
155262306a36Sopenharmony_ci				   MV88E6095_PORT_IEEE_PRIO_REMAP_0123,
155362306a36Sopenharmony_ci				   0x3210);
155462306a36Sopenharmony_ci	if (err)
155562306a36Sopenharmony_ci		return err;
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_ci	return mv88e6xxx_port_write(chip, port,
155862306a36Sopenharmony_ci				    MV88E6095_PORT_IEEE_PRIO_REMAP_4567,
155962306a36Sopenharmony_ci				    0x7654);
156062306a36Sopenharmony_ci}
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_cistatic int mv88e6xxx_port_ieeepmt_write(struct mv88e6xxx_chip *chip,
156362306a36Sopenharmony_ci					int port, u16 table, u8 ptr, u16 data)
156462306a36Sopenharmony_ci{
156562306a36Sopenharmony_ci	u16 reg;
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci	reg = MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_UPDATE | table |
156862306a36Sopenharmony_ci		(ptr << __bf_shf(MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_PTR_MASK)) |
156962306a36Sopenharmony_ci		(data & MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_DATA_MASK);
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci	return mv88e6xxx_port_write(chip, port,
157262306a36Sopenharmony_ci				    MV88E6390_PORT_IEEE_PRIO_MAP_TABLE, reg);
157362306a36Sopenharmony_ci}
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_ciint mv88e6390_port_tag_remap(struct mv88e6xxx_chip *chip, int port)
157662306a36Sopenharmony_ci{
157762306a36Sopenharmony_ci	int err, i;
157862306a36Sopenharmony_ci	u16 table;
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_ci	for (i = 0; i <= 7; i++) {
158162306a36Sopenharmony_ci		table = MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_INGRESS_PCP;
158262306a36Sopenharmony_ci		err = mv88e6xxx_port_ieeepmt_write(chip, port, table, i,
158362306a36Sopenharmony_ci						   (i | i << 4));
158462306a36Sopenharmony_ci		if (err)
158562306a36Sopenharmony_ci			return err;
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci		table = MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_EGRESS_GREEN_PCP;
158862306a36Sopenharmony_ci		err = mv88e6xxx_port_ieeepmt_write(chip, port, table, i, i);
158962306a36Sopenharmony_ci		if (err)
159062306a36Sopenharmony_ci			return err;
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci		table = MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_EGRESS_YELLOW_PCP;
159362306a36Sopenharmony_ci		err = mv88e6xxx_port_ieeepmt_write(chip, port, table, i, i);
159462306a36Sopenharmony_ci		if (err)
159562306a36Sopenharmony_ci			return err;
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci		table = MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_EGRESS_AVB_PCP;
159862306a36Sopenharmony_ci		err = mv88e6xxx_port_ieeepmt_write(chip, port, table, i, i);
159962306a36Sopenharmony_ci		if (err)
160062306a36Sopenharmony_ci			return err;
160162306a36Sopenharmony_ci	}
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_ci	return 0;
160462306a36Sopenharmony_ci}
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ci/* Offset 0x0E: Policy Control Register */
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_cistatic int
160962306a36Sopenharmony_cimv88e6xxx_port_policy_mapping_get_pos(enum mv88e6xxx_policy_mapping mapping,
161062306a36Sopenharmony_ci				      enum mv88e6xxx_policy_action action,
161162306a36Sopenharmony_ci				      u16 *mask, u16 *val, int *shift)
161262306a36Sopenharmony_ci{
161362306a36Sopenharmony_ci	switch (mapping) {
161462306a36Sopenharmony_ci	case MV88E6XXX_POLICY_MAPPING_DA:
161562306a36Sopenharmony_ci		*shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_DA_MASK);
161662306a36Sopenharmony_ci		*mask = MV88E6XXX_PORT_POLICY_CTL_DA_MASK;
161762306a36Sopenharmony_ci		break;
161862306a36Sopenharmony_ci	case MV88E6XXX_POLICY_MAPPING_SA:
161962306a36Sopenharmony_ci		*shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_SA_MASK);
162062306a36Sopenharmony_ci		*mask = MV88E6XXX_PORT_POLICY_CTL_SA_MASK;
162162306a36Sopenharmony_ci		break;
162262306a36Sopenharmony_ci	case MV88E6XXX_POLICY_MAPPING_VTU:
162362306a36Sopenharmony_ci		*shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_VTU_MASK);
162462306a36Sopenharmony_ci		*mask = MV88E6XXX_PORT_POLICY_CTL_VTU_MASK;
162562306a36Sopenharmony_ci		break;
162662306a36Sopenharmony_ci	case MV88E6XXX_POLICY_MAPPING_ETYPE:
162762306a36Sopenharmony_ci		*shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_ETYPE_MASK);
162862306a36Sopenharmony_ci		*mask = MV88E6XXX_PORT_POLICY_CTL_ETYPE_MASK;
162962306a36Sopenharmony_ci		break;
163062306a36Sopenharmony_ci	case MV88E6XXX_POLICY_MAPPING_PPPOE:
163162306a36Sopenharmony_ci		*shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_PPPOE_MASK);
163262306a36Sopenharmony_ci		*mask = MV88E6XXX_PORT_POLICY_CTL_PPPOE_MASK;
163362306a36Sopenharmony_ci		break;
163462306a36Sopenharmony_ci	case MV88E6XXX_POLICY_MAPPING_VBAS:
163562306a36Sopenharmony_ci		*shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_VBAS_MASK);
163662306a36Sopenharmony_ci		*mask = MV88E6XXX_PORT_POLICY_CTL_VBAS_MASK;
163762306a36Sopenharmony_ci		break;
163862306a36Sopenharmony_ci	case MV88E6XXX_POLICY_MAPPING_OPT82:
163962306a36Sopenharmony_ci		*shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_OPT82_MASK);
164062306a36Sopenharmony_ci		*mask = MV88E6XXX_PORT_POLICY_CTL_OPT82_MASK;
164162306a36Sopenharmony_ci		break;
164262306a36Sopenharmony_ci	case MV88E6XXX_POLICY_MAPPING_UDP:
164362306a36Sopenharmony_ci		*shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_UDP_MASK);
164462306a36Sopenharmony_ci		*mask = MV88E6XXX_PORT_POLICY_CTL_UDP_MASK;
164562306a36Sopenharmony_ci		break;
164662306a36Sopenharmony_ci	default:
164762306a36Sopenharmony_ci		return -EOPNOTSUPP;
164862306a36Sopenharmony_ci	}
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci	switch (action) {
165162306a36Sopenharmony_ci	case MV88E6XXX_POLICY_ACTION_NORMAL:
165262306a36Sopenharmony_ci		*val = MV88E6XXX_PORT_POLICY_CTL_NORMAL;
165362306a36Sopenharmony_ci		break;
165462306a36Sopenharmony_ci	case MV88E6XXX_POLICY_ACTION_MIRROR:
165562306a36Sopenharmony_ci		*val = MV88E6XXX_PORT_POLICY_CTL_MIRROR;
165662306a36Sopenharmony_ci		break;
165762306a36Sopenharmony_ci	case MV88E6XXX_POLICY_ACTION_TRAP:
165862306a36Sopenharmony_ci		*val = MV88E6XXX_PORT_POLICY_CTL_TRAP;
165962306a36Sopenharmony_ci		break;
166062306a36Sopenharmony_ci	case MV88E6XXX_POLICY_ACTION_DISCARD:
166162306a36Sopenharmony_ci		*val = MV88E6XXX_PORT_POLICY_CTL_DISCARD;
166262306a36Sopenharmony_ci		break;
166362306a36Sopenharmony_ci	default:
166462306a36Sopenharmony_ci		return -EOPNOTSUPP;
166562306a36Sopenharmony_ci	}
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_ci	return 0;
166862306a36Sopenharmony_ci}
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_ciint mv88e6352_port_set_policy(struct mv88e6xxx_chip *chip, int port,
167162306a36Sopenharmony_ci			      enum mv88e6xxx_policy_mapping mapping,
167262306a36Sopenharmony_ci			      enum mv88e6xxx_policy_action action)
167362306a36Sopenharmony_ci{
167462306a36Sopenharmony_ci	u16 reg, mask, val;
167562306a36Sopenharmony_ci	int shift;
167662306a36Sopenharmony_ci	int err;
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci	err = mv88e6xxx_port_policy_mapping_get_pos(mapping, action, &mask,
167962306a36Sopenharmony_ci						    &val, &shift);
168062306a36Sopenharmony_ci	if (err)
168162306a36Sopenharmony_ci		return err;
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_ci	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_POLICY_CTL, &reg);
168462306a36Sopenharmony_ci	if (err)
168562306a36Sopenharmony_ci		return err;
168662306a36Sopenharmony_ci
168762306a36Sopenharmony_ci	reg &= ~mask;
168862306a36Sopenharmony_ci	reg |= (val << shift) & mask;
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci	return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_POLICY_CTL, reg);
169162306a36Sopenharmony_ci}
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ciint mv88e6393x_port_set_policy(struct mv88e6xxx_chip *chip, int port,
169462306a36Sopenharmony_ci			       enum mv88e6xxx_policy_mapping mapping,
169562306a36Sopenharmony_ci			       enum mv88e6xxx_policy_action action)
169662306a36Sopenharmony_ci{
169762306a36Sopenharmony_ci	u16 mask, val;
169862306a36Sopenharmony_ci	int shift;
169962306a36Sopenharmony_ci	int err;
170062306a36Sopenharmony_ci	u16 ptr;
170162306a36Sopenharmony_ci	u8 reg;
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_ci	err = mv88e6xxx_port_policy_mapping_get_pos(mapping, action, &mask,
170462306a36Sopenharmony_ci						    &val, &shift);
170562306a36Sopenharmony_ci	if (err)
170662306a36Sopenharmony_ci		return err;
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci	/* The 16-bit Port Policy CTL register from older chips is on 6393x
170962306a36Sopenharmony_ci	 * changed to Port Policy MGMT CTL, which can access more data, but
171062306a36Sopenharmony_ci	 * indirectly. The original 16-bit value is divided into two 8-bit
171162306a36Sopenharmony_ci	 * registers.
171262306a36Sopenharmony_ci	 */
171362306a36Sopenharmony_ci	ptr = shift / 8;
171462306a36Sopenharmony_ci	shift %= 8;
171562306a36Sopenharmony_ci	mask >>= ptr * 8;
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_ci	err = mv88e6393x_port_policy_read(chip, port, ptr, &reg);
171862306a36Sopenharmony_ci	if (err)
171962306a36Sopenharmony_ci		return err;
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ci	reg &= ~mask;
172262306a36Sopenharmony_ci	reg |= (val << shift) & mask;
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci	return mv88e6393x_port_policy_write(chip, port, ptr, reg);
172562306a36Sopenharmony_ci}
1726