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, ®); 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, ®); 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, ®); 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, ®); 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, ®); 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, ®); 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, ®); 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, ®); 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, ®); 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, ®); 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, ®); 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, ®); 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, ®); 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, ®); 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, ®); 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, ®); 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, ®); 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, ®); 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, ®); 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 ®); 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, ®); 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 ®); 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 ®); 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 ®); 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, ®); 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, ®); 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, ®); 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, ®); 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, ®); 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, ®); 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, ®); 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, ®); 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 ®); 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 ®); 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, ®); 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, ®); 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