diff options
Diffstat (limited to 'drivers/net/dsa')
| -rw-r--r-- | drivers/net/dsa/b53/b53_common.c | 16 | ||||
| -rw-r--r-- | drivers/net/dsa/bcm_sf2.c | 4 | ||||
| -rw-r--r-- | drivers/net/dsa/mv88e6xxx/Kconfig | 1 | ||||
| -rw-r--r-- | drivers/net/dsa/mv88e6xxx/Makefile | 1 | ||||
| -rw-r--r-- | drivers/net/dsa/mv88e6xxx/chip.c | 2017 | ||||
| -rw-r--r-- | drivers/net/dsa/mv88e6xxx/global1.c | 367 | ||||
| -rw-r--r-- | drivers/net/dsa/mv88e6xxx/global1.h | 18 | ||||
| -rw-r--r-- | drivers/net/dsa/mv88e6xxx/global2.c | 186 | ||||
| -rw-r--r-- | drivers/net/dsa/mv88e6xxx/global2.h | 17 | ||||
| -rw-r--r-- | drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 244 | ||||
| -rw-r--r-- | drivers/net/dsa/mv88e6xxx/port.c | 729 | ||||
| -rw-r--r-- | drivers/net/dsa/mv88e6xxx/port.h | 71 | 
12 files changed, 2929 insertions, 742 deletions
| diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 7717b19dc806..947adda3397d 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -962,9 +962,10 @@ static void b53_vlan_add(struct dsa_switch *ds, int port,  		vl->members |= BIT(port) | BIT(cpu_port);  		if (untagged) -			vl->untag |= BIT(port) | BIT(cpu_port); +			vl->untag |= BIT(port);  		else -			vl->untag &= ~(BIT(port) | BIT(cpu_port)); +			vl->untag &= ~BIT(port); +		vl->untag &= ~BIT(cpu_port);  		b53_set_vlan_entry(dev, vid, vl);  		b53_fast_age_vlan(dev, vid); @@ -973,8 +974,6 @@ static void b53_vlan_add(struct dsa_switch *ds, int port,  	if (pvid) {  		b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_PORT_DEF_TAG(port),  			    vlan->vid_end); -		b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_PORT_DEF_TAG(cpu_port), -			    vlan->vid_end);  		b53_fast_age_vlan(dev, vid);  	}  } @@ -984,7 +983,6 @@ static int b53_vlan_del(struct dsa_switch *ds, int port,  {  	struct b53_device *dev = ds->priv;  	bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; -	unsigned int cpu_port = dev->cpu_port;  	struct b53_vlan *vl;  	u16 vid;  	u16 pvid; @@ -997,8 +995,6 @@ static int b53_vlan_del(struct dsa_switch *ds, int port,  		b53_get_vlan_entry(dev, vid, vl);  		vl->members &= ~BIT(port); -		if ((vl->members & BIT(cpu_port)) == BIT(cpu_port)) -			vl->members = 0;  		if (pvid == vid) {  			if (is5325(dev) || is5365(dev)) @@ -1007,18 +1003,14 @@ static int b53_vlan_del(struct dsa_switch *ds, int port,  				pvid = 0;  		} -		if (untagged) { +		if (untagged)  			vl->untag &= ~(BIT(port)); -			if ((vl->untag & BIT(cpu_port)) == BIT(cpu_port)) -				vl->untag = 0; -		}  		b53_set_vlan_entry(dev, vid, vl);  		b53_fast_age_vlan(dev, vid);  	}  	b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_PORT_DEF_TAG(port), pvid); -	b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_PORT_DEF_TAG(cpu_port), pvid);  	b53_fast_age_vlan(dev, pvid);  	return 0; diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index e3ee27ce13dd..9ec33b51a0ed 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -588,6 +588,7 @@ static void bcm_sf2_sw_adjust_link(struct dsa_switch *ds, int port,  				   struct phy_device *phydev)  {  	struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); +	struct ethtool_eee *p = &priv->port_sts[port].eee;  	u32 id_mode_dis = 0, port_mode;  	const char *str = NULL;  	u32 reg; @@ -662,6 +663,9 @@ force_link:  		reg |= DUPLX_MODE;  	core_writel(priv, reg, CORE_STS_OVERRIDE_GMIIP_PORT(port)); + +	if (!phydev->is_pseudo_fixed_link) +		p->eee_enabled = bcm_sf2_eee_init(ds, port, phydev);  }  static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port, diff --git a/drivers/net/dsa/mv88e6xxx/Kconfig b/drivers/net/dsa/mv88e6xxx/Kconfig index 486668813e15..1aaa7a95ebc4 100644 --- a/drivers/net/dsa/mv88e6xxx/Kconfig +++ b/drivers/net/dsa/mv88e6xxx/Kconfig @@ -1,6 +1,7 @@  config NET_DSA_MV88E6XXX  	tristate "Marvell 88E6xxx Ethernet switch fabric support"  	depends on NET_DSA +	select IRQ_DOMAIN  	select NET_DSA_TAG_EDSA  	select NET_DSA_TAG_DSA  	help diff --git a/drivers/net/dsa/mv88e6xxx/Makefile b/drivers/net/dsa/mv88e6xxx/Makefile index 10ce820daa48..c36be318de1a 100644 --- a/drivers/net/dsa/mv88e6xxx/Makefile +++ b/drivers/net/dsa/mv88e6xxx/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_NET_DSA_MV88E6XXX) += mv88e6xxx.o  mv88e6xxx-objs := chip.o  mv88e6xxx-objs += global1.o  mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_GLOBAL2) += global2.o +mv88e6xxx-objs += port.o diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 883fd9809dd2..4da379f28d5d 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -18,11 +18,15 @@  #include <linux/etherdevice.h>  #include <linux/ethtool.h>  #include <linux/if_bridge.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqdomain.h>  #include <linux/jiffies.h>  #include <linux/list.h>  #include <linux/mdio.h>  #include <linux/module.h>  #include <linux/of_device.h> +#include <linux/of_irq.h>  #include <linux/of_mdio.h>  #include <linux/netdevice.h>  #include <linux/gpio/consumer.h> @@ -33,6 +37,7 @@  #include "mv88e6xxx.h"  #include "global1.h"  #include "global2.h" +#include "port.h"  static void assert_reg_lock(struct mv88e6xxx_chip *chip)  { @@ -217,22 +222,6 @@ int mv88e6xxx_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val)  	return 0;  } -static int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg, -			       u16 *val) -{ -	int addr = chip->info->port_base_addr + port; - -	return mv88e6xxx_read(chip, addr, reg, val); -} - -static int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, -				u16 val) -{ -	int addr = chip->info->port_base_addr + port; - -	return mv88e6xxx_write(chip, addr, reg, val); -} -  static int mv88e6xxx_phy_read(struct mv88e6xxx_chip *chip, int phy,  			      int reg, u16 *val)  { @@ -323,6 +312,180 @@ static int mv88e6xxx_serdes_write(struct mv88e6xxx_chip *chip, int reg, u16 val)  					reg, val);  } +static void mv88e6xxx_g1_irq_mask(struct irq_data *d) +{ +	struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); +	unsigned int n = d->hwirq; + +	chip->g1_irq.masked |= (1 << n); +} + +static void mv88e6xxx_g1_irq_unmask(struct irq_data *d) +{ +	struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); +	unsigned int n = d->hwirq; + +	chip->g1_irq.masked &= ~(1 << n); +} + +static irqreturn_t mv88e6xxx_g1_irq_thread_fn(int irq, void *dev_id) +{ +	struct mv88e6xxx_chip *chip = dev_id; +	unsigned int nhandled = 0; +	unsigned int sub_irq; +	unsigned int n; +	u16 reg; +	int err; + +	mutex_lock(&chip->reg_lock); +	err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, ®); +	mutex_unlock(&chip->reg_lock); + +	if (err) +		goto out; + +	for (n = 0; n < chip->g1_irq.nirqs; ++n) { +		if (reg & (1 << n)) { +			sub_irq = irq_find_mapping(chip->g1_irq.domain, n); +			handle_nested_irq(sub_irq); +			++nhandled; +		} +	} +out: +	return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE); +} + +static void mv88e6xxx_g1_irq_bus_lock(struct irq_data *d) +{ +	struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); + +	mutex_lock(&chip->reg_lock); +} + +static void mv88e6xxx_g1_irq_bus_sync_unlock(struct irq_data *d) +{ +	struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); +	u16 mask = GENMASK(chip->g1_irq.nirqs, 0); +	u16 reg; +	int err; + +	err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, ®); +	if (err) +		goto out; + +	reg &= ~mask; +	reg |= (~chip->g1_irq.masked & mask); + +	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, reg); +	if (err) +		goto out; + +out: +	mutex_unlock(&chip->reg_lock); +} + +static struct irq_chip mv88e6xxx_g1_irq_chip = { +	.name			= "mv88e6xxx-g1", +	.irq_mask		= mv88e6xxx_g1_irq_mask, +	.irq_unmask		= mv88e6xxx_g1_irq_unmask, +	.irq_bus_lock		= mv88e6xxx_g1_irq_bus_lock, +	.irq_bus_sync_unlock	= mv88e6xxx_g1_irq_bus_sync_unlock, +}; + +static int mv88e6xxx_g1_irq_domain_map(struct irq_domain *d, +				       unsigned int irq, +				       irq_hw_number_t hwirq) +{ +	struct mv88e6xxx_chip *chip = d->host_data; + +	irq_set_chip_data(irq, d->host_data); +	irq_set_chip_and_handler(irq, &chip->g1_irq.chip, handle_level_irq); +	irq_set_noprobe(irq); + +	return 0; +} + +static const struct irq_domain_ops mv88e6xxx_g1_irq_domain_ops = { +	.map	= mv88e6xxx_g1_irq_domain_map, +	.xlate	= irq_domain_xlate_twocell, +}; + +static void mv88e6xxx_g1_irq_free(struct mv88e6xxx_chip *chip) +{ +	int irq, virq; +	u16 mask; + +	mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &mask); +	mask |= GENMASK(chip->g1_irq.nirqs, 0); +	mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, mask); + +	free_irq(chip->irq, chip); + +	for (irq = 0; irq < chip->g1_irq.nirqs; irq++) { +		virq = irq_find_mapping(chip->g1_irq.domain, irq); +		irq_dispose_mapping(virq); +	} + +	irq_domain_remove(chip->g1_irq.domain); +} + +static int mv88e6xxx_g1_irq_setup(struct mv88e6xxx_chip *chip) +{ +	int err, irq, virq; +	u16 reg, mask; + +	chip->g1_irq.nirqs = chip->info->g1_irqs; +	chip->g1_irq.domain = irq_domain_add_simple( +		NULL, chip->g1_irq.nirqs, 0, +		&mv88e6xxx_g1_irq_domain_ops, chip); +	if (!chip->g1_irq.domain) +		return -ENOMEM; + +	for (irq = 0; irq < chip->g1_irq.nirqs; irq++) +		irq_create_mapping(chip->g1_irq.domain, irq); + +	chip->g1_irq.chip = mv88e6xxx_g1_irq_chip; +	chip->g1_irq.masked = ~0; + +	err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &mask); +	if (err) +		goto out_mapping; + +	mask &= ~GENMASK(chip->g1_irq.nirqs, 0); + +	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, mask); +	if (err) +		goto out_disable; + +	/* Reading the interrupt status clears (most of) them */ +	err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, ®); +	if (err) +		goto out_disable; + +	err = request_threaded_irq(chip->irq, NULL, +				   mv88e6xxx_g1_irq_thread_fn, +				   IRQF_ONESHOT | IRQF_TRIGGER_FALLING, +				   dev_name(chip->dev), chip); +	if (err) +		goto out_disable; + +	return 0; + +out_disable: +	mask |= GENMASK(chip->g1_irq.nirqs, 0); +	mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, mask); + +out_mapping: +	for (irq = 0; irq < 16; irq++) { +		virq = irq_find_mapping(chip->g1_irq.domain, irq); +		irq_dispose_mapping(virq); +	} + +	irq_domain_remove(chip->g1_irq.domain); + +	return err; +} +  int mv88e6xxx_wait(struct mv88e6xxx_chip *chip, int addr, int reg, u16 mask)  {  	int i; @@ -364,56 +527,18 @@ int mv88e6xxx_update(struct mv88e6xxx_chip *chip, int addr, int reg, u16 update)  static int mv88e6xxx_ppu_disable(struct mv88e6xxx_chip *chip)  { -	u16 val; -	int i, err; - -	err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val); -	if (err) -		return err; - -	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, -				 val & ~GLOBAL_CONTROL_PPU_ENABLE); -	if (err) -		return err; - -	for (i = 0; i < 16; i++) { -		err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &val); -		if (err) -			return err; +	if (!chip->info->ops->ppu_disable) +		return 0; -		usleep_range(1000, 2000); -		if ((val & GLOBAL_STATUS_PPU_MASK) != GLOBAL_STATUS_PPU_POLLING) -			return 0; -	} - -	return -ETIMEDOUT; +	return chip->info->ops->ppu_disable(chip);  }  static int mv88e6xxx_ppu_enable(struct mv88e6xxx_chip *chip)  { -	u16 val; -	int i, err; - -	err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val); -	if (err) -		return err; - -	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, -				 val | GLOBAL_CONTROL_PPU_ENABLE); -	if (err) -		return err; - -	for (i = 0; i < 16; i++) { -		err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &val); -		if (err) -			return err; - -		usleep_range(1000, 2000); -		if ((val & GLOBAL_STATUS_PPU_MASK) == GLOBAL_STATUS_PPU_POLLING) -			return 0; -	} +	if (!chip->info->ops->ppu_enable) +		return 0; -	return -ETIMEDOUT; +	return chip->info->ops->ppu_enable(chip);  }  static void mv88e6xxx_ppu_reenable_work(struct work_struct *ugly) @@ -477,9 +602,8 @@ static void mv88e6xxx_ppu_state_init(struct mv88e6xxx_chip *chip)  {  	mutex_init(&chip->ppu_mutex);  	INIT_WORK(&chip->ppu_work, mv88e6xxx_ppu_reenable_work); -	init_timer(&chip->ppu_timer); -	chip->ppu_timer.data = (unsigned long)chip; -	chip->ppu_timer.function = mv88e6xxx_ppu_reenable_timer; +	setup_timer(&chip->ppu_timer, mv88e6xxx_ppu_reenable_timer, +		    (unsigned long)chip);  }  static void mv88e6xxx_ppu_state_destroy(struct mv88e6xxx_chip *chip) @@ -515,11 +639,6 @@ static int mv88e6xxx_phy_ppu_write(struct mv88e6xxx_chip *chip, int addr,  	return err;  } -static bool mv88e6xxx_6065_family(struct mv88e6xxx_chip *chip) -{ -	return chip->info->family == MV88E6XXX_FAMILY_6065; -} -  static bool mv88e6xxx_6095_family(struct mv88e6xxx_chip *chip)  {  	return chip->info->family == MV88E6XXX_FAMILY_6095; @@ -555,231 +674,152 @@ static bool mv88e6xxx_6352_family(struct mv88e6xxx_chip *chip)  	return chip->info->family == MV88E6XXX_FAMILY_6352;  } -/* We expect the switch to perform auto negotiation if there is a real - * phy. However, in the case of a fixed link phy, we force the port - * settings from the fixed link settings. - */ -static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port, -				  struct phy_device *phydev) +static int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, +				    int link, int speed, int duplex, +				    phy_interface_t mode)  { -	struct mv88e6xxx_chip *chip = ds->priv; -	u16 reg;  	int err; -	if (!phy_is_pseudo_fixed_link(phydev)) -		return; - -	mutex_lock(&chip->reg_lock); +	if (!chip->info->ops->port_set_link) +		return 0; -	err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, ®); +	/* Port's MAC control must not be changed unless the link is down */ +	err = chip->info->ops->port_set_link(chip, port, 0);  	if (err) -		goto out; - -	reg &= ~(PORT_PCS_CTRL_LINK_UP | -		 PORT_PCS_CTRL_FORCE_LINK | -		 PORT_PCS_CTRL_DUPLEX_FULL | -		 PORT_PCS_CTRL_FORCE_DUPLEX | -		 PORT_PCS_CTRL_UNFORCED); - -	reg |= PORT_PCS_CTRL_FORCE_LINK; -	if (phydev->link) -		reg |= PORT_PCS_CTRL_LINK_UP; - -	if (mv88e6xxx_6065_family(chip) && phydev->speed > SPEED_100) -		goto out; +		return err; -	switch (phydev->speed) { -	case SPEED_1000: -		reg |= PORT_PCS_CTRL_1000; -		break; -	case SPEED_100: -		reg |= PORT_PCS_CTRL_100; -		break; -	case SPEED_10: -		reg |= PORT_PCS_CTRL_10; -		break; -	default: -		pr_info("Unknown speed"); -		goto out; +	if (chip->info->ops->port_set_speed) { +		err = chip->info->ops->port_set_speed(chip, port, speed); +		if (err && err != -EOPNOTSUPP) +			goto restore_link;  	} -	reg |= PORT_PCS_CTRL_FORCE_DUPLEX; -	if (phydev->duplex == DUPLEX_FULL) -		reg |= PORT_PCS_CTRL_DUPLEX_FULL; - -	if ((mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip)) && -	    (port >= mv88e6xxx_num_ports(chip) - 2)) { -		if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) -			reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK; -		if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) -			reg |= PORT_PCS_CTRL_RGMII_DELAY_TXCLK; -		if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) -			reg |= (PORT_PCS_CTRL_RGMII_DELAY_RXCLK | -				PORT_PCS_CTRL_RGMII_DELAY_TXCLK); +	if (chip->info->ops->port_set_duplex) { +		err = chip->info->ops->port_set_duplex(chip, port, duplex); +		if (err && err != -EOPNOTSUPP) +			goto restore_link;  	} -	mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg); -out: -	mutex_unlock(&chip->reg_lock); -} - -static int _mv88e6xxx_stats_wait(struct mv88e6xxx_chip *chip) -{ -	u16 val; -	int i, err; - -	for (i = 0; i < 10; i++) { -		err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_OP, &val); -		if ((val & GLOBAL_STATS_OP_BUSY) == 0) -			return 0; +	if (chip->info->ops->port_set_rgmii_delay) { +		err = chip->info->ops->port_set_rgmii_delay(chip, port, mode); +		if (err && err != -EOPNOTSUPP) +			goto restore_link;  	} -	return -ETIMEDOUT; -} - -static int _mv88e6xxx_stats_snapshot(struct mv88e6xxx_chip *chip, int port) -{ -	int err; - -	if (mv88e6xxx_6320_family(chip) || mv88e6xxx_6352_family(chip)) -		port = (port + 1) << 5; - -	/* Snapshot the hardware statistics counters for this port. */ -	err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP, -				 GLOBAL_STATS_OP_CAPTURE_PORT | -				 GLOBAL_STATS_OP_HIST_RX_TX | port); -	if (err) -		return err; +	err = 0; +restore_link: +	if (chip->info->ops->port_set_link(chip, port, link)) +		netdev_err(chip->ds->ports[port].netdev, +			   "failed to restore MAC's link\n"); -	/* Wait for the snapshotting to complete. */ -	return _mv88e6xxx_stats_wait(chip); +	return err;  } -static void _mv88e6xxx_stats_read(struct mv88e6xxx_chip *chip, -				  int stat, u32 *val) +/* We expect the switch to perform auto negotiation if there is a real + * phy. However, in the case of a fixed link phy, we force the port + * settings from the fixed link settings. + */ +static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port, +				  struct phy_device *phydev)  { -	u32 value; -	u16 reg; +	struct mv88e6xxx_chip *chip = ds->priv;  	int err; -	*val = 0; - -	err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP, -				 GLOBAL_STATS_OP_READ_CAPTURED | -				 GLOBAL_STATS_OP_HIST_RX_TX | stat); -	if (err) -		return; - -	err = _mv88e6xxx_stats_wait(chip); -	if (err) +	if (!phy_is_pseudo_fixed_link(phydev))  		return; -	err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_COUNTER_32, ®); -	if (err) -		return; +	mutex_lock(&chip->reg_lock); +	err = mv88e6xxx_port_setup_mac(chip, port, phydev->link, phydev->speed, +				       phydev->duplex, phydev->interface); +	mutex_unlock(&chip->reg_lock); -	value = reg << 16; +	if (err && err != -EOPNOTSUPP) +		netdev_err(ds->ports[port].netdev, "failed to configure MAC\n"); +} -	err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_COUNTER_01, ®); -	if (err) -		return; +static int mv88e6xxx_stats_snapshot(struct mv88e6xxx_chip *chip, int port) +{ +	if (!chip->info->ops->stats_snapshot) +		return -EOPNOTSUPP; -	*val = value | reg; +	return chip->info->ops->stats_snapshot(chip, port);  }  static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = { -	{ "in_good_octets",	8, 0x00, BANK0, }, -	{ "in_bad_octets",	4, 0x02, BANK0, }, -	{ "in_unicast",		4, 0x04, BANK0, }, -	{ "in_broadcasts",	4, 0x06, BANK0, }, -	{ "in_multicasts",	4, 0x07, BANK0, }, -	{ "in_pause",		4, 0x16, BANK0, }, -	{ "in_undersize",	4, 0x18, BANK0, }, -	{ "in_fragments",	4, 0x19, BANK0, }, -	{ "in_oversize",	4, 0x1a, BANK0, }, -	{ "in_jabber",		4, 0x1b, BANK0, }, -	{ "in_rx_error",	4, 0x1c, BANK0, }, -	{ "in_fcs_error",	4, 0x1d, BANK0, }, -	{ "out_octets",		8, 0x0e, BANK0, }, -	{ "out_unicast",	4, 0x10, BANK0, }, -	{ "out_broadcasts",	4, 0x13, BANK0, }, -	{ "out_multicasts",	4, 0x12, BANK0, }, -	{ "out_pause",		4, 0x15, BANK0, }, -	{ "excessive",		4, 0x11, BANK0, }, -	{ "collisions",		4, 0x1e, BANK0, }, -	{ "deferred",		4, 0x05, BANK0, }, -	{ "single",		4, 0x14, BANK0, }, -	{ "multiple",		4, 0x17, BANK0, }, -	{ "out_fcs_error",	4, 0x03, BANK0, }, -	{ "late",		4, 0x1f, BANK0, }, -	{ "hist_64bytes",	4, 0x08, BANK0, }, -	{ "hist_65_127bytes",	4, 0x09, BANK0, }, -	{ "hist_128_255bytes",	4, 0x0a, BANK0, }, -	{ "hist_256_511bytes",	4, 0x0b, BANK0, }, -	{ "hist_512_1023bytes", 4, 0x0c, BANK0, }, -	{ "hist_1024_max_bytes", 4, 0x0d, BANK0, }, -	{ "sw_in_discards",	4, 0x10, PORT, }, -	{ "sw_in_filtered",	2, 0x12, PORT, }, -	{ "sw_out_filtered",	2, 0x13, PORT, }, -	{ "in_discards",	4, 0x00 | GLOBAL_STATS_OP_BANK_1, BANK1, }, -	{ "in_filtered",	4, 0x01 | GLOBAL_STATS_OP_BANK_1, BANK1, }, -	{ "in_accepted",	4, 0x02 | GLOBAL_STATS_OP_BANK_1, BANK1, }, -	{ "in_bad_accepted",	4, 0x03 | GLOBAL_STATS_OP_BANK_1, BANK1, }, -	{ "in_good_avb_class_a", 4, 0x04 | GLOBAL_STATS_OP_BANK_1, BANK1, }, -	{ "in_good_avb_class_b", 4, 0x05 | GLOBAL_STATS_OP_BANK_1, BANK1, }, -	{ "in_bad_avb_class_a", 4, 0x06 | GLOBAL_STATS_OP_BANK_1, BANK1, }, -	{ "in_bad_avb_class_b", 4, 0x07 | GLOBAL_STATS_OP_BANK_1, BANK1, }, -	{ "tcam_counter_0",	4, 0x08 | GLOBAL_STATS_OP_BANK_1, BANK1, }, -	{ "tcam_counter_1",	4, 0x09 | GLOBAL_STATS_OP_BANK_1, BANK1, }, -	{ "tcam_counter_2",	4, 0x0a | GLOBAL_STATS_OP_BANK_1, BANK1, }, -	{ "tcam_counter_3",	4, 0x0b | GLOBAL_STATS_OP_BANK_1, BANK1, }, -	{ "in_da_unknown",	4, 0x0e | GLOBAL_STATS_OP_BANK_1, BANK1, }, -	{ "in_management",	4, 0x0f | GLOBAL_STATS_OP_BANK_1, BANK1, }, -	{ "out_queue_0",	4, 0x10 | GLOBAL_STATS_OP_BANK_1, BANK1, }, -	{ "out_queue_1",	4, 0x11 | GLOBAL_STATS_OP_BANK_1, BANK1, }, -	{ "out_queue_2",	4, 0x12 | GLOBAL_STATS_OP_BANK_1, BANK1, }, -	{ "out_queue_3",	4, 0x13 | GLOBAL_STATS_OP_BANK_1, BANK1, }, -	{ "out_queue_4",	4, 0x14 | GLOBAL_STATS_OP_BANK_1, BANK1, }, -	{ "out_queue_5",	4, 0x15 | GLOBAL_STATS_OP_BANK_1, BANK1, }, -	{ "out_queue_6",	4, 0x16 | GLOBAL_STATS_OP_BANK_1, BANK1, }, -	{ "out_queue_7",	4, 0x17 | GLOBAL_STATS_OP_BANK_1, BANK1, }, -	{ "out_cut_through",	4, 0x18 | GLOBAL_STATS_OP_BANK_1, BANK1, }, -	{ "out_octets_a",	4, 0x1a | GLOBAL_STATS_OP_BANK_1, BANK1, }, -	{ "out_octets_b",	4, 0x1b | GLOBAL_STATS_OP_BANK_1, BANK1, }, -	{ "out_management",	4, 0x1f | GLOBAL_STATS_OP_BANK_1, BANK1, }, +	{ "in_good_octets",		8, 0x00, STATS_TYPE_BANK0, }, +	{ "in_bad_octets",		4, 0x02, STATS_TYPE_BANK0, }, +	{ "in_unicast",			4, 0x04, STATS_TYPE_BANK0, }, +	{ "in_broadcasts",		4, 0x06, STATS_TYPE_BANK0, }, +	{ "in_multicasts",		4, 0x07, STATS_TYPE_BANK0, }, +	{ "in_pause",			4, 0x16, STATS_TYPE_BANK0, }, +	{ "in_undersize",		4, 0x18, STATS_TYPE_BANK0, }, +	{ "in_fragments",		4, 0x19, STATS_TYPE_BANK0, }, +	{ "in_oversize",		4, 0x1a, STATS_TYPE_BANK0, }, +	{ "in_jabber",			4, 0x1b, STATS_TYPE_BANK0, }, +	{ "in_rx_error",		4, 0x1c, STATS_TYPE_BANK0, }, +	{ "in_fcs_error",		4, 0x1d, STATS_TYPE_BANK0, }, +	{ "out_octets",			8, 0x0e, STATS_TYPE_BANK0, }, +	{ "out_unicast",		4, 0x10, STATS_TYPE_BANK0, }, +	{ "out_broadcasts",		4, 0x13, STATS_TYPE_BANK0, }, +	{ "out_multicasts",		4, 0x12, STATS_TYPE_BANK0, }, +	{ "out_pause",			4, 0x15, STATS_TYPE_BANK0, }, +	{ "excessive",			4, 0x11, STATS_TYPE_BANK0, }, +	{ "collisions",			4, 0x1e, STATS_TYPE_BANK0, }, +	{ "deferred",			4, 0x05, STATS_TYPE_BANK0, }, +	{ "single",			4, 0x14, STATS_TYPE_BANK0, }, +	{ "multiple",			4, 0x17, STATS_TYPE_BANK0, }, +	{ "out_fcs_error",		4, 0x03, STATS_TYPE_BANK0, }, +	{ "late",			4, 0x1f, STATS_TYPE_BANK0, }, +	{ "hist_64bytes",		4, 0x08, STATS_TYPE_BANK0, }, +	{ "hist_65_127bytes",		4, 0x09, STATS_TYPE_BANK0, }, +	{ "hist_128_255bytes",		4, 0x0a, STATS_TYPE_BANK0, }, +	{ "hist_256_511bytes",		4, 0x0b, STATS_TYPE_BANK0, }, +	{ "hist_512_1023bytes",		4, 0x0c, STATS_TYPE_BANK0, }, +	{ "hist_1024_max_bytes",	4, 0x0d, STATS_TYPE_BANK0, }, +	{ "sw_in_discards",		4, 0x10, STATS_TYPE_PORT, }, +	{ "sw_in_filtered",		2, 0x12, STATS_TYPE_PORT, }, +	{ "sw_out_filtered",		2, 0x13, STATS_TYPE_PORT, }, +	{ "in_discards",		4, 0x00, STATS_TYPE_BANK1, }, +	{ "in_filtered",		4, 0x01, STATS_TYPE_BANK1, }, +	{ "in_accepted",		4, 0x02, STATS_TYPE_BANK1, }, +	{ "in_bad_accepted",		4, 0x03, STATS_TYPE_BANK1, }, +	{ "in_good_avb_class_a",	4, 0x04, STATS_TYPE_BANK1, }, +	{ "in_good_avb_class_b",	4, 0x05, STATS_TYPE_BANK1, }, +	{ "in_bad_avb_class_a",		4, 0x06, STATS_TYPE_BANK1, }, +	{ "in_bad_avb_class_b",		4, 0x07, STATS_TYPE_BANK1, }, +	{ "tcam_counter_0",		4, 0x08, STATS_TYPE_BANK1, }, +	{ "tcam_counter_1",		4, 0x09, STATS_TYPE_BANK1, }, +	{ "tcam_counter_2",		4, 0x0a, STATS_TYPE_BANK1, }, +	{ "tcam_counter_3",		4, 0x0b, STATS_TYPE_BANK1, }, +	{ "in_da_unknown",		4, 0x0e, STATS_TYPE_BANK1, }, +	{ "in_management",		4, 0x0f, STATS_TYPE_BANK1, }, +	{ "out_queue_0",		4, 0x10, STATS_TYPE_BANK1, }, +	{ "out_queue_1",		4, 0x11, STATS_TYPE_BANK1, }, +	{ "out_queue_2",		4, 0x12, STATS_TYPE_BANK1, }, +	{ "out_queue_3",		4, 0x13, STATS_TYPE_BANK1, }, +	{ "out_queue_4",		4, 0x14, STATS_TYPE_BANK1, }, +	{ "out_queue_5",		4, 0x15, STATS_TYPE_BANK1, }, +	{ "out_queue_6",		4, 0x16, STATS_TYPE_BANK1, }, +	{ "out_queue_7",		4, 0x17, STATS_TYPE_BANK1, }, +	{ "out_cut_through",		4, 0x18, STATS_TYPE_BANK1, }, +	{ "out_octets_a",		4, 0x1a, STATS_TYPE_BANK1, }, +	{ "out_octets_b",		4, 0x1b, STATS_TYPE_BANK1, }, +	{ "out_management",		4, 0x1f, STATS_TYPE_BANK1, },  }; -static bool mv88e6xxx_has_stat(struct mv88e6xxx_chip *chip, -			       struct mv88e6xxx_hw_stat *stat) -{ -	switch (stat->type) { -	case BANK0: -		return true; -	case BANK1: -		return mv88e6xxx_6320_family(chip); -	case PORT: -		return mv88e6xxx_6095_family(chip) || -			mv88e6xxx_6185_family(chip) || -			mv88e6xxx_6097_family(chip) || -			mv88e6xxx_6165_family(chip) || -			mv88e6xxx_6351_family(chip) || -			mv88e6xxx_6352_family(chip); -	} -	return false; -} -  static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_chip *chip,  					    struct mv88e6xxx_hw_stat *s, -					    int port) +					    int port, u16 bank1_select, +					    u16 histogram)  {  	u32 low;  	u32 high = 0; +	u16 reg = 0;  	int err; -	u16 reg;  	u64 value;  	switch (s->type) { -	case PORT: +	case STATS_TYPE_PORT:  		err = mv88e6xxx_port_read(chip, port, s->reg, ®);  		if (err)  			return UINT64_MAX; @@ -792,26 +832,28 @@ static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_chip *chip,  			high = reg;  		}  		break; -	case BANK0: -	case BANK1: -		_mv88e6xxx_stats_read(chip, s->reg, &low); +	case STATS_TYPE_BANK1: +		reg = bank1_select; +		/* fall through */ +	case STATS_TYPE_BANK0: +		reg |= s->reg | histogram; +		mv88e6xxx_g1_stats_read(chip, reg, &low);  		if (s->sizeof_stat == 8) -			_mv88e6xxx_stats_read(chip, s->reg + 1, &high); +			mv88e6xxx_g1_stats_read(chip, reg + 1, &high);  	}  	value = (((u64)high) << 16) | low;  	return value;  } -static void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, -				  uint8_t *data) +static void mv88e6xxx_stats_get_strings(struct mv88e6xxx_chip *chip, +					uint8_t *data, int types)  { -	struct mv88e6xxx_chip *chip = ds->priv;  	struct mv88e6xxx_hw_stat *stat;  	int i, j;  	for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {  		stat = &mv88e6xxx_hw_stats[i]; -		if (mv88e6xxx_has_stat(chip, stat)) { +		if (stat->type & types) {  			memcpy(data + j * ETH_GSTRING_LEN, stat->string,  			       ETH_GSTRING_LEN);  			j++; @@ -819,46 +861,142 @@ static void mv88e6xxx_get_strings(struct dsa_switch *ds, int port,  	}  } -static int mv88e6xxx_get_sset_count(struct dsa_switch *ds) +static void mv88e6095_stats_get_strings(struct mv88e6xxx_chip *chip, +					uint8_t *data) +{ +	mv88e6xxx_stats_get_strings(chip, data, +				    STATS_TYPE_BANK0 | STATS_TYPE_PORT); +} + +static void mv88e6320_stats_get_strings(struct mv88e6xxx_chip *chip, +					uint8_t *data) +{ +	mv88e6xxx_stats_get_strings(chip, data, +				    STATS_TYPE_BANK0 | STATS_TYPE_BANK1); +} + +static void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, +				  uint8_t *data)  {  	struct mv88e6xxx_chip *chip = ds->priv; + +	if (chip->info->ops->stats_get_strings) +		chip->info->ops->stats_get_strings(chip, data); +} + +static int mv88e6xxx_stats_get_sset_count(struct mv88e6xxx_chip *chip, +					  int types) +{  	struct mv88e6xxx_hw_stat *stat;  	int i, j;  	for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {  		stat = &mv88e6xxx_hw_stats[i]; -		if (mv88e6xxx_has_stat(chip, stat)) +		if (stat->type & types)  			j++;  	}  	return j;  } +static int mv88e6095_stats_get_sset_count(struct mv88e6xxx_chip *chip) +{ +	return mv88e6xxx_stats_get_sset_count(chip, STATS_TYPE_BANK0 | +					      STATS_TYPE_PORT); +} + +static int mv88e6320_stats_get_sset_count(struct mv88e6xxx_chip *chip) +{ +	return mv88e6xxx_stats_get_sset_count(chip, STATS_TYPE_BANK0 | +					      STATS_TYPE_BANK1); +} + +static int mv88e6xxx_get_sset_count(struct dsa_switch *ds) +{ +	struct mv88e6xxx_chip *chip = ds->priv; + +	if (chip->info->ops->stats_get_sset_count) +		return chip->info->ops->stats_get_sset_count(chip); + +	return 0; +} + +static void mv88e6xxx_stats_get_stats(struct mv88e6xxx_chip *chip, int port, +				      uint64_t *data, int types, +				      u16 bank1_select, u16 histogram) +{ +	struct mv88e6xxx_hw_stat *stat; +	int i, j; + +	for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) { +		stat = &mv88e6xxx_hw_stats[i]; +		if (stat->type & types) { +			data[j] = _mv88e6xxx_get_ethtool_stat(chip, stat, port, +							      bank1_select, +							      histogram); +			j++; +		} +	} +} + +static void mv88e6095_stats_get_stats(struct mv88e6xxx_chip *chip, int port, +				      uint64_t *data) +{ +	return mv88e6xxx_stats_get_stats(chip, port, data, +					 STATS_TYPE_BANK0 | STATS_TYPE_PORT, +					 0, GLOBAL_STATS_OP_HIST_RX_TX); +} + +static void mv88e6320_stats_get_stats(struct mv88e6xxx_chip *chip, int port, +				      uint64_t *data) +{ +	return mv88e6xxx_stats_get_stats(chip, port, data, +					 STATS_TYPE_BANK0 | STATS_TYPE_BANK1, +					 GLOBAL_STATS_OP_BANK_1_BIT_9, +					 GLOBAL_STATS_OP_HIST_RX_TX); +} + +static void mv88e6390_stats_get_stats(struct mv88e6xxx_chip *chip, int port, +				      uint64_t *data) +{ +	return mv88e6xxx_stats_get_stats(chip, port, data, +					 STATS_TYPE_BANK0 | STATS_TYPE_BANK1, +					 GLOBAL_STATS_OP_BANK_1_BIT_10, 0); +} + +static void mv88e6xxx_get_stats(struct mv88e6xxx_chip *chip, int port, +				uint64_t *data) +{ +	if (chip->info->ops->stats_get_stats) +		chip->info->ops->stats_get_stats(chip, port, data); +} +  static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,  					uint64_t *data)  {  	struct mv88e6xxx_chip *chip = ds->priv; -	struct mv88e6xxx_hw_stat *stat;  	int ret; -	int i, j;  	mutex_lock(&chip->reg_lock); -	ret = _mv88e6xxx_stats_snapshot(chip, port); +	ret = mv88e6xxx_stats_snapshot(chip, port);  	if (ret < 0) {  		mutex_unlock(&chip->reg_lock);  		return;  	} -	for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) { -		stat = &mv88e6xxx_hw_stats[i]; -		if (mv88e6xxx_has_stat(chip, stat)) { -			data[j] = _mv88e6xxx_get_ethtool_stat(chip, stat, port); -			j++; -		} -	} + +	mv88e6xxx_get_stats(chip, port, data);  	mutex_unlock(&chip->reg_lock);  } +static int mv88e6xxx_stats_set_histogram(struct mv88e6xxx_chip *chip) +{ +	if (chip->info->ops->stats_set_histogram) +		return chip->info->ops->stats_set_histogram(chip); + +	return 0; +} +  static int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)  {  	return 32 * sizeof(u16); @@ -1069,54 +1207,16 @@ static int _mv88e6xxx_atu_remove(struct mv88e6xxx_chip *chip, u16 fid,  	return _mv88e6xxx_atu_move(chip, fid, port, 0x0f, static_too);  } -static const char * const mv88e6xxx_port_state_names[] = { -	[PORT_CONTROL_STATE_DISABLED] = "Disabled", -	[PORT_CONTROL_STATE_BLOCKING] = "Blocking/Listening", -	[PORT_CONTROL_STATE_LEARNING] = "Learning", -	[PORT_CONTROL_STATE_FORWARDING] = "Forwarding", -}; - -static int _mv88e6xxx_port_state(struct mv88e6xxx_chip *chip, int port, -				 u8 state) -{ -	struct dsa_switch *ds = chip->ds; -	u16 reg; -	int err; -	u8 oldstate; - -	err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, ®); -	if (err) -		return err; - -	oldstate = reg & PORT_CONTROL_STATE_MASK; - -	reg &= ~PORT_CONTROL_STATE_MASK; -	reg |= state; - -	err = mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg); -	if (err) -		return err; - -	netdev_dbg(ds->ports[port].netdev, "PortState %s (was %s)\n", -		   mv88e6xxx_port_state_names[state], -		   mv88e6xxx_port_state_names[oldstate]); - -	return 0; -} -  static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_chip *chip, int port)  {  	struct net_device *bridge = chip->ports[port].bridge_dev; -	const u16 mask = (1 << mv88e6xxx_num_ports(chip)) - 1;  	struct dsa_switch *ds = chip->ds;  	u16 output_ports = 0; -	u16 reg; -	int err;  	int i;  	/* allow CPU port or DSA link(s) to send frames to every port */  	if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) { -		output_ports = mask; +		output_ports = ~0;  	} else {  		for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {  			/* allow sending frames to every group member */ @@ -1132,14 +1232,7 @@ static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_chip *chip, int port)  	/* prevent frames from going back out of the port they came in on */  	output_ports &= ~BIT(port); -	err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, ®); -	if (err) -		return err; - -	reg &= ~mask; -	reg |= output_ports & mask; - -	return mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg); +	return mv88e6xxx_port_set_vlan_map(chip, port, output_ports);  }  static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port, @@ -1167,13 +1260,11 @@ static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port,  	}  	mutex_lock(&chip->reg_lock); -	err = _mv88e6xxx_port_state(chip, port, stp_state); +	err = mv88e6xxx_port_set_state(chip, port, stp_state);  	mutex_unlock(&chip->reg_lock);  	if (err) -		netdev_err(ds->ports[port].netdev, -			   "failed to update state to %s\n", -			   mv88e6xxx_port_state_names[stp_state]); +		netdev_err(ds->ports[port].netdev, "failed to update state\n");  }  static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port) @@ -1189,49 +1280,6 @@ static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port)  		netdev_err(ds->ports[port].netdev, "failed to flush ATU\n");  } -static int _mv88e6xxx_port_pvid(struct mv88e6xxx_chip *chip, int port, -				u16 *new, u16 *old) -{ -	struct dsa_switch *ds = chip->ds; -	u16 pvid, reg; -	int err; - -	err = mv88e6xxx_port_read(chip, port, PORT_DEFAULT_VLAN, ®); -	if (err) -		return err; - -	pvid = reg & PORT_DEFAULT_VLAN_MASK; - -	if (new) { -		reg &= ~PORT_DEFAULT_VLAN_MASK; -		reg |= *new & PORT_DEFAULT_VLAN_MASK; - -		err = mv88e6xxx_port_write(chip, port, PORT_DEFAULT_VLAN, reg); -		if (err) -			return err; - -		netdev_dbg(ds->ports[port].netdev, -			   "DefaultVID %d (was %d)\n", *new, pvid); -	} - -	if (old) -		*old = pvid; - -	return 0; -} - -static int _mv88e6xxx_port_pvid_get(struct mv88e6xxx_chip *chip, -				    int port, u16 *pvid) -{ -	return _mv88e6xxx_port_pvid(chip, port, NULL, pvid); -} - -static int _mv88e6xxx_port_pvid_set(struct mv88e6xxx_chip *chip, -				    int port, u16 pvid) -{ -	return _mv88e6xxx_port_pvid(chip, port, &pvid, NULL); -} -  static int _mv88e6xxx_vtu_wait(struct mv88e6xxx_chip *chip)  {  	return mv88e6xxx_g1_wait(chip, GLOBAL_VTU_OP, GLOBAL_VTU_OP_BUSY); @@ -1411,7 +1459,7 @@ static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port,  	mutex_lock(&chip->reg_lock); -	err = _mv88e6xxx_port_pvid_get(chip, port, &pvid); +	err = mv88e6xxx_port_get_pvid(chip, port, &pvid);  	if (err)  		goto unlock; @@ -1575,75 +1623,6 @@ loadpurge:  	return _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_STU_LOAD_PURGE);  } -static int _mv88e6xxx_port_fid(struct mv88e6xxx_chip *chip, int port, -			       u16 *new, u16 *old) -{ -	struct dsa_switch *ds = chip->ds; -	u16 upper_mask; -	u16 fid; -	u16 reg; -	int err; - -	if (mv88e6xxx_num_databases(chip) == 4096) -		upper_mask = 0xff; -	else if (mv88e6xxx_num_databases(chip) == 256) -		upper_mask = 0xf; -	else -		return -EOPNOTSUPP; - -	/* Port's default FID bits 3:0 are located in reg 0x06, offset 12 */ -	err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, ®); -	if (err) -		return err; - -	fid = (reg & PORT_BASE_VLAN_FID_3_0_MASK) >> 12; - -	if (new) { -		reg &= ~PORT_BASE_VLAN_FID_3_0_MASK; -		reg |= (*new << 12) & PORT_BASE_VLAN_FID_3_0_MASK; - -		err = mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg); -		if (err) -			return err; -	} - -	/* Port's default FID bits 11:4 are located in reg 0x05, offset 0 */ -	err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, ®); -	if (err) -		return err; - -	fid |= (reg & upper_mask) << 4; - -	if (new) { -		reg &= ~upper_mask; -		reg |= (*new >> 4) & upper_mask; - -		err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_1, reg); -		if (err) -			return err; - -		netdev_dbg(ds->ports[port].netdev, -			   "FID %d (was %d)\n", *new, fid); -	} - -	if (old) -		*old = fid; - -	return 0; -} - -static int _mv88e6xxx_port_fid_get(struct mv88e6xxx_chip *chip, -				   int port, u16 *fid) -{ -	return _mv88e6xxx_port_fid(chip, port, NULL, fid); -} - -static int _mv88e6xxx_port_fid_set(struct mv88e6xxx_chip *chip, -				   int port, u16 fid) -{ -	return _mv88e6xxx_port_fid(chip, port, &fid, NULL); -} -  static int _mv88e6xxx_fid_new(struct mv88e6xxx_chip *chip, u16 *fid)  {  	DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID); @@ -1654,7 +1633,7 @@ static int _mv88e6xxx_fid_new(struct mv88e6xxx_chip *chip, u16 *fid)  	/* Set every FID bit used by the (un)bridged ports */  	for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { -		err = _mv88e6xxx_port_fid_get(chip, i, fid); +		err = mv88e6xxx_port_get_fid(chip, i, fid);  		if (err)  			return err; @@ -1819,48 +1798,19 @@ unlock:  	return err;  } -static const char * const mv88e6xxx_port_8021q_mode_names[] = { -	[PORT_CONTROL_2_8021Q_DISABLED] = "Disabled", -	[PORT_CONTROL_2_8021Q_FALLBACK] = "Fallback", -	[PORT_CONTROL_2_8021Q_CHECK] = "Check", -	[PORT_CONTROL_2_8021Q_SECURE] = "Secure", -}; -  static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,  					 bool vlan_filtering)  {  	struct mv88e6xxx_chip *chip = ds->priv; -	u16 old, new = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE : +	u16 mode = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE :  		PORT_CONTROL_2_8021Q_DISABLED; -	u16 reg;  	int err;  	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))  		return -EOPNOTSUPP;  	mutex_lock(&chip->reg_lock); - -	err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, ®); -	if (err) -		goto unlock; - -	old = reg & PORT_CONTROL_2_8021Q_MASK; - -	if (new != old) { -		reg &= ~PORT_CONTROL_2_8021Q_MASK; -		reg |= new & PORT_CONTROL_2_8021Q_MASK; - -		err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg); -		if (err) -			goto unlock; - -		netdev_dbg(ds->ports[port].netdev, "802.1Q Mode %s (was %s)\n", -			   mv88e6xxx_port_8021q_mode_names[new], -			   mv88e6xxx_port_8021q_mode_names[old]); -	} - -	err = 0; -unlock: +	err = mv88e6xxx_port_set_8021q_mode(chip, port, mode);  	mutex_unlock(&chip->reg_lock);  	return err; @@ -1928,7 +1878,7 @@ static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,  				   "failed to add VLAN %d%c\n",  				   vid, untagged ? 'u' : 't'); -	if (pvid && _mv88e6xxx_port_pvid_set(chip, port, vlan->vid_end)) +	if (pvid && mv88e6xxx_port_set_pvid(chip, port, vlan->vid_end))  		netdev_err(ds->ports[port].netdev, "failed to set PVID %d\n",  			   vlan->vid_end); @@ -1983,7 +1933,7 @@ static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,  	mutex_lock(&chip->reg_lock); -	err = _mv88e6xxx_port_pvid_get(chip, port, &pvid); +	err = mv88e6xxx_port_get_pvid(chip, port, &pvid);  	if (err)  		goto unlock; @@ -1993,7 +1943,7 @@ static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,  			goto unlock;  		if (vid == pvid) { -			err = _mv88e6xxx_port_pvid_set(chip, port, 0); +			err = mv88e6xxx_port_set_pvid(chip, port, 0);  			if (err)  				goto unlock;  		} @@ -2104,7 +2054,7 @@ static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port,  	/* Null VLAN ID corresponds to the port private database */  	if (vid == 0) -		err = _mv88e6xxx_port_fid_get(chip, port, &vlan.fid); +		err = mv88e6xxx_port_get_fid(chip, port, &vlan.fid);  	else  		err = _mv88e6xxx_vtu_get(chip, vid, &vlan, false);  	if (err) @@ -2280,7 +2230,7 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port,  	int err;  	/* Dump port's default Filtering Information Database (VLAN ID 0) */ -	err = _mv88e6xxx_port_fid_get(chip, port, &fid); +	err = mv88e6xxx_port_get_fid(chip, port, &fid);  	if (err)  		return err; @@ -2368,67 +2318,58 @@ static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port)  	mutex_unlock(&chip->reg_lock);  } -static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip) +static int mv88e6xxx_software_reset(struct mv88e6xxx_chip *chip)  { -	bool ppu_active = mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU_ACTIVE); -	u16 is_reset = (ppu_active ? 0x8800 : 0xc800); -	struct gpio_desc *gpiod = chip->reset; -	unsigned long timeout; -	u16 reg; -	int err; -	int i; +	if (chip->info->ops->reset) +		return chip->info->ops->reset(chip); -	/* Set all ports to the disabled state. */ -	for (i = 0; i < mv88e6xxx_num_ports(chip); i++) { -		err = mv88e6xxx_port_read(chip, i, PORT_CONTROL, ®); -		if (err) -			return err; - -		err = mv88e6xxx_port_write(chip, i, PORT_CONTROL, -					   reg & 0xfffc); -		if (err) -			return err; -	} +	return 0; +} -	/* Wait for transmit queues to drain. */ -	usleep_range(2000, 4000); +static void mv88e6xxx_hardware_reset(struct mv88e6xxx_chip *chip) +{ +	struct gpio_desc *gpiod = chip->reset; -	/* If there is a gpio connected to the reset pin, toggle it */ +	/* If there is a GPIO connected to the reset pin, toggle it */  	if (gpiod) {  		gpiod_set_value_cansleep(gpiod, 1);  		usleep_range(10000, 20000);  		gpiod_set_value_cansleep(gpiod, 0);  		usleep_range(10000, 20000);  	} +} -	/* Reset the switch. Keep the PPU active if requested. The PPU -	 * needs to be active to support indirect phy register access -	 * through global registers 0x18 and 0x19. -	 */ -	if (ppu_active) -		err = mv88e6xxx_g1_write(chip, 0x04, 0xc000); -	else -		err = mv88e6xxx_g1_write(chip, 0x04, 0xc400); -	if (err) -		return err; +static int mv88e6xxx_disable_ports(struct mv88e6xxx_chip *chip) +{ +	int i, err; -	/* Wait up to one second for reset to complete. */ -	timeout = jiffies + 1 * HZ; -	while (time_before(jiffies, timeout)) { -		err = mv88e6xxx_g1_read(chip, 0x00, ®); +	/* Set all ports to the Disabled state */ +	for (i = 0; i < mv88e6xxx_num_ports(chip); i++) { +		err = mv88e6xxx_port_set_state(chip, i, +					       PORT_CONTROL_STATE_DISABLED);  		if (err)  			return err; - -		if ((reg & is_reset) == is_reset) -			break; -		usleep_range(1000, 2000);  	} -	if (time_after(jiffies, timeout)) -		err = -ETIMEDOUT; -	else -		err = 0; -	return err; +	/* Wait for transmit queues to drain, +	 * i.e. 2ms for a maximum frame to be transmitted at 10 Mbps. +	 */ +	usleep_range(2000, 4000); + +	return 0; +} + +static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip) +{ +	int err; + +	err = mv88e6xxx_disable_ports(chip); +	if (err) +		return err; + +	mv88e6xxx_hardware_reset(chip); + +	return mv88e6xxx_software_reset(chip);  }  static int mv88e6xxx_serdes_power_on(struct mv88e6xxx_chip *chip) @@ -2449,42 +2390,93 @@ static int mv88e6xxx_serdes_power_on(struct mv88e6xxx_chip *chip)  	return err;  } -static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) +static int mv88e6xxx_setup_port_dsa(struct mv88e6xxx_chip *chip, int port, +				    int upstream_port)  { -	struct dsa_switch *ds = chip->ds;  	int err; -	u16 reg; -	if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) || -	    mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) || -	    mv88e6xxx_6185_family(chip) || mv88e6xxx_6095_family(chip) || -	    mv88e6xxx_6065_family(chip) || mv88e6xxx_6320_family(chip)) { -		/* MAC Forcing register: don't force link, speed, -		 * duplex or flow control state to any particular -		 * values on physical ports, but force the CPU port -		 * and all DSA ports to their maximum bandwidth and -		 * full duplex. -		 */ -		err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, ®); -		if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) { -			reg &= ~PORT_PCS_CTRL_UNFORCED; -			reg |= PORT_PCS_CTRL_FORCE_LINK | -				PORT_PCS_CTRL_LINK_UP | -				PORT_PCS_CTRL_DUPLEX_FULL | -				PORT_PCS_CTRL_FORCE_DUPLEX; -			if (mv88e6xxx_6065_family(chip)) -				reg |= PORT_PCS_CTRL_100; -			else -				reg |= PORT_PCS_CTRL_1000; -		} else { -			reg |= PORT_PCS_CTRL_UNFORCED; -		} +	err = chip->info->ops->port_set_frame_mode( +		chip, port, MV88E6XXX_FRAME_MODE_DSA); +	if (err) +		return err; + +	return chip->info->ops->port_set_egress_unknowns( +		chip, port, port == upstream_port); +} + +static int mv88e6xxx_setup_port_cpu(struct mv88e6xxx_chip *chip, int port) +{ +	int err; + +	switch (chip->info->tag_protocol) { +	case DSA_TAG_PROTO_EDSA: +		err = chip->info->ops->port_set_frame_mode( +			chip, port, MV88E6XXX_FRAME_MODE_ETHERTYPE); +		if (err) +			return err; -		err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg); +		err = mv88e6xxx_port_set_egress_mode( +			chip, port, PORT_CONTROL_EGRESS_ADD_TAG);  		if (err)  			return err; + +		if (chip->info->ops->port_set_ether_type) +			err = chip->info->ops->port_set_ether_type( +				chip, port, ETH_P_EDSA); +		break; + +	case DSA_TAG_PROTO_DSA: +		err = chip->info->ops->port_set_frame_mode( +			chip, port, MV88E6XXX_FRAME_MODE_DSA); +		if (err) +			return err; + +		err = mv88e6xxx_port_set_egress_mode( +			chip, port, PORT_CONTROL_EGRESS_UNMODIFIED); +		break; +	default: +		err = -EINVAL;  	} +	if (err) +		return err; + +	return chip->info->ops->port_set_egress_unknowns(chip, port, true); +} + +static int mv88e6xxx_setup_port_normal(struct mv88e6xxx_chip *chip, int port) +{ +	int err; + +	err = chip->info->ops->port_set_frame_mode( +		chip, port, MV88E6XXX_FRAME_MODE_NORMAL); +	if (err) +		return err; + +	return chip->info->ops->port_set_egress_unknowns(chip, port, false); +} + +static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) +{ +	struct dsa_switch *ds = chip->ds; +	int err; +	u16 reg; + +	/* MAC Forcing register: don't force link, speed, duplex or flow control +	 * state to any particular values on physical ports, but force the CPU +	 * port and all DSA ports to their maximum bandwidth and full duplex. +	 */ +	if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) +		err = mv88e6xxx_port_setup_mac(chip, port, LINK_FORCED_UP, +					       SPEED_MAX, DUPLEX_FULL, +					       PHY_INTERFACE_MODE_NA); +	else +		err = mv88e6xxx_port_setup_mac(chip, port, LINK_UNFORCED, +					       SPEED_UNFORCED, DUPLEX_UNFORCED, +					       PHY_INTERFACE_MODE_NA); +	if (err) +		return err; +  	/* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock,  	 * disable Header mode, enable IGMP/MLD snooping, disable VLAN  	 * tunneling, determine priority by looking at 802.1p and IP @@ -2499,44 +2491,23 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)  	 * If this is the upstream port for this switch, enable  	 * forwarding of unknown unicasts and multicasts.  	 */ -	reg = 0; -	if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) || -	    mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) || -	    mv88e6xxx_6095_family(chip) || mv88e6xxx_6065_family(chip) || -	    mv88e6xxx_6185_family(chip) || mv88e6xxx_6320_family(chip)) -		reg = PORT_CONTROL_IGMP_MLD_SNOOP | +	reg = PORT_CONTROL_IGMP_MLD_SNOOP |  		PORT_CONTROL_USE_TAG | PORT_CONTROL_USE_IP |  		PORT_CONTROL_STATE_FORWARDING; -	if (dsa_is_cpu_port(ds, port)) { -		if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EDSA)) -			reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA | -				PORT_CONTROL_FORWARD_UNKNOWN_MC; -		else -			reg |= PORT_CONTROL_DSA_TAG; -		reg |= PORT_CONTROL_EGRESS_ADD_TAG | -			PORT_CONTROL_FORWARD_UNKNOWN; -	} -	if (dsa_is_dsa_port(ds, port)) { -		if (mv88e6xxx_6095_family(chip) || -		    mv88e6xxx_6185_family(chip)) -			reg |= PORT_CONTROL_DSA_TAG; -		if (mv88e6xxx_6352_family(chip) || -		    mv88e6xxx_6351_family(chip) || -		    mv88e6xxx_6165_family(chip) || -		    mv88e6xxx_6097_family(chip) || -		    mv88e6xxx_6320_family(chip)) { -			reg |= PORT_CONTROL_FRAME_MODE_DSA; -		} +	err = mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg); +	if (err) +		return err; -		if (port == dsa_upstream_port(ds)) -			reg |= PORT_CONTROL_FORWARD_UNKNOWN | -				PORT_CONTROL_FORWARD_UNKNOWN_MC; -	} -	if (reg) { -		err = mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg); -		if (err) -			return err; +	if (dsa_is_cpu_port(ds, port)) { +		err = mv88e6xxx_setup_port_cpu(chip, port); +	} else if (dsa_is_dsa_port(ds, port)) { +		err = mv88e6xxx_setup_port_dsa(chip, port, +					       dsa_upstream_port(ds)); +	} else { +		err = mv88e6xxx_setup_port_normal(chip, port);  	} +	if (err) +		return err;  	/* If this port is connected to a SerDes, make sure the SerDes is not  	 * powered down. @@ -2568,10 +2539,6 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)  	    mv88e6xxx_6185_family(chip))  		reg = PORT_CONTROL_2_MAP_DA; -	if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) || -	    mv88e6xxx_6165_family(chip) || mv88e6xxx_6320_family(chip)) -		reg |= PORT_CONTROL_2_JUMBO_10240; -  	if (mv88e6xxx_6095_family(chip) || mv88e6xxx_6185_family(chip)) {  		/* Set the upstream port this port should use */  		reg |= dsa_upstream_port(ds); @@ -2590,6 +2557,12 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)  			return err;  	} +	if (chip->info->ops->port_jumbo_config) { +		err = chip->info->ops->port_jumbo_config(chip, port); +		if (err) +			return err; +	} +  	/* Port Association Vector: when learning source addresses  	 * of packets, add the address to the address database using  	 * a port bitmap that has only the bit for this port set and @@ -2609,17 +2582,15 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)  	if (err)  		return err; -	if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) || -	    mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) || -	    mv88e6xxx_6320_family(chip)) { -		/* Do not limit the period of time that this port can -		 * be paused for by the remote end or the period of -		 * time that this port can pause the remote end. -		 */ -		err = mv88e6xxx_port_write(chip, port, PORT_PAUSE_CTRL, 0x0000); +	if (chip->info->ops->port_pause_config) { +		err = chip->info->ops->port_pause_config(chip, port);  		if (err)  			return err; +	} +	if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) || +	    mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) || +	    mv88e6xxx_6320_family(chip)) {  		/* Port ATU control: disable limiting the number of  		 * address database entries that this port is allowed  		 * to use. @@ -2633,45 +2604,16 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)  					   0x0000);  		if (err)  			return err; +	} -		/* Port Ethertype: use the Ethertype DSA Ethertype -		 * value. -		 */ -		if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EDSA)) { -			err = mv88e6xxx_port_write(chip, port, PORT_ETH_TYPE, -						   ETH_P_EDSA); -			if (err) -				return err; -		} - -		/* Tag Remap: use an identity 802.1p prio -> switch -		 * prio mapping. -		 */ -		err = mv88e6xxx_port_write(chip, port, PORT_TAG_REGMAP_0123, -					   0x3210); -		if (err) -			return err; - -		/* Tag Remap 2: use an identity 802.1p prio -> switch -		 * prio mapping. -		 */ -		err = mv88e6xxx_port_write(chip, port, PORT_TAG_REGMAP_4567, -					   0x7654); +	if (chip->info->ops->port_tag_remap) { +		err = chip->info->ops->port_tag_remap(chip, port);  		if (err)  			return err;  	} -	/* Rate Control: disable ingress rate limiting. */ -	if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) || -	    mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) || -	    mv88e6xxx_6320_family(chip)) { -		err = mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL, -					   0x0001); -		if (err) -			return err; -	} else if (mv88e6xxx_6185_family(chip) || mv88e6xxx_6095_family(chip)) { -		err = mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL, -					   0x0000); +	if (chip->info->ops->port_egress_rate_limiting) { +		err = chip->info->ops->port_egress_rate_limiting(chip, port);  		if (err)  			return err;  	} @@ -2687,7 +2629,7 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)  	 * database, and allow bidirectional communication between the  	 * CPU and DSA port(s), and the other ports.  	 */ -	err = _mv88e6xxx_port_fid_set(chip, port, 0); +	err = mv88e6xxx_port_set_fid(chip, port, 0);  	if (err)  		return err; @@ -2701,7 +2643,7 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)  	return mv88e6xxx_port_write(chip, port, PORT_DEFAULT_VLAN, 0x0000);  } -int mv88e6xxx_g1_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr) +static int mv88e6xxx_g1_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr)  {  	int err; @@ -2764,30 +2706,26 @@ static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip)  {  	struct dsa_switch *ds = chip->ds;  	u32 upstream_port = dsa_upstream_port(ds); -	u16 reg;  	int err;  	/* Enable the PHY Polling Unit if present, don't discard any packets,  	 * and mask all interrupt sources.  	 */ -	reg = 0; -	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU) || -	    mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU_ACTIVE)) -		reg |= GLOBAL_CONTROL_PPU_ENABLE; - -	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, reg); +	err = mv88e6xxx_ppu_enable(chip);  	if (err)  		return err; -	/* Configure the upstream port, and configure it as the port to which -	 * ingress and egress and ARP monitor frames are to be sent. -	 */ -	reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT | -		upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT | -		upstream_port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT; -	err = mv88e6xxx_g1_write(chip, GLOBAL_MONITOR_CONTROL, reg); -	if (err) -		return err; +	if (chip->info->ops->g1_set_cpu_port) { +		err = chip->info->ops->g1_set_cpu_port(chip, upstream_port); +		if (err) +			return err; +	} + +	if (chip->info->ops->g1_set_egress_port) { +		err = chip->info->ops->g1_set_egress_port(chip, upstream_port); +		if (err) +			return err; +	}  	/* Disable remote management, and set the switch's DSA device number. */  	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL_2, @@ -2850,6 +2788,11 @@ static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip)  	if (err)  		return err; +	/* Initialize the statistics unit */ +	err = mv88e6xxx_stats_set_histogram(chip); +	if (err) +		return err; +  	/* Clear the statistics counters for all ports */  	err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP,  				 GLOBAL_STATS_OP_FLUSH_ALL); @@ -2857,7 +2800,7 @@ static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip)  		return err;  	/* Wait for the flush to complete. */ -	err = _mv88e6xxx_stats_wait(chip); +	err = mv88e6xxx_g1_stats_wait(chip);  	if (err)  		return err; @@ -2875,10 +2818,6 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)  	mutex_lock(&chip->reg_lock); -	err = mv88e6xxx_switch_reset(chip); -	if (err) -		goto unlock; -  	/* Setup Switch Port Registers */  	for (i = 0; i < mv88e6xxx_num_ports(chip); i++) {  		err = mv88e6xxx_setup_port(chip, i); @@ -2898,6 +2837,17 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)  			goto unlock;  	} +	/* Some generations have the configuration of sending reserved +	 * management frames to the CPU in global2, others in +	 * global1. Hence it does not fit the two setup functions +	 * above. +	 */ +	if (chip->info->ops->mgmt_rsvd2cpu) { +		err = chip->info->ops->mgmt_rsvd2cpu(chip); +		if (err) +			goto unlock; +	} +  unlock:  	mutex_unlock(&chip->reg_lock); @@ -3203,119 +3153,653 @@ static int mv88e6xxx_set_eeprom(struct dsa_switch *ds,  }  static const struct mv88e6xxx_ops mv88e6085_ops = { +	/* MV88E6XXX_FAMILY_6097 */  	.set_switch_mac = mv88e6xxx_g1_set_switch_mac,  	.phy_read = mv88e6xxx_phy_ppu_read,  	.phy_write = mv88e6xxx_phy_ppu_write, +	.port_set_link = mv88e6xxx_port_set_link, +	.port_set_duplex = mv88e6xxx_port_set_duplex, +	.port_set_speed = mv88e6185_port_set_speed, +	.port_tag_remap = mv88e6095_port_tag_remap, +	.port_set_frame_mode = mv88e6351_port_set_frame_mode, +	.port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, +	.port_set_ether_type = mv88e6351_port_set_ether_type, +	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, +	.port_pause_config = mv88e6097_port_pause_config, +	.stats_snapshot = mv88e6xxx_g1_stats_snapshot, +	.stats_get_sset_count = mv88e6095_stats_get_sset_count, +	.stats_get_strings = mv88e6095_stats_get_strings, +	.stats_get_stats = mv88e6095_stats_get_stats, +	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port, +	.g1_set_egress_port = mv88e6095_g1_set_egress_port, +	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, +	.ppu_enable = mv88e6185_g1_ppu_enable, +	.ppu_disable = mv88e6185_g1_ppu_disable, +	.reset = mv88e6185_g1_reset,  };  static const struct mv88e6xxx_ops mv88e6095_ops = { +	/* MV88E6XXX_FAMILY_6095 */  	.set_switch_mac = mv88e6xxx_g1_set_switch_mac,  	.phy_read = mv88e6xxx_phy_ppu_read,  	.phy_write = mv88e6xxx_phy_ppu_write, +	.port_set_link = mv88e6xxx_port_set_link, +	.port_set_duplex = mv88e6xxx_port_set_duplex, +	.port_set_speed = mv88e6185_port_set_speed, +	.port_set_frame_mode = mv88e6085_port_set_frame_mode, +	.port_set_egress_unknowns = mv88e6085_port_set_egress_unknowns, +	.stats_snapshot = mv88e6xxx_g1_stats_snapshot, +	.stats_get_sset_count = mv88e6095_stats_get_sset_count, +	.stats_get_strings = mv88e6095_stats_get_strings, +	.stats_get_stats = mv88e6095_stats_get_stats, +	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, +	.ppu_enable = mv88e6185_g1_ppu_enable, +	.ppu_disable = mv88e6185_g1_ppu_disable, +	.reset = mv88e6185_g1_reset, +}; + +static const struct mv88e6xxx_ops mv88e6097_ops = { +	/* MV88E6XXX_FAMILY_6097 */ +	.set_switch_mac = mv88e6xxx_g2_set_switch_mac, +	.phy_read = mv88e6xxx_g2_smi_phy_read, +	.phy_write = mv88e6xxx_g2_smi_phy_write, +	.port_set_link = mv88e6xxx_port_set_link, +	.port_set_duplex = mv88e6xxx_port_set_duplex, +	.port_set_speed = mv88e6185_port_set_speed, +	.port_tag_remap = mv88e6095_port_tag_remap, +	.port_set_frame_mode = mv88e6351_port_set_frame_mode, +	.port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, +	.port_set_ether_type = mv88e6351_port_set_ether_type, +	.port_jumbo_config = mv88e6165_port_jumbo_config, +	.port_egress_rate_limiting = mv88e6095_port_egress_rate_limiting, +	.port_pause_config = mv88e6097_port_pause_config, +	.stats_snapshot = mv88e6xxx_g1_stats_snapshot, +	.stats_get_sset_count = mv88e6095_stats_get_sset_count, +	.stats_get_strings = mv88e6095_stats_get_strings, +	.stats_get_stats = mv88e6095_stats_get_stats, +	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port, +	.g1_set_egress_port = mv88e6095_g1_set_egress_port, +	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, +	.reset = mv88e6352_g1_reset,  };  static const struct mv88e6xxx_ops mv88e6123_ops = { +	/* MV88E6XXX_FAMILY_6165 */  	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,  	.phy_read = mv88e6xxx_read,  	.phy_write = mv88e6xxx_write, +	.port_set_link = mv88e6xxx_port_set_link, +	.port_set_duplex = mv88e6xxx_port_set_duplex, +	.port_set_speed = mv88e6185_port_set_speed, +	.port_set_frame_mode = mv88e6085_port_set_frame_mode, +	.port_set_egress_unknowns = mv88e6085_port_set_egress_unknowns, +	.stats_snapshot = mv88e6xxx_g1_stats_snapshot, +	.stats_get_sset_count = mv88e6095_stats_get_sset_count, +	.stats_get_strings = mv88e6095_stats_get_strings, +	.stats_get_stats = mv88e6095_stats_get_stats, +	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port, +	.g1_set_egress_port = mv88e6095_g1_set_egress_port, +	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, +	.reset = mv88e6352_g1_reset,  };  static const struct mv88e6xxx_ops mv88e6131_ops = { +	/* MV88E6XXX_FAMILY_6185 */  	.set_switch_mac = mv88e6xxx_g1_set_switch_mac,  	.phy_read = mv88e6xxx_phy_ppu_read,  	.phy_write = mv88e6xxx_phy_ppu_write, +	.port_set_link = mv88e6xxx_port_set_link, +	.port_set_duplex = mv88e6xxx_port_set_duplex, +	.port_set_speed = mv88e6185_port_set_speed, +	.port_tag_remap = mv88e6095_port_tag_remap, +	.port_set_frame_mode = mv88e6351_port_set_frame_mode, +	.port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, +	.port_set_ether_type = mv88e6351_port_set_ether_type, +	.port_jumbo_config = mv88e6165_port_jumbo_config, +	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, +	.port_pause_config = mv88e6097_port_pause_config, +	.stats_snapshot = mv88e6xxx_g1_stats_snapshot, +	.stats_get_sset_count = mv88e6095_stats_get_sset_count, +	.stats_get_strings = mv88e6095_stats_get_strings, +	.stats_get_stats = mv88e6095_stats_get_stats, +	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port, +	.g1_set_egress_port = mv88e6095_g1_set_egress_port, +	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, +	.ppu_enable = mv88e6185_g1_ppu_enable, +	.ppu_disable = mv88e6185_g1_ppu_disable, +	.reset = mv88e6185_g1_reset,  };  static const struct mv88e6xxx_ops mv88e6161_ops = { +	/* MV88E6XXX_FAMILY_6165 */  	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,  	.phy_read = mv88e6xxx_read,  	.phy_write = mv88e6xxx_write, +	.port_set_link = mv88e6xxx_port_set_link, +	.port_set_duplex = mv88e6xxx_port_set_duplex, +	.port_set_speed = mv88e6185_port_set_speed, +	.port_tag_remap = mv88e6095_port_tag_remap, +	.port_set_frame_mode = mv88e6351_port_set_frame_mode, +	.port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, +	.port_set_ether_type = mv88e6351_port_set_ether_type, +	.port_jumbo_config = mv88e6165_port_jumbo_config, +	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, +	.port_pause_config = mv88e6097_port_pause_config, +	.stats_snapshot = mv88e6xxx_g1_stats_snapshot, +	.stats_get_sset_count = mv88e6095_stats_get_sset_count, +	.stats_get_strings = mv88e6095_stats_get_strings, +	.stats_get_stats = mv88e6095_stats_get_stats, +	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port, +	.g1_set_egress_port = mv88e6095_g1_set_egress_port, +	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, +	.reset = mv88e6352_g1_reset,  };  static const struct mv88e6xxx_ops mv88e6165_ops = { +	/* MV88E6XXX_FAMILY_6165 */  	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,  	.phy_read = mv88e6xxx_read,  	.phy_write = mv88e6xxx_write, +	.port_set_link = mv88e6xxx_port_set_link, +	.port_set_duplex = mv88e6xxx_port_set_duplex, +	.port_set_speed = mv88e6185_port_set_speed, +	.stats_snapshot = mv88e6xxx_g1_stats_snapshot, +	.stats_get_sset_count = mv88e6095_stats_get_sset_count, +	.stats_get_strings = mv88e6095_stats_get_strings, +	.stats_get_stats = mv88e6095_stats_get_stats, +	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port, +	.g1_set_egress_port = mv88e6095_g1_set_egress_port, +	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, +	.reset = mv88e6352_g1_reset,  };  static const struct mv88e6xxx_ops mv88e6171_ops = { +	/* MV88E6XXX_FAMILY_6351 */  	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,  	.phy_read = mv88e6xxx_g2_smi_phy_read,  	.phy_write = mv88e6xxx_g2_smi_phy_write, +	.port_set_link = mv88e6xxx_port_set_link, +	.port_set_duplex = mv88e6xxx_port_set_duplex, +	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, +	.port_set_speed = mv88e6185_port_set_speed, +	.port_tag_remap = mv88e6095_port_tag_remap, +	.port_set_frame_mode = mv88e6351_port_set_frame_mode, +	.port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, +	.port_set_ether_type = mv88e6351_port_set_ether_type, +	.port_jumbo_config = mv88e6165_port_jumbo_config, +	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, +	.port_pause_config = mv88e6097_port_pause_config, +	.stats_snapshot = mv88e6320_g1_stats_snapshot, +	.stats_get_sset_count = mv88e6095_stats_get_sset_count, +	.stats_get_strings = mv88e6095_stats_get_strings, +	.stats_get_stats = mv88e6095_stats_get_stats, +	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port, +	.g1_set_egress_port = mv88e6095_g1_set_egress_port, +	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, +	.reset = mv88e6352_g1_reset,  };  static const struct mv88e6xxx_ops mv88e6172_ops = { +	/* MV88E6XXX_FAMILY_6352 */  	.get_eeprom = mv88e6xxx_g2_get_eeprom16,  	.set_eeprom = mv88e6xxx_g2_set_eeprom16,  	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,  	.phy_read = mv88e6xxx_g2_smi_phy_read,  	.phy_write = mv88e6xxx_g2_smi_phy_write, +	.port_set_link = mv88e6xxx_port_set_link, +	.port_set_duplex = mv88e6xxx_port_set_duplex, +	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, +	.port_set_speed = mv88e6352_port_set_speed, +	.port_tag_remap = mv88e6095_port_tag_remap, +	.port_set_frame_mode = mv88e6351_port_set_frame_mode, +	.port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, +	.port_set_ether_type = mv88e6351_port_set_ether_type, +	.port_jumbo_config = mv88e6165_port_jumbo_config, +	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, +	.port_pause_config = mv88e6097_port_pause_config, +	.stats_snapshot = mv88e6320_g1_stats_snapshot, +	.stats_get_sset_count = mv88e6095_stats_get_sset_count, +	.stats_get_strings = mv88e6095_stats_get_strings, +	.stats_get_stats = mv88e6095_stats_get_stats, +	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port, +	.g1_set_egress_port = mv88e6095_g1_set_egress_port, +	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, +	.reset = mv88e6352_g1_reset,  };  static const struct mv88e6xxx_ops mv88e6175_ops = { +	/* MV88E6XXX_FAMILY_6351 */  	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,  	.phy_read = mv88e6xxx_g2_smi_phy_read,  	.phy_write = mv88e6xxx_g2_smi_phy_write, +	.port_set_link = mv88e6xxx_port_set_link, +	.port_set_duplex = mv88e6xxx_port_set_duplex, +	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, +	.port_set_speed = mv88e6185_port_set_speed, +	.port_tag_remap = mv88e6095_port_tag_remap, +	.port_set_frame_mode = mv88e6351_port_set_frame_mode, +	.port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, +	.port_set_ether_type = mv88e6351_port_set_ether_type, +	.port_jumbo_config = mv88e6165_port_jumbo_config, +	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, +	.port_pause_config = mv88e6097_port_pause_config, +	.stats_snapshot = mv88e6320_g1_stats_snapshot, +	.stats_get_sset_count = mv88e6095_stats_get_sset_count, +	.stats_get_strings = mv88e6095_stats_get_strings, +	.stats_get_stats = mv88e6095_stats_get_stats, +	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port, +	.g1_set_egress_port = mv88e6095_g1_set_egress_port, +	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, +	.reset = mv88e6352_g1_reset,  };  static const struct mv88e6xxx_ops mv88e6176_ops = { +	/* MV88E6XXX_FAMILY_6352 */  	.get_eeprom = mv88e6xxx_g2_get_eeprom16,  	.set_eeprom = mv88e6xxx_g2_set_eeprom16,  	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,  	.phy_read = mv88e6xxx_g2_smi_phy_read,  	.phy_write = mv88e6xxx_g2_smi_phy_write, +	.port_set_link = mv88e6xxx_port_set_link, +	.port_set_duplex = mv88e6xxx_port_set_duplex, +	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, +	.port_set_speed = mv88e6352_port_set_speed, +	.port_tag_remap = mv88e6095_port_tag_remap, +	.port_set_frame_mode = mv88e6351_port_set_frame_mode, +	.port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, +	.port_set_ether_type = mv88e6351_port_set_ether_type, +	.port_jumbo_config = mv88e6165_port_jumbo_config, +	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, +	.port_pause_config = mv88e6097_port_pause_config, +	.stats_snapshot = mv88e6320_g1_stats_snapshot, +	.stats_get_sset_count = mv88e6095_stats_get_sset_count, +	.stats_get_strings = mv88e6095_stats_get_strings, +	.stats_get_stats = mv88e6095_stats_get_stats, +	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port, +	.g1_set_egress_port = mv88e6095_g1_set_egress_port, +	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, +	.reset = mv88e6352_g1_reset,  };  static const struct mv88e6xxx_ops mv88e6185_ops = { +	/* MV88E6XXX_FAMILY_6185 */  	.set_switch_mac = mv88e6xxx_g1_set_switch_mac,  	.phy_read = mv88e6xxx_phy_ppu_read,  	.phy_write = mv88e6xxx_phy_ppu_write, +	.port_set_link = mv88e6xxx_port_set_link, +	.port_set_duplex = mv88e6xxx_port_set_duplex, +	.port_set_speed = mv88e6185_port_set_speed, +	.port_set_frame_mode = mv88e6085_port_set_frame_mode, +	.port_set_egress_unknowns = mv88e6085_port_set_egress_unknowns, +	.port_egress_rate_limiting = mv88e6095_port_egress_rate_limiting, +	.stats_snapshot = mv88e6xxx_g1_stats_snapshot, +	.stats_get_sset_count = mv88e6095_stats_get_sset_count, +	.stats_get_strings = mv88e6095_stats_get_strings, +	.stats_get_stats = mv88e6095_stats_get_stats, +	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port, +	.g1_set_egress_port = mv88e6095_g1_set_egress_port, +	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, +	.ppu_enable = mv88e6185_g1_ppu_enable, +	.ppu_disable = mv88e6185_g1_ppu_disable, +	.reset = mv88e6185_g1_reset, +}; + +static const struct mv88e6xxx_ops mv88e6190_ops = { +	/* MV88E6XXX_FAMILY_6390 */ +	.set_switch_mac = mv88e6xxx_g2_set_switch_mac, +	.phy_read = mv88e6xxx_g2_smi_phy_read, +	.phy_write = mv88e6xxx_g2_smi_phy_write, +	.port_set_link = mv88e6xxx_port_set_link, +	.port_set_duplex = mv88e6xxx_port_set_duplex, +	.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, +	.port_set_speed = mv88e6390_port_set_speed, +	.port_tag_remap = mv88e6390_port_tag_remap, +	.port_set_frame_mode = mv88e6351_port_set_frame_mode, +	.port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, +	.port_set_ether_type = mv88e6351_port_set_ether_type, +	.port_pause_config = mv88e6390_port_pause_config, +	.stats_snapshot = mv88e6390_g1_stats_snapshot, +	.stats_set_histogram = mv88e6390_g1_stats_set_histogram, +	.stats_get_sset_count = mv88e6320_stats_get_sset_count, +	.stats_get_strings = mv88e6320_stats_get_strings, +	.stats_get_stats = mv88e6390_stats_get_stats, +	.g1_set_cpu_port = mv88e6390_g1_set_cpu_port, +	.g1_set_egress_port = mv88e6390_g1_set_egress_port, +	.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, +	.reset = mv88e6352_g1_reset, +}; + +static const struct mv88e6xxx_ops mv88e6190x_ops = { +	/* MV88E6XXX_FAMILY_6390 */ +	.set_switch_mac = mv88e6xxx_g2_set_switch_mac, +	.phy_read = mv88e6xxx_g2_smi_phy_read, +	.phy_write = mv88e6xxx_g2_smi_phy_write, +	.port_set_link = mv88e6xxx_port_set_link, +	.port_set_duplex = mv88e6xxx_port_set_duplex, +	.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, +	.port_set_speed = mv88e6390x_port_set_speed, +	.port_tag_remap = mv88e6390_port_tag_remap, +	.port_set_frame_mode = mv88e6351_port_set_frame_mode, +	.port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, +	.port_set_ether_type = mv88e6351_port_set_ether_type, +	.port_pause_config = mv88e6390_port_pause_config, +	.stats_snapshot = mv88e6390_g1_stats_snapshot, +	.stats_set_histogram = mv88e6390_g1_stats_set_histogram, +	.stats_get_sset_count = mv88e6320_stats_get_sset_count, +	.stats_get_strings = mv88e6320_stats_get_strings, +	.stats_get_stats = mv88e6390_stats_get_stats, +	.g1_set_cpu_port = mv88e6390_g1_set_cpu_port, +	.g1_set_egress_port = mv88e6390_g1_set_egress_port, +	.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, +	.reset = mv88e6352_g1_reset, +}; + +static const struct mv88e6xxx_ops mv88e6191_ops = { +	/* MV88E6XXX_FAMILY_6390 */ +	.set_switch_mac = mv88e6xxx_g2_set_switch_mac, +	.phy_read = mv88e6xxx_g2_smi_phy_read, +	.phy_write = mv88e6xxx_g2_smi_phy_write, +	.port_set_link = mv88e6xxx_port_set_link, +	.port_set_duplex = mv88e6xxx_port_set_duplex, +	.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, +	.port_set_speed = mv88e6390_port_set_speed, +	.port_tag_remap = mv88e6390_port_tag_remap, +	.port_set_frame_mode = mv88e6351_port_set_frame_mode, +	.port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, +	.port_set_ether_type = mv88e6351_port_set_ether_type, +	.port_pause_config = mv88e6390_port_pause_config, +	.stats_snapshot = mv88e6390_g1_stats_snapshot, +	.stats_set_histogram = mv88e6390_g1_stats_set_histogram, +	.stats_get_sset_count = mv88e6320_stats_get_sset_count, +	.stats_get_strings = mv88e6320_stats_get_strings, +	.stats_get_stats = mv88e6390_stats_get_stats, +	.g1_set_cpu_port = mv88e6390_g1_set_cpu_port, +	.g1_set_egress_port = mv88e6390_g1_set_egress_port, +	.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, +	.reset = mv88e6352_g1_reset,  };  static const struct mv88e6xxx_ops mv88e6240_ops = { +	/* MV88E6XXX_FAMILY_6352 */  	.get_eeprom = mv88e6xxx_g2_get_eeprom16,  	.set_eeprom = mv88e6xxx_g2_set_eeprom16,  	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,  	.phy_read = mv88e6xxx_g2_smi_phy_read,  	.phy_write = mv88e6xxx_g2_smi_phy_write, +	.port_set_link = mv88e6xxx_port_set_link, +	.port_set_duplex = mv88e6xxx_port_set_duplex, +	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, +	.port_set_speed = mv88e6352_port_set_speed, +	.port_tag_remap = mv88e6095_port_tag_remap, +	.port_set_frame_mode = mv88e6351_port_set_frame_mode, +	.port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, +	.port_set_ether_type = mv88e6351_port_set_ether_type, +	.port_jumbo_config = mv88e6165_port_jumbo_config, +	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, +	.port_pause_config = mv88e6097_port_pause_config, +	.stats_snapshot = mv88e6320_g1_stats_snapshot, +	.stats_get_sset_count = mv88e6095_stats_get_sset_count, +	.stats_get_strings = mv88e6095_stats_get_strings, +	.stats_get_stats = mv88e6095_stats_get_stats, +	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port, +	.g1_set_egress_port = mv88e6095_g1_set_egress_port, +	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, +	.reset = mv88e6352_g1_reset, +}; + +static const struct mv88e6xxx_ops mv88e6290_ops = { +	/* MV88E6XXX_FAMILY_6390 */ +	.set_switch_mac = mv88e6xxx_g2_set_switch_mac, +	.phy_read = mv88e6xxx_g2_smi_phy_read, +	.phy_write = mv88e6xxx_g2_smi_phy_write, +	.port_set_link = mv88e6xxx_port_set_link, +	.port_set_duplex = mv88e6xxx_port_set_duplex, +	.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, +	.port_set_speed = mv88e6390_port_set_speed, +	.port_tag_remap = mv88e6390_port_tag_remap, +	.port_set_frame_mode = mv88e6351_port_set_frame_mode, +	.port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, +	.port_set_ether_type = mv88e6351_port_set_ether_type, +	.port_pause_config = mv88e6390_port_pause_config, +	.stats_snapshot = mv88e6390_g1_stats_snapshot, +	.stats_set_histogram = mv88e6390_g1_stats_set_histogram, +	.stats_get_sset_count = mv88e6320_stats_get_sset_count, +	.stats_get_strings = mv88e6320_stats_get_strings, +	.stats_get_stats = mv88e6390_stats_get_stats, +	.g1_set_cpu_port = mv88e6390_g1_set_cpu_port, +	.g1_set_egress_port = mv88e6390_g1_set_egress_port, +	.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, +	.reset = mv88e6352_g1_reset,  };  static const struct mv88e6xxx_ops mv88e6320_ops = { +	/* MV88E6XXX_FAMILY_6320 */  	.get_eeprom = mv88e6xxx_g2_get_eeprom16,  	.set_eeprom = mv88e6xxx_g2_set_eeprom16,  	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,  	.phy_read = mv88e6xxx_g2_smi_phy_read,  	.phy_write = mv88e6xxx_g2_smi_phy_write, +	.port_set_link = mv88e6xxx_port_set_link, +	.port_set_duplex = mv88e6xxx_port_set_duplex, +	.port_set_speed = mv88e6185_port_set_speed, +	.port_tag_remap = mv88e6095_port_tag_remap, +	.port_set_frame_mode = mv88e6351_port_set_frame_mode, +	.port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, +	.port_set_ether_type = mv88e6351_port_set_ether_type, +	.port_jumbo_config = mv88e6165_port_jumbo_config, +	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, +	.port_pause_config = mv88e6097_port_pause_config, +	.stats_snapshot = mv88e6320_g1_stats_snapshot, +	.stats_get_sset_count = mv88e6320_stats_get_sset_count, +	.stats_get_strings = mv88e6320_stats_get_strings, +	.stats_get_stats = mv88e6320_stats_get_stats, +	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port, +	.g1_set_egress_port = mv88e6095_g1_set_egress_port, +	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, +	.reset = mv88e6352_g1_reset,  };  static const struct mv88e6xxx_ops mv88e6321_ops = { +	/* MV88E6XXX_FAMILY_6321 */  	.get_eeprom = mv88e6xxx_g2_get_eeprom16,  	.set_eeprom = mv88e6xxx_g2_set_eeprom16,  	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,  	.phy_read = mv88e6xxx_g2_smi_phy_read,  	.phy_write = mv88e6xxx_g2_smi_phy_write, +	.port_set_link = mv88e6xxx_port_set_link, +	.port_set_duplex = mv88e6xxx_port_set_duplex, +	.port_set_speed = mv88e6185_port_set_speed, +	.port_tag_remap = mv88e6095_port_tag_remap, +	.port_set_frame_mode = mv88e6351_port_set_frame_mode, +	.port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, +	.port_set_ether_type = mv88e6351_port_set_ether_type, +	.port_jumbo_config = mv88e6165_port_jumbo_config, +	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, +	.port_pause_config = mv88e6097_port_pause_config, +	.stats_snapshot = mv88e6320_g1_stats_snapshot, +	.stats_get_sset_count = mv88e6320_stats_get_sset_count, +	.stats_get_strings = mv88e6320_stats_get_strings, +	.stats_get_stats = mv88e6320_stats_get_stats, +	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port, +	.g1_set_egress_port = mv88e6095_g1_set_egress_port, +	.reset = mv88e6352_g1_reset,  };  static const struct mv88e6xxx_ops mv88e6350_ops = { +	/* MV88E6XXX_FAMILY_6351 */  	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,  	.phy_read = mv88e6xxx_g2_smi_phy_read,  	.phy_write = mv88e6xxx_g2_smi_phy_write, +	.port_set_link = mv88e6xxx_port_set_link, +	.port_set_duplex = mv88e6xxx_port_set_duplex, +	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, +	.port_set_speed = mv88e6185_port_set_speed, +	.port_tag_remap = mv88e6095_port_tag_remap, +	.port_set_frame_mode = mv88e6351_port_set_frame_mode, +	.port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, +	.port_set_ether_type = mv88e6351_port_set_ether_type, +	.port_jumbo_config = mv88e6165_port_jumbo_config, +	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, +	.port_pause_config = mv88e6097_port_pause_config, +	.stats_snapshot = mv88e6320_g1_stats_snapshot, +	.stats_get_sset_count = mv88e6095_stats_get_sset_count, +	.stats_get_strings = mv88e6095_stats_get_strings, +	.stats_get_stats = mv88e6095_stats_get_stats, +	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port, +	.g1_set_egress_port = mv88e6095_g1_set_egress_port, +	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, +	.reset = mv88e6352_g1_reset,  };  static const struct mv88e6xxx_ops mv88e6351_ops = { +	/* MV88E6XXX_FAMILY_6351 */  	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,  	.phy_read = mv88e6xxx_g2_smi_phy_read,  	.phy_write = mv88e6xxx_g2_smi_phy_write, +	.port_set_link = mv88e6xxx_port_set_link, +	.port_set_duplex = mv88e6xxx_port_set_duplex, +	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, +	.port_set_speed = mv88e6185_port_set_speed, +	.port_tag_remap = mv88e6095_port_tag_remap, +	.port_set_frame_mode = mv88e6351_port_set_frame_mode, +	.port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, +	.port_set_ether_type = mv88e6351_port_set_ether_type, +	.port_jumbo_config = mv88e6165_port_jumbo_config, +	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, +	.port_pause_config = mv88e6097_port_pause_config, +	.stats_snapshot = mv88e6320_g1_stats_snapshot, +	.stats_get_sset_count = mv88e6095_stats_get_sset_count, +	.stats_get_strings = mv88e6095_stats_get_strings, +	.stats_get_stats = mv88e6095_stats_get_stats, +	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port, +	.g1_set_egress_port = mv88e6095_g1_set_egress_port, +	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, +	.reset = mv88e6352_g1_reset,  };  static const struct mv88e6xxx_ops mv88e6352_ops = { +	/* MV88E6XXX_FAMILY_6352 */  	.get_eeprom = mv88e6xxx_g2_get_eeprom16,  	.set_eeprom = mv88e6xxx_g2_set_eeprom16,  	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,  	.phy_read = mv88e6xxx_g2_smi_phy_read,  	.phy_write = mv88e6xxx_g2_smi_phy_write, +	.port_set_link = mv88e6xxx_port_set_link, +	.port_set_duplex = mv88e6xxx_port_set_duplex, +	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, +	.port_set_speed = mv88e6352_port_set_speed, +	.port_tag_remap = mv88e6095_port_tag_remap, +	.port_set_frame_mode = mv88e6351_port_set_frame_mode, +	.port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, +	.port_set_ether_type = mv88e6351_port_set_ether_type, +	.port_jumbo_config = mv88e6165_port_jumbo_config, +	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, +	.port_pause_config = mv88e6097_port_pause_config, +	.stats_snapshot = mv88e6320_g1_stats_snapshot, +	.stats_get_sset_count = mv88e6095_stats_get_sset_count, +	.stats_get_strings = mv88e6095_stats_get_strings, +	.stats_get_stats = mv88e6095_stats_get_stats, +	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port, +	.g1_set_egress_port = mv88e6095_g1_set_egress_port, +	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, +	.reset = mv88e6352_g1_reset, +}; + +static const struct mv88e6xxx_ops mv88e6390_ops = { +	/* MV88E6XXX_FAMILY_6390 */ +	.set_switch_mac = mv88e6xxx_g2_set_switch_mac, +	.phy_read = mv88e6xxx_g2_smi_phy_read, +	.phy_write = mv88e6xxx_g2_smi_phy_write, +	.port_set_link = mv88e6xxx_port_set_link, +	.port_set_duplex = mv88e6xxx_port_set_duplex, +	.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, +	.port_set_speed = mv88e6390_port_set_speed, +	.port_tag_remap = mv88e6390_port_tag_remap, +	.port_set_frame_mode = mv88e6351_port_set_frame_mode, +	.port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, +	.port_set_ether_type = mv88e6351_port_set_ether_type, +	.port_jumbo_config = mv88e6165_port_jumbo_config, +	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, +	.port_pause_config = mv88e6390_port_pause_config, +	.stats_snapshot = mv88e6390_g1_stats_snapshot, +	.stats_set_histogram = mv88e6390_g1_stats_set_histogram, +	.stats_get_sset_count = mv88e6320_stats_get_sset_count, +	.stats_get_strings = mv88e6320_stats_get_strings, +	.stats_get_stats = mv88e6390_stats_get_stats, +	.g1_set_cpu_port = mv88e6390_g1_set_cpu_port, +	.g1_set_egress_port = mv88e6390_g1_set_egress_port, +	.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, +	.reset = mv88e6352_g1_reset,  }; +static const struct mv88e6xxx_ops mv88e6390x_ops = { +	/* MV88E6XXX_FAMILY_6390 */ +	.set_switch_mac = mv88e6xxx_g2_set_switch_mac, +	.phy_read = mv88e6xxx_g2_smi_phy_read, +	.phy_write = mv88e6xxx_g2_smi_phy_write, +	.port_set_link = mv88e6xxx_port_set_link, +	.port_set_duplex = mv88e6xxx_port_set_duplex, +	.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, +	.port_set_speed = mv88e6390x_port_set_speed, +	.port_tag_remap = mv88e6390_port_tag_remap, +	.port_set_frame_mode = mv88e6351_port_set_frame_mode, +	.port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, +	.port_set_ether_type = mv88e6351_port_set_ether_type, +	.port_jumbo_config = mv88e6165_port_jumbo_config, +	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, +	.port_pause_config = mv88e6390_port_pause_config, +	.stats_snapshot = mv88e6390_g1_stats_snapshot, +	.stats_set_histogram = mv88e6390_g1_stats_set_histogram, +	.stats_get_sset_count = mv88e6320_stats_get_sset_count, +	.stats_get_strings = mv88e6320_stats_get_strings, +	.stats_get_stats = mv88e6390_stats_get_stats, +	.g1_set_cpu_port = mv88e6390_g1_set_cpu_port, +	.g1_set_egress_port = mv88e6390_g1_set_egress_port, +	.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, +	.reset = mv88e6352_g1_reset, +}; + +static const struct mv88e6xxx_ops mv88e6391_ops = { +	/* MV88E6XXX_FAMILY_6390 */ +	.set_switch_mac = mv88e6xxx_g2_set_switch_mac, +	.phy_read = mv88e6xxx_g2_smi_phy_read, +	.phy_write = mv88e6xxx_g2_smi_phy_write, +	.port_set_link = mv88e6xxx_port_set_link, +	.port_set_duplex = mv88e6xxx_port_set_duplex, +	.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, +	.port_set_speed = mv88e6390_port_set_speed, +	.port_tag_remap = mv88e6390_port_tag_remap, +	.port_set_frame_mode = mv88e6351_port_set_frame_mode, +	.port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, +	.port_set_ether_type = mv88e6351_port_set_ether_type, +	.port_pause_config = mv88e6390_port_pause_config, +	.stats_snapshot = mv88e6390_g1_stats_snapshot, +	.stats_set_histogram = mv88e6390_g1_stats_set_histogram, +	.stats_get_sset_count = mv88e6320_stats_get_sset_count, +	.stats_get_strings = mv88e6320_stats_get_strings, +	.stats_get_stats = mv88e6390_stats_get_stats, +	.g1_set_cpu_port = mv88e6390_g1_set_cpu_port, +	.g1_set_egress_port = mv88e6390_g1_set_egress_port, +	.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, +	.reset = mv88e6352_g1_reset, +}; + +static int mv88e6xxx_verify_madatory_ops(struct mv88e6xxx_chip *chip, +					 const struct mv88e6xxx_ops *ops) +{ +	if (!ops->port_set_frame_mode) { +		dev_err(chip->dev, "Missing port_set_frame_mode"); +		return -EINVAL; +	} + +	if (!ops->port_set_egress_unknowns) { +		dev_err(chip->dev, "Missing port_set_egress_mode"); +		return -EINVAL; +	} + +	return 0; +} +  static const struct mv88e6xxx_info mv88e6xxx_table[] = {  	[MV88E6085] = {  		.prod_num = PORT_SWITCH_ID_PROD_NUM_6085, @@ -3326,6 +3810,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {  		.port_base_addr = 0x10,  		.global1_addr = 0x1b,  		.age_time_coeff = 15000, +		.g1_irqs = 8, +		.tag_protocol = DSA_TAG_PROTO_DSA,  		.flags = MV88E6XXX_FLAGS_FAMILY_6097,  		.ops = &mv88e6085_ops,  	}, @@ -3339,10 +3825,27 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {  		.port_base_addr = 0x10,  		.global1_addr = 0x1b,  		.age_time_coeff = 15000, +		.g1_irqs = 8, +		.tag_protocol = DSA_TAG_PROTO_DSA,  		.flags = MV88E6XXX_FLAGS_FAMILY_6095,  		.ops = &mv88e6095_ops,  	}, +	[MV88E6097] = { +		.prod_num = PORT_SWITCH_ID_PROD_NUM_6097, +		.family = MV88E6XXX_FAMILY_6097, +		.name = "Marvell 88E6097/88E6097F", +		.num_databases = 4096, +		.num_ports = 11, +		.port_base_addr = 0x10, +		.global1_addr = 0x1b, +		.age_time_coeff = 15000, +		.g1_irqs = 8, +		.tag_protocol = DSA_TAG_PROTO_EDSA, +		.flags = MV88E6XXX_FLAGS_FAMILY_6097, +		.ops = &mv88e6097_ops, +	}, +  	[MV88E6123] = {  		.prod_num = PORT_SWITCH_ID_PROD_NUM_6123,  		.family = MV88E6XXX_FAMILY_6165, @@ -3352,6 +3855,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {  		.port_base_addr = 0x10,  		.global1_addr = 0x1b,  		.age_time_coeff = 15000, +		.g1_irqs = 9, +		.tag_protocol = DSA_TAG_PROTO_DSA,  		.flags = MV88E6XXX_FLAGS_FAMILY_6165,  		.ops = &mv88e6123_ops,  	}, @@ -3365,6 +3870,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {  		.port_base_addr = 0x10,  		.global1_addr = 0x1b,  		.age_time_coeff = 15000, +		.g1_irqs = 9, +		.tag_protocol = DSA_TAG_PROTO_DSA,  		.flags = MV88E6XXX_FLAGS_FAMILY_6185,  		.ops = &mv88e6131_ops,  	}, @@ -3378,6 +3885,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {  		.port_base_addr = 0x10,  		.global1_addr = 0x1b,  		.age_time_coeff = 15000, +		.g1_irqs = 9, +		.tag_protocol = DSA_TAG_PROTO_DSA,  		.flags = MV88E6XXX_FLAGS_FAMILY_6165,  		.ops = &mv88e6161_ops,  	}, @@ -3391,6 +3900,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {  		.port_base_addr = 0x10,  		.global1_addr = 0x1b,  		.age_time_coeff = 15000, +		.g1_irqs = 9, +		.tag_protocol = DSA_TAG_PROTO_DSA,  		.flags = MV88E6XXX_FLAGS_FAMILY_6165,  		.ops = &mv88e6165_ops,  	}, @@ -3404,6 +3915,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {  		.port_base_addr = 0x10,  		.global1_addr = 0x1b,  		.age_time_coeff = 15000, +		.g1_irqs = 9, +		.tag_protocol = DSA_TAG_PROTO_EDSA,  		.flags = MV88E6XXX_FLAGS_FAMILY_6351,  		.ops = &mv88e6171_ops,  	}, @@ -3417,6 +3930,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {  		.port_base_addr = 0x10,  		.global1_addr = 0x1b,  		.age_time_coeff = 15000, +		.g1_irqs = 9, +		.tag_protocol = DSA_TAG_PROTO_EDSA,  		.flags = MV88E6XXX_FLAGS_FAMILY_6352,  		.ops = &mv88e6172_ops,  	}, @@ -3430,6 +3945,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {  		.port_base_addr = 0x10,  		.global1_addr = 0x1b,  		.age_time_coeff = 15000, +		.g1_irqs = 9, +		.tag_protocol = DSA_TAG_PROTO_EDSA,  		.flags = MV88E6XXX_FLAGS_FAMILY_6351,  		.ops = &mv88e6175_ops,  	}, @@ -3443,6 +3960,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {  		.port_base_addr = 0x10,  		.global1_addr = 0x1b,  		.age_time_coeff = 15000, +		.g1_irqs = 9, +		.tag_protocol = DSA_TAG_PROTO_EDSA,  		.flags = MV88E6XXX_FLAGS_FAMILY_6352,  		.ops = &mv88e6176_ops,  	}, @@ -3456,10 +3975,57 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {  		.port_base_addr = 0x10,  		.global1_addr = 0x1b,  		.age_time_coeff = 15000, +		.g1_irqs = 8, +		.tag_protocol = DSA_TAG_PROTO_EDSA,  		.flags = MV88E6XXX_FLAGS_FAMILY_6185,  		.ops = &mv88e6185_ops,  	}, +	[MV88E6190] = { +		.prod_num = PORT_SWITCH_ID_PROD_NUM_6190, +		.family = MV88E6XXX_FAMILY_6390, +		.name = "Marvell 88E6190", +		.num_databases = 4096, +		.num_ports = 11,	/* 10 + Z80 */ +		.port_base_addr = 0x0, +		.global1_addr = 0x1b, +		.tag_protocol = DSA_TAG_PROTO_DSA, +		.age_time_coeff = 15000, +		.g1_irqs = 9, +		.flags = MV88E6XXX_FLAGS_FAMILY_6390, +		.ops = &mv88e6190_ops, +	}, + +	[MV88E6190X] = { +		.prod_num = PORT_SWITCH_ID_PROD_NUM_6190X, +		.family = MV88E6XXX_FAMILY_6390, +		.name = "Marvell 88E6190X", +		.num_databases = 4096, +		.num_ports = 11,	/* 10 + Z80 */ +		.port_base_addr = 0x0, +		.global1_addr = 0x1b, +		.age_time_coeff = 15000, +		.g1_irqs = 9, +		.tag_protocol = DSA_TAG_PROTO_DSA, +		.flags = MV88E6XXX_FLAGS_FAMILY_6390, +		.ops = &mv88e6190x_ops, +	}, + +	[MV88E6191] = { +		.prod_num = PORT_SWITCH_ID_PROD_NUM_6191, +		.family = MV88E6XXX_FAMILY_6390, +		.name = "Marvell 88E6191", +		.num_databases = 4096, +		.num_ports = 11,	/* 10 + Z80 */ +		.port_base_addr = 0x0, +		.global1_addr = 0x1b, +		.age_time_coeff = 15000, +		.g1_irqs = 9, +		.tag_protocol = DSA_TAG_PROTO_DSA, +		.flags = MV88E6XXX_FLAGS_FAMILY_6390, +		.ops = &mv88e6391_ops, +	}, +  	[MV88E6240] = {  		.prod_num = PORT_SWITCH_ID_PROD_NUM_6240,  		.family = MV88E6XXX_FAMILY_6352, @@ -3469,10 +4035,27 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {  		.port_base_addr = 0x10,  		.global1_addr = 0x1b,  		.age_time_coeff = 15000, +		.g1_irqs = 9, +		.tag_protocol = DSA_TAG_PROTO_EDSA,  		.flags = MV88E6XXX_FLAGS_FAMILY_6352,  		.ops = &mv88e6240_ops,  	}, +	[MV88E6290] = { +		.prod_num = PORT_SWITCH_ID_PROD_NUM_6290, +		.family = MV88E6XXX_FAMILY_6390, +		.name = "Marvell 88E6290", +		.num_databases = 4096, +		.num_ports = 11,	/* 10 + Z80 */ +		.port_base_addr = 0x0, +		.global1_addr = 0x1b, +		.age_time_coeff = 15000, +		.g1_irqs = 9, +		.tag_protocol = DSA_TAG_PROTO_DSA, +		.flags = MV88E6XXX_FLAGS_FAMILY_6390, +		.ops = &mv88e6290_ops, +	}, +  	[MV88E6320] = {  		.prod_num = PORT_SWITCH_ID_PROD_NUM_6320,  		.family = MV88E6XXX_FAMILY_6320, @@ -3482,6 +4065,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {  		.port_base_addr = 0x10,  		.global1_addr = 0x1b,  		.age_time_coeff = 15000, +		.g1_irqs = 8, +		.tag_protocol = DSA_TAG_PROTO_EDSA,  		.flags = MV88E6XXX_FLAGS_FAMILY_6320,  		.ops = &mv88e6320_ops,  	}, @@ -3495,6 +4080,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {  		.port_base_addr = 0x10,  		.global1_addr = 0x1b,  		.age_time_coeff = 15000, +		.g1_irqs = 8, +		.tag_protocol = DSA_TAG_PROTO_EDSA,  		.flags = MV88E6XXX_FLAGS_FAMILY_6320,  		.ops = &mv88e6321_ops,  	}, @@ -3508,6 +4095,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {  		.port_base_addr = 0x10,  		.global1_addr = 0x1b,  		.age_time_coeff = 15000, +		.g1_irqs = 9, +		.tag_protocol = DSA_TAG_PROTO_EDSA,  		.flags = MV88E6XXX_FLAGS_FAMILY_6351,  		.ops = &mv88e6350_ops,  	}, @@ -3521,6 +4110,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {  		.port_base_addr = 0x10,  		.global1_addr = 0x1b,  		.age_time_coeff = 15000, +		.g1_irqs = 9, +		.tag_protocol = DSA_TAG_PROTO_EDSA,  		.flags = MV88E6XXX_FLAGS_FAMILY_6351,  		.ops = &mv88e6351_ops,  	}, @@ -3534,9 +4125,39 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {  		.port_base_addr = 0x10,  		.global1_addr = 0x1b,  		.age_time_coeff = 15000, +		.g1_irqs = 9, +		.tag_protocol = DSA_TAG_PROTO_EDSA,  		.flags = MV88E6XXX_FLAGS_FAMILY_6352,  		.ops = &mv88e6352_ops,  	}, +	[MV88E6390] = { +		.prod_num = PORT_SWITCH_ID_PROD_NUM_6390, +		.family = MV88E6XXX_FAMILY_6390, +		.name = "Marvell 88E6390", +		.num_databases = 4096, +		.num_ports = 11,	/* 10 + Z80 */ +		.port_base_addr = 0x0, +		.global1_addr = 0x1b, +		.age_time_coeff = 15000, +		.g1_irqs = 9, +		.tag_protocol = DSA_TAG_PROTO_DSA, +		.flags = MV88E6XXX_FLAGS_FAMILY_6390, +		.ops = &mv88e6390_ops, +	}, +	[MV88E6390X] = { +		.prod_num = PORT_SWITCH_ID_PROD_NUM_6390X, +		.family = MV88E6XXX_FAMILY_6390, +		.name = "Marvell 88E6390X", +		.num_databases = 4096, +		.num_ports = 11,	/* 10 + Z80 */ +		.port_base_addr = 0x0, +		.global1_addr = 0x1b, +		.age_time_coeff = 15000, +		.g1_irqs = 9, +		.tag_protocol = DSA_TAG_PROTO_DSA, +		.flags = MV88E6XXX_FLAGS_FAMILY_6390, +		.ops = &mv88e6390x_ops, +	},  };  static const struct mv88e6xxx_info *mv88e6xxx_lookup_info(unsigned int prod_num) @@ -3600,13 +4221,13 @@ static struct mv88e6xxx_chip *mv88e6xxx_alloc_chip(struct device *dev)  static void mv88e6xxx_phy_init(struct mv88e6xxx_chip *chip)  { -	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU)) +	if (chip->info->ops->ppu_enable && chip->info->ops->ppu_disable)  		mv88e6xxx_ppu_state_init(chip);  }  static void mv88e6xxx_phy_destroy(struct mv88e6xxx_chip *chip)  { -	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU)) +	if (chip->info->ops->ppu_enable && chip->info->ops->ppu_disable)  		mv88e6xxx_ppu_state_destroy(chip);  } @@ -3634,10 +4255,7 @@ static enum dsa_tag_protocol mv88e6xxx_get_tag_protocol(struct dsa_switch *ds)  {  	struct mv88e6xxx_chip *chip = ds->priv; -	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EDSA)) -		return DSA_TAG_PROTO_EDSA; - -	return DSA_TAG_PROTO_DSA; +	return chip->info->tag_protocol;  }  static const char *mv88e6xxx_drv_probe(struct device *dsa_dev, @@ -3667,6 +4285,12 @@ static const char *mv88e6xxx_drv_probe(struct device *dsa_dev,  	if (err)  		goto free; +	mutex_lock(&chip->reg_lock); +	err = mv88e6xxx_switch_reset(chip); +	mutex_unlock(&chip->reg_lock); +	if (err) +		goto free; +  	mv88e6xxx_phy_init(chip);  	err = mv88e6xxx_mdio_register(chip, NULL); @@ -3819,35 +4443,82 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev)  	chip->info = compat_info; +	err = mv88e6xxx_verify_madatory_ops(chip, chip->info->ops); +	if (err) +		return err; +  	err = mv88e6xxx_smi_init(chip, mdiodev->bus, mdiodev->addr);  	if (err)  		return err; +	chip->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); +	if (IS_ERR(chip->reset)) +		return PTR_ERR(chip->reset); +  	err = mv88e6xxx_detect(chip);  	if (err)  		return err;  	mv88e6xxx_phy_init(chip); -	chip->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS); -	if (IS_ERR(chip->reset)) -		return PTR_ERR(chip->reset); -  	if (chip->info->ops->get_eeprom &&  	    !of_property_read_u32(np, "eeprom-length", &eeprom_len))  		chip->eeprom_len = eeprom_len; +	mutex_lock(&chip->reg_lock); +	err = mv88e6xxx_switch_reset(chip); +	mutex_unlock(&chip->reg_lock); +	if (err) +		goto out; + +	chip->irq = of_irq_get(np, 0); +	if (chip->irq == -EPROBE_DEFER) { +		err = chip->irq; +		goto out; +	} + +	if (chip->irq > 0) { +		/* Has to be performed before the MDIO bus is created, +		 * because the PHYs will link there interrupts to these +		 * interrupt controllers +		 */ +		mutex_lock(&chip->reg_lock); +		err = mv88e6xxx_g1_irq_setup(chip); +		mutex_unlock(&chip->reg_lock); + +		if (err) +			goto out; + +		if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT)) { +			err = mv88e6xxx_g2_irq_setup(chip); +			if (err) +				goto out_g1_irq; +		} +	} +  	err = mv88e6xxx_mdio_register(chip, np);  	if (err) -		return err; +		goto out_g2_irq;  	err = mv88e6xxx_register_switch(chip, np); -	if (err) { -		mv88e6xxx_mdio_unregister(chip); -		return err; -	} +	if (err) +		goto out_mdio;  	return 0; + +out_mdio: +	mv88e6xxx_mdio_unregister(chip); +out_g2_irq: +	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT) && chip->irq > 0) +		mv88e6xxx_g2_irq_free(chip); +out_g1_irq: +	if (chip->irq > 0) { +		mutex_lock(&chip->reg_lock); +		mv88e6xxx_g1_irq_free(chip); +		mutex_unlock(&chip->reg_lock); +	} +out: +	return err;  }  static void mv88e6xxx_remove(struct mdio_device *mdiodev) @@ -3858,6 +4529,12 @@ static void mv88e6xxx_remove(struct mdio_device *mdiodev)  	mv88e6xxx_phy_destroy(chip);  	mv88e6xxx_unregister_switch(chip);  	mv88e6xxx_mdio_unregister(chip); + +	if (chip->irq > 0) { +		if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT)) +			mv88e6xxx_g2_irq_free(chip); +		mv88e6xxx_g1_irq_free(chip); +	}  }  static const struct of_device_id mv88e6xxx_of_match[] = { @@ -3865,6 +4542,10 @@ static const struct of_device_id mv88e6xxx_of_match[] = {  		.compatible = "marvell,mv88e6085",  		.data = &mv88e6xxx_table[MV88E6085],  	}, +	{ +		.compatible = "marvell,mv88e6190", +		.data = &mv88e6xxx_table[MV88E6190], +	},  	{ /* sentinel */ },  }; diff --git a/drivers/net/dsa/mv88e6xxx/global1.c b/drivers/net/dsa/mv88e6xxx/global1.c index d358720b6c2d..75af86a7fad8 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.c +++ b/drivers/net/dsa/mv88e6xxx/global1.c @@ -32,3 +32,370 @@ int mv88e6xxx_g1_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask)  {  	return mv88e6xxx_wait(chip, chip->info->global1_addr, reg, mask);  } + +/* Offset 0x00: Switch Global Status Register */ + +static int mv88e6185_g1_wait_ppu_disabled(struct mv88e6xxx_chip *chip) +{ +	u16 state; +	int i, err; + +	for (i = 0; i < 16; i++) { +		err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &state); +		if (err) +			return err; + +		/* Check the value of the PPUState bits 15:14 */ +		state &= GLOBAL_STATUS_PPU_STATE_MASK; +		if (state != GLOBAL_STATUS_PPU_STATE_POLLING) +			return 0; + +		usleep_range(1000, 2000); +	} + +	return -ETIMEDOUT; +} + +static int mv88e6185_g1_wait_ppu_polling(struct mv88e6xxx_chip *chip) +{ +	u16 state; +	int i, err; + +	for (i = 0; i < 16; ++i) { +		err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &state); +		if (err) +			return err; + +		/* Check the value of the PPUState bits 15:14 */ +		state &= GLOBAL_STATUS_PPU_STATE_MASK; +		if (state == GLOBAL_STATUS_PPU_STATE_POLLING) +			return 0; + +		usleep_range(1000, 2000); +	} + +	return -ETIMEDOUT; +} + +static int mv88e6352_g1_wait_ppu_polling(struct mv88e6xxx_chip *chip) +{ +	u16 state; +	int i, err; + +	for (i = 0; i < 16; ++i) { +		err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &state); +		if (err) +			return err; + +		/* Check the value of the PPUState (or InitState) bit 15 */ +		if (state & GLOBAL_STATUS_PPU_STATE) +			return 0; + +		usleep_range(1000, 2000); +	} + +	return -ETIMEDOUT; +} + +static int mv88e6xxx_g1_wait_init_ready(struct mv88e6xxx_chip *chip) +{ +	const unsigned long timeout = jiffies + 1 * HZ; +	u16 val; +	int err; + +	/* Wait up to 1 second for the switch to be ready. The InitReady bit 11 +	 * is set to a one when all units inside the device (ATU, VTU, etc.) +	 * have finished their initialization and are ready to accept frames. +	 */ +	while (time_before(jiffies, timeout)) { +		err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &val); +		if (err) +			return err; + +		if (val & GLOBAL_STATUS_INIT_READY) +			break; + +		usleep_range(1000, 2000); +	} + +	if (time_after(jiffies, timeout)) +		return -ETIMEDOUT; + +	return 0; +} + +/* Offset 0x04: Switch Global Control Register */ + +int mv88e6185_g1_reset(struct mv88e6xxx_chip *chip) +{ +	u16 val; +	int err; + +	/* Set the SWReset bit 15 along with the PPUEn bit 14, to also restart +	 * the PPU, including re-doing PHY detection and initialization +	 */ +	err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val); +	if (err) +		return err; + +	val |= GLOBAL_CONTROL_SW_RESET; +	val |= GLOBAL_CONTROL_PPU_ENABLE; + +	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, val); +	if (err) +		return err; + +	err = mv88e6xxx_g1_wait_init_ready(chip); +	if (err) +		return err; + +	return mv88e6185_g1_wait_ppu_polling(chip); +} + +int mv88e6352_g1_reset(struct mv88e6xxx_chip *chip) +{ +	u16 val; +	int err; + +	/* Set the SWReset bit 15 */ +	err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val); +	if (err) +		return err; + +	val |= GLOBAL_CONTROL_SW_RESET; + +	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, val); +	if (err) +		return err; + +	err = mv88e6xxx_g1_wait_init_ready(chip); +	if (err) +		return err; + +	return mv88e6352_g1_wait_ppu_polling(chip); +} + +int mv88e6185_g1_ppu_enable(struct mv88e6xxx_chip *chip) +{ +	u16 val; +	int err; + +	err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val); +	if (err) +		return err; + +	val |= GLOBAL_CONTROL_PPU_ENABLE; + +	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, val); +	if (err) +		return err; + +	return mv88e6185_g1_wait_ppu_polling(chip); +} + +int mv88e6185_g1_ppu_disable(struct mv88e6xxx_chip *chip) +{ +	u16 val; +	int err; + +	err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val); +	if (err) +		return err; + +	val &= ~GLOBAL_CONTROL_PPU_ENABLE; + +	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, val); +	if (err) +		return err; + +	return mv88e6185_g1_wait_ppu_disabled(chip); +} + +/* Offset 0x1a: Monitor Control */ +/* Offset 0x1a: Monitor & MGMT Control on some devices */ + +int mv88e6095_g1_set_egress_port(struct mv88e6xxx_chip *chip, int port) +{ +	u16 reg; +	int err; + +	err = mv88e6xxx_g1_read(chip, GLOBAL_MONITOR_CONTROL, ®); +	if (err) +		return err; + +	reg &= ~(GLOBAL_MONITOR_CONTROL_INGRESS_MASK | +		 GLOBAL_MONITOR_CONTROL_EGRESS_MASK); + +	reg |= port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT | +		port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT; + +	return mv88e6xxx_g1_write(chip, GLOBAL_MONITOR_CONTROL, reg); +} + +/* Older generations also call this the ARP destination. It has been + * generalized in more modern devices such that more than ARP can + * egress it + */ +int mv88e6095_g1_set_cpu_port(struct mv88e6xxx_chip *chip, int port) +{ +	u16 reg; +	int err; + +	err = mv88e6xxx_g1_read(chip, GLOBAL_MONITOR_CONTROL, ®); +	if (err) +		return err; + +	reg &= ~GLOBAL_MONITOR_CONTROL_ARP_MASK; +	reg |= port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT; + +	return mv88e6xxx_g1_write(chip, GLOBAL_MONITOR_CONTROL, reg); +} + +static int mv88e6390_g1_monitor_write(struct mv88e6xxx_chip *chip, +				      u16 pointer, u8 data) +{ +	u16 reg; + +	reg = GLOBAL_MONITOR_CONTROL_UPDATE | pointer | data; + +	return mv88e6xxx_g1_write(chip, GLOBAL_MONITOR_CONTROL, reg); +} + +int mv88e6390_g1_set_egress_port(struct mv88e6xxx_chip *chip, int port) +{ +	int err; + +	err = mv88e6390_g1_monitor_write(chip, GLOBAL_MONITOR_CONTROL_INGRESS, +					 port); +	if (err) +		return err; + +	return mv88e6390_g1_monitor_write(chip, GLOBAL_MONITOR_CONTROL_EGRESS, +					  port); +} + +int mv88e6390_g1_set_cpu_port(struct mv88e6xxx_chip *chip, int port) +{ +	return mv88e6390_g1_monitor_write(chip, GLOBAL_MONITOR_CONTROL_CPU_DEST, +					  port); +} + +int mv88e6390_g1_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip) +{ +	int err; + +	/* 01:c2:80:00:00:00:00-01:c2:80:00:00:00:07 are Management */ +	err = mv88e6390_g1_monitor_write( +		chip, GLOBAL_MONITOR_CONTROL_0180C280000000XLO, 0xff); +	if (err) +		return err; + +	/* 01:c2:80:00:00:00:08-01:c2:80:00:00:00:0f are Management */ +	err = mv88e6390_g1_monitor_write( +		chip, GLOBAL_MONITOR_CONTROL_0180C280000000XHI, 0xff); +	if (err) +		return err; + +	/* 01:c2:80:00:00:00:20-01:c2:80:00:00:00:27 are Management */ +	err = mv88e6390_g1_monitor_write( +		chip, GLOBAL_MONITOR_CONTROL_0180C280000002XLO, 0xff); +	if (err) +		return err; + +	/* 01:c2:80:00:00:00:28-01:c2:80:00:00:00:2f are Management */ +	return mv88e6390_g1_monitor_write( +		chip, GLOBAL_MONITOR_CONTROL_0180C280000002XHI, 0xff); +} + +/* Offset 0x1c: Global Control 2 */ + +int mv88e6390_g1_stats_set_histogram(struct mv88e6xxx_chip *chip) +{ +	u16 val; +	int err; + +	err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL_2, &val); +	if (err) +		return err; + +	val |= GLOBAL_CONTROL_2_HIST_RX_TX; + +	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL_2, val); + +	return err; +} + +/* Offset 0x1d: Statistics Operation 2 */ + +int mv88e6xxx_g1_stats_wait(struct mv88e6xxx_chip *chip) +{ +	return mv88e6xxx_g1_wait(chip, GLOBAL_STATS_OP, GLOBAL_STATS_OP_BUSY); +} + +int mv88e6xxx_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port) +{ +	int err; + +	/* Snapshot the hardware statistics counters for this port. */ +	err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP, +				 GLOBAL_STATS_OP_CAPTURE_PORT | +				 GLOBAL_STATS_OP_HIST_RX_TX | port); +	if (err) +		return err; + +	/* Wait for the snapshotting to complete. */ +	return mv88e6xxx_g1_stats_wait(chip); +} + +int mv88e6320_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port) +{ +	port = (port + 1) << 5; + +	return mv88e6xxx_g1_stats_snapshot(chip, port); +} + +int mv88e6390_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port) +{ +	int err; + +	port = (port + 1) << 5; + +	/* Snapshot the hardware statistics counters for this port. */ +	err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP, +				 GLOBAL_STATS_OP_CAPTURE_PORT | port); +	if (err) +		return err; + +	/* Wait for the snapshotting to complete. */ +	return mv88e6xxx_g1_stats_wait(chip); +} + +void mv88e6xxx_g1_stats_read(struct mv88e6xxx_chip *chip, int stat, u32 *val) +{ +	u32 value; +	u16 reg; +	int err; + +	*val = 0; + +	err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP, +				 GLOBAL_STATS_OP_READ_CAPTURED | stat); +	if (err) +		return; + +	err = mv88e6xxx_g1_stats_wait(chip); +	if (err) +		return; + +	err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_COUNTER_32, ®); +	if (err) +		return; + +	value = reg << 16; + +	err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_COUNTER_01, ®); +	if (err) +		return; + +	*val = value | reg; +} diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index 62291e6fe3a3..1aec7382c02d 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -20,4 +20,22 @@ int mv88e6xxx_g1_read(struct mv88e6xxx_chip *chip, int reg, u16 *val);  int mv88e6xxx_g1_write(struct mv88e6xxx_chip *chip, int reg, u16 val);  int mv88e6xxx_g1_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask); +int mv88e6185_g1_reset(struct mv88e6xxx_chip *chip); +int mv88e6352_g1_reset(struct mv88e6xxx_chip *chip); + +int mv88e6185_g1_ppu_enable(struct mv88e6xxx_chip *chip); +int mv88e6185_g1_ppu_disable(struct mv88e6xxx_chip *chip); + +int mv88e6xxx_g1_stats_wait(struct mv88e6xxx_chip *chip); +int mv88e6xxx_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port); +int mv88e6320_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port); +int mv88e6390_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port); +int mv88e6390_g1_stats_set_histogram(struct mv88e6xxx_chip *chip); +void mv88e6xxx_g1_stats_read(struct mv88e6xxx_chip *chip, int stat, u32 *val); +int mv88e6095_g1_set_egress_port(struct mv88e6xxx_chip *chip, int port); +int mv88e6390_g1_set_egress_port(struct mv88e6xxx_chip *chip, int port); +int mv88e6095_g1_set_cpu_port(struct mv88e6xxx_chip *chip, int port); +int mv88e6390_g1_set_cpu_port(struct mv88e6xxx_chip *chip, int port); +int mv88e6390_g1_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip); +  #endif /* _MV88E6XXX_GLOBAL1_H */ diff --git a/drivers/net/dsa/mv88e6xxx/global2.c b/drivers/net/dsa/mv88e6xxx/global2.c index cf686e7506a9..3e77071949ab 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.c +++ b/drivers/net/dsa/mv88e6xxx/global2.c @@ -1,5 +1,6 @@  /* - * Marvell 88E6xxx Switch Global 2 Registers support (device address 0x1C) + * Marvell 88E6xxx Switch Global 2 Registers support (device address + * 0x1C)   *   * Copyright (c) 2008 Marvell Semiconductor   * @@ -11,6 +12,7 @@   * (at your option) any later version.   */ +#include <linux/irqdomain.h>  #include "mv88e6xxx.h"  #include "global2.h" @@ -36,6 +38,31 @@ static int mv88e6xxx_g2_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask)  	return mv88e6xxx_wait(chip, ADDR_GLOBAL2, reg, mask);  } +/* Offset 0x02: Management Enable 2x */ +/* Offset 0x03: Management Enable 0x */ + +int mv88e6095_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip) +{ +	int err; + +	/* Consider the frames with reserved multicast destination +	 * addresses matching 01:80:c2:00:00:2x as MGMT. +	 */ +	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X)) { +		err = mv88e6xxx_g2_write(chip, GLOBAL2_MGMT_EN_2X, 0xffff); +		if (err) +			return err; +	} + +	/* Consider the frames with reserved multicast destination +	 * addresses matching 01:80:c2:00:00:0x as MGMT. +	 */ +	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X)) +		return mv88e6xxx_g2_write(chip, GLOBAL2_MGMT_EN_0X, 0xffff); + +	return 0; +} +  /* Offset 0x06: Device Mapping Table register */  static int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip, @@ -417,29 +444,154 @@ int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip, int addr, int reg,  	return mv88e6xxx_g2_smi_phy_cmd(chip, cmd);  } -int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip) +static void mv88e6xxx_g2_irq_mask(struct irq_data *d)  { -	u16 reg; +	struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); +	unsigned int n = d->hwirq; + +	chip->g2_irq.masked |= (1 << n); +} + +static void mv88e6xxx_g2_irq_unmask(struct irq_data *d) +{ +	struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); +	unsigned int n = d->hwirq; + +	chip->g2_irq.masked &= ~(1 << n); +} + +static irqreturn_t mv88e6xxx_g2_irq_thread_fn(int irq, void *dev_id) +{ +	struct mv88e6xxx_chip *chip = dev_id; +	unsigned int nhandled = 0; +	unsigned int sub_irq; +	unsigned int n;  	int err; +	u16 reg; -	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X)) { -		/* Consider the frames with reserved multicast destination -		 * addresses matching 01:80:c2:00:00:2x as MGMT. -		 */ -		err = mv88e6xxx_g2_write(chip, GLOBAL2_MGMT_EN_2X, 0xffff); -		if (err) -			return err; +	mutex_lock(&chip->reg_lock); +	err = mv88e6xxx_g2_read(chip, GLOBAL2_INT_SOURCE, ®); +	mutex_unlock(&chip->reg_lock); +	if (err) +		goto out; + +	for (n = 0; n < 16; ++n) { +		if (reg & (1 << n)) { +			sub_irq = irq_find_mapping(chip->g2_irq.domain, n); +			handle_nested_irq(sub_irq); +			++nhandled; +		}  	} +out: +	return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE); +} -	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X)) { -		/* Consider the frames with reserved multicast destination -		 * addresses matching 01:80:c2:00:00:0x as MGMT. -		 */ -		err = mv88e6xxx_g2_write(chip, GLOBAL2_MGMT_EN_0X, 0xffff); -		if (err) -			return err; +static void mv88e6xxx_g2_irq_bus_lock(struct irq_data *d) +{ +	struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); + +	mutex_lock(&chip->reg_lock); +} + +static void mv88e6xxx_g2_irq_bus_sync_unlock(struct irq_data *d) +{ +	struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); + +	mv88e6xxx_g2_write(chip, GLOBAL2_INT_MASK, ~chip->g2_irq.masked); + +	mutex_unlock(&chip->reg_lock); +} + +static struct irq_chip mv88e6xxx_g2_irq_chip = { +	.name			= "mv88e6xxx-g2", +	.irq_mask		= mv88e6xxx_g2_irq_mask, +	.irq_unmask		= mv88e6xxx_g2_irq_unmask, +	.irq_bus_lock		= mv88e6xxx_g2_irq_bus_lock, +	.irq_bus_sync_unlock	= mv88e6xxx_g2_irq_bus_sync_unlock, +}; + +static int mv88e6xxx_g2_irq_domain_map(struct irq_domain *d, +				       unsigned int irq, +				       irq_hw_number_t hwirq) +{ +	struct mv88e6xxx_chip *chip = d->host_data; + +	irq_set_chip_data(irq, d->host_data); +	irq_set_chip_and_handler(irq, &chip->g2_irq.chip, handle_level_irq); +	irq_set_noprobe(irq); + +	return 0; +} + +static const struct irq_domain_ops mv88e6xxx_g2_irq_domain_ops = { +	.map	= mv88e6xxx_g2_irq_domain_map, +	.xlate	= irq_domain_xlate_twocell, +}; + +void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip) +{ +	int irq, virq; + +	free_irq(chip->device_irq, chip); +	irq_dispose_mapping(chip->device_irq); + +	for (irq = 0; irq < 16; irq++) { +		virq = irq_find_mapping(chip->g2_irq.domain, irq); +		irq_dispose_mapping(virq);  	} +	irq_domain_remove(chip->g2_irq.domain); +} + +int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip) +{ +	int err, irq, virq; + +	if (!chip->dev->of_node) +		return -EINVAL; + +	chip->g2_irq.domain = irq_domain_add_simple( +		chip->dev->of_node, 16, 0, &mv88e6xxx_g2_irq_domain_ops, chip); +	if (!chip->g2_irq.domain) +		return -ENOMEM; + +	for (irq = 0; irq < 16; irq++) +		irq_create_mapping(chip->g2_irq.domain, irq); + +	chip->g2_irq.chip = mv88e6xxx_g2_irq_chip; +	chip->g2_irq.masked = ~0; + +	chip->device_irq = irq_find_mapping(chip->g1_irq.domain, +					    GLOBAL_STATUS_IRQ_DEVICE); +	if (chip->device_irq < 0) { +		err = chip->device_irq; +		goto out; +	} + +	err = request_threaded_irq(chip->device_irq, NULL, +				   mv88e6xxx_g2_irq_thread_fn, +				   IRQF_ONESHOT, "mv88e6xxx-g1", chip); +	if (err) +		goto out; + +	return 0; + +out: +	for (irq = 0; irq < 16; irq++) { +		virq = irq_find_mapping(chip->g2_irq.domain, irq); +		irq_dispose_mapping(virq); +	} + +	irq_domain_remove(chip->g2_irq.domain); + +	return err; +} + +int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip) +{ +	u16 reg; +	int err; +  	/* Ignore removed tag data on doubly tagged packets, disable  	 * flow control messages, force flow control priority to the  	 * highest, and send all special multicast frames to the CPU diff --git a/drivers/net/dsa/mv88e6xxx/global2.h b/drivers/net/dsa/mv88e6xxx/global2.h index c4bb9035ee3a..9aefb7d8b0ad 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.h +++ b/drivers/net/dsa/mv88e6xxx/global2.h @@ -33,6 +33,9 @@ int mv88e6xxx_g2_get_eeprom16(struct mv88e6xxx_chip *chip,  int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip,  			      struct ethtool_eeprom *eeprom, u8 *data);  int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip); +int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip); +void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip); +int mv88e6095_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip);  #else /* !CONFIG_NET_DSA_MV88E6XXX_GLOBAL2 */ @@ -83,6 +86,20 @@ static inline int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip)  	return -EOPNOTSUPP;  } +static inline int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip) +{ +	return -EOPNOTSUPP; +} + +static inline void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip) +{ +} + +static inline int mv88e6095_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip) +{ +	return -EOPNOTSUPP; +} +  #endif /* CONFIG_NET_DSA_MV88E6XXX_GLOBAL2 */  #endif /* _MV88E6XXX_GLOBAL2_H */ diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index e572121c196e..af54baea47cf 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -13,6 +13,7 @@  #define __MV88E6XXX_H  #include <linux/if_vlan.h> +#include <linux/irq.h>  #include <linux/gpio/consumer.h>  #ifndef UINT64_MAX @@ -60,20 +61,29 @@  #define PORT_PCS_CTRL		0x01  #define PORT_PCS_CTRL_RGMII_DELAY_RXCLK	BIT(15)  #define PORT_PCS_CTRL_RGMII_DELAY_TXCLK	BIT(14) +#define PORT_PCS_CTRL_FORCE_SPEED	BIT(13) /* 6390 */ +#define PORT_PCS_CTRL_ALTSPEED		BIT(12) /* 6390 */ +#define PORT_PCS_CTRL_200BASE		BIT(12) /* 6352 */  #define PORT_PCS_CTRL_FC		BIT(7)  #define PORT_PCS_CTRL_FORCE_FC		BIT(6)  #define PORT_PCS_CTRL_LINK_UP		BIT(5)  #define PORT_PCS_CTRL_FORCE_LINK	BIT(4)  #define PORT_PCS_CTRL_DUPLEX_FULL	BIT(3)  #define PORT_PCS_CTRL_FORCE_DUPLEX	BIT(2) -#define PORT_PCS_CTRL_10		0x00 -#define PORT_PCS_CTRL_100		0x01 -#define PORT_PCS_CTRL_1000		0x02 -#define PORT_PCS_CTRL_UNFORCED		0x03 +#define PORT_PCS_CTRL_SPEED_MASK	(0x03) +#define PORT_PCS_CTRL_SPEED_10		(0x00) +#define PORT_PCS_CTRL_SPEED_100		(0x01) +#define PORT_PCS_CTRL_SPEED_200		(0x02) /* 6065 and non Gb chips */ +#define PORT_PCS_CTRL_SPEED_1000	(0x02) +#define PORT_PCS_CTRL_SPEED_10000	(0x03) /* 6390X */ +#define PORT_PCS_CTRL_SPEED_UNFORCED	(0x03)  #define PORT_PAUSE_CTRL		0x02 +#define PORT_FLOW_CTRL_LIMIT_IN		((0x00 << 8) | BIT(15)) +#define PORT_FLOW_CTRL_LIMIT_OUT	((0x01 << 8) | BIT(15))  #define PORT_SWITCH_ID		0x03  #define PORT_SWITCH_ID_PROD_NUM_6085	0x04a  #define PORT_SWITCH_ID_PROD_NUM_6095	0x095 +#define PORT_SWITCH_ID_PROD_NUM_6097	0x099  #define PORT_SWITCH_ID_PROD_NUM_6131	0x106  #define PORT_SWITCH_ID_PROD_NUM_6320	0x115  #define PORT_SWITCH_ID_PROD_NUM_6123	0x121 @@ -84,11 +94,17 @@  #define PORT_SWITCH_ID_PROD_NUM_6175	0x175  #define PORT_SWITCH_ID_PROD_NUM_6176	0x176  #define PORT_SWITCH_ID_PROD_NUM_6185	0x1a7 +#define PORT_SWITCH_ID_PROD_NUM_6190	0x190 +#define PORT_SWITCH_ID_PROD_NUM_6190X	0x0a0 +#define PORT_SWITCH_ID_PROD_NUM_6191	0x191  #define PORT_SWITCH_ID_PROD_NUM_6240	0x240 +#define PORT_SWITCH_ID_PROD_NUM_6290	0x290  #define PORT_SWITCH_ID_PROD_NUM_6321	0x310  #define PORT_SWITCH_ID_PROD_NUM_6352	0x352  #define PORT_SWITCH_ID_PROD_NUM_6350	0x371  #define PORT_SWITCH_ID_PROD_NUM_6351	0x375 +#define PORT_SWITCH_ID_PROD_NUM_6390	0x390 +#define PORT_SWITCH_ID_PROD_NUM_6390X	0x0a1  #define PORT_CONTROL		0x04  #define PORT_CONTROL_USE_CORE_TAG	BIT(15)  #define PORT_CONTROL_DROP_ON_LOCK	BIT(14) @@ -96,6 +112,7 @@  #define PORT_CONTROL_EGRESS_UNTAGGED	(0x1 << 12)  #define PORT_CONTROL_EGRESS_TAGGED	(0x2 << 12)  #define PORT_CONTROL_EGRESS_ADD_TAG	(0x3 << 12) +#define PORT_CONTROL_EGRESS_MASK	(0x3 << 12)  #define PORT_CONTROL_HEADER		BIT(11)  #define PORT_CONTROL_IGMP_MLD_SNOOP	BIT(10)  #define PORT_CONTROL_DOUBLE_TAG		BIT(9) @@ -103,6 +120,7 @@  #define PORT_CONTROL_FRAME_MODE_DSA		(0x1 << 8)  #define PORT_CONTROL_FRAME_MODE_PROVIDER	(0x2 << 8)  #define PORT_CONTROL_FRAME_ETHER_TYPE_DSA	(0x3 << 8) +#define PORT_CONTROL_FRAME_MASK			(0x3 << 8)  #define PORT_CONTROL_DSA_TAG		BIT(8)  #define PORT_CONTROL_VLAN_TUNNEL	BIT(7)  #define PORT_CONTROL_TAG_IF_BOTH	BIT(6) @@ -110,6 +128,10 @@  #define PORT_CONTROL_USE_TAG		BIT(4)  #define PORT_CONTROL_FORWARD_UNKNOWN_MC	BIT(3)  #define PORT_CONTROL_FORWARD_UNKNOWN	BIT(2) +#define PORT_CONTROL_NOT_EGRESS_UNKNOWN_DA		(0x0 << 2) +#define PORT_CONTROL_NOT_EGRESS_UNKNOWN_MULTICAST_DA	(0x1 << 2) +#define PORT_CONTROL_NOT_EGRESS_UNKNOWN_UNITCAST_DA	(0x2 << 2) +#define PORT_CONTROL_EGRESS_ALL_UNKNOWN_DA		(0x3 << 2)  #define PORT_CONTROL_STATE_MASK		0x03  #define PORT_CONTROL_STATE_DISABLED	0x00  #define PORT_CONTROL_STATE_BLOCKING	0x01 @@ -158,15 +180,34 @@  #define PORT_OUT_FILTERED	0x13  #define PORT_TAG_REGMAP_0123	0x18  #define PORT_TAG_REGMAP_4567	0x19 +#define PORT_IEEE_PRIO_MAP_TABLE	0x18    /* 6390 */ +#define PORT_IEEE_PRIO_MAP_TABLE_UPDATE		BIT(15) +#define PORT_IEEE_PRIO_MAP_TABLE_INGRESS_PCP		(0x0 << 12) +#define PORT_IEEE_PRIO_MAP_TABLE_EGRESS_GREEN_PCP	(0x1 << 12) +#define PORT_IEEE_PRIO_MAP_TABLE_EGRESS_YELLOW_PCP	(0x2 << 12) +#define PORT_IEEE_PRIO_MAP_TABLE_EGRESS_AVB_PCP		(0x3 << 12) +#define PORT_IEEE_PRIO_MAP_TABLE_EGRESS_GREEN_DSCP	(0x5 << 12) +#define PORT_IEEE_PRIO_MAP_TABLE_EGRESS_YELLOW_DSCP	(0x6 << 12) +#define PORT_IEEE_PRIO_MAP_TABLE_EGRESS_AVB_DSCP	(0x7 << 12) +#define PORT_IEEE_PRIO_MAP_TABLE_POINTER_SHIFT		9  #define GLOBAL_STATUS		0x00  #define GLOBAL_STATUS_PPU_STATE BIT(15) /* 6351 and 6171 */ -/* Two bits for 6165, 6185 etc */ -#define GLOBAL_STATUS_PPU_MASK		(0x3 << 14) -#define GLOBAL_STATUS_PPU_DISABLED_RST	(0x0 << 14) -#define GLOBAL_STATUS_PPU_INITIALIZING	(0x1 << 14) -#define GLOBAL_STATUS_PPU_DISABLED	(0x2 << 14) -#define GLOBAL_STATUS_PPU_POLLING	(0x3 << 14) +#define GLOBAL_STATUS_PPU_STATE_MASK		(0x3 << 14) /* 6165 6185 */ +#define GLOBAL_STATUS_PPU_STATE_DISABLED_RST	(0x0 << 14) +#define GLOBAL_STATUS_PPU_STATE_INITIALIZING	(0x1 << 14) +#define GLOBAL_STATUS_PPU_STATE_DISABLED	(0x2 << 14) +#define GLOBAL_STATUS_PPU_STATE_POLLING		(0x3 << 14) +#define GLOBAL_STATUS_INIT_READY	BIT(11) +#define GLOBAL_STATUS_IRQ_AVB		8 +#define GLOBAL_STATUS_IRQ_DEVICE	7 +#define GLOBAL_STATUS_IRQ_STATS		6 +#define GLOBAL_STATUS_IRQ_VTU_PROBLEM	5 +#define GLOBAL_STATUS_IRQ_VTU_DONE	4 +#define GLOBAL_STATUS_IRQ_ATU_PROBLEM	3 +#define GLOBAL_STATUS_IRQ_ATU_DONE	2 +#define GLOBAL_STATUS_IRQ_TCAM_DONE	1 +#define GLOBAL_STATUS_IRQ_EEPROM_DONE	0  #define GLOBAL_MAC_01		0x01  #define GLOBAL_MAC_23		0x02  #define GLOBAL_MAC_45		0x03 @@ -254,14 +295,27 @@  #define GLOBAL_CORE_TAG_TYPE	0x19  #define GLOBAL_MONITOR_CONTROL	0x1a  #define GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT	12 +#define GLOBAL_MONITOR_CONTROL_INGRESS_MASK	(0xf << 12)  #define GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT	8 +#define GLOBAL_MONITOR_CONTROL_EGRESS_MASK	(0xf << 8)  #define GLOBAL_MONITOR_CONTROL_ARP_SHIFT	4 +#define GLOBAL_MONITOR_CONTROL_ARP_MASK	        (0xf << 4)  #define GLOBAL_MONITOR_CONTROL_MIRROR_SHIFT	0  #define GLOBAL_MONITOR_CONTROL_ARP_DISABLED	(0xf0) +#define GLOBAL_MONITOR_CONTROL_UPDATE			BIT(15) +#define GLOBAL_MONITOR_CONTROL_0180C280000000XLO	(0x00 << 8) +#define GLOBAL_MONITOR_CONTROL_0180C280000000XHI	(0x01 << 8) +#define GLOBAL_MONITOR_CONTROL_0180C280000002XLO	(0x02 << 8) +#define GLOBAL_MONITOR_CONTROL_0180C280000002XHI	(0x03 << 8) +#define GLOBAL_MONITOR_CONTROL_INGRESS			(0x20 << 8) +#define GLOBAL_MONITOR_CONTROL_EGRESS			(0x21 << 8) +#define GLOBAL_MONITOR_CONTROL_CPU_DEST			(0x30 << 8)  #define GLOBAL_CONTROL_2	0x1c  #define GLOBAL_CONTROL_2_NO_CASCADE		0xe000  #define GLOBAL_CONTROL_2_MULTIPLE_CASCADE	0xf000 - +#define GLOBAL_CONTROL_2_HIST_RX	       (0x1 << 6) +#define GLOBAL_CONTROL_2_HIST_TX	       (0x2 << 6) +#define GLOBAL_CONTROL_2_HIST_RX_TX	       (0x3 << 6)  #define GLOBAL_STATS_OP		0x1d  #define GLOBAL_STATS_OP_BUSY	BIT(15)  #define GLOBAL_STATS_OP_NOP		(0 << 12) @@ -272,7 +326,8 @@  #define GLOBAL_STATS_OP_HIST_RX		((1 << 10) | GLOBAL_STATS_OP_BUSY)  #define GLOBAL_STATS_OP_HIST_TX		((2 << 10) | GLOBAL_STATS_OP_BUSY)  #define GLOBAL_STATS_OP_HIST_RX_TX	((3 << 10) | GLOBAL_STATS_OP_BUSY) -#define GLOBAL_STATS_OP_BANK_1	BIT(9) +#define GLOBAL_STATS_OP_BANK_1_BIT_9	BIT(9) +#define GLOBAL_STATS_OP_BANK_1_BIT_10	BIT(10)  #define GLOBAL_STATS_COUNTER_32	0x1e  #define GLOBAL_STATS_COUNTER_01	0x1f @@ -349,10 +404,18 @@  #define MV88E6XXX_N_FID		4096 +enum mv88e6xxx_frame_mode { +	MV88E6XXX_FRAME_MODE_NORMAL, +	MV88E6XXX_FRAME_MODE_DSA, +	MV88E6XXX_FRAME_MODE_PROVIDER, +	MV88E6XXX_FRAME_MODE_ETHERTYPE, +}; +  /* List of supported models */  enum mv88e6xxx_model {  	MV88E6085,  	MV88E6095, +	MV88E6097,  	MV88E6123,  	MV88E6131,  	MV88E6161, @@ -362,12 +425,18 @@ enum mv88e6xxx_model {  	MV88E6175,  	MV88E6176,  	MV88E6185, +	MV88E6190, +	MV88E6190X, +	MV88E6191,  	MV88E6240, +	MV88E6290,  	MV88E6320,  	MV88E6321,  	MV88E6350,  	MV88E6351,  	MV88E6352, +	MV88E6390, +	MV88E6390X,  };  enum mv88e6xxx_family { @@ -380,15 +449,10 @@ enum mv88e6xxx_family {  	MV88E6XXX_FAMILY_6320,	/* 6320 6321 */  	MV88E6XXX_FAMILY_6351,	/* 6171 6175 6350 6351 */  	MV88E6XXX_FAMILY_6352,	/* 6172 6176 6240 6352 */ +	MV88E6XXX_FAMILY_6390,  /* 6190 6190X 6191 6290 6390 6390X */  };  enum mv88e6xxx_cap { -	/* Two different tag protocols can be used by the driver. All -	 * switches support DSA, but only later generations support -	 * EDSA. -	 */ -	MV88E6XXX_CAP_EDSA, -  	/* Energy Efficient Ethernet.  	 */  	MV88E6XXX_CAP_EEE, @@ -417,6 +481,7 @@ enum mv88e6xxx_cap {  	 * The device contains a second set of global 16-bit registers.  	 */  	MV88E6XXX_CAP_GLOBAL2, +	MV88E6XXX_CAP_G2_INT,		/* (0x00) Interrupt Status */  	MV88E6XXX_CAP_G2_MGMT_EN_2X,	/* (0x02) MGMT Enable Register 2x */  	MV88E6XXX_CAP_G2_MGMT_EN_0X,	/* (0x03) MGMT Enable Register 0x */  	MV88E6XXX_CAP_G2_IRL_CMD,	/* (0x09) Ingress Rate Command */ @@ -425,12 +490,6 @@ enum mv88e6xxx_cap {  	MV88E6XXX_CAP_G2_PVT_DATA,	/* (0x0c) Cross Chip Port VLAN Data */  	MV88E6XXX_CAP_G2_POT,		/* (0x0f) Priority Override Table */ -	/* PHY Polling Unit. -	 * See GLOBAL_CONTROL_PPU_ENABLE and GLOBAL_STATUS_PPU_POLLING. -	 */ -	MV88E6XXX_CAP_PPU, -	MV88E6XXX_CAP_PPU_ACTIVE, -  	/* Per VLAN Spanning Tree Unit (STU).  	 * The Port State database, if present, is accessed through VTU  	 * operations and dedicated SID registers. See GLOBAL_VTU_SID. @@ -450,7 +509,6 @@ enum mv88e6xxx_cap {  };  /* Bitmask of capabilities */ -#define MV88E6XXX_FLAG_EDSA		BIT_ULL(MV88E6XXX_CAP_EDSA)  #define MV88E6XXX_FLAG_EEE		BIT_ULL(MV88E6XXX_CAP_EEE)  #define MV88E6XXX_FLAG_SMI_CMD		BIT_ULL(MV88E6XXX_CAP_SMI_CMD) @@ -464,6 +522,7 @@ enum mv88e6xxx_cap {  #define MV88E6XXX_FLAG_G1_VTU_FID	BIT_ULL(MV88E6XXX_CAP_G1_VTU_FID)  #define MV88E6XXX_FLAG_GLOBAL2		BIT_ULL(MV88E6XXX_CAP_GLOBAL2) +#define MV88E6XXX_FLAG_G2_INT		BIT_ULL(MV88E6XXX_CAP_G2_INT)  #define MV88E6XXX_FLAG_G2_MGMT_EN_2X	BIT_ULL(MV88E6XXX_CAP_G2_MGMT_EN_2X)  #define MV88E6XXX_FLAG_G2_MGMT_EN_0X	BIT_ULL(MV88E6XXX_CAP_G2_MGMT_EN_0X)  #define MV88E6XXX_FLAG_G2_IRL_CMD	BIT_ULL(MV88E6XXX_CAP_G2_IRL_CMD) @@ -472,8 +531,6 @@ enum mv88e6xxx_cap {  #define MV88E6XXX_FLAG_G2_PVT_DATA	BIT_ULL(MV88E6XXX_CAP_G2_PVT_DATA)  #define MV88E6XXX_FLAG_G2_POT		BIT_ULL(MV88E6XXX_CAP_G2_POT) -#define MV88E6XXX_FLAG_PPU		BIT_ULL(MV88E6XXX_CAP_PPU) -#define MV88E6XXX_FLAG_PPU_ACTIVE	BIT_ULL(MV88E6XXX_CAP_PPU_ACTIVE)  #define MV88E6XXX_FLAG_STU		BIT_ULL(MV88E6XXX_CAP_STU)  #define MV88E6XXX_FLAG_TEMP		BIT_ULL(MV88E6XXX_CAP_TEMP)  #define MV88E6XXX_FLAG_TEMP_LIMIT	BIT_ULL(MV88E6XXX_CAP_TEMP_LIMIT) @@ -502,7 +559,6 @@ enum mv88e6xxx_cap {  #define MV88E6XXX_FLAGS_FAMILY_6095	\  	(MV88E6XXX_FLAG_GLOBAL2 |	\  	 MV88E6XXX_FLAG_G2_MGMT_EN_0X |	\ -	 MV88E6XXX_FLAG_PPU |		\  	 MV88E6XXX_FLAG_VTU |		\  	 MV88E6XXX_FLAGS_MULTI_CHIP) @@ -513,7 +569,6 @@ enum mv88e6xxx_cap {  	 MV88E6XXX_FLAG_G2_MGMT_EN_2X |	\  	 MV88E6XXX_FLAG_G2_MGMT_EN_0X |	\  	 MV88E6XXX_FLAG_G2_POT |	\ -	 MV88E6XXX_FLAG_PPU |		\  	 MV88E6XXX_FLAG_STU |		\  	 MV88E6XXX_FLAG_VTU |		\  	 MV88E6XXX_FLAGS_IRL |		\ @@ -524,6 +579,7 @@ enum mv88e6xxx_cap {  	(MV88E6XXX_FLAG_G1_ATU_FID |	\  	 MV88E6XXX_FLAG_G1_VTU_FID |	\  	 MV88E6XXX_FLAG_GLOBAL2 |	\ +	 MV88E6XXX_FLAG_G2_INT |	\  	 MV88E6XXX_FLAG_G2_MGMT_EN_2X |	\  	 MV88E6XXX_FLAG_G2_MGMT_EN_0X |	\  	 MV88E6XXX_FLAG_G2_POT |	\ @@ -536,19 +592,17 @@ enum mv88e6xxx_cap {  #define MV88E6XXX_FLAGS_FAMILY_6185	\  	(MV88E6XXX_FLAG_GLOBAL2 |	\ +	 MV88E6XXX_FLAG_G2_INT |	\  	 MV88E6XXX_FLAG_G2_MGMT_EN_0X |	\  	 MV88E6XXX_FLAGS_MULTI_CHIP |	\ -	 MV88E6XXX_FLAG_PPU |		\  	 MV88E6XXX_FLAG_VTU)  #define MV88E6XXX_FLAGS_FAMILY_6320	\ -	(MV88E6XXX_FLAG_EDSA |		\ -	 MV88E6XXX_FLAG_EEE |		\ +	(MV88E6XXX_FLAG_EEE |		\  	 MV88E6XXX_FLAG_GLOBAL2 |	\  	 MV88E6XXX_FLAG_G2_MGMT_EN_2X |	\  	 MV88E6XXX_FLAG_G2_MGMT_EN_0X |	\  	 MV88E6XXX_FLAG_G2_POT |	\ -	 MV88E6XXX_FLAG_PPU_ACTIVE |	\  	 MV88E6XXX_FLAG_TEMP |		\  	 MV88E6XXX_FLAG_TEMP_LIMIT |	\  	 MV88E6XXX_FLAG_VTU |		\ @@ -557,14 +611,13 @@ enum mv88e6xxx_cap {  	 MV88E6XXX_FLAGS_PVT)  #define MV88E6XXX_FLAGS_FAMILY_6351	\ -	(MV88E6XXX_FLAG_EDSA |		\ -	 MV88E6XXX_FLAG_G1_ATU_FID |	\ +	(MV88E6XXX_FLAG_G1_ATU_FID |	\  	 MV88E6XXX_FLAG_G1_VTU_FID |	\  	 MV88E6XXX_FLAG_GLOBAL2 |	\ +	 MV88E6XXX_FLAG_G2_INT |	\  	 MV88E6XXX_FLAG_G2_MGMT_EN_2X |	\  	 MV88E6XXX_FLAG_G2_MGMT_EN_0X |	\  	 MV88E6XXX_FLAG_G2_POT |	\ -	 MV88E6XXX_FLAG_PPU_ACTIVE |	\  	 MV88E6XXX_FLAG_STU |		\  	 MV88E6XXX_FLAG_TEMP |		\  	 MV88E6XXX_FLAG_VTU |		\ @@ -573,15 +626,14 @@ enum mv88e6xxx_cap {  	 MV88E6XXX_FLAGS_PVT)  #define MV88E6XXX_FLAGS_FAMILY_6352	\ -	(MV88E6XXX_FLAG_EDSA |		\ -	 MV88E6XXX_FLAG_EEE |		\ +	(MV88E6XXX_FLAG_EEE |		\  	 MV88E6XXX_FLAG_G1_ATU_FID |	\  	 MV88E6XXX_FLAG_G1_VTU_FID |	\  	 MV88E6XXX_FLAG_GLOBAL2 |	\ +	 MV88E6XXX_FLAG_G2_INT |	\  	 MV88E6XXX_FLAG_G2_MGMT_EN_2X |	\  	 MV88E6XXX_FLAG_G2_MGMT_EN_0X |	\  	 MV88E6XXX_FLAG_G2_POT |	\ -	 MV88E6XXX_FLAG_PPU_ACTIVE |	\  	 MV88E6XXX_FLAG_STU |		\  	 MV88E6XXX_FLAG_TEMP |		\  	 MV88E6XXX_FLAG_TEMP_LIMIT |	\ @@ -593,6 +645,17 @@ enum mv88e6xxx_cap {  struct mv88e6xxx_ops; +#define MV88E6XXX_FLAGS_FAMILY_6390	\ +	(MV88E6XXX_FLAG_EEE |		\ +	 MV88E6XXX_FLAG_GLOBAL2 |	\ +	 MV88E6XXX_FLAG_STU |		\ +	 MV88E6XXX_FLAG_TEMP |		\ +	 MV88E6XXX_FLAG_TEMP_LIMIT |	\ +	 MV88E6XXX_FLAG_VTU |		\ +	 MV88E6XXX_FLAGS_IRL |		\ +	 MV88E6XXX_FLAGS_MULTI_CHIP |	\ +	 MV88E6XXX_FLAGS_PVT) +  struct mv88e6xxx_info {  	enum mv88e6xxx_family family;  	u16 prod_num; @@ -602,6 +665,8 @@ struct mv88e6xxx_info {  	unsigned int port_base_addr;  	unsigned int global1_addr;  	unsigned int age_time_coeff; +	unsigned int g1_irqs; +	enum dsa_tag_protocol tag_protocol;  	unsigned long long flags;  	const struct mv88e6xxx_ops *ops;  }; @@ -628,6 +693,13 @@ struct mv88e6xxx_priv_port {  	struct net_device *bridge_dev;  }; +struct mv88e6xxx_irq { +	u16 masked; +	struct irq_chip chip; +	struct irq_domain *domain; +	unsigned int nirqs; +}; +  struct mv88e6xxx_chip {  	const struct mv88e6xxx_info *info; @@ -677,6 +749,14 @@ struct mv88e6xxx_chip {  	/* And the MDIO bus itself */  	struct mii_bus *mdio_bus; + +	/* There can be two interrupt controllers, which are chained +	 * off a GPIO as interrupt source +	 */ +	struct mv88e6xxx_irq g1_irq; +	struct mv88e6xxx_irq g2_irq; +	int irq; +	int device_irq;  };  struct mv88e6xxx_bus_ops { @@ -696,19 +776,93 @@ struct mv88e6xxx_ops {  			u16 *val);  	int (*phy_write)(struct mv88e6xxx_chip *chip, int addr, int reg,  			 u16 val); -}; -enum stat_type { -	BANK0, -	BANK1, -	PORT, +	/* PHY Polling Unit (PPU) operations */ +	int (*ppu_enable)(struct mv88e6xxx_chip *chip); +	int (*ppu_disable)(struct mv88e6xxx_chip *chip); + +	/* Switch Software Reset */ +	int (*reset)(struct mv88e6xxx_chip *chip); + +	/* RGMII Receive/Transmit Timing Control +	 * Add delay on PHY_INTERFACE_MODE_RGMII_*ID, no delay otherwise. +	 */ +	int (*port_set_rgmii_delay)(struct mv88e6xxx_chip *chip, int port, +				    phy_interface_t mode); + +#define LINK_FORCED_DOWN	0 +#define LINK_FORCED_UP		1 +#define LINK_UNFORCED		-2 + +	/* Port's MAC link state +	 * Use LINK_FORCED_UP or LINK_FORCED_DOWN to force link up or down, +	 * or LINK_UNFORCED for normal link detection. +	 */ +	int (*port_set_link)(struct mv88e6xxx_chip *chip, int port, int link); + +#define DUPLEX_UNFORCED		-2 + +	/* Port's MAC duplex mode +	 * +	 * Use DUPLEX_HALF or DUPLEX_FULL to force half or full duplex, +	 * or DUPLEX_UNFORCED for normal duplex detection. +	 */ +	int (*port_set_duplex)(struct mv88e6xxx_chip *chip, int port, int dup); + +#define SPEED_MAX		INT_MAX +#define SPEED_UNFORCED		-2 + +	/* Port's MAC speed (in Mbps) +	 * +	 * Depending on the chip, 10, 100, 200, 1000, 2500, 10000 are valid. +	 * Use SPEED_UNFORCED for normal detection, SPEED_MAX for max value. +	 */ +	int (*port_set_speed)(struct mv88e6xxx_chip *chip, int port, int speed); + +	int (*port_tag_remap)(struct mv88e6xxx_chip *chip, int port); + +	int (*port_set_frame_mode)(struct mv88e6xxx_chip *chip, int port, +				   enum mv88e6xxx_frame_mode mode); +	int (*port_set_egress_unknowns)(struct mv88e6xxx_chip *chip, int port, +					bool on); +	int (*port_set_ether_type)(struct mv88e6xxx_chip *chip, int port, +				   u16 etype); +	int (*port_jumbo_config)(struct mv88e6xxx_chip *chip, int port); + +	int (*port_egress_rate_limiting)(struct mv88e6xxx_chip *chip, int port); +	int (*port_pause_config)(struct mv88e6xxx_chip *chip, int port); + +	/* Snapshot the statistics for a port. The statistics can then +	 * be read back a leisure but still with a consistent view. +	 */ +	int (*stats_snapshot)(struct mv88e6xxx_chip *chip, int port); + +	/* Set the histogram mode for statistics, when the control registers +	 * are separated out of the STATS_OP register. +	 */ +	int (*stats_set_histogram)(struct mv88e6xxx_chip *chip); + +	/* Return the number of strings describing statistics */ +	int (*stats_get_sset_count)(struct mv88e6xxx_chip *chip); +	void (*stats_get_strings)(struct mv88e6xxx_chip *chip,  uint8_t *data); +	void (*stats_get_stats)(struct mv88e6xxx_chip *chip,  int port, +				uint64_t *data); +	int (*g1_set_cpu_port)(struct mv88e6xxx_chip *chip, int port); +	int (*g1_set_egress_port)(struct mv88e6xxx_chip *chip, int port); + +	/* Can be either in g1 or g2, so don't use a prefix */ +	int (*mgmt_rsvd2cpu)(struct mv88e6xxx_chip *chip);  }; +#define STATS_TYPE_PORT		BIT(0) +#define STATS_TYPE_BANK0	BIT(1) +#define STATS_TYPE_BANK1	BIT(2) +  struct mv88e6xxx_hw_stat {  	char string[ETH_GSTRING_LEN];  	int sizeof_stat;  	int reg; -	enum stat_type type; +	int type;  };  static inline bool mv88e6xxx_has(struct mv88e6xxx_chip *chip, diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c new file mode 100644 index 000000000000..0db7fa0373ae --- /dev/null +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -0,0 +1,729 @@ +/* + * Marvell 88E6xxx Switch Port Registers support + * + * Copyright (c) 2008 Marvell Semiconductor + * + * Copyright (c) 2016 Vivien Didelot <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include "mv88e6xxx.h" +#include "port.h" + +int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg, +			u16 *val) +{ +	int addr = chip->info->port_base_addr + port; + +	return mv88e6xxx_read(chip, addr, reg, val); +} + +int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, +			 u16 val) +{ +	int addr = chip->info->port_base_addr + port; + +	return mv88e6xxx_write(chip, addr, reg, val); +} + +/* Offset 0x01: MAC (or PCS or Physical) Control Register + * + * Link, Duplex and Flow Control have one force bit, one value bit. + * + * For port's MAC speed, ForceSpd (or SpdValue) bits 1:0 program the value. + * Alternative values require the 200BASE (or AltSpeed) bit 12 set. + * Newer chips need a ForcedSpd bit 13 set to consider the value. + */ + +static int mv88e6xxx_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, +					  phy_interface_t mode) +{ +	u16 reg; +	int err; + +	err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, ®); +	if (err) +		return err; + +	reg &= ~(PORT_PCS_CTRL_RGMII_DELAY_RXCLK | +		 PORT_PCS_CTRL_RGMII_DELAY_TXCLK); + +	switch (mode) { +	case PHY_INTERFACE_MODE_RGMII_RXID: +		reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK; +		break; +	case PHY_INTERFACE_MODE_RGMII_TXID: +		reg |= PORT_PCS_CTRL_RGMII_DELAY_TXCLK; +		break; +	case PHY_INTERFACE_MODE_RGMII_ID: +		reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK | +			PORT_PCS_CTRL_RGMII_DELAY_TXCLK; +		break; +	case PHY_INTERFACE_MODE_RGMII: +		break; +	default: +		return 0; +	} + +	err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg); +	if (err) +		return err; + +	netdev_dbg(chip->ds->ports[port].netdev, "delay RXCLK %s, TXCLK %s\n", +		   reg & PORT_PCS_CTRL_RGMII_DELAY_RXCLK ? "yes" : "no", +		   reg & PORT_PCS_CTRL_RGMII_DELAY_TXCLK ? "yes" : "no"); + +	return 0; +} + +int mv88e6352_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, +				   phy_interface_t mode) +{ +	if (port < 5) +		return -EOPNOTSUPP; + +	return mv88e6xxx_port_set_rgmii_delay(chip, port, mode); +} + +int mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, +				   phy_interface_t mode) +{ +	if (port != 0) +		return -EOPNOTSUPP; + +	return mv88e6xxx_port_set_rgmii_delay(chip, port, mode); +} + +int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link) +{ +	u16 reg; +	int err; + +	err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, ®); +	if (err) +		return err; + +	reg &= ~(PORT_PCS_CTRL_FORCE_LINK | PORT_PCS_CTRL_LINK_UP); + +	switch (link) { +	case LINK_FORCED_DOWN: +		reg |= PORT_PCS_CTRL_FORCE_LINK; +		break; +	case LINK_FORCED_UP: +		reg |= PORT_PCS_CTRL_FORCE_LINK | PORT_PCS_CTRL_LINK_UP; +		break; +	case LINK_UNFORCED: +		/* normal link detection */ +		break; +	default: +		return -EINVAL; +	} + +	err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg); +	if (err) +		return err; + +	netdev_dbg(chip->ds->ports[port].netdev, "%s link %s\n", +		   reg & PORT_PCS_CTRL_FORCE_LINK ? "Force" : "Unforce", +		   reg & PORT_PCS_CTRL_LINK_UP ? "up" : "down"); + +	return 0; +} + +int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup) +{ +	u16 reg; +	int err; + +	err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, ®); +	if (err) +		return err; + +	reg &= ~(PORT_PCS_CTRL_FORCE_DUPLEX | PORT_PCS_CTRL_DUPLEX_FULL); + +	switch (dup) { +	case DUPLEX_HALF: +		reg |= PORT_PCS_CTRL_FORCE_DUPLEX; +		break; +	case DUPLEX_FULL: +		reg |= PORT_PCS_CTRL_FORCE_DUPLEX | PORT_PCS_CTRL_DUPLEX_FULL; +		break; +	case DUPLEX_UNFORCED: +		/* normal duplex detection */ +		break; +	default: +		return -EINVAL; +	} + +	err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg); +	if (err) +		return err; + +	netdev_dbg(chip->ds->ports[port].netdev, "%s %s duplex\n", +		   reg & PORT_PCS_CTRL_FORCE_DUPLEX ? "Force" : "Unforce", +		   reg & PORT_PCS_CTRL_DUPLEX_FULL ? "full" : "half"); + +	return 0; +} + +static int mv88e6xxx_port_set_speed(struct mv88e6xxx_chip *chip, int port, +				    int speed, bool alt_bit, bool force_bit) +{ +	u16 reg, ctrl; +	int err; + +	switch (speed) { +	case 10: +		ctrl = PORT_PCS_CTRL_SPEED_10; +		break; +	case 100: +		ctrl = PORT_PCS_CTRL_SPEED_100; +		break; +	case 200: +		if (alt_bit) +			ctrl = PORT_PCS_CTRL_SPEED_100 | PORT_PCS_CTRL_ALTSPEED; +		else +			ctrl = PORT_PCS_CTRL_SPEED_200; +		break; +	case 1000: +		ctrl = PORT_PCS_CTRL_SPEED_1000; +		break; +	case 2500: +		ctrl = PORT_PCS_CTRL_SPEED_1000 | PORT_PCS_CTRL_ALTSPEED; +		break; +	case 10000: +		/* all bits set, fall through... */ +	case SPEED_UNFORCED: +		ctrl = PORT_PCS_CTRL_SPEED_UNFORCED; +		break; +	default: +		return -EOPNOTSUPP; +	} + +	err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, ®); +	if (err) +		return err; + +	reg &= ~PORT_PCS_CTRL_SPEED_MASK; +	if (alt_bit) +		reg &= ~PORT_PCS_CTRL_ALTSPEED; +	if (force_bit) { +		reg &= ~PORT_PCS_CTRL_FORCE_SPEED; +		if (speed != SPEED_UNFORCED) +			ctrl |= PORT_PCS_CTRL_FORCE_SPEED; +	} +	reg |= ctrl; + +	err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg); +	if (err) +		return err; + +	if (speed) +		netdev_dbg(chip->ds->ports[port].netdev, +			   "Speed set to %d Mbps\n", speed); +	else +		netdev_dbg(chip->ds->ports[port].netdev, "Speed unforced\n"); + +	return 0; +} + +/* Support 10, 100, 200 Mbps (e.g. 88E6065 family) */ +int mv88e6065_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +{ +	if (speed == SPEED_MAX) +		speed = 200; + +	if (speed > 200) +		return -EOPNOTSUPP; + +	/* Setting 200 Mbps on port 0 to 3 selects 100 Mbps */ +	return mv88e6xxx_port_set_speed(chip, port, speed, false, false); +} + +/* Support 10, 100, 1000 Mbps (e.g. 88E6185 family) */ +int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +{ +	if (speed == SPEED_MAX) +		speed = 1000; + +	if (speed == 200 || speed > 1000) +		return -EOPNOTSUPP; + +	return mv88e6xxx_port_set_speed(chip, port, speed, false, false); +} + +/* Support 10, 100, 200, 1000 Mbps (e.g. 88E6352 family) */ +int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +{ +	if (speed == SPEED_MAX) +		speed = 1000; + +	if (speed > 1000) +		return -EOPNOTSUPP; + +	if (speed == 200 && port < 5) +		return -EOPNOTSUPP; + +	return mv88e6xxx_port_set_speed(chip, port, speed, true, false); +} + +/* Support 10, 100, 200, 1000, 2500 Mbps (e.g. 88E6390) */ +int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +{ +	if (speed == SPEED_MAX) +		speed = port < 9 ? 1000 : 2500; + +	if (speed > 2500) +		return -EOPNOTSUPP; + +	if (speed == 200 && port != 0) +		return -EOPNOTSUPP; + +	if (speed == 2500 && port < 9) +		return -EOPNOTSUPP; + +	return mv88e6xxx_port_set_speed(chip, port, speed, true, true); +} + +/* Support 10, 100, 200, 1000, 2500, 10000 Mbps (e.g. 88E6190X) */ +int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +{ +	if (speed == SPEED_MAX) +		speed = port < 9 ? 1000 : 10000; + +	if (speed == 200 && port != 0) +		return -EOPNOTSUPP; + +	if (speed >= 2500 && port < 9) +		return -EOPNOTSUPP; + +	return mv88e6xxx_port_set_speed(chip, port, speed, true, true); +} + +/* Offset 0x02: Pause Control + * + * Do not limit the period of time that this port can be paused for by + * the remote end or the period of time that this port can pause the + * remote end. + */ +int mv88e6097_port_pause_config(struct mv88e6xxx_chip *chip, int port) +{ +	return mv88e6xxx_port_write(chip, port, PORT_PAUSE_CTRL, 0x0000); +} + +int mv88e6390_port_pause_config(struct mv88e6xxx_chip *chip, int port) +{ +	int err; + +	err = mv88e6xxx_port_write(chip, port, PORT_PAUSE_CTRL, +				   PORT_FLOW_CTRL_LIMIT_IN | 0); +	if (err) +		return err; + +	return mv88e6xxx_port_write(chip, port, PORT_PAUSE_CTRL, +				    PORT_FLOW_CTRL_LIMIT_OUT | 0); +} + +/* Offset 0x04: Port Control Register */ + +static const char * const mv88e6xxx_port_state_names[] = { +	[PORT_CONTROL_STATE_DISABLED] = "Disabled", +	[PORT_CONTROL_STATE_BLOCKING] = "Blocking/Listening", +	[PORT_CONTROL_STATE_LEARNING] = "Learning", +	[PORT_CONTROL_STATE_FORWARDING] = "Forwarding", +}; + +int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state) +{ +	u16 reg; +	int err; + +	err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, ®); +	if (err) +		return err; + +	reg &= ~PORT_CONTROL_STATE_MASK; +	reg |= state; + +	err = mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg); +	if (err) +		return err; + +	netdev_dbg(chip->ds->ports[port].netdev, "PortState set to %s\n", +		   mv88e6xxx_port_state_names[state]); + +	return 0; +} + +int mv88e6xxx_port_set_egress_mode(struct mv88e6xxx_chip *chip, int port, +				   u16 mode) +{ +	int err; +	u16 reg; + +	err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, ®); +	if (err) +		return err; + +	reg &= ~PORT_CONTROL_EGRESS_MASK; +	reg |= mode; + +	return mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg); +} + +int mv88e6085_port_set_frame_mode(struct mv88e6xxx_chip *chip, int port, +				  enum mv88e6xxx_frame_mode mode) +{ +	int err; +	u16 reg; + +	err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, ®); +	if (err) +		return err; + +	reg &= ~PORT_CONTROL_FRAME_MODE_DSA; + +	switch (mode) { +	case MV88E6XXX_FRAME_MODE_NORMAL: +		reg |= PORT_CONTROL_FRAME_MODE_NORMAL; +		break; +	case MV88E6XXX_FRAME_MODE_DSA: +		reg |= PORT_CONTROL_FRAME_MODE_DSA; +		break; +	default: +		return -EINVAL; +	} + +	return mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg); +} + +int mv88e6351_port_set_frame_mode(struct mv88e6xxx_chip *chip, int port, +				  enum mv88e6xxx_frame_mode mode) +{ +	int err; +	u16 reg; + +	err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, ®); +	if (err) +		return err; + +	reg &= ~PORT_CONTROL_FRAME_MASK; + +	switch (mode) { +	case MV88E6XXX_FRAME_MODE_NORMAL: +		reg |= PORT_CONTROL_FRAME_MODE_NORMAL; +		break; +	case MV88E6XXX_FRAME_MODE_DSA: +		reg |= PORT_CONTROL_FRAME_MODE_DSA; +		break; +	case MV88E6XXX_FRAME_MODE_PROVIDER: +		reg |= PORT_CONTROL_FRAME_MODE_PROVIDER; +		break; +	case MV88E6XXX_FRAME_MODE_ETHERTYPE: +		reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA; +		break; +	default: +		return -EINVAL; +	} + +	return mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg); +} + +int mv88e6085_port_set_egress_unknowns(struct mv88e6xxx_chip *chip, int port, +				       bool on) +{ +	int err; +	u16 reg; + +	err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, ®); +	if (err) +		return err; + +	if (on) +		reg |= PORT_CONTROL_FORWARD_UNKNOWN; +	else +		reg &= ~PORT_CONTROL_FORWARD_UNKNOWN; + +	return mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg); +} + +int mv88e6351_port_set_egress_unknowns(struct mv88e6xxx_chip *chip, int port, +				       bool on) +{ +	int err; +	u16 reg; + +	err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, ®); +	if (err) +		return err; + +	if (on) +		reg |= PORT_CONTROL_EGRESS_ALL_UNKNOWN_DA; +	else +		reg &= ~PORT_CONTROL_EGRESS_ALL_UNKNOWN_DA; + +	return mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg); +} + +/* Offset 0x05: Port Control 1 */ + +/* Offset 0x06: Port Based VLAN Map */ + +int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map) +{ +	const u16 mask = GENMASK(mv88e6xxx_num_ports(chip) - 1, 0); +	u16 reg; +	int err; + +	err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, ®); +	if (err) +		return err; + +	reg &= ~mask; +	reg |= map & mask; + +	err = mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg); +	if (err) +		return err; + +	netdev_dbg(chip->ds->ports[port].netdev, "VLANTable set to %.3x\n", +		   map); + +	return 0; +} + +int mv88e6xxx_port_get_fid(struct mv88e6xxx_chip *chip, int port, u16 *fid) +{ +	const u16 upper_mask = (mv88e6xxx_num_databases(chip) - 1) >> 4; +	u16 reg; +	int err; + +	/* Port's default FID lower 4 bits are located in reg 0x06, offset 12 */ +	err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, ®); +	if (err) +		return err; + +	*fid = (reg & 0xf000) >> 12; + +	/* Port's default FID upper bits are located in reg 0x05, offset 0 */ +	if (upper_mask) { +		err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, ®); +		if (err) +			return err; + +		*fid |= (reg & upper_mask) << 4; +	} + +	return 0; +} + +int mv88e6xxx_port_set_fid(struct mv88e6xxx_chip *chip, int port, u16 fid) +{ +	const u16 upper_mask = (mv88e6xxx_num_databases(chip) - 1) >> 4; +	u16 reg; +	int err; + +	if (fid >= mv88e6xxx_num_databases(chip)) +		return -EINVAL; + +	/* Port's default FID lower 4 bits are located in reg 0x06, offset 12 */ +	err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, ®); +	if (err) +		return err; + +	reg &= 0x0fff; +	reg |= (fid & 0x000f) << 12; + +	err = mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg); +	if (err) +		return err; + +	/* Port's default FID upper bits are located in reg 0x05, offset 0 */ +	if (upper_mask) { +		err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, ®); +		if (err) +			return err; + +		reg &= ~upper_mask; +		reg |= (fid >> 4) & upper_mask; + +		err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_1, reg); +		if (err) +			return err; +	} + +	netdev_dbg(chip->ds->ports[port].netdev, "FID set to %u\n", fid); + +	return 0; +} + +/* Offset 0x07: Default Port VLAN ID & Priority */ + +int mv88e6xxx_port_get_pvid(struct mv88e6xxx_chip *chip, int port, u16 *pvid) +{ +	u16 reg; +	int err; + +	err = mv88e6xxx_port_read(chip, port, PORT_DEFAULT_VLAN, ®); +	if (err) +		return err; + +	*pvid = reg & PORT_DEFAULT_VLAN_MASK; + +	return 0; +} + +int mv88e6xxx_port_set_pvid(struct mv88e6xxx_chip *chip, int port, u16 pvid) +{ +	u16 reg; +	int err; + +	err = mv88e6xxx_port_read(chip, port, PORT_DEFAULT_VLAN, ®); +	if (err) +		return err; + +	reg &= ~PORT_DEFAULT_VLAN_MASK; +	reg |= pvid & PORT_DEFAULT_VLAN_MASK; + +	err = mv88e6xxx_port_write(chip, port, PORT_DEFAULT_VLAN, reg); +	if (err) +		return err; + +	netdev_dbg(chip->ds->ports[port].netdev, "DefaultVID set to %u\n", +		   pvid); + +	return 0; +} + +/* Offset 0x08: Port Control 2 Register */ + +static const char * const mv88e6xxx_port_8021q_mode_names[] = { +	[PORT_CONTROL_2_8021Q_DISABLED] = "Disabled", +	[PORT_CONTROL_2_8021Q_FALLBACK] = "Fallback", +	[PORT_CONTROL_2_8021Q_CHECK] = "Check", +	[PORT_CONTROL_2_8021Q_SECURE] = "Secure", +}; + +int mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port, +				  u16 mode) +{ +	u16 reg; +	int err; + +	err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, ®); +	if (err) +		return err; + +	reg &= ~PORT_CONTROL_2_8021Q_MASK; +	reg |= mode & PORT_CONTROL_2_8021Q_MASK; + +	err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg); +	if (err) +		return err; + +	netdev_dbg(chip->ds->ports[port].netdev, "802.1QMode set to %s\n", +		   mv88e6xxx_port_8021q_mode_names[mode]); + +	return 0; +} + +int mv88e6165_port_jumbo_config(struct mv88e6xxx_chip *chip, int port) +{ +	u16 reg; +	int err; + +	err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, ®); +	if (err) +		return err; + +	reg |= PORT_CONTROL_2_JUMBO_10240; + +	return mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg); +} + +/* Offset 0x09: Port Rate Control */ + +int mv88e6095_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port) +{ +	return mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL, 0x0000); +} + +int mv88e6097_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port) +{ +	return mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL, 0x0001); +} + +/* Offset 0x0f: Port Ether type */ + +int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port, +				  u16 etype) +{ +	return mv88e6xxx_port_write(chip, port, PORT_ETH_TYPE, etype); +} + +/* Offset 0x18: Port IEEE Priority Remapping Registers [0-3] + * Offset 0x19: Port IEEE Priority Remapping Registers [4-7] + */ + +int mv88e6095_port_tag_remap(struct mv88e6xxx_chip *chip, int port) +{ +	int err; + +	/* Use a direct priority mapping for all IEEE tagged frames */ +	err = mv88e6xxx_port_write(chip, port, PORT_TAG_REGMAP_0123, 0x3210); +	if (err) +		return err; + +	return mv88e6xxx_port_write(chip, port, PORT_TAG_REGMAP_4567, 0x7654); +} + +static int mv88e6xxx_port_ieeepmt_write(struct mv88e6xxx_chip *chip, +					int port, u16 table, +					u8 pointer, u16 data) +{ +	u16 reg; + +	reg = PORT_IEEE_PRIO_MAP_TABLE_UPDATE | +		table | +		(pointer << PORT_IEEE_PRIO_MAP_TABLE_POINTER_SHIFT) | +		data; + +	return mv88e6xxx_port_write(chip, port, PORT_IEEE_PRIO_MAP_TABLE, reg); +} + +int mv88e6390_port_tag_remap(struct mv88e6xxx_chip *chip, int port) +{ +	int err, i; + +	for (i = 0; i <= 7; i++) { +		err = mv88e6xxx_port_ieeepmt_write( +			chip, port, PORT_IEEE_PRIO_MAP_TABLE_INGRESS_PCP, +			i, (i | i << 4)); +		if (err) +			return err; + +		err = mv88e6xxx_port_ieeepmt_write( +			chip, port, PORT_IEEE_PRIO_MAP_TABLE_EGRESS_GREEN_PCP, +			i, i); +		if (err) +			return err; + +		err = mv88e6xxx_port_ieeepmt_write( +			chip, port, PORT_IEEE_PRIO_MAP_TABLE_EGRESS_YELLOW_PCP, +			i, i); +		if (err) +			return err; + +		err = mv88e6xxx_port_ieeepmt_write( +			chip, port, PORT_IEEE_PRIO_MAP_TABLE_EGRESS_AVB_PCP, +			i, i); +		if (err) +			return err; +	} + +	return 0; +} diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h new file mode 100644 index 000000000000..7b3bacaacbfe --- /dev/null +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -0,0 +1,71 @@ +/* + * Marvell 88E6xxx Switch Port Registers support + * + * Copyright (c) 2008 Marvell Semiconductor + * + * Copyright (c) 2016 Vivien Didelot <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _MV88E6XXX_PORT_H +#define _MV88E6XXX_PORT_H + +#include "mv88e6xxx.h" + +int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg, +			u16 *val); +int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, +			 u16 val); + +int mv88e6352_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, +				   phy_interface_t mode); +int mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, +				   phy_interface_t mode); + +int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link); + +int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup); + +int mv88e6065_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); +int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); +int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); +int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); +int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); + +int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state); + +int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map); + +int mv88e6xxx_port_get_fid(struct mv88e6xxx_chip *chip, int port, u16 *fid); +int mv88e6xxx_port_set_fid(struct mv88e6xxx_chip *chip, int port, u16 fid); + +int mv88e6xxx_port_get_pvid(struct mv88e6xxx_chip *chip, int port, u16 *pvid); +int mv88e6xxx_port_set_pvid(struct mv88e6xxx_chip *chip, int port, u16 pvid); + +int mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port, +				  u16 mode); +int mv88e6095_port_tag_remap(struct mv88e6xxx_chip *chip, int port); +int mv88e6390_port_tag_remap(struct mv88e6xxx_chip *chip, int port); +int mv88e6xxx_port_set_egress_mode(struct mv88e6xxx_chip *chip, int port, +				   u16 mode); +int mv88e6085_port_set_frame_mode(struct mv88e6xxx_chip *chip, int port, +				  enum mv88e6xxx_frame_mode mode); +int mv88e6351_port_set_frame_mode(struct mv88e6xxx_chip *chip, int port, +				  enum mv88e6xxx_frame_mode mode); +int mv88e6085_port_set_egress_unknowns(struct mv88e6xxx_chip *chip, int port, +				       bool on); +int mv88e6351_port_set_egress_unknowns(struct mv88e6xxx_chip *chip, int port, +				       bool on); +int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port, +				  u16 etype); +int mv88e6165_port_jumbo_config(struct mv88e6xxx_chip *chip, int port); +int mv88e6095_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port); +int mv88e6097_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port); +int mv88e6097_port_pause_config(struct mv88e6xxx_chip *chip, int port); +int mv88e6390_port_pause_config(struct mv88e6xxx_chip *chip, int port); + +#endif /* _MV88E6XXX_PORT_H */ |