1fad09c73SVivien Didelot /* 2fad09c73SVivien Didelot * Marvell 88e6xxx Ethernet switch single-chip support 3fad09c73SVivien Didelot * 4fad09c73SVivien Didelot * Copyright (c) 2008 Marvell Semiconductor 5fad09c73SVivien Didelot * 6fad09c73SVivien Didelot * Copyright (c) 2015 CMC Electronics, Inc. 7fad09c73SVivien Didelot * Added support for VLAN Table Unit operations 8fad09c73SVivien Didelot * 9fad09c73SVivien Didelot * Copyright (c) 2016 Andrew Lunn <andrew@lunn.ch> 10fad09c73SVivien Didelot * 11fad09c73SVivien Didelot * This program is free software; you can redistribute it and/or modify 12fad09c73SVivien Didelot * it under the terms of the GNU General Public License as published by 13fad09c73SVivien Didelot * the Free Software Foundation; either version 2 of the License, or 14fad09c73SVivien Didelot * (at your option) any later version. 15fad09c73SVivien Didelot */ 16fad09c73SVivien Didelot 17fad09c73SVivien Didelot #include <linux/delay.h> 18fad09c73SVivien Didelot #include <linux/etherdevice.h> 19fad09c73SVivien Didelot #include <linux/ethtool.h> 20fad09c73SVivien Didelot #include <linux/if_bridge.h> 21dc30c35bSAndrew Lunn #include <linux/interrupt.h> 22dc30c35bSAndrew Lunn #include <linux/irq.h> 23dc30c35bSAndrew Lunn #include <linux/irqdomain.h> 24fad09c73SVivien Didelot #include <linux/jiffies.h> 25fad09c73SVivien Didelot #include <linux/list.h> 26fad09c73SVivien Didelot #include <linux/mdio.h> 27fad09c73SVivien Didelot #include <linux/module.h> 28fad09c73SVivien Didelot #include <linux/of_device.h> 29dc30c35bSAndrew Lunn #include <linux/of_irq.h> 30fad09c73SVivien Didelot #include <linux/of_mdio.h> 31fad09c73SVivien Didelot #include <linux/netdevice.h> 32fad09c73SVivien Didelot #include <linux/gpio/consumer.h> 33fad09c73SVivien Didelot #include <linux/phy.h> 34fad09c73SVivien Didelot #include <net/dsa.h> 35fad09c73SVivien Didelot #include <net/switchdev.h> 36ec561276SVivien Didelot 37fad09c73SVivien Didelot #include "mv88e6xxx.h" 38a935c052SVivien Didelot #include "global1.h" 39ec561276SVivien Didelot #include "global2.h" 4018abed21SVivien Didelot #include "port.h" 41fad09c73SVivien Didelot 42fad09c73SVivien Didelot static void assert_reg_lock(struct mv88e6xxx_chip *chip) 43fad09c73SVivien Didelot { 44fad09c73SVivien Didelot if (unlikely(!mutex_is_locked(&chip->reg_lock))) { 45fad09c73SVivien Didelot dev_err(chip->dev, "Switch registers lock not held!\n"); 46fad09c73SVivien Didelot dump_stack(); 47fad09c73SVivien Didelot } 48fad09c73SVivien Didelot } 49fad09c73SVivien Didelot 50fad09c73SVivien Didelot /* The switch ADDR[4:1] configuration pins define the chip SMI device address 51fad09c73SVivien Didelot * (ADDR[0] is always zero, thus only even SMI addresses can be strapped). 52fad09c73SVivien Didelot * 53fad09c73SVivien Didelot * When ADDR is all zero, the chip uses Single-chip Addressing Mode, assuming it 54fad09c73SVivien Didelot * is the only device connected to the SMI master. In this mode it responds to 55fad09c73SVivien Didelot * all 32 possible SMI addresses, and thus maps directly the internal devices. 56fad09c73SVivien Didelot * 57fad09c73SVivien Didelot * When ADDR is non-zero, the chip uses Multi-chip Addressing Mode, allowing 58fad09c73SVivien Didelot * multiple devices to share the SMI interface. In this mode it responds to only 59fad09c73SVivien Didelot * 2 registers, used to indirectly access the internal SMI devices. 60fad09c73SVivien Didelot */ 61fad09c73SVivien Didelot 62fad09c73SVivien Didelot static int mv88e6xxx_smi_read(struct mv88e6xxx_chip *chip, 63fad09c73SVivien Didelot int addr, int reg, u16 *val) 64fad09c73SVivien Didelot { 65fad09c73SVivien Didelot if (!chip->smi_ops) 66fad09c73SVivien Didelot return -EOPNOTSUPP; 67fad09c73SVivien Didelot 68fad09c73SVivien Didelot return chip->smi_ops->read(chip, addr, reg, val); 69fad09c73SVivien Didelot } 70fad09c73SVivien Didelot 71fad09c73SVivien Didelot static int mv88e6xxx_smi_write(struct mv88e6xxx_chip *chip, 72fad09c73SVivien Didelot int addr, int reg, u16 val) 73fad09c73SVivien Didelot { 74fad09c73SVivien Didelot if (!chip->smi_ops) 75fad09c73SVivien Didelot return -EOPNOTSUPP; 76fad09c73SVivien Didelot 77fad09c73SVivien Didelot return chip->smi_ops->write(chip, addr, reg, val); 78fad09c73SVivien Didelot } 79fad09c73SVivien Didelot 80fad09c73SVivien Didelot static int mv88e6xxx_smi_single_chip_read(struct mv88e6xxx_chip *chip, 81fad09c73SVivien Didelot int addr, int reg, u16 *val) 82fad09c73SVivien Didelot { 83fad09c73SVivien Didelot int ret; 84fad09c73SVivien Didelot 85fad09c73SVivien Didelot ret = mdiobus_read_nested(chip->bus, addr, reg); 86fad09c73SVivien Didelot if (ret < 0) 87fad09c73SVivien Didelot return ret; 88fad09c73SVivien Didelot 89fad09c73SVivien Didelot *val = ret & 0xffff; 90fad09c73SVivien Didelot 91fad09c73SVivien Didelot return 0; 92fad09c73SVivien Didelot } 93fad09c73SVivien Didelot 94fad09c73SVivien Didelot static int mv88e6xxx_smi_single_chip_write(struct mv88e6xxx_chip *chip, 95fad09c73SVivien Didelot int addr, int reg, u16 val) 96fad09c73SVivien Didelot { 97fad09c73SVivien Didelot int ret; 98fad09c73SVivien Didelot 99fad09c73SVivien Didelot ret = mdiobus_write_nested(chip->bus, addr, reg, val); 100fad09c73SVivien Didelot if (ret < 0) 101fad09c73SVivien Didelot return ret; 102fad09c73SVivien Didelot 103fad09c73SVivien Didelot return 0; 104fad09c73SVivien Didelot } 105fad09c73SVivien Didelot 106c08026abSVivien Didelot static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_single_chip_ops = { 107fad09c73SVivien Didelot .read = mv88e6xxx_smi_single_chip_read, 108fad09c73SVivien Didelot .write = mv88e6xxx_smi_single_chip_write, 109fad09c73SVivien Didelot }; 110fad09c73SVivien Didelot 111fad09c73SVivien Didelot static int mv88e6xxx_smi_multi_chip_wait(struct mv88e6xxx_chip *chip) 112fad09c73SVivien Didelot { 113fad09c73SVivien Didelot int ret; 114fad09c73SVivien Didelot int i; 115fad09c73SVivien Didelot 116fad09c73SVivien Didelot for (i = 0; i < 16; i++) { 117fad09c73SVivien Didelot ret = mdiobus_read_nested(chip->bus, chip->sw_addr, SMI_CMD); 118fad09c73SVivien Didelot if (ret < 0) 119fad09c73SVivien Didelot return ret; 120fad09c73SVivien Didelot 121fad09c73SVivien Didelot if ((ret & SMI_CMD_BUSY) == 0) 122fad09c73SVivien Didelot return 0; 123fad09c73SVivien Didelot } 124fad09c73SVivien Didelot 125fad09c73SVivien Didelot return -ETIMEDOUT; 126fad09c73SVivien Didelot } 127fad09c73SVivien Didelot 128fad09c73SVivien Didelot static int mv88e6xxx_smi_multi_chip_read(struct mv88e6xxx_chip *chip, 129fad09c73SVivien Didelot int addr, int reg, u16 *val) 130fad09c73SVivien Didelot { 131fad09c73SVivien Didelot int ret; 132fad09c73SVivien Didelot 133fad09c73SVivien Didelot /* Wait for the bus to become free. */ 134fad09c73SVivien Didelot ret = mv88e6xxx_smi_multi_chip_wait(chip); 135fad09c73SVivien Didelot if (ret < 0) 136fad09c73SVivien Didelot return ret; 137fad09c73SVivien Didelot 138fad09c73SVivien Didelot /* Transmit the read command. */ 139fad09c73SVivien Didelot ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_CMD, 140fad09c73SVivien Didelot SMI_CMD_OP_22_READ | (addr << 5) | reg); 141fad09c73SVivien Didelot if (ret < 0) 142fad09c73SVivien Didelot return ret; 143fad09c73SVivien Didelot 144fad09c73SVivien Didelot /* Wait for the read command to complete. */ 145fad09c73SVivien Didelot ret = mv88e6xxx_smi_multi_chip_wait(chip); 146fad09c73SVivien Didelot if (ret < 0) 147fad09c73SVivien Didelot return ret; 148fad09c73SVivien Didelot 149fad09c73SVivien Didelot /* Read the data. */ 150fad09c73SVivien Didelot ret = mdiobus_read_nested(chip->bus, chip->sw_addr, SMI_DATA); 151fad09c73SVivien Didelot if (ret < 0) 152fad09c73SVivien Didelot return ret; 153fad09c73SVivien Didelot 154fad09c73SVivien Didelot *val = ret & 0xffff; 155fad09c73SVivien Didelot 156fad09c73SVivien Didelot return 0; 157fad09c73SVivien Didelot } 158fad09c73SVivien Didelot 159fad09c73SVivien Didelot static int mv88e6xxx_smi_multi_chip_write(struct mv88e6xxx_chip *chip, 160fad09c73SVivien Didelot int addr, int reg, u16 val) 161fad09c73SVivien Didelot { 162fad09c73SVivien Didelot int ret; 163fad09c73SVivien Didelot 164fad09c73SVivien Didelot /* Wait for the bus to become free. */ 165fad09c73SVivien Didelot ret = mv88e6xxx_smi_multi_chip_wait(chip); 166fad09c73SVivien Didelot if (ret < 0) 167fad09c73SVivien Didelot return ret; 168fad09c73SVivien Didelot 169fad09c73SVivien Didelot /* Transmit the data to write. */ 170fad09c73SVivien Didelot ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_DATA, val); 171fad09c73SVivien Didelot if (ret < 0) 172fad09c73SVivien Didelot return ret; 173fad09c73SVivien Didelot 174fad09c73SVivien Didelot /* Transmit the write command. */ 175fad09c73SVivien Didelot ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_CMD, 176fad09c73SVivien Didelot SMI_CMD_OP_22_WRITE | (addr << 5) | reg); 177fad09c73SVivien Didelot if (ret < 0) 178fad09c73SVivien Didelot return ret; 179fad09c73SVivien Didelot 180fad09c73SVivien Didelot /* Wait for the write command to complete. */ 181fad09c73SVivien Didelot ret = mv88e6xxx_smi_multi_chip_wait(chip); 182fad09c73SVivien Didelot if (ret < 0) 183fad09c73SVivien Didelot return ret; 184fad09c73SVivien Didelot 185fad09c73SVivien Didelot return 0; 186fad09c73SVivien Didelot } 187fad09c73SVivien Didelot 188c08026abSVivien Didelot static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_multi_chip_ops = { 189fad09c73SVivien Didelot .read = mv88e6xxx_smi_multi_chip_read, 190fad09c73SVivien Didelot .write = mv88e6xxx_smi_multi_chip_write, 191fad09c73SVivien Didelot }; 192fad09c73SVivien Didelot 193ec561276SVivien Didelot int mv88e6xxx_read(struct mv88e6xxx_chip *chip, int addr, int reg, u16 *val) 194fad09c73SVivien Didelot { 195fad09c73SVivien Didelot int err; 196fad09c73SVivien Didelot 197fad09c73SVivien Didelot assert_reg_lock(chip); 198fad09c73SVivien Didelot 199fad09c73SVivien Didelot err = mv88e6xxx_smi_read(chip, addr, reg, val); 200fad09c73SVivien Didelot if (err) 201fad09c73SVivien Didelot return err; 202fad09c73SVivien Didelot 203fad09c73SVivien Didelot dev_dbg(chip->dev, "<- addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n", 204fad09c73SVivien Didelot addr, reg, *val); 205fad09c73SVivien Didelot 206fad09c73SVivien Didelot return 0; 207fad09c73SVivien Didelot } 208fad09c73SVivien Didelot 209ec561276SVivien Didelot int mv88e6xxx_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val) 210fad09c73SVivien Didelot { 211fad09c73SVivien Didelot int err; 212fad09c73SVivien Didelot 213fad09c73SVivien Didelot assert_reg_lock(chip); 214fad09c73SVivien Didelot 215fad09c73SVivien Didelot err = mv88e6xxx_smi_write(chip, addr, reg, val); 216fad09c73SVivien Didelot if (err) 217fad09c73SVivien Didelot return err; 218fad09c73SVivien Didelot 219fad09c73SVivien Didelot dev_dbg(chip->dev, "-> addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n", 220fad09c73SVivien Didelot addr, reg, val); 221fad09c73SVivien Didelot 222fad09c73SVivien Didelot return 0; 223fad09c73SVivien Didelot } 224fad09c73SVivien Didelot 225e57e5e77SVivien Didelot static int mv88e6xxx_phy_read(struct mv88e6xxx_chip *chip, int phy, 226e57e5e77SVivien Didelot int reg, u16 *val) 227e57e5e77SVivien Didelot { 228e57e5e77SVivien Didelot int addr = phy; /* PHY devices addresses start at 0x0 */ 229e57e5e77SVivien Didelot 230b3469dd8SVivien Didelot if (!chip->info->ops->phy_read) 231e57e5e77SVivien Didelot return -EOPNOTSUPP; 232e57e5e77SVivien Didelot 233b3469dd8SVivien Didelot return chip->info->ops->phy_read(chip, addr, reg, val); 234e57e5e77SVivien Didelot } 235e57e5e77SVivien Didelot 236e57e5e77SVivien Didelot static int mv88e6xxx_phy_write(struct mv88e6xxx_chip *chip, int phy, 237e57e5e77SVivien Didelot int reg, u16 val) 238e57e5e77SVivien Didelot { 239e57e5e77SVivien Didelot int addr = phy; /* PHY devices addresses start at 0x0 */ 240e57e5e77SVivien Didelot 241b3469dd8SVivien Didelot if (!chip->info->ops->phy_write) 242e57e5e77SVivien Didelot return -EOPNOTSUPP; 243e57e5e77SVivien Didelot 244b3469dd8SVivien Didelot return chip->info->ops->phy_write(chip, addr, reg, val); 245e57e5e77SVivien Didelot } 246e57e5e77SVivien Didelot 24709cb7dfdSVivien Didelot static int mv88e6xxx_phy_page_get(struct mv88e6xxx_chip *chip, int phy, u8 page) 24809cb7dfdSVivien Didelot { 24909cb7dfdSVivien Didelot if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_PHY_PAGE)) 25009cb7dfdSVivien Didelot return -EOPNOTSUPP; 25109cb7dfdSVivien Didelot 25209cb7dfdSVivien Didelot return mv88e6xxx_phy_write(chip, phy, PHY_PAGE, page); 25309cb7dfdSVivien Didelot } 25409cb7dfdSVivien Didelot 25509cb7dfdSVivien Didelot static void mv88e6xxx_phy_page_put(struct mv88e6xxx_chip *chip, int phy) 25609cb7dfdSVivien Didelot { 25709cb7dfdSVivien Didelot int err; 25809cb7dfdSVivien Didelot 25909cb7dfdSVivien Didelot /* Restore PHY page Copper 0x0 for access via the registered MDIO bus */ 26009cb7dfdSVivien Didelot err = mv88e6xxx_phy_write(chip, phy, PHY_PAGE, PHY_PAGE_COPPER); 26109cb7dfdSVivien Didelot if (unlikely(err)) { 26209cb7dfdSVivien Didelot dev_err(chip->dev, "failed to restore PHY %d page Copper (%d)\n", 26309cb7dfdSVivien Didelot phy, err); 26409cb7dfdSVivien Didelot } 26509cb7dfdSVivien Didelot } 26609cb7dfdSVivien Didelot 26709cb7dfdSVivien Didelot static int mv88e6xxx_phy_page_read(struct mv88e6xxx_chip *chip, int phy, 26809cb7dfdSVivien Didelot u8 page, int reg, u16 *val) 26909cb7dfdSVivien Didelot { 27009cb7dfdSVivien Didelot int err; 27109cb7dfdSVivien Didelot 27209cb7dfdSVivien Didelot /* There is no paging for registers 22 */ 27309cb7dfdSVivien Didelot if (reg == PHY_PAGE) 27409cb7dfdSVivien Didelot return -EINVAL; 27509cb7dfdSVivien Didelot 27609cb7dfdSVivien Didelot err = mv88e6xxx_phy_page_get(chip, phy, page); 27709cb7dfdSVivien Didelot if (!err) { 27809cb7dfdSVivien Didelot err = mv88e6xxx_phy_read(chip, phy, reg, val); 27909cb7dfdSVivien Didelot mv88e6xxx_phy_page_put(chip, phy); 28009cb7dfdSVivien Didelot } 28109cb7dfdSVivien Didelot 28209cb7dfdSVivien Didelot return err; 28309cb7dfdSVivien Didelot } 28409cb7dfdSVivien Didelot 28509cb7dfdSVivien Didelot static int mv88e6xxx_phy_page_write(struct mv88e6xxx_chip *chip, int phy, 28609cb7dfdSVivien Didelot u8 page, int reg, u16 val) 28709cb7dfdSVivien Didelot { 28809cb7dfdSVivien Didelot int err; 28909cb7dfdSVivien Didelot 29009cb7dfdSVivien Didelot /* There is no paging for registers 22 */ 29109cb7dfdSVivien Didelot if (reg == PHY_PAGE) 29209cb7dfdSVivien Didelot return -EINVAL; 29309cb7dfdSVivien Didelot 29409cb7dfdSVivien Didelot err = mv88e6xxx_phy_page_get(chip, phy, page); 29509cb7dfdSVivien Didelot if (!err) { 29609cb7dfdSVivien Didelot err = mv88e6xxx_phy_write(chip, phy, PHY_PAGE, page); 29709cb7dfdSVivien Didelot mv88e6xxx_phy_page_put(chip, phy); 29809cb7dfdSVivien Didelot } 29909cb7dfdSVivien Didelot 30009cb7dfdSVivien Didelot return err; 30109cb7dfdSVivien Didelot } 30209cb7dfdSVivien Didelot 30309cb7dfdSVivien Didelot static int mv88e6xxx_serdes_read(struct mv88e6xxx_chip *chip, int reg, u16 *val) 30409cb7dfdSVivien Didelot { 30509cb7dfdSVivien Didelot return mv88e6xxx_phy_page_read(chip, ADDR_SERDES, SERDES_PAGE_FIBER, 30609cb7dfdSVivien Didelot reg, val); 30709cb7dfdSVivien Didelot } 30809cb7dfdSVivien Didelot 30909cb7dfdSVivien Didelot static int mv88e6xxx_serdes_write(struct mv88e6xxx_chip *chip, int reg, u16 val) 31009cb7dfdSVivien Didelot { 31109cb7dfdSVivien Didelot return mv88e6xxx_phy_page_write(chip, ADDR_SERDES, SERDES_PAGE_FIBER, 31209cb7dfdSVivien Didelot reg, val); 31309cb7dfdSVivien Didelot } 31409cb7dfdSVivien Didelot 315dc30c35bSAndrew Lunn static void mv88e6xxx_g1_irq_mask(struct irq_data *d) 316dc30c35bSAndrew Lunn { 317dc30c35bSAndrew Lunn struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); 318dc30c35bSAndrew Lunn unsigned int n = d->hwirq; 319dc30c35bSAndrew Lunn 320dc30c35bSAndrew Lunn chip->g1_irq.masked |= (1 << n); 321dc30c35bSAndrew Lunn } 322dc30c35bSAndrew Lunn 323dc30c35bSAndrew Lunn static void mv88e6xxx_g1_irq_unmask(struct irq_data *d) 324dc30c35bSAndrew Lunn { 325dc30c35bSAndrew Lunn struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); 326dc30c35bSAndrew Lunn unsigned int n = d->hwirq; 327dc30c35bSAndrew Lunn 328dc30c35bSAndrew Lunn chip->g1_irq.masked &= ~(1 << n); 329dc30c35bSAndrew Lunn } 330dc30c35bSAndrew Lunn 331dc30c35bSAndrew Lunn static irqreturn_t mv88e6xxx_g1_irq_thread_fn(int irq, void *dev_id) 332dc30c35bSAndrew Lunn { 333dc30c35bSAndrew Lunn struct mv88e6xxx_chip *chip = dev_id; 334dc30c35bSAndrew Lunn unsigned int nhandled = 0; 335dc30c35bSAndrew Lunn unsigned int sub_irq; 336dc30c35bSAndrew Lunn unsigned int n; 337dc30c35bSAndrew Lunn u16 reg; 338dc30c35bSAndrew Lunn int err; 339dc30c35bSAndrew Lunn 340dc30c35bSAndrew Lunn mutex_lock(&chip->reg_lock); 341dc30c35bSAndrew Lunn err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, ®); 342dc30c35bSAndrew Lunn mutex_unlock(&chip->reg_lock); 343dc30c35bSAndrew Lunn 344dc30c35bSAndrew Lunn if (err) 345dc30c35bSAndrew Lunn goto out; 346dc30c35bSAndrew Lunn 347dc30c35bSAndrew Lunn for (n = 0; n < chip->g1_irq.nirqs; ++n) { 348dc30c35bSAndrew Lunn if (reg & (1 << n)) { 349dc30c35bSAndrew Lunn sub_irq = irq_find_mapping(chip->g1_irq.domain, n); 350dc30c35bSAndrew Lunn handle_nested_irq(sub_irq); 351dc30c35bSAndrew Lunn ++nhandled; 352dc30c35bSAndrew Lunn } 353dc30c35bSAndrew Lunn } 354dc30c35bSAndrew Lunn out: 355dc30c35bSAndrew Lunn return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE); 356dc30c35bSAndrew Lunn } 357dc30c35bSAndrew Lunn 358dc30c35bSAndrew Lunn static void mv88e6xxx_g1_irq_bus_lock(struct irq_data *d) 359dc30c35bSAndrew Lunn { 360dc30c35bSAndrew Lunn struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); 361dc30c35bSAndrew Lunn 362dc30c35bSAndrew Lunn mutex_lock(&chip->reg_lock); 363dc30c35bSAndrew Lunn } 364dc30c35bSAndrew Lunn 365dc30c35bSAndrew Lunn static void mv88e6xxx_g1_irq_bus_sync_unlock(struct irq_data *d) 366dc30c35bSAndrew Lunn { 367dc30c35bSAndrew Lunn struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); 368dc30c35bSAndrew Lunn u16 mask = GENMASK(chip->g1_irq.nirqs, 0); 369dc30c35bSAndrew Lunn u16 reg; 370dc30c35bSAndrew Lunn int err; 371dc30c35bSAndrew Lunn 372dc30c35bSAndrew Lunn err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, ®); 373dc30c35bSAndrew Lunn if (err) 374dc30c35bSAndrew Lunn goto out; 375dc30c35bSAndrew Lunn 376dc30c35bSAndrew Lunn reg &= ~mask; 377dc30c35bSAndrew Lunn reg |= (~chip->g1_irq.masked & mask); 378dc30c35bSAndrew Lunn 379dc30c35bSAndrew Lunn err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, reg); 380dc30c35bSAndrew Lunn if (err) 381dc30c35bSAndrew Lunn goto out; 382dc30c35bSAndrew Lunn 383dc30c35bSAndrew Lunn out: 384dc30c35bSAndrew Lunn mutex_unlock(&chip->reg_lock); 385dc30c35bSAndrew Lunn } 386dc30c35bSAndrew Lunn 387dc30c35bSAndrew Lunn static struct irq_chip mv88e6xxx_g1_irq_chip = { 388dc30c35bSAndrew Lunn .name = "mv88e6xxx-g1", 389dc30c35bSAndrew Lunn .irq_mask = mv88e6xxx_g1_irq_mask, 390dc30c35bSAndrew Lunn .irq_unmask = mv88e6xxx_g1_irq_unmask, 391dc30c35bSAndrew Lunn .irq_bus_lock = mv88e6xxx_g1_irq_bus_lock, 392dc30c35bSAndrew Lunn .irq_bus_sync_unlock = mv88e6xxx_g1_irq_bus_sync_unlock, 393dc30c35bSAndrew Lunn }; 394dc30c35bSAndrew Lunn 395dc30c35bSAndrew Lunn static int mv88e6xxx_g1_irq_domain_map(struct irq_domain *d, 396dc30c35bSAndrew Lunn unsigned int irq, 397dc30c35bSAndrew Lunn irq_hw_number_t hwirq) 398dc30c35bSAndrew Lunn { 399dc30c35bSAndrew Lunn struct mv88e6xxx_chip *chip = d->host_data; 400dc30c35bSAndrew Lunn 401dc30c35bSAndrew Lunn irq_set_chip_data(irq, d->host_data); 402dc30c35bSAndrew Lunn irq_set_chip_and_handler(irq, &chip->g1_irq.chip, handle_level_irq); 403dc30c35bSAndrew Lunn irq_set_noprobe(irq); 404dc30c35bSAndrew Lunn 405dc30c35bSAndrew Lunn return 0; 406dc30c35bSAndrew Lunn } 407dc30c35bSAndrew Lunn 408dc30c35bSAndrew Lunn static const struct irq_domain_ops mv88e6xxx_g1_irq_domain_ops = { 409dc30c35bSAndrew Lunn .map = mv88e6xxx_g1_irq_domain_map, 410dc30c35bSAndrew Lunn .xlate = irq_domain_xlate_twocell, 411dc30c35bSAndrew Lunn }; 412dc30c35bSAndrew Lunn 413dc30c35bSAndrew Lunn static void mv88e6xxx_g1_irq_free(struct mv88e6xxx_chip *chip) 414dc30c35bSAndrew Lunn { 415dc30c35bSAndrew Lunn int irq, virq; 4163460a577SAndrew Lunn u16 mask; 4173460a577SAndrew Lunn 4183460a577SAndrew Lunn mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &mask); 4193460a577SAndrew Lunn mask |= GENMASK(chip->g1_irq.nirqs, 0); 4203460a577SAndrew Lunn mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, mask); 4213460a577SAndrew Lunn 4223460a577SAndrew Lunn free_irq(chip->irq, chip); 423dc30c35bSAndrew Lunn 424dc30c35bSAndrew Lunn for (irq = 0; irq < 16; irq++) { 425a3db3d3aSAndrew Lunn virq = irq_find_mapping(chip->g1_irq.domain, irq); 426dc30c35bSAndrew Lunn irq_dispose_mapping(virq); 427dc30c35bSAndrew Lunn } 428dc30c35bSAndrew Lunn 429a3db3d3aSAndrew Lunn irq_domain_remove(chip->g1_irq.domain); 430dc30c35bSAndrew Lunn } 431dc30c35bSAndrew Lunn 432dc30c35bSAndrew Lunn static int mv88e6xxx_g1_irq_setup(struct mv88e6xxx_chip *chip) 433dc30c35bSAndrew Lunn { 4343dd0ef05SAndrew Lunn int err, irq, virq; 4353dd0ef05SAndrew Lunn u16 reg, mask; 436dc30c35bSAndrew Lunn 437dc30c35bSAndrew Lunn chip->g1_irq.nirqs = chip->info->g1_irqs; 438dc30c35bSAndrew Lunn chip->g1_irq.domain = irq_domain_add_simple( 439dc30c35bSAndrew Lunn NULL, chip->g1_irq.nirqs, 0, 440dc30c35bSAndrew Lunn &mv88e6xxx_g1_irq_domain_ops, chip); 441dc30c35bSAndrew Lunn if (!chip->g1_irq.domain) 442dc30c35bSAndrew Lunn return -ENOMEM; 443dc30c35bSAndrew Lunn 444dc30c35bSAndrew Lunn for (irq = 0; irq < chip->g1_irq.nirqs; irq++) 445dc30c35bSAndrew Lunn irq_create_mapping(chip->g1_irq.domain, irq); 446dc30c35bSAndrew Lunn 447dc30c35bSAndrew Lunn chip->g1_irq.chip = mv88e6xxx_g1_irq_chip; 448dc30c35bSAndrew Lunn chip->g1_irq.masked = ~0; 449dc30c35bSAndrew Lunn 4503dd0ef05SAndrew Lunn err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &mask); 451dc30c35bSAndrew Lunn if (err) 4523dd0ef05SAndrew Lunn goto out_mapping; 453dc30c35bSAndrew Lunn 4543dd0ef05SAndrew Lunn mask &= ~GENMASK(chip->g1_irq.nirqs, 0); 455dc30c35bSAndrew Lunn 4563dd0ef05SAndrew Lunn err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, mask); 457dc30c35bSAndrew Lunn if (err) 4583dd0ef05SAndrew Lunn goto out_disable; 459dc30c35bSAndrew Lunn 460dc30c35bSAndrew Lunn /* Reading the interrupt status clears (most of) them */ 461dc30c35bSAndrew Lunn err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, ®); 462dc30c35bSAndrew Lunn if (err) 4633dd0ef05SAndrew Lunn goto out_disable; 464dc30c35bSAndrew Lunn 465dc30c35bSAndrew Lunn err = request_threaded_irq(chip->irq, NULL, 466dc30c35bSAndrew Lunn mv88e6xxx_g1_irq_thread_fn, 467dc30c35bSAndrew Lunn IRQF_ONESHOT | IRQF_TRIGGER_FALLING, 468dc30c35bSAndrew Lunn dev_name(chip->dev), chip); 469dc30c35bSAndrew Lunn if (err) 4703dd0ef05SAndrew Lunn goto out_disable; 471dc30c35bSAndrew Lunn 472dc30c35bSAndrew Lunn return 0; 473dc30c35bSAndrew Lunn 4743dd0ef05SAndrew Lunn out_disable: 4753dd0ef05SAndrew Lunn mask |= GENMASK(chip->g1_irq.nirqs, 0); 4763dd0ef05SAndrew Lunn mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, mask); 4773dd0ef05SAndrew Lunn 4783dd0ef05SAndrew Lunn out_mapping: 4793dd0ef05SAndrew Lunn for (irq = 0; irq < 16; irq++) { 4803dd0ef05SAndrew Lunn virq = irq_find_mapping(chip->g1_irq.domain, irq); 4813dd0ef05SAndrew Lunn irq_dispose_mapping(virq); 4823dd0ef05SAndrew Lunn } 4833dd0ef05SAndrew Lunn 4843dd0ef05SAndrew Lunn irq_domain_remove(chip->g1_irq.domain); 485dc30c35bSAndrew Lunn 486dc30c35bSAndrew Lunn return err; 487dc30c35bSAndrew Lunn } 488dc30c35bSAndrew Lunn 489ec561276SVivien Didelot int mv88e6xxx_wait(struct mv88e6xxx_chip *chip, int addr, int reg, u16 mask) 4902d79af6eSVivien Didelot { 4916441e669SAndrew Lunn int i; 4922d79af6eSVivien Didelot 4936441e669SAndrew Lunn for (i = 0; i < 16; i++) { 4942d79af6eSVivien Didelot u16 val; 4952d79af6eSVivien Didelot int err; 4962d79af6eSVivien Didelot 4972d79af6eSVivien Didelot err = mv88e6xxx_read(chip, addr, reg, &val); 4982d79af6eSVivien Didelot if (err) 4992d79af6eSVivien Didelot return err; 5002d79af6eSVivien Didelot 5012d79af6eSVivien Didelot if (!(val & mask)) 5022d79af6eSVivien Didelot return 0; 5032d79af6eSVivien Didelot 5042d79af6eSVivien Didelot usleep_range(1000, 2000); 5052d79af6eSVivien Didelot } 5062d79af6eSVivien Didelot 50730853553SAndrew Lunn dev_err(chip->dev, "Timeout while waiting for switch\n"); 5082d79af6eSVivien Didelot return -ETIMEDOUT; 5092d79af6eSVivien Didelot } 5102d79af6eSVivien Didelot 511f22ab641SVivien Didelot /* Indirect write to single pointer-data register with an Update bit */ 512ec561276SVivien Didelot int mv88e6xxx_update(struct mv88e6xxx_chip *chip, int addr, int reg, u16 update) 513f22ab641SVivien Didelot { 514f22ab641SVivien Didelot u16 val; 5150f02b4f7SAndrew Lunn int err; 516f22ab641SVivien Didelot 517f22ab641SVivien Didelot /* Wait until the previous operation is completed */ 5180f02b4f7SAndrew Lunn err = mv88e6xxx_wait(chip, addr, reg, BIT(15)); 519f22ab641SVivien Didelot if (err) 520f22ab641SVivien Didelot return err; 521f22ab641SVivien Didelot 522f22ab641SVivien Didelot /* Set the Update bit to trigger a write operation */ 523f22ab641SVivien Didelot val = BIT(15) | update; 524f22ab641SVivien Didelot 525f22ab641SVivien Didelot return mv88e6xxx_write(chip, addr, reg, val); 526f22ab641SVivien Didelot } 527f22ab641SVivien Didelot 528a935c052SVivien Didelot static int mv88e6xxx_ppu_disable(struct mv88e6xxx_chip *chip) 529fad09c73SVivien Didelot { 530fad09c73SVivien Didelot u16 val; 531a935c052SVivien Didelot int i, err; 532fad09c73SVivien Didelot 533a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val); 534fad09c73SVivien Didelot if (err) 535fad09c73SVivien Didelot return err; 536fad09c73SVivien Didelot 537a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, 538a935c052SVivien Didelot val & ~GLOBAL_CONTROL_PPU_ENABLE); 539a935c052SVivien Didelot if (err) 540a935c052SVivien Didelot return err; 541fad09c73SVivien Didelot 5426441e669SAndrew Lunn for (i = 0; i < 16; i++) { 543a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &val); 544a935c052SVivien Didelot if (err) 545a935c052SVivien Didelot return err; 546fad09c73SVivien Didelot 547fad09c73SVivien Didelot usleep_range(1000, 2000); 548a935c052SVivien Didelot if ((val & GLOBAL_STATUS_PPU_MASK) != GLOBAL_STATUS_PPU_POLLING) 549fad09c73SVivien Didelot return 0; 550fad09c73SVivien Didelot } 551fad09c73SVivien Didelot 552fad09c73SVivien Didelot return -ETIMEDOUT; 553fad09c73SVivien Didelot } 554fad09c73SVivien Didelot 555fad09c73SVivien Didelot static int mv88e6xxx_ppu_enable(struct mv88e6xxx_chip *chip) 556fad09c73SVivien Didelot { 557a935c052SVivien Didelot u16 val; 558a935c052SVivien Didelot int i, err; 559fad09c73SVivien Didelot 560a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val); 561a935c052SVivien Didelot if (err) 562a935c052SVivien Didelot return err; 563fad09c73SVivien Didelot 564a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, 565a935c052SVivien Didelot val | GLOBAL_CONTROL_PPU_ENABLE); 566fad09c73SVivien Didelot if (err) 567fad09c73SVivien Didelot return err; 568fad09c73SVivien Didelot 5696441e669SAndrew Lunn for (i = 0; i < 16; i++) { 570a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &val); 571a935c052SVivien Didelot if (err) 572a935c052SVivien Didelot return err; 573fad09c73SVivien Didelot 574fad09c73SVivien Didelot usleep_range(1000, 2000); 575a935c052SVivien Didelot if ((val & GLOBAL_STATUS_PPU_MASK) == GLOBAL_STATUS_PPU_POLLING) 576fad09c73SVivien Didelot return 0; 577fad09c73SVivien Didelot } 578fad09c73SVivien Didelot 579fad09c73SVivien Didelot return -ETIMEDOUT; 580fad09c73SVivien Didelot } 581fad09c73SVivien Didelot 582fad09c73SVivien Didelot static void mv88e6xxx_ppu_reenable_work(struct work_struct *ugly) 583fad09c73SVivien Didelot { 584fad09c73SVivien Didelot struct mv88e6xxx_chip *chip; 585fad09c73SVivien Didelot 586fad09c73SVivien Didelot chip = container_of(ugly, struct mv88e6xxx_chip, ppu_work); 587fad09c73SVivien Didelot 588fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 589fad09c73SVivien Didelot 590fad09c73SVivien Didelot if (mutex_trylock(&chip->ppu_mutex)) { 591fad09c73SVivien Didelot if (mv88e6xxx_ppu_enable(chip) == 0) 592fad09c73SVivien Didelot chip->ppu_disabled = 0; 593fad09c73SVivien Didelot mutex_unlock(&chip->ppu_mutex); 594fad09c73SVivien Didelot } 595fad09c73SVivien Didelot 596fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 597fad09c73SVivien Didelot } 598fad09c73SVivien Didelot 599fad09c73SVivien Didelot static void mv88e6xxx_ppu_reenable_timer(unsigned long _ps) 600fad09c73SVivien Didelot { 601fad09c73SVivien Didelot struct mv88e6xxx_chip *chip = (void *)_ps; 602fad09c73SVivien Didelot 603fad09c73SVivien Didelot schedule_work(&chip->ppu_work); 604fad09c73SVivien Didelot } 605fad09c73SVivien Didelot 606fad09c73SVivien Didelot static int mv88e6xxx_ppu_access_get(struct mv88e6xxx_chip *chip) 607fad09c73SVivien Didelot { 608fad09c73SVivien Didelot int ret; 609fad09c73SVivien Didelot 610fad09c73SVivien Didelot mutex_lock(&chip->ppu_mutex); 611fad09c73SVivien Didelot 612fad09c73SVivien Didelot /* If the PHY polling unit is enabled, disable it so that 613fad09c73SVivien Didelot * we can access the PHY registers. If it was already 614fad09c73SVivien Didelot * disabled, cancel the timer that is going to re-enable 615fad09c73SVivien Didelot * it. 616fad09c73SVivien Didelot */ 617fad09c73SVivien Didelot if (!chip->ppu_disabled) { 618fad09c73SVivien Didelot ret = mv88e6xxx_ppu_disable(chip); 619fad09c73SVivien Didelot if (ret < 0) { 620fad09c73SVivien Didelot mutex_unlock(&chip->ppu_mutex); 621fad09c73SVivien Didelot return ret; 622fad09c73SVivien Didelot } 623fad09c73SVivien Didelot chip->ppu_disabled = 1; 624fad09c73SVivien Didelot } else { 625fad09c73SVivien Didelot del_timer(&chip->ppu_timer); 626fad09c73SVivien Didelot ret = 0; 627fad09c73SVivien Didelot } 628fad09c73SVivien Didelot 629fad09c73SVivien Didelot return ret; 630fad09c73SVivien Didelot } 631fad09c73SVivien Didelot 632fad09c73SVivien Didelot static void mv88e6xxx_ppu_access_put(struct mv88e6xxx_chip *chip) 633fad09c73SVivien Didelot { 634fad09c73SVivien Didelot /* Schedule a timer to re-enable the PHY polling unit. */ 635fad09c73SVivien Didelot mod_timer(&chip->ppu_timer, jiffies + msecs_to_jiffies(10)); 636fad09c73SVivien Didelot mutex_unlock(&chip->ppu_mutex); 637fad09c73SVivien Didelot } 638fad09c73SVivien Didelot 639fad09c73SVivien Didelot static void mv88e6xxx_ppu_state_init(struct mv88e6xxx_chip *chip) 640fad09c73SVivien Didelot { 641fad09c73SVivien Didelot mutex_init(&chip->ppu_mutex); 642fad09c73SVivien Didelot INIT_WORK(&chip->ppu_work, mv88e6xxx_ppu_reenable_work); 64368497a87SWei Yongjun setup_timer(&chip->ppu_timer, mv88e6xxx_ppu_reenable_timer, 64468497a87SWei Yongjun (unsigned long)chip); 645fad09c73SVivien Didelot } 646fad09c73SVivien Didelot 647930188ceSAndrew Lunn static void mv88e6xxx_ppu_state_destroy(struct mv88e6xxx_chip *chip) 648930188ceSAndrew Lunn { 649930188ceSAndrew Lunn del_timer_sync(&chip->ppu_timer); 650930188ceSAndrew Lunn } 651930188ceSAndrew Lunn 652e57e5e77SVivien Didelot static int mv88e6xxx_phy_ppu_read(struct mv88e6xxx_chip *chip, int addr, 653e57e5e77SVivien Didelot int reg, u16 *val) 654fad09c73SVivien Didelot { 655e57e5e77SVivien Didelot int err; 656fad09c73SVivien Didelot 657e57e5e77SVivien Didelot err = mv88e6xxx_ppu_access_get(chip); 658e57e5e77SVivien Didelot if (!err) { 659e57e5e77SVivien Didelot err = mv88e6xxx_read(chip, addr, reg, val); 660fad09c73SVivien Didelot mv88e6xxx_ppu_access_put(chip); 661fad09c73SVivien Didelot } 662fad09c73SVivien Didelot 663e57e5e77SVivien Didelot return err; 664fad09c73SVivien Didelot } 665fad09c73SVivien Didelot 666e57e5e77SVivien Didelot static int mv88e6xxx_phy_ppu_write(struct mv88e6xxx_chip *chip, int addr, 667e57e5e77SVivien Didelot int reg, u16 val) 668fad09c73SVivien Didelot { 669e57e5e77SVivien Didelot int err; 670fad09c73SVivien Didelot 671e57e5e77SVivien Didelot err = mv88e6xxx_ppu_access_get(chip); 672e57e5e77SVivien Didelot if (!err) { 673e57e5e77SVivien Didelot err = mv88e6xxx_write(chip, addr, reg, val); 674fad09c73SVivien Didelot mv88e6xxx_ppu_access_put(chip); 675fad09c73SVivien Didelot } 676fad09c73SVivien Didelot 677e57e5e77SVivien Didelot return err; 678fad09c73SVivien Didelot } 679fad09c73SVivien Didelot 680fad09c73SVivien Didelot static bool mv88e6xxx_6065_family(struct mv88e6xxx_chip *chip) 681fad09c73SVivien Didelot { 682fad09c73SVivien Didelot return chip->info->family == MV88E6XXX_FAMILY_6065; 683fad09c73SVivien Didelot } 684fad09c73SVivien Didelot 685fad09c73SVivien Didelot static bool mv88e6xxx_6095_family(struct mv88e6xxx_chip *chip) 686fad09c73SVivien Didelot { 687fad09c73SVivien Didelot return chip->info->family == MV88E6XXX_FAMILY_6095; 688fad09c73SVivien Didelot } 689fad09c73SVivien Didelot 690fad09c73SVivien Didelot static bool mv88e6xxx_6097_family(struct mv88e6xxx_chip *chip) 691fad09c73SVivien Didelot { 692fad09c73SVivien Didelot return chip->info->family == MV88E6XXX_FAMILY_6097; 693fad09c73SVivien Didelot } 694fad09c73SVivien Didelot 695fad09c73SVivien Didelot static bool mv88e6xxx_6165_family(struct mv88e6xxx_chip *chip) 696fad09c73SVivien Didelot { 697fad09c73SVivien Didelot return chip->info->family == MV88E6XXX_FAMILY_6165; 698fad09c73SVivien Didelot } 699fad09c73SVivien Didelot 700fad09c73SVivien Didelot static bool mv88e6xxx_6185_family(struct mv88e6xxx_chip *chip) 701fad09c73SVivien Didelot { 702fad09c73SVivien Didelot return chip->info->family == MV88E6XXX_FAMILY_6185; 703fad09c73SVivien Didelot } 704fad09c73SVivien Didelot 705fad09c73SVivien Didelot static bool mv88e6xxx_6320_family(struct mv88e6xxx_chip *chip) 706fad09c73SVivien Didelot { 707fad09c73SVivien Didelot return chip->info->family == MV88E6XXX_FAMILY_6320; 708fad09c73SVivien Didelot } 709fad09c73SVivien Didelot 710fad09c73SVivien Didelot static bool mv88e6xxx_6351_family(struct mv88e6xxx_chip *chip) 711fad09c73SVivien Didelot { 712fad09c73SVivien Didelot return chip->info->family == MV88E6XXX_FAMILY_6351; 713fad09c73SVivien Didelot } 714fad09c73SVivien Didelot 715fad09c73SVivien Didelot static bool mv88e6xxx_6352_family(struct mv88e6xxx_chip *chip) 716fad09c73SVivien Didelot { 717fad09c73SVivien Didelot return chip->info->family == MV88E6XXX_FAMILY_6352; 718fad09c73SVivien Didelot } 719fad09c73SVivien Didelot 720d78343d2SVivien Didelot static int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, 721d78343d2SVivien Didelot int link, int speed, int duplex, 722d78343d2SVivien Didelot phy_interface_t mode) 723d78343d2SVivien Didelot { 724d78343d2SVivien Didelot int err; 725d78343d2SVivien Didelot 726d78343d2SVivien Didelot if (!chip->info->ops->port_set_link) 727d78343d2SVivien Didelot return 0; 728d78343d2SVivien Didelot 729d78343d2SVivien Didelot /* Port's MAC control must not be changed unless the link is down */ 730d78343d2SVivien Didelot err = chip->info->ops->port_set_link(chip, port, 0); 731d78343d2SVivien Didelot if (err) 732d78343d2SVivien Didelot return err; 733d78343d2SVivien Didelot 734d78343d2SVivien Didelot if (chip->info->ops->port_set_speed) { 735d78343d2SVivien Didelot err = chip->info->ops->port_set_speed(chip, port, speed); 736d78343d2SVivien Didelot if (err && err != -EOPNOTSUPP) 737d78343d2SVivien Didelot goto restore_link; 738d78343d2SVivien Didelot } 739d78343d2SVivien Didelot 740d78343d2SVivien Didelot if (chip->info->ops->port_set_duplex) { 741d78343d2SVivien Didelot err = chip->info->ops->port_set_duplex(chip, port, duplex); 742d78343d2SVivien Didelot if (err && err != -EOPNOTSUPP) 743d78343d2SVivien Didelot goto restore_link; 744d78343d2SVivien Didelot } 745d78343d2SVivien Didelot 746d78343d2SVivien Didelot if (chip->info->ops->port_set_rgmii_delay) { 747d78343d2SVivien Didelot err = chip->info->ops->port_set_rgmii_delay(chip, port, mode); 748d78343d2SVivien Didelot if (err && err != -EOPNOTSUPP) 749d78343d2SVivien Didelot goto restore_link; 750d78343d2SVivien Didelot } 751d78343d2SVivien Didelot 752d78343d2SVivien Didelot err = 0; 753d78343d2SVivien Didelot restore_link: 754d78343d2SVivien Didelot if (chip->info->ops->port_set_link(chip, port, link)) 755d78343d2SVivien Didelot netdev_err(chip->ds->ports[port].netdev, 756d78343d2SVivien Didelot "failed to restore MAC's link\n"); 757d78343d2SVivien Didelot 758d78343d2SVivien Didelot return err; 759d78343d2SVivien Didelot } 760d78343d2SVivien Didelot 761fad09c73SVivien Didelot /* We expect the switch to perform auto negotiation if there is a real 762fad09c73SVivien Didelot * phy. However, in the case of a fixed link phy, we force the port 763fad09c73SVivien Didelot * settings from the fixed link settings. 764fad09c73SVivien Didelot */ 765fad09c73SVivien Didelot static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port, 766fad09c73SVivien Didelot struct phy_device *phydev) 767fad09c73SVivien Didelot { 76804bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 7690e7b9925SAndrew Lunn int err; 770fad09c73SVivien Didelot 771fad09c73SVivien Didelot if (!phy_is_pseudo_fixed_link(phydev)) 772fad09c73SVivien Didelot return; 773fad09c73SVivien Didelot 774fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 775d78343d2SVivien Didelot err = mv88e6xxx_port_setup_mac(chip, port, phydev->link, phydev->speed, 776d78343d2SVivien Didelot phydev->duplex, phydev->interface); 777fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 778d78343d2SVivien Didelot 779d78343d2SVivien Didelot if (err && err != -EOPNOTSUPP) 780d78343d2SVivien Didelot netdev_err(ds->ports[port].netdev, "failed to configure MAC\n"); 781fad09c73SVivien Didelot } 782fad09c73SVivien Didelot 783fad09c73SVivien Didelot static int _mv88e6xxx_stats_wait(struct mv88e6xxx_chip *chip) 784fad09c73SVivien Didelot { 785a935c052SVivien Didelot u16 val; 786a935c052SVivien Didelot int i, err; 787fad09c73SVivien Didelot 788fad09c73SVivien Didelot for (i = 0; i < 10; i++) { 789a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_OP, &val); 790a935c052SVivien Didelot if ((val & GLOBAL_STATS_OP_BUSY) == 0) 791fad09c73SVivien Didelot return 0; 792fad09c73SVivien Didelot } 793fad09c73SVivien Didelot 794fad09c73SVivien Didelot return -ETIMEDOUT; 795fad09c73SVivien Didelot } 796fad09c73SVivien Didelot 797fad09c73SVivien Didelot static int _mv88e6xxx_stats_snapshot(struct mv88e6xxx_chip *chip, int port) 798fad09c73SVivien Didelot { 799a935c052SVivien Didelot int err; 800fad09c73SVivien Didelot 801fad09c73SVivien Didelot if (mv88e6xxx_6320_family(chip) || mv88e6xxx_6352_family(chip)) 802fad09c73SVivien Didelot port = (port + 1) << 5; 803fad09c73SVivien Didelot 804fad09c73SVivien Didelot /* Snapshot the hardware statistics counters for this port. */ 805a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP, 806fad09c73SVivien Didelot GLOBAL_STATS_OP_CAPTURE_PORT | 807fad09c73SVivien Didelot GLOBAL_STATS_OP_HIST_RX_TX | port); 808a935c052SVivien Didelot if (err) 809a935c052SVivien Didelot return err; 810fad09c73SVivien Didelot 811fad09c73SVivien Didelot /* Wait for the snapshotting to complete. */ 812a935c052SVivien Didelot return _mv88e6xxx_stats_wait(chip); 813fad09c73SVivien Didelot } 814fad09c73SVivien Didelot 815fad09c73SVivien Didelot static void _mv88e6xxx_stats_read(struct mv88e6xxx_chip *chip, 816fad09c73SVivien Didelot int stat, u32 *val) 817fad09c73SVivien Didelot { 818a935c052SVivien Didelot u32 value; 819a935c052SVivien Didelot u16 reg; 820a935c052SVivien Didelot int err; 821fad09c73SVivien Didelot 822fad09c73SVivien Didelot *val = 0; 823fad09c73SVivien Didelot 824a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP, 825fad09c73SVivien Didelot GLOBAL_STATS_OP_READ_CAPTURED | 826fad09c73SVivien Didelot GLOBAL_STATS_OP_HIST_RX_TX | stat); 827a935c052SVivien Didelot if (err) 828fad09c73SVivien Didelot return; 829fad09c73SVivien Didelot 830a935c052SVivien Didelot err = _mv88e6xxx_stats_wait(chip); 831a935c052SVivien Didelot if (err) 832fad09c73SVivien Didelot return; 833fad09c73SVivien Didelot 834a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_COUNTER_32, ®); 835a935c052SVivien Didelot if (err) 836fad09c73SVivien Didelot return; 837fad09c73SVivien Didelot 838a935c052SVivien Didelot value = reg << 16; 839fad09c73SVivien Didelot 840a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_COUNTER_01, ®); 841a935c052SVivien Didelot if (err) 842fad09c73SVivien Didelot return; 843fad09c73SVivien Didelot 844a935c052SVivien Didelot *val = value | reg; 845fad09c73SVivien Didelot } 846fad09c73SVivien Didelot 847fad09c73SVivien Didelot static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = { 848fad09c73SVivien Didelot { "in_good_octets", 8, 0x00, BANK0, }, 849fad09c73SVivien Didelot { "in_bad_octets", 4, 0x02, BANK0, }, 850fad09c73SVivien Didelot { "in_unicast", 4, 0x04, BANK0, }, 851fad09c73SVivien Didelot { "in_broadcasts", 4, 0x06, BANK0, }, 852fad09c73SVivien Didelot { "in_multicasts", 4, 0x07, BANK0, }, 853fad09c73SVivien Didelot { "in_pause", 4, 0x16, BANK0, }, 854fad09c73SVivien Didelot { "in_undersize", 4, 0x18, BANK0, }, 855fad09c73SVivien Didelot { "in_fragments", 4, 0x19, BANK0, }, 856fad09c73SVivien Didelot { "in_oversize", 4, 0x1a, BANK0, }, 857fad09c73SVivien Didelot { "in_jabber", 4, 0x1b, BANK0, }, 858fad09c73SVivien Didelot { "in_rx_error", 4, 0x1c, BANK0, }, 859fad09c73SVivien Didelot { "in_fcs_error", 4, 0x1d, BANK0, }, 860fad09c73SVivien Didelot { "out_octets", 8, 0x0e, BANK0, }, 861fad09c73SVivien Didelot { "out_unicast", 4, 0x10, BANK0, }, 862fad09c73SVivien Didelot { "out_broadcasts", 4, 0x13, BANK0, }, 863fad09c73SVivien Didelot { "out_multicasts", 4, 0x12, BANK0, }, 864fad09c73SVivien Didelot { "out_pause", 4, 0x15, BANK0, }, 865fad09c73SVivien Didelot { "excessive", 4, 0x11, BANK0, }, 866fad09c73SVivien Didelot { "collisions", 4, 0x1e, BANK0, }, 867fad09c73SVivien Didelot { "deferred", 4, 0x05, BANK0, }, 868fad09c73SVivien Didelot { "single", 4, 0x14, BANK0, }, 869fad09c73SVivien Didelot { "multiple", 4, 0x17, BANK0, }, 870fad09c73SVivien Didelot { "out_fcs_error", 4, 0x03, BANK0, }, 871fad09c73SVivien Didelot { "late", 4, 0x1f, BANK0, }, 872fad09c73SVivien Didelot { "hist_64bytes", 4, 0x08, BANK0, }, 873fad09c73SVivien Didelot { "hist_65_127bytes", 4, 0x09, BANK0, }, 874fad09c73SVivien Didelot { "hist_128_255bytes", 4, 0x0a, BANK0, }, 875fad09c73SVivien Didelot { "hist_256_511bytes", 4, 0x0b, BANK0, }, 876fad09c73SVivien Didelot { "hist_512_1023bytes", 4, 0x0c, BANK0, }, 877fad09c73SVivien Didelot { "hist_1024_max_bytes", 4, 0x0d, BANK0, }, 878fad09c73SVivien Didelot { "sw_in_discards", 4, 0x10, PORT, }, 879fad09c73SVivien Didelot { "sw_in_filtered", 2, 0x12, PORT, }, 880fad09c73SVivien Didelot { "sw_out_filtered", 2, 0x13, PORT, }, 881fad09c73SVivien Didelot { "in_discards", 4, 0x00 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 882fad09c73SVivien Didelot { "in_filtered", 4, 0x01 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 883fad09c73SVivien Didelot { "in_accepted", 4, 0x02 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 884fad09c73SVivien Didelot { "in_bad_accepted", 4, 0x03 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 885fad09c73SVivien Didelot { "in_good_avb_class_a", 4, 0x04 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 886fad09c73SVivien Didelot { "in_good_avb_class_b", 4, 0x05 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 887fad09c73SVivien Didelot { "in_bad_avb_class_a", 4, 0x06 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 888fad09c73SVivien Didelot { "in_bad_avb_class_b", 4, 0x07 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 889fad09c73SVivien Didelot { "tcam_counter_0", 4, 0x08 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 890fad09c73SVivien Didelot { "tcam_counter_1", 4, 0x09 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 891fad09c73SVivien Didelot { "tcam_counter_2", 4, 0x0a | GLOBAL_STATS_OP_BANK_1, BANK1, }, 892fad09c73SVivien Didelot { "tcam_counter_3", 4, 0x0b | GLOBAL_STATS_OP_BANK_1, BANK1, }, 893fad09c73SVivien Didelot { "in_da_unknown", 4, 0x0e | GLOBAL_STATS_OP_BANK_1, BANK1, }, 894fad09c73SVivien Didelot { "in_management", 4, 0x0f | GLOBAL_STATS_OP_BANK_1, BANK1, }, 895fad09c73SVivien Didelot { "out_queue_0", 4, 0x10 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 896fad09c73SVivien Didelot { "out_queue_1", 4, 0x11 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 897fad09c73SVivien Didelot { "out_queue_2", 4, 0x12 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 898fad09c73SVivien Didelot { "out_queue_3", 4, 0x13 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 899fad09c73SVivien Didelot { "out_queue_4", 4, 0x14 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 900fad09c73SVivien Didelot { "out_queue_5", 4, 0x15 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 901fad09c73SVivien Didelot { "out_queue_6", 4, 0x16 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 902fad09c73SVivien Didelot { "out_queue_7", 4, 0x17 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 903fad09c73SVivien Didelot { "out_cut_through", 4, 0x18 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 904fad09c73SVivien Didelot { "out_octets_a", 4, 0x1a | GLOBAL_STATS_OP_BANK_1, BANK1, }, 905fad09c73SVivien Didelot { "out_octets_b", 4, 0x1b | GLOBAL_STATS_OP_BANK_1, BANK1, }, 906fad09c73SVivien Didelot { "out_management", 4, 0x1f | GLOBAL_STATS_OP_BANK_1, BANK1, }, 907fad09c73SVivien Didelot }; 908fad09c73SVivien Didelot 909fad09c73SVivien Didelot static bool mv88e6xxx_has_stat(struct mv88e6xxx_chip *chip, 910fad09c73SVivien Didelot struct mv88e6xxx_hw_stat *stat) 911fad09c73SVivien Didelot { 912fad09c73SVivien Didelot switch (stat->type) { 913fad09c73SVivien Didelot case BANK0: 914fad09c73SVivien Didelot return true; 915fad09c73SVivien Didelot case BANK1: 916fad09c73SVivien Didelot return mv88e6xxx_6320_family(chip); 917fad09c73SVivien Didelot case PORT: 918fad09c73SVivien Didelot return mv88e6xxx_6095_family(chip) || 919fad09c73SVivien Didelot mv88e6xxx_6185_family(chip) || 920fad09c73SVivien Didelot mv88e6xxx_6097_family(chip) || 921fad09c73SVivien Didelot mv88e6xxx_6165_family(chip) || 922fad09c73SVivien Didelot mv88e6xxx_6351_family(chip) || 923fad09c73SVivien Didelot mv88e6xxx_6352_family(chip); 924fad09c73SVivien Didelot } 925fad09c73SVivien Didelot return false; 926fad09c73SVivien Didelot } 927fad09c73SVivien Didelot 928fad09c73SVivien Didelot static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_chip *chip, 929fad09c73SVivien Didelot struct mv88e6xxx_hw_stat *s, 930fad09c73SVivien Didelot int port) 931fad09c73SVivien Didelot { 932fad09c73SVivien Didelot u32 low; 933fad09c73SVivien Didelot u32 high = 0; 9340e7b9925SAndrew Lunn int err; 9350e7b9925SAndrew Lunn u16 reg; 936fad09c73SVivien Didelot u64 value; 937fad09c73SVivien Didelot 938fad09c73SVivien Didelot switch (s->type) { 939fad09c73SVivien Didelot case PORT: 9400e7b9925SAndrew Lunn err = mv88e6xxx_port_read(chip, port, s->reg, ®); 9410e7b9925SAndrew Lunn if (err) 942fad09c73SVivien Didelot return UINT64_MAX; 943fad09c73SVivien Didelot 9440e7b9925SAndrew Lunn low = reg; 945fad09c73SVivien Didelot if (s->sizeof_stat == 4) { 9460e7b9925SAndrew Lunn err = mv88e6xxx_port_read(chip, port, s->reg + 1, ®); 9470e7b9925SAndrew Lunn if (err) 948fad09c73SVivien Didelot return UINT64_MAX; 9490e7b9925SAndrew Lunn high = reg; 950fad09c73SVivien Didelot } 951fad09c73SVivien Didelot break; 952fad09c73SVivien Didelot case BANK0: 953fad09c73SVivien Didelot case BANK1: 954fad09c73SVivien Didelot _mv88e6xxx_stats_read(chip, s->reg, &low); 955fad09c73SVivien Didelot if (s->sizeof_stat == 8) 956fad09c73SVivien Didelot _mv88e6xxx_stats_read(chip, s->reg + 1, &high); 957fad09c73SVivien Didelot } 958fad09c73SVivien Didelot value = (((u64)high) << 16) | low; 959fad09c73SVivien Didelot return value; 960fad09c73SVivien Didelot } 961fad09c73SVivien Didelot 962fad09c73SVivien Didelot static void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, 963fad09c73SVivien Didelot uint8_t *data) 964fad09c73SVivien Didelot { 96504bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 966fad09c73SVivien Didelot struct mv88e6xxx_hw_stat *stat; 967fad09c73SVivien Didelot int i, j; 968fad09c73SVivien Didelot 969fad09c73SVivien Didelot for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) { 970fad09c73SVivien Didelot stat = &mv88e6xxx_hw_stats[i]; 971fad09c73SVivien Didelot if (mv88e6xxx_has_stat(chip, stat)) { 972fad09c73SVivien Didelot memcpy(data + j * ETH_GSTRING_LEN, stat->string, 973fad09c73SVivien Didelot ETH_GSTRING_LEN); 974fad09c73SVivien Didelot j++; 975fad09c73SVivien Didelot } 976fad09c73SVivien Didelot } 977fad09c73SVivien Didelot } 978fad09c73SVivien Didelot 979fad09c73SVivien Didelot static int mv88e6xxx_get_sset_count(struct dsa_switch *ds) 980fad09c73SVivien Didelot { 98104bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 982fad09c73SVivien Didelot struct mv88e6xxx_hw_stat *stat; 983fad09c73SVivien Didelot int i, j; 984fad09c73SVivien Didelot 985fad09c73SVivien Didelot for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) { 986fad09c73SVivien Didelot stat = &mv88e6xxx_hw_stats[i]; 987fad09c73SVivien Didelot if (mv88e6xxx_has_stat(chip, stat)) 988fad09c73SVivien Didelot j++; 989fad09c73SVivien Didelot } 990fad09c73SVivien Didelot return j; 991fad09c73SVivien Didelot } 992fad09c73SVivien Didelot 993fad09c73SVivien Didelot static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port, 994fad09c73SVivien Didelot uint64_t *data) 995fad09c73SVivien Didelot { 99604bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 997fad09c73SVivien Didelot struct mv88e6xxx_hw_stat *stat; 998fad09c73SVivien Didelot int ret; 999fad09c73SVivien Didelot int i, j; 1000fad09c73SVivien Didelot 1001fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 1002fad09c73SVivien Didelot 1003fad09c73SVivien Didelot ret = _mv88e6xxx_stats_snapshot(chip, port); 1004fad09c73SVivien Didelot if (ret < 0) { 1005fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 1006fad09c73SVivien Didelot return; 1007fad09c73SVivien Didelot } 1008fad09c73SVivien Didelot for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) { 1009fad09c73SVivien Didelot stat = &mv88e6xxx_hw_stats[i]; 1010fad09c73SVivien Didelot if (mv88e6xxx_has_stat(chip, stat)) { 1011fad09c73SVivien Didelot data[j] = _mv88e6xxx_get_ethtool_stat(chip, stat, port); 1012fad09c73SVivien Didelot j++; 1013fad09c73SVivien Didelot } 1014fad09c73SVivien Didelot } 1015fad09c73SVivien Didelot 1016fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 1017fad09c73SVivien Didelot } 1018fad09c73SVivien Didelot 1019fad09c73SVivien Didelot static int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port) 1020fad09c73SVivien Didelot { 1021fad09c73SVivien Didelot return 32 * sizeof(u16); 1022fad09c73SVivien Didelot } 1023fad09c73SVivien Didelot 1024fad09c73SVivien Didelot static void mv88e6xxx_get_regs(struct dsa_switch *ds, int port, 1025fad09c73SVivien Didelot struct ethtool_regs *regs, void *_p) 1026fad09c73SVivien Didelot { 102704bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 10280e7b9925SAndrew Lunn int err; 10290e7b9925SAndrew Lunn u16 reg; 1030fad09c73SVivien Didelot u16 *p = _p; 1031fad09c73SVivien Didelot int i; 1032fad09c73SVivien Didelot 1033fad09c73SVivien Didelot regs->version = 0; 1034fad09c73SVivien Didelot 1035fad09c73SVivien Didelot memset(p, 0xff, 32 * sizeof(u16)); 1036fad09c73SVivien Didelot 1037fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 1038fad09c73SVivien Didelot 1039fad09c73SVivien Didelot for (i = 0; i < 32; i++) { 1040fad09c73SVivien Didelot 10410e7b9925SAndrew Lunn err = mv88e6xxx_port_read(chip, port, i, ®); 10420e7b9925SAndrew Lunn if (!err) 10430e7b9925SAndrew Lunn p[i] = reg; 1044fad09c73SVivien Didelot } 1045fad09c73SVivien Didelot 1046fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 1047fad09c73SVivien Didelot } 1048fad09c73SVivien Didelot 1049fad09c73SVivien Didelot static int _mv88e6xxx_atu_wait(struct mv88e6xxx_chip *chip) 1050fad09c73SVivien Didelot { 1051a935c052SVivien Didelot return mv88e6xxx_g1_wait(chip, GLOBAL_ATU_OP, GLOBAL_ATU_OP_BUSY); 1052fad09c73SVivien Didelot } 1053fad09c73SVivien Didelot 1054fad09c73SVivien Didelot static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, 1055fad09c73SVivien Didelot struct ethtool_eee *e) 1056fad09c73SVivien Didelot { 105704bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 10589c93829cSVivien Didelot u16 reg; 10599c93829cSVivien Didelot int err; 1060fad09c73SVivien Didelot 1061fad09c73SVivien Didelot if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEE)) 1062fad09c73SVivien Didelot return -EOPNOTSUPP; 1063fad09c73SVivien Didelot 1064fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 1065fad09c73SVivien Didelot 10669c93829cSVivien Didelot err = mv88e6xxx_phy_read(chip, port, 16, ®); 10679c93829cSVivien Didelot if (err) 1068fad09c73SVivien Didelot goto out; 1069fad09c73SVivien Didelot 1070fad09c73SVivien Didelot e->eee_enabled = !!(reg & 0x0200); 1071fad09c73SVivien Didelot e->tx_lpi_enabled = !!(reg & 0x0100); 1072fad09c73SVivien Didelot 10730e7b9925SAndrew Lunn err = mv88e6xxx_port_read(chip, port, PORT_STATUS, ®); 10749c93829cSVivien Didelot if (err) 1075fad09c73SVivien Didelot goto out; 1076fad09c73SVivien Didelot 1077fad09c73SVivien Didelot e->eee_active = !!(reg & PORT_STATUS_EEE); 1078fad09c73SVivien Didelot out: 1079fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 10809c93829cSVivien Didelot 10819c93829cSVivien Didelot return err; 1082fad09c73SVivien Didelot } 1083fad09c73SVivien Didelot 1084fad09c73SVivien Didelot static int mv88e6xxx_set_eee(struct dsa_switch *ds, int port, 1085fad09c73SVivien Didelot struct phy_device *phydev, struct ethtool_eee *e) 1086fad09c73SVivien Didelot { 108704bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 10889c93829cSVivien Didelot u16 reg; 10899c93829cSVivien Didelot int err; 1090fad09c73SVivien Didelot 1091fad09c73SVivien Didelot if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEE)) 1092fad09c73SVivien Didelot return -EOPNOTSUPP; 1093fad09c73SVivien Didelot 1094fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 1095fad09c73SVivien Didelot 10969c93829cSVivien Didelot err = mv88e6xxx_phy_read(chip, port, 16, ®); 10979c93829cSVivien Didelot if (err) 1098fad09c73SVivien Didelot goto out; 1099fad09c73SVivien Didelot 11009c93829cSVivien Didelot reg &= ~0x0300; 1101fad09c73SVivien Didelot if (e->eee_enabled) 1102fad09c73SVivien Didelot reg |= 0x0200; 1103fad09c73SVivien Didelot if (e->tx_lpi_enabled) 1104fad09c73SVivien Didelot reg |= 0x0100; 1105fad09c73SVivien Didelot 11069c93829cSVivien Didelot err = mv88e6xxx_phy_write(chip, port, 16, reg); 1107fad09c73SVivien Didelot out: 1108fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 1109fad09c73SVivien Didelot 11109c93829cSVivien Didelot return err; 1111fad09c73SVivien Didelot } 1112fad09c73SVivien Didelot 1113fad09c73SVivien Didelot static int _mv88e6xxx_atu_cmd(struct mv88e6xxx_chip *chip, u16 fid, u16 cmd) 1114fad09c73SVivien Didelot { 1115a935c052SVivien Didelot u16 val; 1116a935c052SVivien Didelot int err; 1117fad09c73SVivien Didelot 11186dc10bbcSVivien Didelot if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G1_ATU_FID)) { 1119a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_FID, fid); 1120a935c052SVivien Didelot if (err) 1121a935c052SVivien Didelot return err; 1122fad09c73SVivien Didelot } else if (mv88e6xxx_num_databases(chip) == 256) { 1123fad09c73SVivien Didelot /* ATU DBNum[7:4] are located in ATU Control 15:12 */ 1124a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_CONTROL, &val); 1125a935c052SVivien Didelot if (err) 1126a935c052SVivien Didelot return err; 1127fad09c73SVivien Didelot 1128a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL, 1129a935c052SVivien Didelot (val & 0xfff) | ((fid << 8) & 0xf000)); 1130a935c052SVivien Didelot if (err) 1131a935c052SVivien Didelot return err; 1132fad09c73SVivien Didelot 1133fad09c73SVivien Didelot /* ATU DBNum[3:0] are located in ATU Operation 3:0 */ 1134fad09c73SVivien Didelot cmd |= fid & 0xf; 1135fad09c73SVivien Didelot } 1136fad09c73SVivien Didelot 1137a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_OP, cmd); 1138a935c052SVivien Didelot if (err) 1139a935c052SVivien Didelot return err; 1140fad09c73SVivien Didelot 1141fad09c73SVivien Didelot return _mv88e6xxx_atu_wait(chip); 1142fad09c73SVivien Didelot } 1143fad09c73SVivien Didelot 1144fad09c73SVivien Didelot static int _mv88e6xxx_atu_data_write(struct mv88e6xxx_chip *chip, 1145fad09c73SVivien Didelot struct mv88e6xxx_atu_entry *entry) 1146fad09c73SVivien Didelot { 1147fad09c73SVivien Didelot u16 data = entry->state & GLOBAL_ATU_DATA_STATE_MASK; 1148fad09c73SVivien Didelot 1149fad09c73SVivien Didelot if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) { 1150fad09c73SVivien Didelot unsigned int mask, shift; 1151fad09c73SVivien Didelot 1152fad09c73SVivien Didelot if (entry->trunk) { 1153fad09c73SVivien Didelot data |= GLOBAL_ATU_DATA_TRUNK; 1154fad09c73SVivien Didelot mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK; 1155fad09c73SVivien Didelot shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT; 1156fad09c73SVivien Didelot } else { 1157fad09c73SVivien Didelot mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK; 1158fad09c73SVivien Didelot shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT; 1159fad09c73SVivien Didelot } 1160fad09c73SVivien Didelot 1161fad09c73SVivien Didelot data |= (entry->portv_trunkid << shift) & mask; 1162fad09c73SVivien Didelot } 1163fad09c73SVivien Didelot 1164a935c052SVivien Didelot return mv88e6xxx_g1_write(chip, GLOBAL_ATU_DATA, data); 1165fad09c73SVivien Didelot } 1166fad09c73SVivien Didelot 1167fad09c73SVivien Didelot static int _mv88e6xxx_atu_flush_move(struct mv88e6xxx_chip *chip, 1168fad09c73SVivien Didelot struct mv88e6xxx_atu_entry *entry, 1169fad09c73SVivien Didelot bool static_too) 1170fad09c73SVivien Didelot { 1171fad09c73SVivien Didelot int op; 1172fad09c73SVivien Didelot int err; 1173fad09c73SVivien Didelot 1174fad09c73SVivien Didelot err = _mv88e6xxx_atu_wait(chip); 1175fad09c73SVivien Didelot if (err) 1176fad09c73SVivien Didelot return err; 1177fad09c73SVivien Didelot 1178fad09c73SVivien Didelot err = _mv88e6xxx_atu_data_write(chip, entry); 1179fad09c73SVivien Didelot if (err) 1180fad09c73SVivien Didelot return err; 1181fad09c73SVivien Didelot 1182fad09c73SVivien Didelot if (entry->fid) { 1183fad09c73SVivien Didelot op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB : 1184fad09c73SVivien Didelot GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB; 1185fad09c73SVivien Didelot } else { 1186fad09c73SVivien Didelot op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL : 1187fad09c73SVivien Didelot GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC; 1188fad09c73SVivien Didelot } 1189fad09c73SVivien Didelot 1190fad09c73SVivien Didelot return _mv88e6xxx_atu_cmd(chip, entry->fid, op); 1191fad09c73SVivien Didelot } 1192fad09c73SVivien Didelot 1193fad09c73SVivien Didelot static int _mv88e6xxx_atu_flush(struct mv88e6xxx_chip *chip, 1194fad09c73SVivien Didelot u16 fid, bool static_too) 1195fad09c73SVivien Didelot { 1196fad09c73SVivien Didelot struct mv88e6xxx_atu_entry entry = { 1197fad09c73SVivien Didelot .fid = fid, 1198fad09c73SVivien Didelot .state = 0, /* EntryState bits must be 0 */ 1199fad09c73SVivien Didelot }; 1200fad09c73SVivien Didelot 1201fad09c73SVivien Didelot return _mv88e6xxx_atu_flush_move(chip, &entry, static_too); 1202fad09c73SVivien Didelot } 1203fad09c73SVivien Didelot 1204fad09c73SVivien Didelot static int _mv88e6xxx_atu_move(struct mv88e6xxx_chip *chip, u16 fid, 1205fad09c73SVivien Didelot int from_port, int to_port, bool static_too) 1206fad09c73SVivien Didelot { 1207fad09c73SVivien Didelot struct mv88e6xxx_atu_entry entry = { 1208fad09c73SVivien Didelot .trunk = false, 1209fad09c73SVivien Didelot .fid = fid, 1210fad09c73SVivien Didelot }; 1211fad09c73SVivien Didelot 1212fad09c73SVivien Didelot /* EntryState bits must be 0xF */ 1213fad09c73SVivien Didelot entry.state = GLOBAL_ATU_DATA_STATE_MASK; 1214fad09c73SVivien Didelot 1215fad09c73SVivien Didelot /* ToPort and FromPort are respectively in PortVec bits 7:4 and 3:0 */ 1216fad09c73SVivien Didelot entry.portv_trunkid = (to_port & 0x0f) << 4; 1217fad09c73SVivien Didelot entry.portv_trunkid |= from_port & 0x0f; 1218fad09c73SVivien Didelot 1219fad09c73SVivien Didelot return _mv88e6xxx_atu_flush_move(chip, &entry, static_too); 1220fad09c73SVivien Didelot } 1221fad09c73SVivien Didelot 1222fad09c73SVivien Didelot static int _mv88e6xxx_atu_remove(struct mv88e6xxx_chip *chip, u16 fid, 1223fad09c73SVivien Didelot int port, bool static_too) 1224fad09c73SVivien Didelot { 1225fad09c73SVivien Didelot /* Destination port 0xF means remove the entries */ 1226fad09c73SVivien Didelot return _mv88e6xxx_atu_move(chip, fid, port, 0x0f, static_too); 1227fad09c73SVivien Didelot } 1228fad09c73SVivien Didelot 1229fad09c73SVivien Didelot static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_chip *chip, int port) 1230fad09c73SVivien Didelot { 1231fad09c73SVivien Didelot struct net_device *bridge = chip->ports[port].bridge_dev; 1232fad09c73SVivien Didelot struct dsa_switch *ds = chip->ds; 1233fad09c73SVivien Didelot u16 output_ports = 0; 1234fad09c73SVivien Didelot int i; 1235fad09c73SVivien Didelot 1236fad09c73SVivien Didelot /* allow CPU port or DSA link(s) to send frames to every port */ 1237fad09c73SVivien Didelot if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) { 12385a7921f4SVivien Didelot output_ports = ~0; 1239fad09c73SVivien Didelot } else { 1240370b4ffbSVivien Didelot for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { 1241fad09c73SVivien Didelot /* allow sending frames to every group member */ 1242fad09c73SVivien Didelot if (bridge && chip->ports[i].bridge_dev == bridge) 1243fad09c73SVivien Didelot output_ports |= BIT(i); 1244fad09c73SVivien Didelot 1245fad09c73SVivien Didelot /* allow sending frames to CPU port and DSA link(s) */ 1246fad09c73SVivien Didelot if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i)) 1247fad09c73SVivien Didelot output_ports |= BIT(i); 1248fad09c73SVivien Didelot } 1249fad09c73SVivien Didelot } 1250fad09c73SVivien Didelot 1251fad09c73SVivien Didelot /* prevent frames from going back out of the port they came in on */ 1252fad09c73SVivien Didelot output_ports &= ~BIT(port); 1253fad09c73SVivien Didelot 12545a7921f4SVivien Didelot return mv88e6xxx_port_set_vlan_map(chip, port, output_ports); 1255fad09c73SVivien Didelot } 1256fad09c73SVivien Didelot 1257fad09c73SVivien Didelot static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port, 1258fad09c73SVivien Didelot u8 state) 1259fad09c73SVivien Didelot { 126004bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 1261fad09c73SVivien Didelot int stp_state; 1262fad09c73SVivien Didelot int err; 1263fad09c73SVivien Didelot 1264fad09c73SVivien Didelot switch (state) { 1265fad09c73SVivien Didelot case BR_STATE_DISABLED: 1266fad09c73SVivien Didelot stp_state = PORT_CONTROL_STATE_DISABLED; 1267fad09c73SVivien Didelot break; 1268fad09c73SVivien Didelot case BR_STATE_BLOCKING: 1269fad09c73SVivien Didelot case BR_STATE_LISTENING: 1270fad09c73SVivien Didelot stp_state = PORT_CONTROL_STATE_BLOCKING; 1271fad09c73SVivien Didelot break; 1272fad09c73SVivien Didelot case BR_STATE_LEARNING: 1273fad09c73SVivien Didelot stp_state = PORT_CONTROL_STATE_LEARNING; 1274fad09c73SVivien Didelot break; 1275fad09c73SVivien Didelot case BR_STATE_FORWARDING: 1276fad09c73SVivien Didelot default: 1277fad09c73SVivien Didelot stp_state = PORT_CONTROL_STATE_FORWARDING; 1278fad09c73SVivien Didelot break; 1279fad09c73SVivien Didelot } 1280fad09c73SVivien Didelot 1281fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 1282e28def33SVivien Didelot err = mv88e6xxx_port_set_state(chip, port, stp_state); 1283fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 1284fad09c73SVivien Didelot 1285fad09c73SVivien Didelot if (err) 1286e28def33SVivien Didelot netdev_err(ds->ports[port].netdev, "failed to update state\n"); 1287fad09c73SVivien Didelot } 1288fad09c73SVivien Didelot 1289749efcb8SVivien Didelot static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port) 1290749efcb8SVivien Didelot { 1291749efcb8SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 1292749efcb8SVivien Didelot int err; 1293749efcb8SVivien Didelot 1294749efcb8SVivien Didelot mutex_lock(&chip->reg_lock); 1295749efcb8SVivien Didelot err = _mv88e6xxx_atu_remove(chip, 0, port, false); 1296749efcb8SVivien Didelot mutex_unlock(&chip->reg_lock); 1297749efcb8SVivien Didelot 1298749efcb8SVivien Didelot if (err) 1299749efcb8SVivien Didelot netdev_err(ds->ports[port].netdev, "failed to flush ATU\n"); 1300749efcb8SVivien Didelot } 1301749efcb8SVivien Didelot 1302fad09c73SVivien Didelot static int _mv88e6xxx_vtu_wait(struct mv88e6xxx_chip *chip) 1303fad09c73SVivien Didelot { 1304a935c052SVivien Didelot return mv88e6xxx_g1_wait(chip, GLOBAL_VTU_OP, GLOBAL_VTU_OP_BUSY); 1305fad09c73SVivien Didelot } 1306fad09c73SVivien Didelot 1307fad09c73SVivien Didelot static int _mv88e6xxx_vtu_cmd(struct mv88e6xxx_chip *chip, u16 op) 1308fad09c73SVivien Didelot { 1309a935c052SVivien Didelot int err; 1310fad09c73SVivien Didelot 1311a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_OP, op); 1312a935c052SVivien Didelot if (err) 1313a935c052SVivien Didelot return err; 1314fad09c73SVivien Didelot 1315fad09c73SVivien Didelot return _mv88e6xxx_vtu_wait(chip); 1316fad09c73SVivien Didelot } 1317fad09c73SVivien Didelot 1318fad09c73SVivien Didelot static int _mv88e6xxx_vtu_stu_flush(struct mv88e6xxx_chip *chip) 1319fad09c73SVivien Didelot { 1320fad09c73SVivien Didelot int ret; 1321fad09c73SVivien Didelot 1322fad09c73SVivien Didelot ret = _mv88e6xxx_vtu_wait(chip); 1323fad09c73SVivien Didelot if (ret < 0) 1324fad09c73SVivien Didelot return ret; 1325fad09c73SVivien Didelot 1326fad09c73SVivien Didelot return _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_FLUSH_ALL); 1327fad09c73SVivien Didelot } 1328fad09c73SVivien Didelot 1329fad09c73SVivien Didelot static int _mv88e6xxx_vtu_stu_data_read(struct mv88e6xxx_chip *chip, 1330b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry *entry, 1331fad09c73SVivien Didelot unsigned int nibble_offset) 1332fad09c73SVivien Didelot { 1333fad09c73SVivien Didelot u16 regs[3]; 1334a935c052SVivien Didelot int i, err; 1335fad09c73SVivien Didelot 1336fad09c73SVivien Didelot for (i = 0; i < 3; ++i) { 1337a935c052SVivien Didelot u16 *reg = ®s[i]; 1338fad09c73SVivien Didelot 1339a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_DATA_0_3 + i, reg); 1340a935c052SVivien Didelot if (err) 1341a935c052SVivien Didelot return err; 1342fad09c73SVivien Didelot } 1343fad09c73SVivien Didelot 1344370b4ffbSVivien Didelot for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { 1345fad09c73SVivien Didelot unsigned int shift = (i % 4) * 4 + nibble_offset; 1346fad09c73SVivien Didelot u16 reg = regs[i / 4]; 1347fad09c73SVivien Didelot 1348fad09c73SVivien Didelot entry->data[i] = (reg >> shift) & GLOBAL_VTU_STU_DATA_MASK; 1349fad09c73SVivien Didelot } 1350fad09c73SVivien Didelot 1351fad09c73SVivien Didelot return 0; 1352fad09c73SVivien Didelot } 1353fad09c73SVivien Didelot 1354fad09c73SVivien Didelot static int mv88e6xxx_vtu_data_read(struct mv88e6xxx_chip *chip, 1355b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry *entry) 1356fad09c73SVivien Didelot { 1357fad09c73SVivien Didelot return _mv88e6xxx_vtu_stu_data_read(chip, entry, 0); 1358fad09c73SVivien Didelot } 1359fad09c73SVivien Didelot 1360fad09c73SVivien Didelot static int mv88e6xxx_stu_data_read(struct mv88e6xxx_chip *chip, 1361b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry *entry) 1362fad09c73SVivien Didelot { 1363fad09c73SVivien Didelot return _mv88e6xxx_vtu_stu_data_read(chip, entry, 2); 1364fad09c73SVivien Didelot } 1365fad09c73SVivien Didelot 1366fad09c73SVivien Didelot static int _mv88e6xxx_vtu_stu_data_write(struct mv88e6xxx_chip *chip, 1367b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry *entry, 1368fad09c73SVivien Didelot unsigned int nibble_offset) 1369fad09c73SVivien Didelot { 1370fad09c73SVivien Didelot u16 regs[3] = { 0 }; 1371a935c052SVivien Didelot int i, err; 1372fad09c73SVivien Didelot 1373370b4ffbSVivien Didelot for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { 1374fad09c73SVivien Didelot unsigned int shift = (i % 4) * 4 + nibble_offset; 1375fad09c73SVivien Didelot u8 data = entry->data[i]; 1376fad09c73SVivien Didelot 1377fad09c73SVivien Didelot regs[i / 4] |= (data & GLOBAL_VTU_STU_DATA_MASK) << shift; 1378fad09c73SVivien Didelot } 1379fad09c73SVivien Didelot 1380fad09c73SVivien Didelot for (i = 0; i < 3; ++i) { 1381a935c052SVivien Didelot u16 reg = regs[i]; 1382a935c052SVivien Didelot 1383a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_DATA_0_3 + i, reg); 1384a935c052SVivien Didelot if (err) 1385a935c052SVivien Didelot return err; 1386fad09c73SVivien Didelot } 1387fad09c73SVivien Didelot 1388fad09c73SVivien Didelot return 0; 1389fad09c73SVivien Didelot } 1390fad09c73SVivien Didelot 1391fad09c73SVivien Didelot static int mv88e6xxx_vtu_data_write(struct mv88e6xxx_chip *chip, 1392b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry *entry) 1393fad09c73SVivien Didelot { 1394fad09c73SVivien Didelot return _mv88e6xxx_vtu_stu_data_write(chip, entry, 0); 1395fad09c73SVivien Didelot } 1396fad09c73SVivien Didelot 1397fad09c73SVivien Didelot static int mv88e6xxx_stu_data_write(struct mv88e6xxx_chip *chip, 1398b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry *entry) 1399fad09c73SVivien Didelot { 1400fad09c73SVivien Didelot return _mv88e6xxx_vtu_stu_data_write(chip, entry, 2); 1401fad09c73SVivien Didelot } 1402fad09c73SVivien Didelot 1403fad09c73SVivien Didelot static int _mv88e6xxx_vtu_vid_write(struct mv88e6xxx_chip *chip, u16 vid) 1404fad09c73SVivien Didelot { 1405a935c052SVivien Didelot return mv88e6xxx_g1_write(chip, GLOBAL_VTU_VID, 1406fad09c73SVivien Didelot vid & GLOBAL_VTU_VID_MASK); 1407fad09c73SVivien Didelot } 1408fad09c73SVivien Didelot 1409fad09c73SVivien Didelot static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_chip *chip, 1410b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry *entry) 1411fad09c73SVivien Didelot { 1412b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry next = { 0 }; 1413a935c052SVivien Didelot u16 val; 1414a935c052SVivien Didelot int err; 1415fad09c73SVivien Didelot 1416a935c052SVivien Didelot err = _mv88e6xxx_vtu_wait(chip); 1417a935c052SVivien Didelot if (err) 1418a935c052SVivien Didelot return err; 1419fad09c73SVivien Didelot 1420a935c052SVivien Didelot err = _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_VTU_GET_NEXT); 1421a935c052SVivien Didelot if (err) 1422a935c052SVivien Didelot return err; 1423fad09c73SVivien Didelot 1424a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_VID, &val); 1425a935c052SVivien Didelot if (err) 1426a935c052SVivien Didelot return err; 1427fad09c73SVivien Didelot 1428a935c052SVivien Didelot next.vid = val & GLOBAL_VTU_VID_MASK; 1429a935c052SVivien Didelot next.valid = !!(val & GLOBAL_VTU_VID_VALID); 1430fad09c73SVivien Didelot 1431fad09c73SVivien Didelot if (next.valid) { 1432a935c052SVivien Didelot err = mv88e6xxx_vtu_data_read(chip, &next); 1433a935c052SVivien Didelot if (err) 1434a935c052SVivien Didelot return err; 1435fad09c73SVivien Didelot 14366dc10bbcSVivien Didelot if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G1_VTU_FID)) { 1437a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_FID, &val); 1438a935c052SVivien Didelot if (err) 1439a935c052SVivien Didelot return err; 1440fad09c73SVivien Didelot 1441a935c052SVivien Didelot next.fid = val & GLOBAL_VTU_FID_MASK; 1442fad09c73SVivien Didelot } else if (mv88e6xxx_num_databases(chip) == 256) { 1443fad09c73SVivien Didelot /* VTU DBNum[7:4] are located in VTU Operation 11:8, and 1444fad09c73SVivien Didelot * VTU DBNum[3:0] are located in VTU Operation 3:0 1445fad09c73SVivien Didelot */ 1446a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_OP, &val); 1447a935c052SVivien Didelot if (err) 1448a935c052SVivien Didelot return err; 1449fad09c73SVivien Didelot 1450a935c052SVivien Didelot next.fid = (val & 0xf00) >> 4; 1451a935c052SVivien Didelot next.fid |= val & 0xf; 1452fad09c73SVivien Didelot } 1453fad09c73SVivien Didelot 1454fad09c73SVivien Didelot if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_STU)) { 1455a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_SID, &val); 1456a935c052SVivien Didelot if (err) 1457a935c052SVivien Didelot return err; 1458fad09c73SVivien Didelot 1459a935c052SVivien Didelot next.sid = val & GLOBAL_VTU_SID_MASK; 1460fad09c73SVivien Didelot } 1461fad09c73SVivien Didelot } 1462fad09c73SVivien Didelot 1463fad09c73SVivien Didelot *entry = next; 1464fad09c73SVivien Didelot return 0; 1465fad09c73SVivien Didelot } 1466fad09c73SVivien Didelot 1467fad09c73SVivien Didelot static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port, 1468fad09c73SVivien Didelot struct switchdev_obj_port_vlan *vlan, 1469fad09c73SVivien Didelot int (*cb)(struct switchdev_obj *obj)) 1470fad09c73SVivien Didelot { 147104bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 1472b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry next; 1473fad09c73SVivien Didelot u16 pvid; 1474fad09c73SVivien Didelot int err; 1475fad09c73SVivien Didelot 1476fad09c73SVivien Didelot if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU)) 1477fad09c73SVivien Didelot return -EOPNOTSUPP; 1478fad09c73SVivien Didelot 1479fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 1480fad09c73SVivien Didelot 148177064f37SVivien Didelot err = mv88e6xxx_port_get_pvid(chip, port, &pvid); 1482fad09c73SVivien Didelot if (err) 1483fad09c73SVivien Didelot goto unlock; 1484fad09c73SVivien Didelot 1485fad09c73SVivien Didelot err = _mv88e6xxx_vtu_vid_write(chip, GLOBAL_VTU_VID_MASK); 1486fad09c73SVivien Didelot if (err) 1487fad09c73SVivien Didelot goto unlock; 1488fad09c73SVivien Didelot 1489fad09c73SVivien Didelot do { 1490fad09c73SVivien Didelot err = _mv88e6xxx_vtu_getnext(chip, &next); 1491fad09c73SVivien Didelot if (err) 1492fad09c73SVivien Didelot break; 1493fad09c73SVivien Didelot 1494fad09c73SVivien Didelot if (!next.valid) 1495fad09c73SVivien Didelot break; 1496fad09c73SVivien Didelot 1497fad09c73SVivien Didelot if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) 1498fad09c73SVivien Didelot continue; 1499fad09c73SVivien Didelot 1500fad09c73SVivien Didelot /* reinit and dump this VLAN obj */ 1501fad09c73SVivien Didelot vlan->vid_begin = next.vid; 1502fad09c73SVivien Didelot vlan->vid_end = next.vid; 1503fad09c73SVivien Didelot vlan->flags = 0; 1504fad09c73SVivien Didelot 1505fad09c73SVivien Didelot if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED) 1506fad09c73SVivien Didelot vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED; 1507fad09c73SVivien Didelot 1508fad09c73SVivien Didelot if (next.vid == pvid) 1509fad09c73SVivien Didelot vlan->flags |= BRIDGE_VLAN_INFO_PVID; 1510fad09c73SVivien Didelot 1511fad09c73SVivien Didelot err = cb(&vlan->obj); 1512fad09c73SVivien Didelot if (err) 1513fad09c73SVivien Didelot break; 1514fad09c73SVivien Didelot } while (next.vid < GLOBAL_VTU_VID_MASK); 1515fad09c73SVivien Didelot 1516fad09c73SVivien Didelot unlock: 1517fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 1518fad09c73SVivien Didelot 1519fad09c73SVivien Didelot return err; 1520fad09c73SVivien Didelot } 1521fad09c73SVivien Didelot 1522fad09c73SVivien Didelot static int _mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_chip *chip, 1523b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry *entry) 1524fad09c73SVivien Didelot { 1525fad09c73SVivien Didelot u16 op = GLOBAL_VTU_OP_VTU_LOAD_PURGE; 1526fad09c73SVivien Didelot u16 reg = 0; 1527a935c052SVivien Didelot int err; 1528fad09c73SVivien Didelot 1529a935c052SVivien Didelot err = _mv88e6xxx_vtu_wait(chip); 1530a935c052SVivien Didelot if (err) 1531a935c052SVivien Didelot return err; 1532fad09c73SVivien Didelot 1533fad09c73SVivien Didelot if (!entry->valid) 1534fad09c73SVivien Didelot goto loadpurge; 1535fad09c73SVivien Didelot 1536fad09c73SVivien Didelot /* Write port member tags */ 1537a935c052SVivien Didelot err = mv88e6xxx_vtu_data_write(chip, entry); 1538a935c052SVivien Didelot if (err) 1539a935c052SVivien Didelot return err; 1540fad09c73SVivien Didelot 1541fad09c73SVivien Didelot if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_STU)) { 1542fad09c73SVivien Didelot reg = entry->sid & GLOBAL_VTU_SID_MASK; 1543a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_SID, reg); 1544a935c052SVivien Didelot if (err) 1545a935c052SVivien Didelot return err; 1546fad09c73SVivien Didelot } 1547fad09c73SVivien Didelot 15486dc10bbcSVivien Didelot if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G1_VTU_FID)) { 1549fad09c73SVivien Didelot reg = entry->fid & GLOBAL_VTU_FID_MASK; 1550a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_FID, reg); 1551a935c052SVivien Didelot if (err) 1552a935c052SVivien Didelot return err; 1553fad09c73SVivien Didelot } else if (mv88e6xxx_num_databases(chip) == 256) { 1554fad09c73SVivien Didelot /* VTU DBNum[7:4] are located in VTU Operation 11:8, and 1555fad09c73SVivien Didelot * VTU DBNum[3:0] are located in VTU Operation 3:0 1556fad09c73SVivien Didelot */ 1557fad09c73SVivien Didelot op |= (entry->fid & 0xf0) << 8; 1558fad09c73SVivien Didelot op |= entry->fid & 0xf; 1559fad09c73SVivien Didelot } 1560fad09c73SVivien Didelot 1561fad09c73SVivien Didelot reg = GLOBAL_VTU_VID_VALID; 1562fad09c73SVivien Didelot loadpurge: 1563fad09c73SVivien Didelot reg |= entry->vid & GLOBAL_VTU_VID_MASK; 1564a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_VID, reg); 1565a935c052SVivien Didelot if (err) 1566a935c052SVivien Didelot return err; 1567fad09c73SVivien Didelot 1568fad09c73SVivien Didelot return _mv88e6xxx_vtu_cmd(chip, op); 1569fad09c73SVivien Didelot } 1570fad09c73SVivien Didelot 1571fad09c73SVivien Didelot static int _mv88e6xxx_stu_getnext(struct mv88e6xxx_chip *chip, u8 sid, 1572b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry *entry) 1573fad09c73SVivien Didelot { 1574b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry next = { 0 }; 1575a935c052SVivien Didelot u16 val; 1576a935c052SVivien Didelot int err; 1577fad09c73SVivien Didelot 1578a935c052SVivien Didelot err = _mv88e6xxx_vtu_wait(chip); 1579a935c052SVivien Didelot if (err) 1580a935c052SVivien Didelot return err; 1581fad09c73SVivien Didelot 1582a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_SID, 1583fad09c73SVivien Didelot sid & GLOBAL_VTU_SID_MASK); 1584a935c052SVivien Didelot if (err) 1585a935c052SVivien Didelot return err; 1586fad09c73SVivien Didelot 1587a935c052SVivien Didelot err = _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_STU_GET_NEXT); 1588a935c052SVivien Didelot if (err) 1589a935c052SVivien Didelot return err; 1590fad09c73SVivien Didelot 1591a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_SID, &val); 1592a935c052SVivien Didelot if (err) 1593a935c052SVivien Didelot return err; 1594fad09c73SVivien Didelot 1595a935c052SVivien Didelot next.sid = val & GLOBAL_VTU_SID_MASK; 1596fad09c73SVivien Didelot 1597a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_VID, &val); 1598a935c052SVivien Didelot if (err) 1599a935c052SVivien Didelot return err; 1600fad09c73SVivien Didelot 1601a935c052SVivien Didelot next.valid = !!(val & GLOBAL_VTU_VID_VALID); 1602fad09c73SVivien Didelot 1603fad09c73SVivien Didelot if (next.valid) { 1604a935c052SVivien Didelot err = mv88e6xxx_stu_data_read(chip, &next); 1605a935c052SVivien Didelot if (err) 1606a935c052SVivien Didelot return err; 1607fad09c73SVivien Didelot } 1608fad09c73SVivien Didelot 1609fad09c73SVivien Didelot *entry = next; 1610fad09c73SVivien Didelot return 0; 1611fad09c73SVivien Didelot } 1612fad09c73SVivien Didelot 1613fad09c73SVivien Didelot static int _mv88e6xxx_stu_loadpurge(struct mv88e6xxx_chip *chip, 1614b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry *entry) 1615fad09c73SVivien Didelot { 1616fad09c73SVivien Didelot u16 reg = 0; 1617a935c052SVivien Didelot int err; 1618fad09c73SVivien Didelot 1619a935c052SVivien Didelot err = _mv88e6xxx_vtu_wait(chip); 1620a935c052SVivien Didelot if (err) 1621a935c052SVivien Didelot return err; 1622fad09c73SVivien Didelot 1623fad09c73SVivien Didelot if (!entry->valid) 1624fad09c73SVivien Didelot goto loadpurge; 1625fad09c73SVivien Didelot 1626fad09c73SVivien Didelot /* Write port states */ 1627a935c052SVivien Didelot err = mv88e6xxx_stu_data_write(chip, entry); 1628a935c052SVivien Didelot if (err) 1629a935c052SVivien Didelot return err; 1630fad09c73SVivien Didelot 1631fad09c73SVivien Didelot reg = GLOBAL_VTU_VID_VALID; 1632fad09c73SVivien Didelot loadpurge: 1633a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_VID, reg); 1634a935c052SVivien Didelot if (err) 1635a935c052SVivien Didelot return err; 1636fad09c73SVivien Didelot 1637fad09c73SVivien Didelot reg = entry->sid & GLOBAL_VTU_SID_MASK; 1638a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_SID, reg); 1639a935c052SVivien Didelot if (err) 1640a935c052SVivien Didelot return err; 1641fad09c73SVivien Didelot 1642fad09c73SVivien Didelot return _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_STU_LOAD_PURGE); 1643fad09c73SVivien Didelot } 1644fad09c73SVivien Didelot 1645fad09c73SVivien Didelot static int _mv88e6xxx_fid_new(struct mv88e6xxx_chip *chip, u16 *fid) 1646fad09c73SVivien Didelot { 1647fad09c73SVivien Didelot DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID); 1648b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry vlan; 1649fad09c73SVivien Didelot int i, err; 1650fad09c73SVivien Didelot 1651fad09c73SVivien Didelot bitmap_zero(fid_bitmap, MV88E6XXX_N_FID); 1652fad09c73SVivien Didelot 1653fad09c73SVivien Didelot /* Set every FID bit used by the (un)bridged ports */ 1654370b4ffbSVivien Didelot for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { 1655b4e48c50SVivien Didelot err = mv88e6xxx_port_get_fid(chip, i, fid); 1656fad09c73SVivien Didelot if (err) 1657fad09c73SVivien Didelot return err; 1658fad09c73SVivien Didelot 1659fad09c73SVivien Didelot set_bit(*fid, fid_bitmap); 1660fad09c73SVivien Didelot } 1661fad09c73SVivien Didelot 1662fad09c73SVivien Didelot /* Set every FID bit used by the VLAN entries */ 1663fad09c73SVivien Didelot err = _mv88e6xxx_vtu_vid_write(chip, GLOBAL_VTU_VID_MASK); 1664fad09c73SVivien Didelot if (err) 1665fad09c73SVivien Didelot return err; 1666fad09c73SVivien Didelot 1667fad09c73SVivien Didelot do { 1668fad09c73SVivien Didelot err = _mv88e6xxx_vtu_getnext(chip, &vlan); 1669fad09c73SVivien Didelot if (err) 1670fad09c73SVivien Didelot return err; 1671fad09c73SVivien Didelot 1672fad09c73SVivien Didelot if (!vlan.valid) 1673fad09c73SVivien Didelot break; 1674fad09c73SVivien Didelot 1675fad09c73SVivien Didelot set_bit(vlan.fid, fid_bitmap); 1676fad09c73SVivien Didelot } while (vlan.vid < GLOBAL_VTU_VID_MASK); 1677fad09c73SVivien Didelot 1678fad09c73SVivien Didelot /* The reset value 0x000 is used to indicate that multiple address 1679fad09c73SVivien Didelot * databases are not needed. Return the next positive available. 1680fad09c73SVivien Didelot */ 1681fad09c73SVivien Didelot *fid = find_next_zero_bit(fid_bitmap, MV88E6XXX_N_FID, 1); 1682fad09c73SVivien Didelot if (unlikely(*fid >= mv88e6xxx_num_databases(chip))) 1683fad09c73SVivien Didelot return -ENOSPC; 1684fad09c73SVivien Didelot 1685fad09c73SVivien Didelot /* Clear the database */ 1686fad09c73SVivien Didelot return _mv88e6xxx_atu_flush(chip, *fid, true); 1687fad09c73SVivien Didelot } 1688fad09c73SVivien Didelot 1689fad09c73SVivien Didelot static int _mv88e6xxx_vtu_new(struct mv88e6xxx_chip *chip, u16 vid, 1690b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry *entry) 1691fad09c73SVivien Didelot { 1692fad09c73SVivien Didelot struct dsa_switch *ds = chip->ds; 1693b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry vlan = { 1694fad09c73SVivien Didelot .valid = true, 1695fad09c73SVivien Didelot .vid = vid, 1696fad09c73SVivien Didelot }; 1697fad09c73SVivien Didelot int i, err; 1698fad09c73SVivien Didelot 1699fad09c73SVivien Didelot err = _mv88e6xxx_fid_new(chip, &vlan.fid); 1700fad09c73SVivien Didelot if (err) 1701fad09c73SVivien Didelot return err; 1702fad09c73SVivien Didelot 1703fad09c73SVivien Didelot /* exclude all ports except the CPU and DSA ports */ 1704370b4ffbSVivien Didelot for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) 1705fad09c73SVivien Didelot vlan.data[i] = dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i) 1706fad09c73SVivien Didelot ? GLOBAL_VTU_DATA_MEMBER_TAG_UNMODIFIED 1707fad09c73SVivien Didelot : GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER; 1708fad09c73SVivien Didelot 1709fad09c73SVivien Didelot if (mv88e6xxx_6097_family(chip) || mv88e6xxx_6165_family(chip) || 1710fad09c73SVivien Didelot mv88e6xxx_6351_family(chip) || mv88e6xxx_6352_family(chip)) { 1711b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry vstp; 1712fad09c73SVivien Didelot 1713fad09c73SVivien Didelot /* Adding a VTU entry requires a valid STU entry. As VSTP is not 1714fad09c73SVivien Didelot * implemented, only one STU entry is needed to cover all VTU 1715fad09c73SVivien Didelot * entries. Thus, validate the SID 0. 1716fad09c73SVivien Didelot */ 1717fad09c73SVivien Didelot vlan.sid = 0; 1718fad09c73SVivien Didelot err = _mv88e6xxx_stu_getnext(chip, GLOBAL_VTU_SID_MASK, &vstp); 1719fad09c73SVivien Didelot if (err) 1720fad09c73SVivien Didelot return err; 1721fad09c73SVivien Didelot 1722fad09c73SVivien Didelot if (vstp.sid != vlan.sid || !vstp.valid) { 1723fad09c73SVivien Didelot memset(&vstp, 0, sizeof(vstp)); 1724fad09c73SVivien Didelot vstp.valid = true; 1725fad09c73SVivien Didelot vstp.sid = vlan.sid; 1726fad09c73SVivien Didelot 1727fad09c73SVivien Didelot err = _mv88e6xxx_stu_loadpurge(chip, &vstp); 1728fad09c73SVivien Didelot if (err) 1729fad09c73SVivien Didelot return err; 1730fad09c73SVivien Didelot } 1731fad09c73SVivien Didelot } 1732fad09c73SVivien Didelot 1733fad09c73SVivien Didelot *entry = vlan; 1734fad09c73SVivien Didelot return 0; 1735fad09c73SVivien Didelot } 1736fad09c73SVivien Didelot 1737fad09c73SVivien Didelot static int _mv88e6xxx_vtu_get(struct mv88e6xxx_chip *chip, u16 vid, 1738b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry *entry, bool creat) 1739fad09c73SVivien Didelot { 1740fad09c73SVivien Didelot int err; 1741fad09c73SVivien Didelot 1742fad09c73SVivien Didelot if (!vid) 1743fad09c73SVivien Didelot return -EINVAL; 1744fad09c73SVivien Didelot 1745fad09c73SVivien Didelot err = _mv88e6xxx_vtu_vid_write(chip, vid - 1); 1746fad09c73SVivien Didelot if (err) 1747fad09c73SVivien Didelot return err; 1748fad09c73SVivien Didelot 1749fad09c73SVivien Didelot err = _mv88e6xxx_vtu_getnext(chip, entry); 1750fad09c73SVivien Didelot if (err) 1751fad09c73SVivien Didelot return err; 1752fad09c73SVivien Didelot 1753fad09c73SVivien Didelot if (entry->vid != vid || !entry->valid) { 1754fad09c73SVivien Didelot if (!creat) 1755fad09c73SVivien Didelot return -EOPNOTSUPP; 1756fad09c73SVivien Didelot /* -ENOENT would've been more appropriate, but switchdev expects 1757fad09c73SVivien Didelot * -EOPNOTSUPP to inform bridge about an eventual software VLAN. 1758fad09c73SVivien Didelot */ 1759fad09c73SVivien Didelot 1760fad09c73SVivien Didelot err = _mv88e6xxx_vtu_new(chip, vid, entry); 1761fad09c73SVivien Didelot } 1762fad09c73SVivien Didelot 1763fad09c73SVivien Didelot return err; 1764fad09c73SVivien Didelot } 1765fad09c73SVivien Didelot 1766fad09c73SVivien Didelot static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port, 1767fad09c73SVivien Didelot u16 vid_begin, u16 vid_end) 1768fad09c73SVivien Didelot { 176904bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 1770b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry vlan; 1771fad09c73SVivien Didelot int i, err; 1772fad09c73SVivien Didelot 1773fad09c73SVivien Didelot if (!vid_begin) 1774fad09c73SVivien Didelot return -EOPNOTSUPP; 1775fad09c73SVivien Didelot 1776fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 1777fad09c73SVivien Didelot 1778fad09c73SVivien Didelot err = _mv88e6xxx_vtu_vid_write(chip, vid_begin - 1); 1779fad09c73SVivien Didelot if (err) 1780fad09c73SVivien Didelot goto unlock; 1781fad09c73SVivien Didelot 1782fad09c73SVivien Didelot do { 1783fad09c73SVivien Didelot err = _mv88e6xxx_vtu_getnext(chip, &vlan); 1784fad09c73SVivien Didelot if (err) 1785fad09c73SVivien Didelot goto unlock; 1786fad09c73SVivien Didelot 1787fad09c73SVivien Didelot if (!vlan.valid) 1788fad09c73SVivien Didelot break; 1789fad09c73SVivien Didelot 1790fad09c73SVivien Didelot if (vlan.vid > vid_end) 1791fad09c73SVivien Didelot break; 1792fad09c73SVivien Didelot 1793370b4ffbSVivien Didelot for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { 1794fad09c73SVivien Didelot if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i)) 1795fad09c73SVivien Didelot continue; 1796fad09c73SVivien Didelot 1797fad09c73SVivien Didelot if (vlan.data[i] == 1798fad09c73SVivien Didelot GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) 1799fad09c73SVivien Didelot continue; 1800fad09c73SVivien Didelot 1801fad09c73SVivien Didelot if (chip->ports[i].bridge_dev == 1802fad09c73SVivien Didelot chip->ports[port].bridge_dev) 1803fad09c73SVivien Didelot break; /* same bridge, check next VLAN */ 1804fad09c73SVivien Didelot 1805fad09c73SVivien Didelot netdev_warn(ds->ports[port].netdev, 1806fad09c73SVivien Didelot "hardware VLAN %d already used by %s\n", 1807fad09c73SVivien Didelot vlan.vid, 1808fad09c73SVivien Didelot netdev_name(chip->ports[i].bridge_dev)); 1809fad09c73SVivien Didelot err = -EOPNOTSUPP; 1810fad09c73SVivien Didelot goto unlock; 1811fad09c73SVivien Didelot } 1812fad09c73SVivien Didelot } while (vlan.vid < vid_end); 1813fad09c73SVivien Didelot 1814fad09c73SVivien Didelot unlock: 1815fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 1816fad09c73SVivien Didelot 1817fad09c73SVivien Didelot return err; 1818fad09c73SVivien Didelot } 1819fad09c73SVivien Didelot 1820fad09c73SVivien Didelot static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port, 1821fad09c73SVivien Didelot bool vlan_filtering) 1822fad09c73SVivien Didelot { 182304bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 1824385a0995SVivien Didelot u16 mode = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE : 1825fad09c73SVivien Didelot PORT_CONTROL_2_8021Q_DISABLED; 18260e7b9925SAndrew Lunn int err; 1827fad09c73SVivien Didelot 1828fad09c73SVivien Didelot if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU)) 1829fad09c73SVivien Didelot return -EOPNOTSUPP; 1830fad09c73SVivien Didelot 1831fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 1832385a0995SVivien Didelot err = mv88e6xxx_port_set_8021q_mode(chip, port, mode); 1833fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 1834fad09c73SVivien Didelot 18350e7b9925SAndrew Lunn return err; 1836fad09c73SVivien Didelot } 1837fad09c73SVivien Didelot 1838fad09c73SVivien Didelot static int 1839fad09c73SVivien Didelot mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port, 1840fad09c73SVivien Didelot const struct switchdev_obj_port_vlan *vlan, 1841fad09c73SVivien Didelot struct switchdev_trans *trans) 1842fad09c73SVivien Didelot { 184304bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 1844fad09c73SVivien Didelot int err; 1845fad09c73SVivien Didelot 1846fad09c73SVivien Didelot if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU)) 1847fad09c73SVivien Didelot return -EOPNOTSUPP; 1848fad09c73SVivien Didelot 1849fad09c73SVivien Didelot /* If the requested port doesn't belong to the same bridge as the VLAN 1850fad09c73SVivien Didelot * members, do not support it (yet) and fallback to software VLAN. 1851fad09c73SVivien Didelot */ 1852fad09c73SVivien Didelot err = mv88e6xxx_port_check_hw_vlan(ds, port, vlan->vid_begin, 1853fad09c73SVivien Didelot vlan->vid_end); 1854fad09c73SVivien Didelot if (err) 1855fad09c73SVivien Didelot return err; 1856fad09c73SVivien Didelot 1857fad09c73SVivien Didelot /* We don't need any dynamic resource from the kernel (yet), 1858fad09c73SVivien Didelot * so skip the prepare phase. 1859fad09c73SVivien Didelot */ 1860fad09c73SVivien Didelot return 0; 1861fad09c73SVivien Didelot } 1862fad09c73SVivien Didelot 1863fad09c73SVivien Didelot static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_chip *chip, int port, 1864fad09c73SVivien Didelot u16 vid, bool untagged) 1865fad09c73SVivien Didelot { 1866b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry vlan; 1867fad09c73SVivien Didelot int err; 1868fad09c73SVivien Didelot 1869fad09c73SVivien Didelot err = _mv88e6xxx_vtu_get(chip, vid, &vlan, true); 1870fad09c73SVivien Didelot if (err) 1871fad09c73SVivien Didelot return err; 1872fad09c73SVivien Didelot 1873fad09c73SVivien Didelot vlan.data[port] = untagged ? 1874fad09c73SVivien Didelot GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED : 1875fad09c73SVivien Didelot GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED; 1876fad09c73SVivien Didelot 1877fad09c73SVivien Didelot return _mv88e6xxx_vtu_loadpurge(chip, &vlan); 1878fad09c73SVivien Didelot } 1879fad09c73SVivien Didelot 1880fad09c73SVivien Didelot static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, 1881fad09c73SVivien Didelot const struct switchdev_obj_port_vlan *vlan, 1882fad09c73SVivien Didelot struct switchdev_trans *trans) 1883fad09c73SVivien Didelot { 188404bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 1885fad09c73SVivien Didelot bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; 1886fad09c73SVivien Didelot bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; 1887fad09c73SVivien Didelot u16 vid; 1888fad09c73SVivien Didelot 1889fad09c73SVivien Didelot if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU)) 1890fad09c73SVivien Didelot return; 1891fad09c73SVivien Didelot 1892fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 1893fad09c73SVivien Didelot 1894fad09c73SVivien Didelot for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) 1895fad09c73SVivien Didelot if (_mv88e6xxx_port_vlan_add(chip, port, vid, untagged)) 1896fad09c73SVivien Didelot netdev_err(ds->ports[port].netdev, 1897fad09c73SVivien Didelot "failed to add VLAN %d%c\n", 1898fad09c73SVivien Didelot vid, untagged ? 'u' : 't'); 1899fad09c73SVivien Didelot 190077064f37SVivien Didelot if (pvid && mv88e6xxx_port_set_pvid(chip, port, vlan->vid_end)) 1901fad09c73SVivien Didelot netdev_err(ds->ports[port].netdev, "failed to set PVID %d\n", 1902fad09c73SVivien Didelot vlan->vid_end); 1903fad09c73SVivien Didelot 1904fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 1905fad09c73SVivien Didelot } 1906fad09c73SVivien Didelot 1907fad09c73SVivien Didelot static int _mv88e6xxx_port_vlan_del(struct mv88e6xxx_chip *chip, 1908fad09c73SVivien Didelot int port, u16 vid) 1909fad09c73SVivien Didelot { 1910fad09c73SVivien Didelot struct dsa_switch *ds = chip->ds; 1911b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry vlan; 1912fad09c73SVivien Didelot int i, err; 1913fad09c73SVivien Didelot 1914fad09c73SVivien Didelot err = _mv88e6xxx_vtu_get(chip, vid, &vlan, false); 1915fad09c73SVivien Didelot if (err) 1916fad09c73SVivien Didelot return err; 1917fad09c73SVivien Didelot 1918fad09c73SVivien Didelot /* Tell switchdev if this VLAN is handled in software */ 1919fad09c73SVivien Didelot if (vlan.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) 1920fad09c73SVivien Didelot return -EOPNOTSUPP; 1921fad09c73SVivien Didelot 1922fad09c73SVivien Didelot vlan.data[port] = GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER; 1923fad09c73SVivien Didelot 1924fad09c73SVivien Didelot /* keep the VLAN unless all ports are excluded */ 1925fad09c73SVivien Didelot vlan.valid = false; 1926370b4ffbSVivien Didelot for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { 1927fad09c73SVivien Didelot if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i)) 1928fad09c73SVivien Didelot continue; 1929fad09c73SVivien Didelot 1930fad09c73SVivien Didelot if (vlan.data[i] != GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) { 1931fad09c73SVivien Didelot vlan.valid = true; 1932fad09c73SVivien Didelot break; 1933fad09c73SVivien Didelot } 1934fad09c73SVivien Didelot } 1935fad09c73SVivien Didelot 1936fad09c73SVivien Didelot err = _mv88e6xxx_vtu_loadpurge(chip, &vlan); 1937fad09c73SVivien Didelot if (err) 1938fad09c73SVivien Didelot return err; 1939fad09c73SVivien Didelot 1940fad09c73SVivien Didelot return _mv88e6xxx_atu_remove(chip, vlan.fid, port, false); 1941fad09c73SVivien Didelot } 1942fad09c73SVivien Didelot 1943fad09c73SVivien Didelot static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, 1944fad09c73SVivien Didelot const struct switchdev_obj_port_vlan *vlan) 1945fad09c73SVivien Didelot { 194604bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 1947fad09c73SVivien Didelot u16 pvid, vid; 1948fad09c73SVivien Didelot int err = 0; 1949fad09c73SVivien Didelot 1950fad09c73SVivien Didelot if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU)) 1951fad09c73SVivien Didelot return -EOPNOTSUPP; 1952fad09c73SVivien Didelot 1953fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 1954fad09c73SVivien Didelot 195577064f37SVivien Didelot err = mv88e6xxx_port_get_pvid(chip, port, &pvid); 1956fad09c73SVivien Didelot if (err) 1957fad09c73SVivien Didelot goto unlock; 1958fad09c73SVivien Didelot 1959fad09c73SVivien Didelot for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { 1960fad09c73SVivien Didelot err = _mv88e6xxx_port_vlan_del(chip, port, vid); 1961fad09c73SVivien Didelot if (err) 1962fad09c73SVivien Didelot goto unlock; 1963fad09c73SVivien Didelot 1964fad09c73SVivien Didelot if (vid == pvid) { 196577064f37SVivien Didelot err = mv88e6xxx_port_set_pvid(chip, port, 0); 1966fad09c73SVivien Didelot if (err) 1967fad09c73SVivien Didelot goto unlock; 1968fad09c73SVivien Didelot } 1969fad09c73SVivien Didelot } 1970fad09c73SVivien Didelot 1971fad09c73SVivien Didelot unlock: 1972fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 1973fad09c73SVivien Didelot 1974fad09c73SVivien Didelot return err; 1975fad09c73SVivien Didelot } 1976fad09c73SVivien Didelot 1977fad09c73SVivien Didelot static int _mv88e6xxx_atu_mac_write(struct mv88e6xxx_chip *chip, 1978fad09c73SVivien Didelot const unsigned char *addr) 1979fad09c73SVivien Didelot { 1980a935c052SVivien Didelot int i, err; 1981fad09c73SVivien Didelot 1982fad09c73SVivien Didelot for (i = 0; i < 3; i++) { 1983a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_MAC_01 + i, 1984fad09c73SVivien Didelot (addr[i * 2] << 8) | addr[i * 2 + 1]); 1985a935c052SVivien Didelot if (err) 1986a935c052SVivien Didelot return err; 1987fad09c73SVivien Didelot } 1988fad09c73SVivien Didelot 1989fad09c73SVivien Didelot return 0; 1990fad09c73SVivien Didelot } 1991fad09c73SVivien Didelot 1992fad09c73SVivien Didelot static int _mv88e6xxx_atu_mac_read(struct mv88e6xxx_chip *chip, 1993fad09c73SVivien Didelot unsigned char *addr) 1994fad09c73SVivien Didelot { 1995a935c052SVivien Didelot u16 val; 1996a935c052SVivien Didelot int i, err; 1997fad09c73SVivien Didelot 1998fad09c73SVivien Didelot for (i = 0; i < 3; i++) { 1999a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_MAC_01 + i, &val); 2000a935c052SVivien Didelot if (err) 2001a935c052SVivien Didelot return err; 2002a935c052SVivien Didelot 2003a935c052SVivien Didelot addr[i * 2] = val >> 8; 2004a935c052SVivien Didelot addr[i * 2 + 1] = val & 0xff; 2005fad09c73SVivien Didelot } 2006fad09c73SVivien Didelot 2007fad09c73SVivien Didelot return 0; 2008fad09c73SVivien Didelot } 2009fad09c73SVivien Didelot 2010fad09c73SVivien Didelot static int _mv88e6xxx_atu_load(struct mv88e6xxx_chip *chip, 2011fad09c73SVivien Didelot struct mv88e6xxx_atu_entry *entry) 2012fad09c73SVivien Didelot { 2013fad09c73SVivien Didelot int ret; 2014fad09c73SVivien Didelot 2015fad09c73SVivien Didelot ret = _mv88e6xxx_atu_wait(chip); 2016fad09c73SVivien Didelot if (ret < 0) 2017fad09c73SVivien Didelot return ret; 2018fad09c73SVivien Didelot 2019fad09c73SVivien Didelot ret = _mv88e6xxx_atu_mac_write(chip, entry->mac); 2020fad09c73SVivien Didelot if (ret < 0) 2021fad09c73SVivien Didelot return ret; 2022fad09c73SVivien Didelot 2023fad09c73SVivien Didelot ret = _mv88e6xxx_atu_data_write(chip, entry); 2024fad09c73SVivien Didelot if (ret < 0) 2025fad09c73SVivien Didelot return ret; 2026fad09c73SVivien Didelot 2027fad09c73SVivien Didelot return _mv88e6xxx_atu_cmd(chip, entry->fid, GLOBAL_ATU_OP_LOAD_DB); 2028fad09c73SVivien Didelot } 2029fad09c73SVivien Didelot 203088472939SVivien Didelot static int _mv88e6xxx_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid, 203188472939SVivien Didelot struct mv88e6xxx_atu_entry *entry); 203288472939SVivien Didelot 203388472939SVivien Didelot static int mv88e6xxx_atu_get(struct mv88e6xxx_chip *chip, int fid, 203488472939SVivien Didelot const u8 *addr, struct mv88e6xxx_atu_entry *entry) 203588472939SVivien Didelot { 203688472939SVivien Didelot struct mv88e6xxx_atu_entry next; 203788472939SVivien Didelot int err; 203888472939SVivien Didelot 203988472939SVivien Didelot eth_broadcast_addr(next.mac); 204088472939SVivien Didelot 204188472939SVivien Didelot err = _mv88e6xxx_atu_mac_write(chip, next.mac); 204288472939SVivien Didelot if (err) 204388472939SVivien Didelot return err; 204488472939SVivien Didelot 204588472939SVivien Didelot do { 204688472939SVivien Didelot err = _mv88e6xxx_atu_getnext(chip, fid, &next); 204788472939SVivien Didelot if (err) 204888472939SVivien Didelot return err; 204988472939SVivien Didelot 205088472939SVivien Didelot if (next.state == GLOBAL_ATU_DATA_STATE_UNUSED) 205188472939SVivien Didelot break; 205288472939SVivien Didelot 205388472939SVivien Didelot if (ether_addr_equal(next.mac, addr)) { 205488472939SVivien Didelot *entry = next; 205588472939SVivien Didelot return 0; 205688472939SVivien Didelot } 205788472939SVivien Didelot } while (!is_broadcast_ether_addr(next.mac)); 205888472939SVivien Didelot 205988472939SVivien Didelot memset(entry, 0, sizeof(*entry)); 206088472939SVivien Didelot entry->fid = fid; 206188472939SVivien Didelot ether_addr_copy(entry->mac, addr); 206288472939SVivien Didelot 206388472939SVivien Didelot return 0; 206488472939SVivien Didelot } 206588472939SVivien Didelot 206683dabd1fSVivien Didelot static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port, 2067fad09c73SVivien Didelot const unsigned char *addr, u16 vid, 2068fad09c73SVivien Didelot u8 state) 2069fad09c73SVivien Didelot { 2070b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry vlan; 207188472939SVivien Didelot struct mv88e6xxx_atu_entry entry; 2072fad09c73SVivien Didelot int err; 2073fad09c73SVivien Didelot 2074fad09c73SVivien Didelot /* Null VLAN ID corresponds to the port private database */ 2075fad09c73SVivien Didelot if (vid == 0) 2076b4e48c50SVivien Didelot err = mv88e6xxx_port_get_fid(chip, port, &vlan.fid); 2077fad09c73SVivien Didelot else 2078fad09c73SVivien Didelot err = _mv88e6xxx_vtu_get(chip, vid, &vlan, false); 2079fad09c73SVivien Didelot if (err) 2080fad09c73SVivien Didelot return err; 2081fad09c73SVivien Didelot 208288472939SVivien Didelot err = mv88e6xxx_atu_get(chip, vlan.fid, addr, &entry); 208388472939SVivien Didelot if (err) 208488472939SVivien Didelot return err; 208588472939SVivien Didelot 208688472939SVivien Didelot /* Purge the ATU entry only if no port is using it anymore */ 208788472939SVivien Didelot if (state == GLOBAL_ATU_DATA_STATE_UNUSED) { 208888472939SVivien Didelot entry.portv_trunkid &= ~BIT(port); 208988472939SVivien Didelot if (!entry.portv_trunkid) 209088472939SVivien Didelot entry.state = GLOBAL_ATU_DATA_STATE_UNUSED; 209188472939SVivien Didelot } else { 209288472939SVivien Didelot entry.portv_trunkid |= BIT(port); 2093fad09c73SVivien Didelot entry.state = state; 2094fad09c73SVivien Didelot } 2095fad09c73SVivien Didelot 2096fad09c73SVivien Didelot return _mv88e6xxx_atu_load(chip, &entry); 2097fad09c73SVivien Didelot } 2098fad09c73SVivien Didelot 2099fad09c73SVivien Didelot static int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port, 2100fad09c73SVivien Didelot const struct switchdev_obj_port_fdb *fdb, 2101fad09c73SVivien Didelot struct switchdev_trans *trans) 2102fad09c73SVivien Didelot { 2103fad09c73SVivien Didelot /* We don't need any dynamic resource from the kernel (yet), 2104fad09c73SVivien Didelot * so skip the prepare phase. 2105fad09c73SVivien Didelot */ 2106fad09c73SVivien Didelot return 0; 2107fad09c73SVivien Didelot } 2108fad09c73SVivien Didelot 2109fad09c73SVivien Didelot static void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, 2110fad09c73SVivien Didelot const struct switchdev_obj_port_fdb *fdb, 2111fad09c73SVivien Didelot struct switchdev_trans *trans) 2112fad09c73SVivien Didelot { 211304bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 2114fad09c73SVivien Didelot 2115fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 211683dabd1fSVivien Didelot if (mv88e6xxx_port_db_load_purge(chip, port, fdb->addr, fdb->vid, 211783dabd1fSVivien Didelot GLOBAL_ATU_DATA_STATE_UC_STATIC)) 211883dabd1fSVivien Didelot netdev_err(ds->ports[port].netdev, "failed to load unicast MAC address\n"); 2119fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 2120fad09c73SVivien Didelot } 2121fad09c73SVivien Didelot 2122fad09c73SVivien Didelot static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, 2123fad09c73SVivien Didelot const struct switchdev_obj_port_fdb *fdb) 2124fad09c73SVivien Didelot { 212504bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 212683dabd1fSVivien Didelot int err; 2127fad09c73SVivien Didelot 2128fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 212983dabd1fSVivien Didelot err = mv88e6xxx_port_db_load_purge(chip, port, fdb->addr, fdb->vid, 2130fad09c73SVivien Didelot GLOBAL_ATU_DATA_STATE_UNUSED); 2131fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 2132fad09c73SVivien Didelot 213383dabd1fSVivien Didelot return err; 2134fad09c73SVivien Didelot } 2135fad09c73SVivien Didelot 2136fad09c73SVivien Didelot static int _mv88e6xxx_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid, 2137fad09c73SVivien Didelot struct mv88e6xxx_atu_entry *entry) 2138fad09c73SVivien Didelot { 2139fad09c73SVivien Didelot struct mv88e6xxx_atu_entry next = { 0 }; 2140a935c052SVivien Didelot u16 val; 2141a935c052SVivien Didelot int err; 2142fad09c73SVivien Didelot 2143fad09c73SVivien Didelot next.fid = fid; 2144fad09c73SVivien Didelot 2145a935c052SVivien Didelot err = _mv88e6xxx_atu_wait(chip); 2146a935c052SVivien Didelot if (err) 2147a935c052SVivien Didelot return err; 2148fad09c73SVivien Didelot 2149a935c052SVivien Didelot err = _mv88e6xxx_atu_cmd(chip, fid, GLOBAL_ATU_OP_GET_NEXT_DB); 2150a935c052SVivien Didelot if (err) 2151a935c052SVivien Didelot return err; 2152fad09c73SVivien Didelot 2153a935c052SVivien Didelot err = _mv88e6xxx_atu_mac_read(chip, next.mac); 2154a935c052SVivien Didelot if (err) 2155a935c052SVivien Didelot return err; 2156fad09c73SVivien Didelot 2157a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_DATA, &val); 2158a935c052SVivien Didelot if (err) 2159a935c052SVivien Didelot return err; 2160fad09c73SVivien Didelot 2161a935c052SVivien Didelot next.state = val & GLOBAL_ATU_DATA_STATE_MASK; 2162fad09c73SVivien Didelot if (next.state != GLOBAL_ATU_DATA_STATE_UNUSED) { 2163fad09c73SVivien Didelot unsigned int mask, shift; 2164fad09c73SVivien Didelot 2165a935c052SVivien Didelot if (val & GLOBAL_ATU_DATA_TRUNK) { 2166fad09c73SVivien Didelot next.trunk = true; 2167fad09c73SVivien Didelot mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK; 2168fad09c73SVivien Didelot shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT; 2169fad09c73SVivien Didelot } else { 2170fad09c73SVivien Didelot next.trunk = false; 2171fad09c73SVivien Didelot mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK; 2172fad09c73SVivien Didelot shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT; 2173fad09c73SVivien Didelot } 2174fad09c73SVivien Didelot 2175a935c052SVivien Didelot next.portv_trunkid = (val & mask) >> shift; 2176fad09c73SVivien Didelot } 2177fad09c73SVivien Didelot 2178fad09c73SVivien Didelot *entry = next; 2179fad09c73SVivien Didelot return 0; 2180fad09c73SVivien Didelot } 2181fad09c73SVivien Didelot 218283dabd1fSVivien Didelot static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip, 2183fad09c73SVivien Didelot u16 fid, u16 vid, int port, 218483dabd1fSVivien Didelot struct switchdev_obj *obj, 2185fad09c73SVivien Didelot int (*cb)(struct switchdev_obj *obj)) 2186fad09c73SVivien Didelot { 2187fad09c73SVivien Didelot struct mv88e6xxx_atu_entry addr = { 2188fad09c73SVivien Didelot .mac = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, 2189fad09c73SVivien Didelot }; 2190fad09c73SVivien Didelot int err; 2191fad09c73SVivien Didelot 2192fad09c73SVivien Didelot err = _mv88e6xxx_atu_mac_write(chip, addr.mac); 2193fad09c73SVivien Didelot if (err) 2194fad09c73SVivien Didelot return err; 2195fad09c73SVivien Didelot 2196fad09c73SVivien Didelot do { 2197fad09c73SVivien Didelot err = _mv88e6xxx_atu_getnext(chip, fid, &addr); 2198fad09c73SVivien Didelot if (err) 219983dabd1fSVivien Didelot return err; 2200fad09c73SVivien Didelot 2201fad09c73SVivien Didelot if (addr.state == GLOBAL_ATU_DATA_STATE_UNUSED) 2202fad09c73SVivien Didelot break; 2203fad09c73SVivien Didelot 220483dabd1fSVivien Didelot if (addr.trunk || (addr.portv_trunkid & BIT(port)) == 0) 220583dabd1fSVivien Didelot continue; 2206fad09c73SVivien Didelot 220783dabd1fSVivien Didelot if (obj->id == SWITCHDEV_OBJ_ID_PORT_FDB) { 220883dabd1fSVivien Didelot struct switchdev_obj_port_fdb *fdb; 220983dabd1fSVivien Didelot 221083dabd1fSVivien Didelot if (!is_unicast_ether_addr(addr.mac)) 221183dabd1fSVivien Didelot continue; 221283dabd1fSVivien Didelot 221383dabd1fSVivien Didelot fdb = SWITCHDEV_OBJ_PORT_FDB(obj); 2214fad09c73SVivien Didelot fdb->vid = vid; 2215fad09c73SVivien Didelot ether_addr_copy(fdb->addr, addr.mac); 221683dabd1fSVivien Didelot if (addr.state == GLOBAL_ATU_DATA_STATE_UC_STATIC) 221783dabd1fSVivien Didelot fdb->ndm_state = NUD_NOARP; 221883dabd1fSVivien Didelot else 221983dabd1fSVivien Didelot fdb->ndm_state = NUD_REACHABLE; 22207df8fbddSVivien Didelot } else if (obj->id == SWITCHDEV_OBJ_ID_PORT_MDB) { 22217df8fbddSVivien Didelot struct switchdev_obj_port_mdb *mdb; 22227df8fbddSVivien Didelot 22237df8fbddSVivien Didelot if (!is_multicast_ether_addr(addr.mac)) 22247df8fbddSVivien Didelot continue; 22257df8fbddSVivien Didelot 22267df8fbddSVivien Didelot mdb = SWITCHDEV_OBJ_PORT_MDB(obj); 22277df8fbddSVivien Didelot mdb->vid = vid; 22287df8fbddSVivien Didelot ether_addr_copy(mdb->addr, addr.mac); 222983dabd1fSVivien Didelot } else { 223083dabd1fSVivien Didelot return -EOPNOTSUPP; 2231fad09c73SVivien Didelot } 223283dabd1fSVivien Didelot 223383dabd1fSVivien Didelot err = cb(obj); 223483dabd1fSVivien Didelot if (err) 223583dabd1fSVivien Didelot return err; 2236fad09c73SVivien Didelot } while (!is_broadcast_ether_addr(addr.mac)); 2237fad09c73SVivien Didelot 2238fad09c73SVivien Didelot return err; 2239fad09c73SVivien Didelot } 2240fad09c73SVivien Didelot 224183dabd1fSVivien Didelot static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port, 224283dabd1fSVivien Didelot struct switchdev_obj *obj, 224383dabd1fSVivien Didelot int (*cb)(struct switchdev_obj *obj)) 224483dabd1fSVivien Didelot { 2245b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry vlan = { 224683dabd1fSVivien Didelot .vid = GLOBAL_VTU_VID_MASK, /* all ones */ 224783dabd1fSVivien Didelot }; 224883dabd1fSVivien Didelot u16 fid; 224983dabd1fSVivien Didelot int err; 225083dabd1fSVivien Didelot 225183dabd1fSVivien Didelot /* Dump port's default Filtering Information Database (VLAN ID 0) */ 2252b4e48c50SVivien Didelot err = mv88e6xxx_port_get_fid(chip, port, &fid); 225383dabd1fSVivien Didelot if (err) 225483dabd1fSVivien Didelot return err; 225583dabd1fSVivien Didelot 225683dabd1fSVivien Didelot err = mv88e6xxx_port_db_dump_fid(chip, fid, 0, port, obj, cb); 225783dabd1fSVivien Didelot if (err) 225883dabd1fSVivien Didelot return err; 225983dabd1fSVivien Didelot 226083dabd1fSVivien Didelot /* Dump VLANs' Filtering Information Databases */ 226183dabd1fSVivien Didelot err = _mv88e6xxx_vtu_vid_write(chip, vlan.vid); 226283dabd1fSVivien Didelot if (err) 226383dabd1fSVivien Didelot return err; 226483dabd1fSVivien Didelot 226583dabd1fSVivien Didelot do { 226683dabd1fSVivien Didelot err = _mv88e6xxx_vtu_getnext(chip, &vlan); 226783dabd1fSVivien Didelot if (err) 226883dabd1fSVivien Didelot return err; 226983dabd1fSVivien Didelot 227083dabd1fSVivien Didelot if (!vlan.valid) 227183dabd1fSVivien Didelot break; 227283dabd1fSVivien Didelot 227383dabd1fSVivien Didelot err = mv88e6xxx_port_db_dump_fid(chip, vlan.fid, vlan.vid, port, 227483dabd1fSVivien Didelot obj, cb); 227583dabd1fSVivien Didelot if (err) 227683dabd1fSVivien Didelot return err; 227783dabd1fSVivien Didelot } while (vlan.vid < GLOBAL_VTU_VID_MASK); 227883dabd1fSVivien Didelot 227983dabd1fSVivien Didelot return err; 228083dabd1fSVivien Didelot } 228183dabd1fSVivien Didelot 2282fad09c73SVivien Didelot static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port, 2283fad09c73SVivien Didelot struct switchdev_obj_port_fdb *fdb, 2284fad09c73SVivien Didelot int (*cb)(struct switchdev_obj *obj)) 2285fad09c73SVivien Didelot { 228604bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 2287fad09c73SVivien Didelot int err; 2288fad09c73SVivien Didelot 2289fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 229083dabd1fSVivien Didelot err = mv88e6xxx_port_db_dump(chip, port, &fdb->obj, cb); 2291fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 2292fad09c73SVivien Didelot 2293fad09c73SVivien Didelot return err; 2294fad09c73SVivien Didelot } 2295fad09c73SVivien Didelot 2296fad09c73SVivien Didelot static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port, 2297fad09c73SVivien Didelot struct net_device *bridge) 2298fad09c73SVivien Didelot { 229904bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 2300fad09c73SVivien Didelot int i, err = 0; 2301fad09c73SVivien Didelot 2302fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 2303fad09c73SVivien Didelot 2304fad09c73SVivien Didelot /* Assign the bridge and remap each port's VLANTable */ 2305fad09c73SVivien Didelot chip->ports[port].bridge_dev = bridge; 2306fad09c73SVivien Didelot 2307370b4ffbSVivien Didelot for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { 2308fad09c73SVivien Didelot if (chip->ports[i].bridge_dev == bridge) { 2309fad09c73SVivien Didelot err = _mv88e6xxx_port_based_vlan_map(chip, i); 2310fad09c73SVivien Didelot if (err) 2311fad09c73SVivien Didelot break; 2312fad09c73SVivien Didelot } 2313fad09c73SVivien Didelot } 2314fad09c73SVivien Didelot 2315fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 2316fad09c73SVivien Didelot 2317fad09c73SVivien Didelot return err; 2318fad09c73SVivien Didelot } 2319fad09c73SVivien Didelot 2320fad09c73SVivien Didelot static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port) 2321fad09c73SVivien Didelot { 232204bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 2323fad09c73SVivien Didelot struct net_device *bridge = chip->ports[port].bridge_dev; 2324fad09c73SVivien Didelot int i; 2325fad09c73SVivien Didelot 2326fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 2327fad09c73SVivien Didelot 2328fad09c73SVivien Didelot /* Unassign the bridge and remap each port's VLANTable */ 2329fad09c73SVivien Didelot chip->ports[port].bridge_dev = NULL; 2330fad09c73SVivien Didelot 2331370b4ffbSVivien Didelot for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) 2332fad09c73SVivien Didelot if (i == port || chip->ports[i].bridge_dev == bridge) 2333fad09c73SVivien Didelot if (_mv88e6xxx_port_based_vlan_map(chip, i)) 2334fad09c73SVivien Didelot netdev_warn(ds->ports[i].netdev, 2335fad09c73SVivien Didelot "failed to remap\n"); 2336fad09c73SVivien Didelot 2337fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 2338fad09c73SVivien Didelot } 2339fad09c73SVivien Didelot 2340fad09c73SVivien Didelot static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip) 2341fad09c73SVivien Didelot { 2342fad09c73SVivien Didelot bool ppu_active = mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU_ACTIVE); 2343fad09c73SVivien Didelot u16 is_reset = (ppu_active ? 0x8800 : 0xc800); 2344fad09c73SVivien Didelot struct gpio_desc *gpiod = chip->reset; 2345fad09c73SVivien Didelot unsigned long timeout; 23460e7b9925SAndrew Lunn u16 reg; 2347a935c052SVivien Didelot int err; 2348fad09c73SVivien Didelot int i; 2349fad09c73SVivien Didelot 2350fad09c73SVivien Didelot /* Set all ports to the disabled state. */ 2351370b4ffbSVivien Didelot for (i = 0; i < mv88e6xxx_num_ports(chip); i++) { 2352e28def33SVivien Didelot err = mv88e6xxx_port_set_state(chip, i, 2353e28def33SVivien Didelot PORT_CONTROL_STATE_DISABLED); 23540e7b9925SAndrew Lunn if (err) 23550e7b9925SAndrew Lunn return err; 2356fad09c73SVivien Didelot } 2357fad09c73SVivien Didelot 2358fad09c73SVivien Didelot /* Wait for transmit queues to drain. */ 2359fad09c73SVivien Didelot usleep_range(2000, 4000); 2360fad09c73SVivien Didelot 2361fad09c73SVivien Didelot /* If there is a gpio connected to the reset pin, toggle it */ 2362fad09c73SVivien Didelot if (gpiod) { 2363fad09c73SVivien Didelot gpiod_set_value_cansleep(gpiod, 1); 2364fad09c73SVivien Didelot usleep_range(10000, 20000); 2365fad09c73SVivien Didelot gpiod_set_value_cansleep(gpiod, 0); 2366fad09c73SVivien Didelot usleep_range(10000, 20000); 2367fad09c73SVivien Didelot } 2368fad09c73SVivien Didelot 2369fad09c73SVivien Didelot /* Reset the switch. Keep the PPU active if requested. The PPU 2370fad09c73SVivien Didelot * needs to be active to support indirect phy register access 2371fad09c73SVivien Didelot * through global registers 0x18 and 0x19. 2372fad09c73SVivien Didelot */ 2373fad09c73SVivien Didelot if (ppu_active) 2374a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, 0x04, 0xc000); 2375fad09c73SVivien Didelot else 2376a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, 0x04, 0xc400); 23770e7b9925SAndrew Lunn if (err) 23780e7b9925SAndrew Lunn return err; 2379fad09c73SVivien Didelot 2380fad09c73SVivien Didelot /* Wait up to one second for reset to complete. */ 2381fad09c73SVivien Didelot timeout = jiffies + 1 * HZ; 2382fad09c73SVivien Didelot while (time_before(jiffies, timeout)) { 2383a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, 0x00, ®); 2384a935c052SVivien Didelot if (err) 2385a935c052SVivien Didelot return err; 2386fad09c73SVivien Didelot 2387a935c052SVivien Didelot if ((reg & is_reset) == is_reset) 2388fad09c73SVivien Didelot break; 2389fad09c73SVivien Didelot usleep_range(1000, 2000); 2390fad09c73SVivien Didelot } 2391fad09c73SVivien Didelot if (time_after(jiffies, timeout)) 23920e7b9925SAndrew Lunn err = -ETIMEDOUT; 2393fad09c73SVivien Didelot else 23940e7b9925SAndrew Lunn err = 0; 2395fad09c73SVivien Didelot 23960e7b9925SAndrew Lunn return err; 2397fad09c73SVivien Didelot } 2398fad09c73SVivien Didelot 239909cb7dfdSVivien Didelot static int mv88e6xxx_serdes_power_on(struct mv88e6xxx_chip *chip) 2400fad09c73SVivien Didelot { 240109cb7dfdSVivien Didelot u16 val; 240209cb7dfdSVivien Didelot int err; 2403fad09c73SVivien Didelot 240409cb7dfdSVivien Didelot /* Clear Power Down bit */ 240509cb7dfdSVivien Didelot err = mv88e6xxx_serdes_read(chip, MII_BMCR, &val); 240609cb7dfdSVivien Didelot if (err) 240709cb7dfdSVivien Didelot return err; 2408fad09c73SVivien Didelot 240909cb7dfdSVivien Didelot if (val & BMCR_PDOWN) { 241009cb7dfdSVivien Didelot val &= ~BMCR_PDOWN; 241109cb7dfdSVivien Didelot err = mv88e6xxx_serdes_write(chip, MII_BMCR, val); 2412fad09c73SVivien Didelot } 2413fad09c73SVivien Didelot 241409cb7dfdSVivien Didelot return err; 2415fad09c73SVivien Didelot } 2416fad09c73SVivien Didelot 2417fad09c73SVivien Didelot static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) 2418fad09c73SVivien Didelot { 2419fad09c73SVivien Didelot struct dsa_switch *ds = chip->ds; 24200e7b9925SAndrew Lunn int err; 2421fad09c73SVivien Didelot u16 reg; 2422fad09c73SVivien Didelot 2423d78343d2SVivien Didelot /* MAC Forcing register: don't force link, speed, duplex or flow control 2424d78343d2SVivien Didelot * state to any particular values on physical ports, but force the CPU 2425d78343d2SVivien Didelot * port and all DSA ports to their maximum bandwidth and full duplex. 2426fad09c73SVivien Didelot */ 2427d78343d2SVivien Didelot if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) 2428d78343d2SVivien Didelot err = mv88e6xxx_port_setup_mac(chip, port, LINK_FORCED_UP, 2429d78343d2SVivien Didelot SPEED_MAX, DUPLEX_FULL, 2430d78343d2SVivien Didelot PHY_INTERFACE_MODE_NA); 2431fad09c73SVivien Didelot else 2432d78343d2SVivien Didelot err = mv88e6xxx_port_setup_mac(chip, port, LINK_UNFORCED, 2433d78343d2SVivien Didelot SPEED_UNFORCED, DUPLEX_UNFORCED, 2434d78343d2SVivien Didelot PHY_INTERFACE_MODE_NA); 24350e7b9925SAndrew Lunn if (err) 24360e7b9925SAndrew Lunn return err; 2437fad09c73SVivien Didelot 2438fad09c73SVivien Didelot /* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock, 2439fad09c73SVivien Didelot * disable Header mode, enable IGMP/MLD snooping, disable VLAN 2440fad09c73SVivien Didelot * tunneling, determine priority by looking at 802.1p and IP 2441fad09c73SVivien Didelot * priority fields (IP prio has precedence), and set STP state 2442fad09c73SVivien Didelot * to Forwarding. 2443fad09c73SVivien Didelot * 2444fad09c73SVivien Didelot * If this is the CPU link, use DSA or EDSA tagging depending 2445fad09c73SVivien Didelot * on which tagging mode was configured. 2446fad09c73SVivien Didelot * 2447fad09c73SVivien Didelot * If this is a link to another switch, use DSA tagging mode. 2448fad09c73SVivien Didelot * 2449fad09c73SVivien Didelot * If this is the upstream port for this switch, enable 2450fad09c73SVivien Didelot * forwarding of unknown unicasts and multicasts. 2451fad09c73SVivien Didelot */ 2452fad09c73SVivien Didelot reg = 0; 2453fad09c73SVivien Didelot if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) || 2454fad09c73SVivien Didelot mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) || 2455fad09c73SVivien Didelot mv88e6xxx_6095_family(chip) || mv88e6xxx_6065_family(chip) || 2456fad09c73SVivien Didelot mv88e6xxx_6185_family(chip) || mv88e6xxx_6320_family(chip)) 2457fad09c73SVivien Didelot reg = PORT_CONTROL_IGMP_MLD_SNOOP | 2458fad09c73SVivien Didelot PORT_CONTROL_USE_TAG | PORT_CONTROL_USE_IP | 2459fad09c73SVivien Didelot PORT_CONTROL_STATE_FORWARDING; 2460fad09c73SVivien Didelot if (dsa_is_cpu_port(ds, port)) { 24612bbb33beSAndrew Lunn if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EDSA)) 2462fad09c73SVivien Didelot reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA | 2463fad09c73SVivien Didelot PORT_CONTROL_FORWARD_UNKNOWN_MC; 24642bbb33beSAndrew Lunn else 24652bbb33beSAndrew Lunn reg |= PORT_CONTROL_DSA_TAG; 2466f027e0ccSJamie Lentin reg |= PORT_CONTROL_EGRESS_ADD_TAG | 2467f027e0ccSJamie Lentin PORT_CONTROL_FORWARD_UNKNOWN; 2468fad09c73SVivien Didelot } 2469fad09c73SVivien Didelot if (dsa_is_dsa_port(ds, port)) { 2470fad09c73SVivien Didelot if (mv88e6xxx_6095_family(chip) || 2471fad09c73SVivien Didelot mv88e6xxx_6185_family(chip)) 2472fad09c73SVivien Didelot reg |= PORT_CONTROL_DSA_TAG; 2473fad09c73SVivien Didelot if (mv88e6xxx_6352_family(chip) || 2474fad09c73SVivien Didelot mv88e6xxx_6351_family(chip) || 2475fad09c73SVivien Didelot mv88e6xxx_6165_family(chip) || 2476fad09c73SVivien Didelot mv88e6xxx_6097_family(chip) || 2477fad09c73SVivien Didelot mv88e6xxx_6320_family(chip)) { 2478fad09c73SVivien Didelot reg |= PORT_CONTROL_FRAME_MODE_DSA; 2479fad09c73SVivien Didelot } 2480fad09c73SVivien Didelot 2481fad09c73SVivien Didelot if (port == dsa_upstream_port(ds)) 2482fad09c73SVivien Didelot reg |= PORT_CONTROL_FORWARD_UNKNOWN | 2483fad09c73SVivien Didelot PORT_CONTROL_FORWARD_UNKNOWN_MC; 2484fad09c73SVivien Didelot } 2485fad09c73SVivien Didelot if (reg) { 24860e7b9925SAndrew Lunn err = mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg); 24870e7b9925SAndrew Lunn if (err) 24880e7b9925SAndrew Lunn return err; 2489fad09c73SVivien Didelot } 2490fad09c73SVivien Didelot 2491fad09c73SVivien Didelot /* If this port is connected to a SerDes, make sure the SerDes is not 2492fad09c73SVivien Didelot * powered down. 2493fad09c73SVivien Didelot */ 249409cb7dfdSVivien Didelot if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_SERDES)) { 24950e7b9925SAndrew Lunn err = mv88e6xxx_port_read(chip, port, PORT_STATUS, ®); 24960e7b9925SAndrew Lunn if (err) 24970e7b9925SAndrew Lunn return err; 24980e7b9925SAndrew Lunn reg &= PORT_STATUS_CMODE_MASK; 24990e7b9925SAndrew Lunn if ((reg == PORT_STATUS_CMODE_100BASE_X) || 25000e7b9925SAndrew Lunn (reg == PORT_STATUS_CMODE_1000BASE_X) || 25010e7b9925SAndrew Lunn (reg == PORT_STATUS_CMODE_SGMII)) { 25020e7b9925SAndrew Lunn err = mv88e6xxx_serdes_power_on(chip); 25030e7b9925SAndrew Lunn if (err < 0) 25040e7b9925SAndrew Lunn return err; 2505fad09c73SVivien Didelot } 2506fad09c73SVivien Didelot } 2507fad09c73SVivien Didelot 2508fad09c73SVivien Didelot /* Port Control 2: don't force a good FCS, set the maximum frame size to 2509fad09c73SVivien Didelot * 10240 bytes, disable 802.1q tags checking, don't discard tagged or 2510fad09c73SVivien Didelot * untagged frames on this port, do a destination address lookup on all 2511fad09c73SVivien Didelot * received packets as usual, disable ARP mirroring and don't send a 2512fad09c73SVivien Didelot * copy of all transmitted/received frames on this port to the CPU. 2513fad09c73SVivien Didelot */ 2514fad09c73SVivien Didelot reg = 0; 2515fad09c73SVivien Didelot if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) || 2516fad09c73SVivien Didelot mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) || 2517fad09c73SVivien Didelot mv88e6xxx_6095_family(chip) || mv88e6xxx_6320_family(chip) || 2518fad09c73SVivien Didelot mv88e6xxx_6185_family(chip)) 2519fad09c73SVivien Didelot reg = PORT_CONTROL_2_MAP_DA; 2520fad09c73SVivien Didelot 2521fad09c73SVivien Didelot if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) || 2522fad09c73SVivien Didelot mv88e6xxx_6165_family(chip) || mv88e6xxx_6320_family(chip)) 2523fad09c73SVivien Didelot reg |= PORT_CONTROL_2_JUMBO_10240; 2524fad09c73SVivien Didelot 2525fad09c73SVivien Didelot if (mv88e6xxx_6095_family(chip) || mv88e6xxx_6185_family(chip)) { 2526fad09c73SVivien Didelot /* Set the upstream port this port should use */ 2527fad09c73SVivien Didelot reg |= dsa_upstream_port(ds); 2528fad09c73SVivien Didelot /* enable forwarding of unknown multicast addresses to 2529fad09c73SVivien Didelot * the upstream port 2530fad09c73SVivien Didelot */ 2531fad09c73SVivien Didelot if (port == dsa_upstream_port(ds)) 2532fad09c73SVivien Didelot reg |= PORT_CONTROL_2_FORWARD_UNKNOWN; 2533fad09c73SVivien Didelot } 2534fad09c73SVivien Didelot 2535fad09c73SVivien Didelot reg |= PORT_CONTROL_2_8021Q_DISABLED; 2536fad09c73SVivien Didelot 2537fad09c73SVivien Didelot if (reg) { 25380e7b9925SAndrew Lunn err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg); 25390e7b9925SAndrew Lunn if (err) 25400e7b9925SAndrew Lunn return err; 2541fad09c73SVivien Didelot } 2542fad09c73SVivien Didelot 2543fad09c73SVivien Didelot /* Port Association Vector: when learning source addresses 2544fad09c73SVivien Didelot * of packets, add the address to the address database using 2545fad09c73SVivien Didelot * a port bitmap that has only the bit for this port set and 2546fad09c73SVivien Didelot * the other bits clear. 2547fad09c73SVivien Didelot */ 2548fad09c73SVivien Didelot reg = 1 << port; 2549fad09c73SVivien Didelot /* Disable learning for CPU port */ 2550fad09c73SVivien Didelot if (dsa_is_cpu_port(ds, port)) 2551fad09c73SVivien Didelot reg = 0; 2552fad09c73SVivien Didelot 25530e7b9925SAndrew Lunn err = mv88e6xxx_port_write(chip, port, PORT_ASSOC_VECTOR, reg); 25540e7b9925SAndrew Lunn if (err) 25550e7b9925SAndrew Lunn return err; 2556fad09c73SVivien Didelot 2557fad09c73SVivien Didelot /* Egress rate control 2: disable egress rate control. */ 25580e7b9925SAndrew Lunn err = mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL_2, 0x0000); 25590e7b9925SAndrew Lunn if (err) 25600e7b9925SAndrew Lunn return err; 2561fad09c73SVivien Didelot 2562fad09c73SVivien Didelot if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) || 2563fad09c73SVivien Didelot mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) || 2564fad09c73SVivien Didelot mv88e6xxx_6320_family(chip)) { 2565fad09c73SVivien Didelot /* Do not limit the period of time that this port can 2566fad09c73SVivien Didelot * be paused for by the remote end or the period of 2567fad09c73SVivien Didelot * time that this port can pause the remote end. 2568fad09c73SVivien Didelot */ 25690e7b9925SAndrew Lunn err = mv88e6xxx_port_write(chip, port, PORT_PAUSE_CTRL, 0x0000); 25700e7b9925SAndrew Lunn if (err) 25710e7b9925SAndrew Lunn return err; 2572fad09c73SVivien Didelot 2573fad09c73SVivien Didelot /* Port ATU control: disable limiting the number of 2574fad09c73SVivien Didelot * address database entries that this port is allowed 2575fad09c73SVivien Didelot * to use. 2576fad09c73SVivien Didelot */ 25770e7b9925SAndrew Lunn err = mv88e6xxx_port_write(chip, port, PORT_ATU_CONTROL, 25780e7b9925SAndrew Lunn 0x0000); 2579fad09c73SVivien Didelot /* Priority Override: disable DA, SA and VTU priority 2580fad09c73SVivien Didelot * override. 2581fad09c73SVivien Didelot */ 25820e7b9925SAndrew Lunn err = mv88e6xxx_port_write(chip, port, PORT_PRI_OVERRIDE, 25830e7b9925SAndrew Lunn 0x0000); 25840e7b9925SAndrew Lunn if (err) 25850e7b9925SAndrew Lunn return err; 2586fad09c73SVivien Didelot 2587fad09c73SVivien Didelot /* Port Ethertype: use the Ethertype DSA Ethertype 2588fad09c73SVivien Didelot * value. 2589fad09c73SVivien Didelot */ 25902bbb33beSAndrew Lunn if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EDSA)) { 25910e7b9925SAndrew Lunn err = mv88e6xxx_port_write(chip, port, PORT_ETH_TYPE, 25920e7b9925SAndrew Lunn ETH_P_EDSA); 25930e7b9925SAndrew Lunn if (err) 25940e7b9925SAndrew Lunn return err; 25952bbb33beSAndrew Lunn } 25962bbb33beSAndrew Lunn 2597fad09c73SVivien Didelot /* Tag Remap: use an identity 802.1p prio -> switch 2598fad09c73SVivien Didelot * prio mapping. 2599fad09c73SVivien Didelot */ 26000e7b9925SAndrew Lunn err = mv88e6xxx_port_write(chip, port, PORT_TAG_REGMAP_0123, 26010e7b9925SAndrew Lunn 0x3210); 26020e7b9925SAndrew Lunn if (err) 26030e7b9925SAndrew Lunn return err; 2604fad09c73SVivien Didelot 2605fad09c73SVivien Didelot /* Tag Remap 2: use an identity 802.1p prio -> switch 2606fad09c73SVivien Didelot * prio mapping. 2607fad09c73SVivien Didelot */ 26080e7b9925SAndrew Lunn err = mv88e6xxx_port_write(chip, port, PORT_TAG_REGMAP_4567, 26090e7b9925SAndrew Lunn 0x7654); 26100e7b9925SAndrew Lunn if (err) 26110e7b9925SAndrew Lunn return err; 2612fad09c73SVivien Didelot } 2613fad09c73SVivien Didelot 26141bc261faSJamie Lentin /* Rate Control: disable ingress rate limiting. */ 2615fad09c73SVivien Didelot if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) || 2616fad09c73SVivien Didelot mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) || 2617fad09c73SVivien Didelot mv88e6xxx_6320_family(chip)) { 26180e7b9925SAndrew Lunn err = mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL, 26190e7b9925SAndrew Lunn 0x0001); 26200e7b9925SAndrew Lunn if (err) 26210e7b9925SAndrew Lunn return err; 26221bc261faSJamie Lentin } else if (mv88e6xxx_6185_family(chip) || mv88e6xxx_6095_family(chip)) { 26230e7b9925SAndrew Lunn err = mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL, 26240e7b9925SAndrew Lunn 0x0000); 26250e7b9925SAndrew Lunn if (err) 26260e7b9925SAndrew Lunn return err; 2627fad09c73SVivien Didelot } 2628fad09c73SVivien Didelot 2629fad09c73SVivien Didelot /* Port Control 1: disable trunking, disable sending 2630fad09c73SVivien Didelot * learning messages to this port. 2631fad09c73SVivien Didelot */ 26320e7b9925SAndrew Lunn err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_1, 0x0000); 26330e7b9925SAndrew Lunn if (err) 26340e7b9925SAndrew Lunn return err; 2635fad09c73SVivien Didelot 2636fad09c73SVivien Didelot /* Port based VLAN map: give each port the same default address 2637fad09c73SVivien Didelot * database, and allow bidirectional communication between the 2638fad09c73SVivien Didelot * CPU and DSA port(s), and the other ports. 2639fad09c73SVivien Didelot */ 2640b4e48c50SVivien Didelot err = mv88e6xxx_port_set_fid(chip, port, 0); 26410e7b9925SAndrew Lunn if (err) 26420e7b9925SAndrew Lunn return err; 2643fad09c73SVivien Didelot 26440e7b9925SAndrew Lunn err = _mv88e6xxx_port_based_vlan_map(chip, port); 26450e7b9925SAndrew Lunn if (err) 26460e7b9925SAndrew Lunn return err; 2647fad09c73SVivien Didelot 2648fad09c73SVivien Didelot /* Default VLAN ID and priority: don't set a default VLAN 2649fad09c73SVivien Didelot * ID, and set the default packet priority to zero. 2650fad09c73SVivien Didelot */ 26510e7b9925SAndrew Lunn return mv88e6xxx_port_write(chip, port, PORT_DEFAULT_VLAN, 0x0000); 2652fad09c73SVivien Didelot } 2653fad09c73SVivien Didelot 2654aa0938c6SWei Yongjun static int mv88e6xxx_g1_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr) 26553b4caa1bSVivien Didelot { 26563b4caa1bSVivien Didelot int err; 26573b4caa1bSVivien Didelot 2658a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_MAC_01, (addr[0] << 8) | addr[1]); 26593b4caa1bSVivien Didelot if (err) 26603b4caa1bSVivien Didelot return err; 26613b4caa1bSVivien Didelot 2662a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_MAC_23, (addr[2] << 8) | addr[3]); 26633b4caa1bSVivien Didelot if (err) 26643b4caa1bSVivien Didelot return err; 26653b4caa1bSVivien Didelot 2666a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_MAC_45, (addr[4] << 8) | addr[5]); 2667a935c052SVivien Didelot if (err) 2668a935c052SVivien Didelot return err; 2669a935c052SVivien Didelot 2670a935c052SVivien Didelot return 0; 26713b4caa1bSVivien Didelot } 26723b4caa1bSVivien Didelot 2673acddbd21SVivien Didelot static int mv88e6xxx_g1_set_age_time(struct mv88e6xxx_chip *chip, 2674acddbd21SVivien Didelot unsigned int msecs) 2675acddbd21SVivien Didelot { 2676acddbd21SVivien Didelot const unsigned int coeff = chip->info->age_time_coeff; 2677acddbd21SVivien Didelot const unsigned int min = 0x01 * coeff; 2678acddbd21SVivien Didelot const unsigned int max = 0xff * coeff; 2679acddbd21SVivien Didelot u8 age_time; 2680acddbd21SVivien Didelot u16 val; 2681acddbd21SVivien Didelot int err; 2682acddbd21SVivien Didelot 2683acddbd21SVivien Didelot if (msecs < min || msecs > max) 2684acddbd21SVivien Didelot return -ERANGE; 2685acddbd21SVivien Didelot 2686acddbd21SVivien Didelot /* Round to nearest multiple of coeff */ 2687acddbd21SVivien Didelot age_time = (msecs + coeff / 2) / coeff; 2688acddbd21SVivien Didelot 2689a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_CONTROL, &val); 2690acddbd21SVivien Didelot if (err) 2691acddbd21SVivien Didelot return err; 2692acddbd21SVivien Didelot 2693acddbd21SVivien Didelot /* AgeTime is 11:4 bits */ 2694acddbd21SVivien Didelot val &= ~0xff0; 2695acddbd21SVivien Didelot val |= age_time << 4; 2696acddbd21SVivien Didelot 2697a935c052SVivien Didelot return mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL, val); 2698acddbd21SVivien Didelot } 2699acddbd21SVivien Didelot 27002cfcd964SVivien Didelot static int mv88e6xxx_set_ageing_time(struct dsa_switch *ds, 27012cfcd964SVivien Didelot unsigned int ageing_time) 27022cfcd964SVivien Didelot { 270304bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 27042cfcd964SVivien Didelot int err; 27052cfcd964SVivien Didelot 27062cfcd964SVivien Didelot mutex_lock(&chip->reg_lock); 27072cfcd964SVivien Didelot err = mv88e6xxx_g1_set_age_time(chip, ageing_time); 27082cfcd964SVivien Didelot mutex_unlock(&chip->reg_lock); 27092cfcd964SVivien Didelot 27102cfcd964SVivien Didelot return err; 27112cfcd964SVivien Didelot } 27122cfcd964SVivien Didelot 27139729934cSVivien Didelot static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip) 2714fad09c73SVivien Didelot { 2715fad09c73SVivien Didelot struct dsa_switch *ds = chip->ds; 2716fad09c73SVivien Didelot u32 upstream_port = dsa_upstream_port(ds); 2717fad09c73SVivien Didelot u16 reg; 2718fad09c73SVivien Didelot int err; 2719fad09c73SVivien Didelot 2720fad09c73SVivien Didelot /* Enable the PHY Polling Unit if present, don't discard any packets, 2721fad09c73SVivien Didelot * and mask all interrupt sources. 2722fad09c73SVivien Didelot */ 2723dc30c35bSAndrew Lunn err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, ®); 2724dc30c35bSAndrew Lunn if (err < 0) 2725dc30c35bSAndrew Lunn return err; 2726dc30c35bSAndrew Lunn 2727dc30c35bSAndrew Lunn reg &= ~GLOBAL_CONTROL_PPU_ENABLE; 2728fad09c73SVivien Didelot if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU) || 2729fad09c73SVivien Didelot mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU_ACTIVE)) 2730fad09c73SVivien Didelot reg |= GLOBAL_CONTROL_PPU_ENABLE; 2731fad09c73SVivien Didelot 2732a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, reg); 2733fad09c73SVivien Didelot if (err) 2734fad09c73SVivien Didelot return err; 2735fad09c73SVivien Didelot 2736fad09c73SVivien Didelot /* Configure the upstream port, and configure it as the port to which 2737fad09c73SVivien Didelot * ingress and egress and ARP monitor frames are to be sent. 2738fad09c73SVivien Didelot */ 2739fad09c73SVivien Didelot reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT | 2740fad09c73SVivien Didelot upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT | 2741fad09c73SVivien Didelot upstream_port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT; 2742a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_MONITOR_CONTROL, reg); 2743fad09c73SVivien Didelot if (err) 2744fad09c73SVivien Didelot return err; 2745fad09c73SVivien Didelot 2746fad09c73SVivien Didelot /* Disable remote management, and set the switch's DSA device number. */ 2747a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL_2, 2748fad09c73SVivien Didelot GLOBAL_CONTROL_2_MULTIPLE_CASCADE | 2749fad09c73SVivien Didelot (ds->index & 0x1f)); 2750fad09c73SVivien Didelot if (err) 2751fad09c73SVivien Didelot return err; 2752fad09c73SVivien Didelot 2753acddbd21SVivien Didelot /* Clear all the VTU and STU entries */ 2754acddbd21SVivien Didelot err = _mv88e6xxx_vtu_stu_flush(chip); 2755acddbd21SVivien Didelot if (err < 0) 2756acddbd21SVivien Didelot return err; 2757acddbd21SVivien Didelot 2758fad09c73SVivien Didelot /* Set the default address aging time to 5 minutes, and 2759fad09c73SVivien Didelot * enable address learn messages to be sent to all message 2760fad09c73SVivien Didelot * ports. 2761fad09c73SVivien Didelot */ 2762a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL, 2763acddbd21SVivien Didelot GLOBAL_ATU_CONTROL_LEARN2ALL); 2764fad09c73SVivien Didelot if (err) 2765fad09c73SVivien Didelot return err; 2766fad09c73SVivien Didelot 2767acddbd21SVivien Didelot err = mv88e6xxx_g1_set_age_time(chip, 300000); 2768acddbd21SVivien Didelot if (err) 27699729934cSVivien Didelot return err; 27709729934cSVivien Didelot 27719729934cSVivien Didelot /* Clear all ATU entries */ 27729729934cSVivien Didelot err = _mv88e6xxx_atu_flush(chip, 0, true); 27739729934cSVivien Didelot if (err) 27749729934cSVivien Didelot return err; 27759729934cSVivien Didelot 2776fad09c73SVivien Didelot /* Configure the IP ToS mapping registers. */ 2777a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_0, 0x0000); 2778fad09c73SVivien Didelot if (err) 2779fad09c73SVivien Didelot return err; 2780a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_1, 0x0000); 2781fad09c73SVivien Didelot if (err) 2782fad09c73SVivien Didelot return err; 2783a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_2, 0x5555); 2784fad09c73SVivien Didelot if (err) 2785fad09c73SVivien Didelot return err; 2786a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_3, 0x5555); 2787fad09c73SVivien Didelot if (err) 2788fad09c73SVivien Didelot return err; 2789a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_4, 0xaaaa); 2790fad09c73SVivien Didelot if (err) 2791fad09c73SVivien Didelot return err; 2792a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_5, 0xaaaa); 2793fad09c73SVivien Didelot if (err) 2794fad09c73SVivien Didelot return err; 2795a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_6, 0xffff); 2796fad09c73SVivien Didelot if (err) 2797fad09c73SVivien Didelot return err; 2798a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_7, 0xffff); 2799fad09c73SVivien Didelot if (err) 2800fad09c73SVivien Didelot return err; 2801fad09c73SVivien Didelot 2802fad09c73SVivien Didelot /* Configure the IEEE 802.1p priority mapping register. */ 2803a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_IEEE_PRI, 0xfa41); 2804fad09c73SVivien Didelot if (err) 2805fad09c73SVivien Didelot return err; 2806fad09c73SVivien Didelot 28079729934cSVivien Didelot /* Clear the statistics counters for all ports */ 2808a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP, 28099729934cSVivien Didelot GLOBAL_STATS_OP_FLUSH_ALL); 28109729934cSVivien Didelot if (err) 28119729934cSVivien Didelot return err; 28129729934cSVivien Didelot 28139729934cSVivien Didelot /* Wait for the flush to complete. */ 28149729934cSVivien Didelot err = _mv88e6xxx_stats_wait(chip); 28159729934cSVivien Didelot if (err) 28169729934cSVivien Didelot return err; 28179729934cSVivien Didelot 28189729934cSVivien Didelot return 0; 28199729934cSVivien Didelot } 28209729934cSVivien Didelot 2821fad09c73SVivien Didelot static int mv88e6xxx_setup(struct dsa_switch *ds) 2822fad09c73SVivien Didelot { 282304bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 2824fad09c73SVivien Didelot int err; 2825fad09c73SVivien Didelot int i; 2826fad09c73SVivien Didelot 2827fad09c73SVivien Didelot chip->ds = ds; 2828fad09c73SVivien Didelot ds->slave_mii_bus = chip->mdio_bus; 2829fad09c73SVivien Didelot 2830fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 2831fad09c73SVivien Didelot 28329729934cSVivien Didelot /* Setup Switch Port Registers */ 2833370b4ffbSVivien Didelot for (i = 0; i < mv88e6xxx_num_ports(chip); i++) { 28349729934cSVivien Didelot err = mv88e6xxx_setup_port(chip, i); 28359729934cSVivien Didelot if (err) 28369729934cSVivien Didelot goto unlock; 28379729934cSVivien Didelot } 28389729934cSVivien Didelot 28399729934cSVivien Didelot /* Setup Switch Global 1 Registers */ 28409729934cSVivien Didelot err = mv88e6xxx_g1_setup(chip); 2841fad09c73SVivien Didelot if (err) 2842fad09c73SVivien Didelot goto unlock; 2843fad09c73SVivien Didelot 28449729934cSVivien Didelot /* Setup Switch Global 2 Registers */ 28459729934cSVivien Didelot if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_GLOBAL2)) { 28469729934cSVivien Didelot err = mv88e6xxx_g2_setup(chip); 2847fad09c73SVivien Didelot if (err) 2848fad09c73SVivien Didelot goto unlock; 2849fad09c73SVivien Didelot } 2850fad09c73SVivien Didelot 2851fad09c73SVivien Didelot unlock: 2852fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 2853fad09c73SVivien Didelot 2854fad09c73SVivien Didelot return err; 2855fad09c73SVivien Didelot } 2856fad09c73SVivien Didelot 28573b4caa1bSVivien Didelot static int mv88e6xxx_set_addr(struct dsa_switch *ds, u8 *addr) 28583b4caa1bSVivien Didelot { 285904bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 28603b4caa1bSVivien Didelot int err; 28613b4caa1bSVivien Didelot 2862b073d4e2SVivien Didelot if (!chip->info->ops->set_switch_mac) 2863b073d4e2SVivien Didelot return -EOPNOTSUPP; 2864b073d4e2SVivien Didelot 28653b4caa1bSVivien Didelot mutex_lock(&chip->reg_lock); 2866b073d4e2SVivien Didelot err = chip->info->ops->set_switch_mac(chip, addr); 28673b4caa1bSVivien Didelot mutex_unlock(&chip->reg_lock); 28683b4caa1bSVivien Didelot 28693b4caa1bSVivien Didelot return err; 28703b4caa1bSVivien Didelot } 28713b4caa1bSVivien Didelot 2872e57e5e77SVivien Didelot static int mv88e6xxx_mdio_read(struct mii_bus *bus, int phy, int reg) 2873fad09c73SVivien Didelot { 2874fad09c73SVivien Didelot struct mv88e6xxx_chip *chip = bus->priv; 2875e57e5e77SVivien Didelot u16 val; 2876e57e5e77SVivien Didelot int err; 2877fad09c73SVivien Didelot 2878370b4ffbSVivien Didelot if (phy >= mv88e6xxx_num_ports(chip)) 2879fad09c73SVivien Didelot return 0xffff; 2880fad09c73SVivien Didelot 2881fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 2882e57e5e77SVivien Didelot err = mv88e6xxx_phy_read(chip, phy, reg, &val); 2883fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 2884e57e5e77SVivien Didelot 2885e57e5e77SVivien Didelot return err ? err : val; 2886fad09c73SVivien Didelot } 2887fad09c73SVivien Didelot 2888e57e5e77SVivien Didelot static int mv88e6xxx_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val) 2889fad09c73SVivien Didelot { 2890fad09c73SVivien Didelot struct mv88e6xxx_chip *chip = bus->priv; 2891e57e5e77SVivien Didelot int err; 2892fad09c73SVivien Didelot 2893370b4ffbSVivien Didelot if (phy >= mv88e6xxx_num_ports(chip)) 2894fad09c73SVivien Didelot return 0xffff; 2895fad09c73SVivien Didelot 2896fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 2897e57e5e77SVivien Didelot err = mv88e6xxx_phy_write(chip, phy, reg, val); 2898fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 2899e57e5e77SVivien Didelot 2900e57e5e77SVivien Didelot return err; 2901fad09c73SVivien Didelot } 2902fad09c73SVivien Didelot 2903fad09c73SVivien Didelot static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip, 2904fad09c73SVivien Didelot struct device_node *np) 2905fad09c73SVivien Didelot { 2906fad09c73SVivien Didelot static int index; 2907fad09c73SVivien Didelot struct mii_bus *bus; 2908fad09c73SVivien Didelot int err; 2909fad09c73SVivien Didelot 2910fad09c73SVivien Didelot if (np) 2911fad09c73SVivien Didelot chip->mdio_np = of_get_child_by_name(np, "mdio"); 2912fad09c73SVivien Didelot 2913fad09c73SVivien Didelot bus = devm_mdiobus_alloc(chip->dev); 2914fad09c73SVivien Didelot if (!bus) 2915fad09c73SVivien Didelot return -ENOMEM; 2916fad09c73SVivien Didelot 2917fad09c73SVivien Didelot bus->priv = (void *)chip; 2918fad09c73SVivien Didelot if (np) { 2919fad09c73SVivien Didelot bus->name = np->full_name; 2920fad09c73SVivien Didelot snprintf(bus->id, MII_BUS_ID_SIZE, "%s", np->full_name); 2921fad09c73SVivien Didelot } else { 2922fad09c73SVivien Didelot bus->name = "mv88e6xxx SMI"; 2923fad09c73SVivien Didelot snprintf(bus->id, MII_BUS_ID_SIZE, "mv88e6xxx-%d", index++); 2924fad09c73SVivien Didelot } 2925fad09c73SVivien Didelot 2926fad09c73SVivien Didelot bus->read = mv88e6xxx_mdio_read; 2927fad09c73SVivien Didelot bus->write = mv88e6xxx_mdio_write; 2928fad09c73SVivien Didelot bus->parent = chip->dev; 2929fad09c73SVivien Didelot 2930fad09c73SVivien Didelot if (chip->mdio_np) 2931fad09c73SVivien Didelot err = of_mdiobus_register(bus, chip->mdio_np); 2932fad09c73SVivien Didelot else 2933fad09c73SVivien Didelot err = mdiobus_register(bus); 2934fad09c73SVivien Didelot if (err) { 2935fad09c73SVivien Didelot dev_err(chip->dev, "Cannot register MDIO bus (%d)\n", err); 2936fad09c73SVivien Didelot goto out; 2937fad09c73SVivien Didelot } 2938fad09c73SVivien Didelot chip->mdio_bus = bus; 2939fad09c73SVivien Didelot 2940fad09c73SVivien Didelot return 0; 2941fad09c73SVivien Didelot 2942fad09c73SVivien Didelot out: 2943fad09c73SVivien Didelot if (chip->mdio_np) 2944fad09c73SVivien Didelot of_node_put(chip->mdio_np); 2945fad09c73SVivien Didelot 2946fad09c73SVivien Didelot return err; 2947fad09c73SVivien Didelot } 2948fad09c73SVivien Didelot 2949fad09c73SVivien Didelot static void mv88e6xxx_mdio_unregister(struct mv88e6xxx_chip *chip) 2950fad09c73SVivien Didelot 2951fad09c73SVivien Didelot { 2952fad09c73SVivien Didelot struct mii_bus *bus = chip->mdio_bus; 2953fad09c73SVivien Didelot 2954fad09c73SVivien Didelot mdiobus_unregister(bus); 2955fad09c73SVivien Didelot 2956fad09c73SVivien Didelot if (chip->mdio_np) 2957fad09c73SVivien Didelot of_node_put(chip->mdio_np); 2958fad09c73SVivien Didelot } 2959fad09c73SVivien Didelot 2960fad09c73SVivien Didelot #ifdef CONFIG_NET_DSA_HWMON 2961fad09c73SVivien Didelot 2962fad09c73SVivien Didelot static int mv88e61xx_get_temp(struct dsa_switch *ds, int *temp) 2963fad09c73SVivien Didelot { 296404bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 29659c93829cSVivien Didelot u16 val; 2966fad09c73SVivien Didelot int ret; 2967fad09c73SVivien Didelot 2968fad09c73SVivien Didelot *temp = 0; 2969fad09c73SVivien Didelot 2970fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 2971fad09c73SVivien Didelot 29729c93829cSVivien Didelot ret = mv88e6xxx_phy_write(chip, 0x0, 0x16, 0x6); 2973fad09c73SVivien Didelot if (ret < 0) 2974fad09c73SVivien Didelot goto error; 2975fad09c73SVivien Didelot 2976fad09c73SVivien Didelot /* Enable temperature sensor */ 29779c93829cSVivien Didelot ret = mv88e6xxx_phy_read(chip, 0x0, 0x1a, &val); 2978fad09c73SVivien Didelot if (ret < 0) 2979fad09c73SVivien Didelot goto error; 2980fad09c73SVivien Didelot 29819c93829cSVivien Didelot ret = mv88e6xxx_phy_write(chip, 0x0, 0x1a, val | (1 << 5)); 2982fad09c73SVivien Didelot if (ret < 0) 2983fad09c73SVivien Didelot goto error; 2984fad09c73SVivien Didelot 2985fad09c73SVivien Didelot /* Wait for temperature to stabilize */ 2986fad09c73SVivien Didelot usleep_range(10000, 12000); 2987fad09c73SVivien Didelot 29889c93829cSVivien Didelot ret = mv88e6xxx_phy_read(chip, 0x0, 0x1a, &val); 29899c93829cSVivien Didelot if (ret < 0) 2990fad09c73SVivien Didelot goto error; 2991fad09c73SVivien Didelot 2992fad09c73SVivien Didelot /* Disable temperature sensor */ 29939c93829cSVivien Didelot ret = mv88e6xxx_phy_write(chip, 0x0, 0x1a, val & ~(1 << 5)); 2994fad09c73SVivien Didelot if (ret < 0) 2995fad09c73SVivien Didelot goto error; 2996fad09c73SVivien Didelot 2997fad09c73SVivien Didelot *temp = ((val & 0x1f) - 5) * 5; 2998fad09c73SVivien Didelot 2999fad09c73SVivien Didelot error: 30009c93829cSVivien Didelot mv88e6xxx_phy_write(chip, 0x0, 0x16, 0x0); 3001fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 3002fad09c73SVivien Didelot return ret; 3003fad09c73SVivien Didelot } 3004fad09c73SVivien Didelot 3005fad09c73SVivien Didelot static int mv88e63xx_get_temp(struct dsa_switch *ds, int *temp) 3006fad09c73SVivien Didelot { 300704bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 3008fad09c73SVivien Didelot int phy = mv88e6xxx_6320_family(chip) ? 3 : 0; 30099c93829cSVivien Didelot u16 val; 3010fad09c73SVivien Didelot int ret; 3011fad09c73SVivien Didelot 3012fad09c73SVivien Didelot *temp = 0; 3013fad09c73SVivien Didelot 30149c93829cSVivien Didelot mutex_lock(&chip->reg_lock); 30159c93829cSVivien Didelot ret = mv88e6xxx_phy_page_read(chip, phy, 6, 27, &val); 30169c93829cSVivien Didelot mutex_unlock(&chip->reg_lock); 3017fad09c73SVivien Didelot if (ret < 0) 3018fad09c73SVivien Didelot return ret; 3019fad09c73SVivien Didelot 30209c93829cSVivien Didelot *temp = (val & 0xff) - 25; 3021fad09c73SVivien Didelot 3022fad09c73SVivien Didelot return 0; 3023fad09c73SVivien Didelot } 3024fad09c73SVivien Didelot 3025fad09c73SVivien Didelot static int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp) 3026fad09c73SVivien Didelot { 302704bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 3028fad09c73SVivien Didelot 3029fad09c73SVivien Didelot if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP)) 3030fad09c73SVivien Didelot return -EOPNOTSUPP; 3031fad09c73SVivien Didelot 3032fad09c73SVivien Didelot if (mv88e6xxx_6320_family(chip) || mv88e6xxx_6352_family(chip)) 3033fad09c73SVivien Didelot return mv88e63xx_get_temp(ds, temp); 3034fad09c73SVivien Didelot 3035fad09c73SVivien Didelot return mv88e61xx_get_temp(ds, temp); 3036fad09c73SVivien Didelot } 3037fad09c73SVivien Didelot 3038fad09c73SVivien Didelot static int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp) 3039fad09c73SVivien Didelot { 304004bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 3041fad09c73SVivien Didelot int phy = mv88e6xxx_6320_family(chip) ? 3 : 0; 30429c93829cSVivien Didelot u16 val; 3043fad09c73SVivien Didelot int ret; 3044fad09c73SVivien Didelot 3045fad09c73SVivien Didelot if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT)) 3046fad09c73SVivien Didelot return -EOPNOTSUPP; 3047fad09c73SVivien Didelot 3048fad09c73SVivien Didelot *temp = 0; 3049fad09c73SVivien Didelot 30509c93829cSVivien Didelot mutex_lock(&chip->reg_lock); 30519c93829cSVivien Didelot ret = mv88e6xxx_phy_page_read(chip, phy, 6, 26, &val); 30529c93829cSVivien Didelot mutex_unlock(&chip->reg_lock); 3053fad09c73SVivien Didelot if (ret < 0) 3054fad09c73SVivien Didelot return ret; 3055fad09c73SVivien Didelot 30569c93829cSVivien Didelot *temp = (((val >> 8) & 0x1f) * 5) - 25; 3057fad09c73SVivien Didelot 3058fad09c73SVivien Didelot return 0; 3059fad09c73SVivien Didelot } 3060fad09c73SVivien Didelot 3061fad09c73SVivien Didelot static int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp) 3062fad09c73SVivien Didelot { 306304bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 3064fad09c73SVivien Didelot int phy = mv88e6xxx_6320_family(chip) ? 3 : 0; 30659c93829cSVivien Didelot u16 val; 30669c93829cSVivien Didelot int err; 3067fad09c73SVivien Didelot 3068fad09c73SVivien Didelot if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT)) 3069fad09c73SVivien Didelot return -EOPNOTSUPP; 3070fad09c73SVivien Didelot 30719c93829cSVivien Didelot mutex_lock(&chip->reg_lock); 30729c93829cSVivien Didelot err = mv88e6xxx_phy_page_read(chip, phy, 6, 26, &val); 30739c93829cSVivien Didelot if (err) 30749c93829cSVivien Didelot goto unlock; 3075fad09c73SVivien Didelot temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f); 30769c93829cSVivien Didelot err = mv88e6xxx_phy_page_write(chip, phy, 6, 26, 30779c93829cSVivien Didelot (val & 0xe0ff) | (temp << 8)); 30789c93829cSVivien Didelot unlock: 30799c93829cSVivien Didelot mutex_unlock(&chip->reg_lock); 30809c93829cSVivien Didelot 30819c93829cSVivien Didelot return err; 3082fad09c73SVivien Didelot } 3083fad09c73SVivien Didelot 3084fad09c73SVivien Didelot static int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm) 3085fad09c73SVivien Didelot { 308604bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 3087fad09c73SVivien Didelot int phy = mv88e6xxx_6320_family(chip) ? 3 : 0; 30889c93829cSVivien Didelot u16 val; 3089fad09c73SVivien Didelot int ret; 3090fad09c73SVivien Didelot 3091fad09c73SVivien Didelot if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT)) 3092fad09c73SVivien Didelot return -EOPNOTSUPP; 3093fad09c73SVivien Didelot 3094fad09c73SVivien Didelot *alarm = false; 3095fad09c73SVivien Didelot 30969c93829cSVivien Didelot mutex_lock(&chip->reg_lock); 30979c93829cSVivien Didelot ret = mv88e6xxx_phy_page_read(chip, phy, 6, 26, &val); 30989c93829cSVivien Didelot mutex_unlock(&chip->reg_lock); 3099fad09c73SVivien Didelot if (ret < 0) 3100fad09c73SVivien Didelot return ret; 3101fad09c73SVivien Didelot 31029c93829cSVivien Didelot *alarm = !!(val & 0x40); 3103fad09c73SVivien Didelot 3104fad09c73SVivien Didelot return 0; 3105fad09c73SVivien Didelot } 3106fad09c73SVivien Didelot #endif /* CONFIG_NET_DSA_HWMON */ 3107fad09c73SVivien Didelot 3108855b1932SVivien Didelot static int mv88e6xxx_get_eeprom_len(struct dsa_switch *ds) 3109855b1932SVivien Didelot { 311004bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 3111855b1932SVivien Didelot 3112855b1932SVivien Didelot return chip->eeprom_len; 3113855b1932SVivien Didelot } 3114855b1932SVivien Didelot 3115855b1932SVivien Didelot static int mv88e6xxx_get_eeprom(struct dsa_switch *ds, 3116855b1932SVivien Didelot struct ethtool_eeprom *eeprom, u8 *data) 3117855b1932SVivien Didelot { 311804bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 3119855b1932SVivien Didelot int err; 3120855b1932SVivien Didelot 3121ee4dc2e7SVivien Didelot if (!chip->info->ops->get_eeprom) 3122ee4dc2e7SVivien Didelot return -EOPNOTSUPP; 3123ee4dc2e7SVivien Didelot 3124855b1932SVivien Didelot mutex_lock(&chip->reg_lock); 3125ee4dc2e7SVivien Didelot err = chip->info->ops->get_eeprom(chip, eeprom, data); 3126855b1932SVivien Didelot mutex_unlock(&chip->reg_lock); 3127855b1932SVivien Didelot 3128855b1932SVivien Didelot if (err) 3129855b1932SVivien Didelot return err; 3130855b1932SVivien Didelot 3131855b1932SVivien Didelot eeprom->magic = 0xc3ec4951; 3132855b1932SVivien Didelot 3133855b1932SVivien Didelot return 0; 3134855b1932SVivien Didelot } 3135855b1932SVivien Didelot 3136855b1932SVivien Didelot static int mv88e6xxx_set_eeprom(struct dsa_switch *ds, 3137855b1932SVivien Didelot struct ethtool_eeprom *eeprom, u8 *data) 3138855b1932SVivien Didelot { 313904bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 3140855b1932SVivien Didelot int err; 3141855b1932SVivien Didelot 3142ee4dc2e7SVivien Didelot if (!chip->info->ops->set_eeprom) 3143ee4dc2e7SVivien Didelot return -EOPNOTSUPP; 3144ee4dc2e7SVivien Didelot 3145855b1932SVivien Didelot if (eeprom->magic != 0xc3ec4951) 3146855b1932SVivien Didelot return -EINVAL; 3147855b1932SVivien Didelot 3148855b1932SVivien Didelot mutex_lock(&chip->reg_lock); 3149ee4dc2e7SVivien Didelot err = chip->info->ops->set_eeprom(chip, eeprom, data); 3150855b1932SVivien Didelot mutex_unlock(&chip->reg_lock); 3151855b1932SVivien Didelot 3152855b1932SVivien Didelot return err; 3153855b1932SVivien Didelot } 3154855b1932SVivien Didelot 3155b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6085_ops = { 3156b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g1_set_switch_mac, 3157b3469dd8SVivien Didelot .phy_read = mv88e6xxx_phy_ppu_read, 3158b3469dd8SVivien Didelot .phy_write = mv88e6xxx_phy_ppu_write, 315908ef7f10SVivien Didelot .port_set_link = mv88e6xxx_port_set_link, 31607f1ae07bSVivien Didelot .port_set_duplex = mv88e6xxx_port_set_duplex, 316196a2b40cSVivien Didelot .port_set_speed = mv88e6185_port_set_speed, 3162b3469dd8SVivien Didelot }; 3163b3469dd8SVivien Didelot 3164b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6095_ops = { 3165b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g1_set_switch_mac, 3166b3469dd8SVivien Didelot .phy_read = mv88e6xxx_phy_ppu_read, 3167b3469dd8SVivien Didelot .phy_write = mv88e6xxx_phy_ppu_write, 316808ef7f10SVivien Didelot .port_set_link = mv88e6xxx_port_set_link, 31697f1ae07bSVivien Didelot .port_set_duplex = mv88e6xxx_port_set_duplex, 317096a2b40cSVivien Didelot .port_set_speed = mv88e6185_port_set_speed, 3171b3469dd8SVivien Didelot }; 3172b3469dd8SVivien Didelot 3173b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6123_ops = { 3174b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g2_set_switch_mac, 3175b3469dd8SVivien Didelot .phy_read = mv88e6xxx_read, 3176b3469dd8SVivien Didelot .phy_write = mv88e6xxx_write, 317708ef7f10SVivien Didelot .port_set_link = mv88e6xxx_port_set_link, 31787f1ae07bSVivien Didelot .port_set_duplex = mv88e6xxx_port_set_duplex, 317996a2b40cSVivien Didelot .port_set_speed = mv88e6185_port_set_speed, 3180b3469dd8SVivien Didelot }; 3181b3469dd8SVivien Didelot 3182b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6131_ops = { 3183b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g1_set_switch_mac, 3184b3469dd8SVivien Didelot .phy_read = mv88e6xxx_phy_ppu_read, 3185b3469dd8SVivien Didelot .phy_write = mv88e6xxx_phy_ppu_write, 318608ef7f10SVivien Didelot .port_set_link = mv88e6xxx_port_set_link, 31877f1ae07bSVivien Didelot .port_set_duplex = mv88e6xxx_port_set_duplex, 318896a2b40cSVivien Didelot .port_set_speed = mv88e6185_port_set_speed, 3189b3469dd8SVivien Didelot }; 3190b3469dd8SVivien Didelot 3191b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6161_ops = { 3192b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g2_set_switch_mac, 3193b3469dd8SVivien Didelot .phy_read = mv88e6xxx_read, 3194b3469dd8SVivien Didelot .phy_write = mv88e6xxx_write, 319508ef7f10SVivien Didelot .port_set_link = mv88e6xxx_port_set_link, 31967f1ae07bSVivien Didelot .port_set_duplex = mv88e6xxx_port_set_duplex, 319796a2b40cSVivien Didelot .port_set_speed = mv88e6185_port_set_speed, 3198b3469dd8SVivien Didelot }; 3199b3469dd8SVivien Didelot 3200b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6165_ops = { 3201b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g2_set_switch_mac, 3202b3469dd8SVivien Didelot .phy_read = mv88e6xxx_read, 3203b3469dd8SVivien Didelot .phy_write = mv88e6xxx_write, 320408ef7f10SVivien Didelot .port_set_link = mv88e6xxx_port_set_link, 32057f1ae07bSVivien Didelot .port_set_duplex = mv88e6xxx_port_set_duplex, 320696a2b40cSVivien Didelot .port_set_speed = mv88e6185_port_set_speed, 3207b3469dd8SVivien Didelot }; 3208b3469dd8SVivien Didelot 3209b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6171_ops = { 3210b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g2_set_switch_mac, 3211b3469dd8SVivien Didelot .phy_read = mv88e6xxx_g2_smi_phy_read, 3212b3469dd8SVivien Didelot .phy_write = mv88e6xxx_g2_smi_phy_write, 321308ef7f10SVivien Didelot .port_set_link = mv88e6xxx_port_set_link, 32147f1ae07bSVivien Didelot .port_set_duplex = mv88e6xxx_port_set_duplex, 321594d66ae6SAndrew Lunn .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, 321696a2b40cSVivien Didelot .port_set_speed = mv88e6185_port_set_speed, 3217b3469dd8SVivien Didelot }; 3218b3469dd8SVivien Didelot 3219b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6172_ops = { 3220ee4dc2e7SVivien Didelot .get_eeprom = mv88e6xxx_g2_get_eeprom16, 3221ee4dc2e7SVivien Didelot .set_eeprom = mv88e6xxx_g2_set_eeprom16, 3222b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g2_set_switch_mac, 3223b3469dd8SVivien Didelot .phy_read = mv88e6xxx_g2_smi_phy_read, 3224b3469dd8SVivien Didelot .phy_write = mv88e6xxx_g2_smi_phy_write, 322508ef7f10SVivien Didelot .port_set_link = mv88e6xxx_port_set_link, 32267f1ae07bSVivien Didelot .port_set_duplex = mv88e6xxx_port_set_duplex, 3227a0a0f622SVivien Didelot .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, 322896a2b40cSVivien Didelot .port_set_speed = mv88e6352_port_set_speed, 3229b3469dd8SVivien Didelot }; 3230b3469dd8SVivien Didelot 3231b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6175_ops = { 3232b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g2_set_switch_mac, 3233b3469dd8SVivien Didelot .phy_read = mv88e6xxx_g2_smi_phy_read, 3234b3469dd8SVivien Didelot .phy_write = mv88e6xxx_g2_smi_phy_write, 323508ef7f10SVivien Didelot .port_set_link = mv88e6xxx_port_set_link, 32367f1ae07bSVivien Didelot .port_set_duplex = mv88e6xxx_port_set_duplex, 323794d66ae6SAndrew Lunn .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, 323896a2b40cSVivien Didelot .port_set_speed = mv88e6185_port_set_speed, 3239b3469dd8SVivien Didelot }; 3240b3469dd8SVivien Didelot 3241b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6176_ops = { 3242ee4dc2e7SVivien Didelot .get_eeprom = mv88e6xxx_g2_get_eeprom16, 3243ee4dc2e7SVivien Didelot .set_eeprom = mv88e6xxx_g2_set_eeprom16, 3244b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g2_set_switch_mac, 3245b3469dd8SVivien Didelot .phy_read = mv88e6xxx_g2_smi_phy_read, 3246b3469dd8SVivien Didelot .phy_write = mv88e6xxx_g2_smi_phy_write, 324708ef7f10SVivien Didelot .port_set_link = mv88e6xxx_port_set_link, 32487f1ae07bSVivien Didelot .port_set_duplex = mv88e6xxx_port_set_duplex, 3249a0a0f622SVivien Didelot .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, 325096a2b40cSVivien Didelot .port_set_speed = mv88e6352_port_set_speed, 3251b3469dd8SVivien Didelot }; 3252b3469dd8SVivien Didelot 3253b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6185_ops = { 3254b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g1_set_switch_mac, 3255b3469dd8SVivien Didelot .phy_read = mv88e6xxx_phy_ppu_read, 3256b3469dd8SVivien Didelot .phy_write = mv88e6xxx_phy_ppu_write, 325708ef7f10SVivien Didelot .port_set_link = mv88e6xxx_port_set_link, 32587f1ae07bSVivien Didelot .port_set_duplex = mv88e6xxx_port_set_duplex, 325996a2b40cSVivien Didelot .port_set_speed = mv88e6185_port_set_speed, 3260b3469dd8SVivien Didelot }; 3261b3469dd8SVivien Didelot 3262b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6240_ops = { 3263ee4dc2e7SVivien Didelot .get_eeprom = mv88e6xxx_g2_get_eeprom16, 3264ee4dc2e7SVivien Didelot .set_eeprom = mv88e6xxx_g2_set_eeprom16, 3265b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g2_set_switch_mac, 3266b3469dd8SVivien Didelot .phy_read = mv88e6xxx_g2_smi_phy_read, 3267b3469dd8SVivien Didelot .phy_write = mv88e6xxx_g2_smi_phy_write, 326808ef7f10SVivien Didelot .port_set_link = mv88e6xxx_port_set_link, 32697f1ae07bSVivien Didelot .port_set_duplex = mv88e6xxx_port_set_duplex, 3270a0a0f622SVivien Didelot .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, 327196a2b40cSVivien Didelot .port_set_speed = mv88e6352_port_set_speed, 3272b3469dd8SVivien Didelot }; 3273b3469dd8SVivien Didelot 3274b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6320_ops = { 3275ee4dc2e7SVivien Didelot .get_eeprom = mv88e6xxx_g2_get_eeprom16, 3276ee4dc2e7SVivien Didelot .set_eeprom = mv88e6xxx_g2_set_eeprom16, 3277b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g2_set_switch_mac, 3278b3469dd8SVivien Didelot .phy_read = mv88e6xxx_g2_smi_phy_read, 3279b3469dd8SVivien Didelot .phy_write = mv88e6xxx_g2_smi_phy_write, 328008ef7f10SVivien Didelot .port_set_link = mv88e6xxx_port_set_link, 32817f1ae07bSVivien Didelot .port_set_duplex = mv88e6xxx_port_set_duplex, 328296a2b40cSVivien Didelot .port_set_speed = mv88e6185_port_set_speed, 3283b3469dd8SVivien Didelot }; 3284b3469dd8SVivien Didelot 3285b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6321_ops = { 3286ee4dc2e7SVivien Didelot .get_eeprom = mv88e6xxx_g2_get_eeprom16, 3287ee4dc2e7SVivien Didelot .set_eeprom = mv88e6xxx_g2_set_eeprom16, 3288b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g2_set_switch_mac, 3289b3469dd8SVivien Didelot .phy_read = mv88e6xxx_g2_smi_phy_read, 3290b3469dd8SVivien Didelot .phy_write = mv88e6xxx_g2_smi_phy_write, 329108ef7f10SVivien Didelot .port_set_link = mv88e6xxx_port_set_link, 32927f1ae07bSVivien Didelot .port_set_duplex = mv88e6xxx_port_set_duplex, 329396a2b40cSVivien Didelot .port_set_speed = mv88e6185_port_set_speed, 3294b3469dd8SVivien Didelot }; 3295b3469dd8SVivien Didelot 3296b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6350_ops = { 3297b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g2_set_switch_mac, 3298b3469dd8SVivien Didelot .phy_read = mv88e6xxx_g2_smi_phy_read, 3299b3469dd8SVivien Didelot .phy_write = mv88e6xxx_g2_smi_phy_write, 330008ef7f10SVivien Didelot .port_set_link = mv88e6xxx_port_set_link, 33017f1ae07bSVivien Didelot .port_set_duplex = mv88e6xxx_port_set_duplex, 330294d66ae6SAndrew Lunn .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, 330396a2b40cSVivien Didelot .port_set_speed = mv88e6185_port_set_speed, 3304b3469dd8SVivien Didelot }; 3305b3469dd8SVivien Didelot 3306b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6351_ops = { 3307b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g2_set_switch_mac, 3308b3469dd8SVivien Didelot .phy_read = mv88e6xxx_g2_smi_phy_read, 3309b3469dd8SVivien Didelot .phy_write = mv88e6xxx_g2_smi_phy_write, 331008ef7f10SVivien Didelot .port_set_link = mv88e6xxx_port_set_link, 33117f1ae07bSVivien Didelot .port_set_duplex = mv88e6xxx_port_set_duplex, 331294d66ae6SAndrew Lunn .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, 331396a2b40cSVivien Didelot .port_set_speed = mv88e6185_port_set_speed, 3314b3469dd8SVivien Didelot }; 3315b3469dd8SVivien Didelot 3316b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6352_ops = { 3317ee4dc2e7SVivien Didelot .get_eeprom = mv88e6xxx_g2_get_eeprom16, 3318ee4dc2e7SVivien Didelot .set_eeprom = mv88e6xxx_g2_set_eeprom16, 3319b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g2_set_switch_mac, 3320b3469dd8SVivien Didelot .phy_read = mv88e6xxx_g2_smi_phy_read, 3321b3469dd8SVivien Didelot .phy_write = mv88e6xxx_g2_smi_phy_write, 332208ef7f10SVivien Didelot .port_set_link = mv88e6xxx_port_set_link, 33237f1ae07bSVivien Didelot .port_set_duplex = mv88e6xxx_port_set_duplex, 3324a0a0f622SVivien Didelot .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, 332596a2b40cSVivien Didelot .port_set_speed = mv88e6352_port_set_speed, 3326b3469dd8SVivien Didelot }; 3327b3469dd8SVivien Didelot 3328fad09c73SVivien Didelot static const struct mv88e6xxx_info mv88e6xxx_table[] = { 3329fad09c73SVivien Didelot [MV88E6085] = { 3330fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6085, 3331fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6097, 3332fad09c73SVivien Didelot .name = "Marvell 88E6085", 3333fad09c73SVivien Didelot .num_databases = 4096, 3334fad09c73SVivien Didelot .num_ports = 10, 3335fad09c73SVivien Didelot .port_base_addr = 0x10, 3336a935c052SVivien Didelot .global1_addr = 0x1b, 3337acddbd21SVivien Didelot .age_time_coeff = 15000, 3338dc30c35bSAndrew Lunn .g1_irqs = 8, 3339fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6097, 3340b3469dd8SVivien Didelot .ops = &mv88e6085_ops, 3341fad09c73SVivien Didelot }, 3342fad09c73SVivien Didelot 3343fad09c73SVivien Didelot [MV88E6095] = { 3344fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6095, 3345fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6095, 3346fad09c73SVivien Didelot .name = "Marvell 88E6095/88E6095F", 3347fad09c73SVivien Didelot .num_databases = 256, 3348fad09c73SVivien Didelot .num_ports = 11, 3349fad09c73SVivien Didelot .port_base_addr = 0x10, 3350a935c052SVivien Didelot .global1_addr = 0x1b, 3351acddbd21SVivien Didelot .age_time_coeff = 15000, 3352dc30c35bSAndrew Lunn .g1_irqs = 8, 3353fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6095, 3354b3469dd8SVivien Didelot .ops = &mv88e6095_ops, 3355fad09c73SVivien Didelot }, 3356fad09c73SVivien Didelot 3357fad09c73SVivien Didelot [MV88E6123] = { 3358fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6123, 3359fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6165, 3360fad09c73SVivien Didelot .name = "Marvell 88E6123", 3361fad09c73SVivien Didelot .num_databases = 4096, 3362fad09c73SVivien Didelot .num_ports = 3, 3363fad09c73SVivien Didelot .port_base_addr = 0x10, 3364a935c052SVivien Didelot .global1_addr = 0x1b, 3365acddbd21SVivien Didelot .age_time_coeff = 15000, 3366dc30c35bSAndrew Lunn .g1_irqs = 9, 3367fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6165, 3368b3469dd8SVivien Didelot .ops = &mv88e6123_ops, 3369fad09c73SVivien Didelot }, 3370fad09c73SVivien Didelot 3371fad09c73SVivien Didelot [MV88E6131] = { 3372fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6131, 3373fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6185, 3374fad09c73SVivien Didelot .name = "Marvell 88E6131", 3375fad09c73SVivien Didelot .num_databases = 256, 3376fad09c73SVivien Didelot .num_ports = 8, 3377fad09c73SVivien Didelot .port_base_addr = 0x10, 3378a935c052SVivien Didelot .global1_addr = 0x1b, 3379acddbd21SVivien Didelot .age_time_coeff = 15000, 3380dc30c35bSAndrew Lunn .g1_irqs = 9, 3381fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6185, 3382b3469dd8SVivien Didelot .ops = &mv88e6131_ops, 3383fad09c73SVivien Didelot }, 3384fad09c73SVivien Didelot 3385fad09c73SVivien Didelot [MV88E6161] = { 3386fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6161, 3387fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6165, 3388fad09c73SVivien Didelot .name = "Marvell 88E6161", 3389fad09c73SVivien Didelot .num_databases = 4096, 3390fad09c73SVivien Didelot .num_ports = 6, 3391fad09c73SVivien Didelot .port_base_addr = 0x10, 3392a935c052SVivien Didelot .global1_addr = 0x1b, 3393acddbd21SVivien Didelot .age_time_coeff = 15000, 3394dc30c35bSAndrew Lunn .g1_irqs = 9, 3395fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6165, 3396b3469dd8SVivien Didelot .ops = &mv88e6161_ops, 3397fad09c73SVivien Didelot }, 3398fad09c73SVivien Didelot 3399fad09c73SVivien Didelot [MV88E6165] = { 3400fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6165, 3401fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6165, 3402fad09c73SVivien Didelot .name = "Marvell 88E6165", 3403fad09c73SVivien Didelot .num_databases = 4096, 3404fad09c73SVivien Didelot .num_ports = 6, 3405fad09c73SVivien Didelot .port_base_addr = 0x10, 3406a935c052SVivien Didelot .global1_addr = 0x1b, 3407acddbd21SVivien Didelot .age_time_coeff = 15000, 3408dc30c35bSAndrew Lunn .g1_irqs = 9, 3409fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6165, 3410b3469dd8SVivien Didelot .ops = &mv88e6165_ops, 3411fad09c73SVivien Didelot }, 3412fad09c73SVivien Didelot 3413fad09c73SVivien Didelot [MV88E6171] = { 3414fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6171, 3415fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6351, 3416fad09c73SVivien Didelot .name = "Marvell 88E6171", 3417fad09c73SVivien Didelot .num_databases = 4096, 3418fad09c73SVivien Didelot .num_ports = 7, 3419fad09c73SVivien Didelot .port_base_addr = 0x10, 3420a935c052SVivien Didelot .global1_addr = 0x1b, 3421acddbd21SVivien Didelot .age_time_coeff = 15000, 3422dc30c35bSAndrew Lunn .g1_irqs = 9, 3423fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6351, 3424b3469dd8SVivien Didelot .ops = &mv88e6171_ops, 3425fad09c73SVivien Didelot }, 3426fad09c73SVivien Didelot 3427fad09c73SVivien Didelot [MV88E6172] = { 3428fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6172, 3429fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6352, 3430fad09c73SVivien Didelot .name = "Marvell 88E6172", 3431fad09c73SVivien Didelot .num_databases = 4096, 3432fad09c73SVivien Didelot .num_ports = 7, 3433fad09c73SVivien Didelot .port_base_addr = 0x10, 3434a935c052SVivien Didelot .global1_addr = 0x1b, 3435acddbd21SVivien Didelot .age_time_coeff = 15000, 3436dc30c35bSAndrew Lunn .g1_irqs = 9, 3437fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6352, 3438b3469dd8SVivien Didelot .ops = &mv88e6172_ops, 3439fad09c73SVivien Didelot }, 3440fad09c73SVivien Didelot 3441fad09c73SVivien Didelot [MV88E6175] = { 3442fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6175, 3443fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6351, 3444fad09c73SVivien Didelot .name = "Marvell 88E6175", 3445fad09c73SVivien Didelot .num_databases = 4096, 3446fad09c73SVivien Didelot .num_ports = 7, 3447fad09c73SVivien Didelot .port_base_addr = 0x10, 3448a935c052SVivien Didelot .global1_addr = 0x1b, 3449acddbd21SVivien Didelot .age_time_coeff = 15000, 3450dc30c35bSAndrew Lunn .g1_irqs = 9, 3451fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6351, 3452b3469dd8SVivien Didelot .ops = &mv88e6175_ops, 3453fad09c73SVivien Didelot }, 3454fad09c73SVivien Didelot 3455fad09c73SVivien Didelot [MV88E6176] = { 3456fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6176, 3457fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6352, 3458fad09c73SVivien Didelot .name = "Marvell 88E6176", 3459fad09c73SVivien Didelot .num_databases = 4096, 3460fad09c73SVivien Didelot .num_ports = 7, 3461fad09c73SVivien Didelot .port_base_addr = 0x10, 3462a935c052SVivien Didelot .global1_addr = 0x1b, 3463acddbd21SVivien Didelot .age_time_coeff = 15000, 3464dc30c35bSAndrew Lunn .g1_irqs = 9, 3465fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6352, 3466b3469dd8SVivien Didelot .ops = &mv88e6176_ops, 3467fad09c73SVivien Didelot }, 3468fad09c73SVivien Didelot 3469fad09c73SVivien Didelot [MV88E6185] = { 3470fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6185, 3471fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6185, 3472fad09c73SVivien Didelot .name = "Marvell 88E6185", 3473fad09c73SVivien Didelot .num_databases = 256, 3474fad09c73SVivien Didelot .num_ports = 10, 3475fad09c73SVivien Didelot .port_base_addr = 0x10, 3476a935c052SVivien Didelot .global1_addr = 0x1b, 3477acddbd21SVivien Didelot .age_time_coeff = 15000, 3478dc30c35bSAndrew Lunn .g1_irqs = 8, 3479fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6185, 3480b3469dd8SVivien Didelot .ops = &mv88e6185_ops, 3481fad09c73SVivien Didelot }, 3482fad09c73SVivien Didelot 3483fad09c73SVivien Didelot [MV88E6240] = { 3484fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6240, 3485fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6352, 3486fad09c73SVivien Didelot .name = "Marvell 88E6240", 3487fad09c73SVivien Didelot .num_databases = 4096, 3488fad09c73SVivien Didelot .num_ports = 7, 3489fad09c73SVivien Didelot .port_base_addr = 0x10, 3490a935c052SVivien Didelot .global1_addr = 0x1b, 3491acddbd21SVivien Didelot .age_time_coeff = 15000, 3492dc30c35bSAndrew Lunn .g1_irqs = 9, 3493fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6352, 3494b3469dd8SVivien Didelot .ops = &mv88e6240_ops, 3495fad09c73SVivien Didelot }, 3496fad09c73SVivien Didelot 3497fad09c73SVivien Didelot [MV88E6320] = { 3498fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6320, 3499fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6320, 3500fad09c73SVivien Didelot .name = "Marvell 88E6320", 3501fad09c73SVivien Didelot .num_databases = 4096, 3502fad09c73SVivien Didelot .num_ports = 7, 3503fad09c73SVivien Didelot .port_base_addr = 0x10, 3504a935c052SVivien Didelot .global1_addr = 0x1b, 3505acddbd21SVivien Didelot .age_time_coeff = 15000, 3506dc30c35bSAndrew Lunn .g1_irqs = 8, 3507fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6320, 3508b3469dd8SVivien Didelot .ops = &mv88e6320_ops, 3509fad09c73SVivien Didelot }, 3510fad09c73SVivien Didelot 3511fad09c73SVivien Didelot [MV88E6321] = { 3512fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6321, 3513fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6320, 3514fad09c73SVivien Didelot .name = "Marvell 88E6321", 3515fad09c73SVivien Didelot .num_databases = 4096, 3516fad09c73SVivien Didelot .num_ports = 7, 3517fad09c73SVivien Didelot .port_base_addr = 0x10, 3518a935c052SVivien Didelot .global1_addr = 0x1b, 3519acddbd21SVivien Didelot .age_time_coeff = 15000, 3520dc30c35bSAndrew Lunn .g1_irqs = 8, 3521fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6320, 3522b3469dd8SVivien Didelot .ops = &mv88e6321_ops, 3523fad09c73SVivien Didelot }, 3524fad09c73SVivien Didelot 3525fad09c73SVivien Didelot [MV88E6350] = { 3526fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6350, 3527fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6351, 3528fad09c73SVivien Didelot .name = "Marvell 88E6350", 3529fad09c73SVivien Didelot .num_databases = 4096, 3530fad09c73SVivien Didelot .num_ports = 7, 3531fad09c73SVivien Didelot .port_base_addr = 0x10, 3532a935c052SVivien Didelot .global1_addr = 0x1b, 3533acddbd21SVivien Didelot .age_time_coeff = 15000, 3534dc30c35bSAndrew Lunn .g1_irqs = 9, 3535fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6351, 3536b3469dd8SVivien Didelot .ops = &mv88e6350_ops, 3537fad09c73SVivien Didelot }, 3538fad09c73SVivien Didelot 3539fad09c73SVivien Didelot [MV88E6351] = { 3540fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6351, 3541fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6351, 3542fad09c73SVivien Didelot .name = "Marvell 88E6351", 3543fad09c73SVivien Didelot .num_databases = 4096, 3544fad09c73SVivien Didelot .num_ports = 7, 3545fad09c73SVivien Didelot .port_base_addr = 0x10, 3546a935c052SVivien Didelot .global1_addr = 0x1b, 3547acddbd21SVivien Didelot .age_time_coeff = 15000, 3548dc30c35bSAndrew Lunn .g1_irqs = 9, 3549fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6351, 3550b3469dd8SVivien Didelot .ops = &mv88e6351_ops, 3551fad09c73SVivien Didelot }, 3552fad09c73SVivien Didelot 3553fad09c73SVivien Didelot [MV88E6352] = { 3554fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6352, 3555fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6352, 3556fad09c73SVivien Didelot .name = "Marvell 88E6352", 3557fad09c73SVivien Didelot .num_databases = 4096, 3558fad09c73SVivien Didelot .num_ports = 7, 3559fad09c73SVivien Didelot .port_base_addr = 0x10, 3560a935c052SVivien Didelot .global1_addr = 0x1b, 3561acddbd21SVivien Didelot .age_time_coeff = 15000, 3562dc30c35bSAndrew Lunn .g1_irqs = 9, 3563fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6352, 3564b3469dd8SVivien Didelot .ops = &mv88e6352_ops, 3565fad09c73SVivien Didelot }, 3566fad09c73SVivien Didelot }; 3567fad09c73SVivien Didelot 3568fad09c73SVivien Didelot static const struct mv88e6xxx_info *mv88e6xxx_lookup_info(unsigned int prod_num) 3569fad09c73SVivien Didelot { 3570fad09c73SVivien Didelot int i; 3571fad09c73SVivien Didelot 3572fad09c73SVivien Didelot for (i = 0; i < ARRAY_SIZE(mv88e6xxx_table); ++i) 3573fad09c73SVivien Didelot if (mv88e6xxx_table[i].prod_num == prod_num) 3574fad09c73SVivien Didelot return &mv88e6xxx_table[i]; 3575fad09c73SVivien Didelot 3576fad09c73SVivien Didelot return NULL; 3577fad09c73SVivien Didelot } 3578fad09c73SVivien Didelot 3579fad09c73SVivien Didelot static int mv88e6xxx_detect(struct mv88e6xxx_chip *chip) 3580fad09c73SVivien Didelot { 3581fad09c73SVivien Didelot const struct mv88e6xxx_info *info; 35828f6345b2SVivien Didelot unsigned int prod_num, rev; 35838f6345b2SVivien Didelot u16 id; 35848f6345b2SVivien Didelot int err; 3585fad09c73SVivien Didelot 35868f6345b2SVivien Didelot mutex_lock(&chip->reg_lock); 35878f6345b2SVivien Didelot err = mv88e6xxx_port_read(chip, 0, PORT_SWITCH_ID, &id); 35888f6345b2SVivien Didelot mutex_unlock(&chip->reg_lock); 35898f6345b2SVivien Didelot if (err) 35908f6345b2SVivien Didelot return err; 3591fad09c73SVivien Didelot 3592fad09c73SVivien Didelot prod_num = (id & 0xfff0) >> 4; 3593fad09c73SVivien Didelot rev = id & 0x000f; 3594fad09c73SVivien Didelot 3595fad09c73SVivien Didelot info = mv88e6xxx_lookup_info(prod_num); 3596fad09c73SVivien Didelot if (!info) 3597fad09c73SVivien Didelot return -ENODEV; 3598fad09c73SVivien Didelot 3599fad09c73SVivien Didelot /* Update the compatible info with the probed one */ 3600fad09c73SVivien Didelot chip->info = info; 3601fad09c73SVivien Didelot 3602ca070c10SVivien Didelot err = mv88e6xxx_g2_require(chip); 3603ca070c10SVivien Didelot if (err) 3604ca070c10SVivien Didelot return err; 3605ca070c10SVivien Didelot 3606fad09c73SVivien Didelot dev_info(chip->dev, "switch 0x%x detected: %s, revision %u\n", 3607fad09c73SVivien Didelot chip->info->prod_num, chip->info->name, rev); 3608fad09c73SVivien Didelot 3609fad09c73SVivien Didelot return 0; 3610fad09c73SVivien Didelot } 3611fad09c73SVivien Didelot 3612fad09c73SVivien Didelot static struct mv88e6xxx_chip *mv88e6xxx_alloc_chip(struct device *dev) 3613fad09c73SVivien Didelot { 3614fad09c73SVivien Didelot struct mv88e6xxx_chip *chip; 3615fad09c73SVivien Didelot 3616fad09c73SVivien Didelot chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); 3617fad09c73SVivien Didelot if (!chip) 3618fad09c73SVivien Didelot return NULL; 3619fad09c73SVivien Didelot 3620fad09c73SVivien Didelot chip->dev = dev; 3621fad09c73SVivien Didelot 3622fad09c73SVivien Didelot mutex_init(&chip->reg_lock); 3623fad09c73SVivien Didelot 3624fad09c73SVivien Didelot return chip; 3625fad09c73SVivien Didelot } 3626fad09c73SVivien Didelot 3627e57e5e77SVivien Didelot static void mv88e6xxx_phy_init(struct mv88e6xxx_chip *chip) 3628e57e5e77SVivien Didelot { 3629b3469dd8SVivien Didelot if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU)) 3630e57e5e77SVivien Didelot mv88e6xxx_ppu_state_init(chip); 3631e57e5e77SVivien Didelot } 3632e57e5e77SVivien Didelot 3633930188ceSAndrew Lunn static void mv88e6xxx_phy_destroy(struct mv88e6xxx_chip *chip) 3634930188ceSAndrew Lunn { 3635b3469dd8SVivien Didelot if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU)) 3636930188ceSAndrew Lunn mv88e6xxx_ppu_state_destroy(chip); 3637930188ceSAndrew Lunn } 3638930188ceSAndrew Lunn 3639fad09c73SVivien Didelot static int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip, 3640fad09c73SVivien Didelot struct mii_bus *bus, int sw_addr) 3641fad09c73SVivien Didelot { 3642fad09c73SVivien Didelot /* ADDR[0] pin is unavailable externally and considered zero */ 3643fad09c73SVivien Didelot if (sw_addr & 0x1) 3644fad09c73SVivien Didelot return -EINVAL; 3645fad09c73SVivien Didelot 3646fad09c73SVivien Didelot if (sw_addr == 0) 3647fad09c73SVivien Didelot chip->smi_ops = &mv88e6xxx_smi_single_chip_ops; 3648a0ffff24SVivien Didelot else if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_MULTI_CHIP)) 3649fad09c73SVivien Didelot chip->smi_ops = &mv88e6xxx_smi_multi_chip_ops; 3650fad09c73SVivien Didelot else 3651fad09c73SVivien Didelot return -EINVAL; 3652fad09c73SVivien Didelot 3653fad09c73SVivien Didelot chip->bus = bus; 3654fad09c73SVivien Didelot chip->sw_addr = sw_addr; 3655fad09c73SVivien Didelot 3656fad09c73SVivien Didelot return 0; 3657fad09c73SVivien Didelot } 3658fad09c73SVivien Didelot 36597b314362SAndrew Lunn static enum dsa_tag_protocol mv88e6xxx_get_tag_protocol(struct dsa_switch *ds) 36607b314362SAndrew Lunn { 366104bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 36622bbb33beSAndrew Lunn 36632bbb33beSAndrew Lunn if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EDSA)) 36647b314362SAndrew Lunn return DSA_TAG_PROTO_EDSA; 36652bbb33beSAndrew Lunn 36662bbb33beSAndrew Lunn return DSA_TAG_PROTO_DSA; 36677b314362SAndrew Lunn } 36687b314362SAndrew Lunn 3669fad09c73SVivien Didelot static const char *mv88e6xxx_drv_probe(struct device *dsa_dev, 3670fad09c73SVivien Didelot struct device *host_dev, int sw_addr, 3671fad09c73SVivien Didelot void **priv) 3672fad09c73SVivien Didelot { 3673fad09c73SVivien Didelot struct mv88e6xxx_chip *chip; 3674fad09c73SVivien Didelot struct mii_bus *bus; 3675fad09c73SVivien Didelot int err; 3676fad09c73SVivien Didelot 3677fad09c73SVivien Didelot bus = dsa_host_dev_to_mii_bus(host_dev); 3678fad09c73SVivien Didelot if (!bus) 3679fad09c73SVivien Didelot return NULL; 3680fad09c73SVivien Didelot 3681fad09c73SVivien Didelot chip = mv88e6xxx_alloc_chip(dsa_dev); 3682fad09c73SVivien Didelot if (!chip) 3683fad09c73SVivien Didelot return NULL; 3684fad09c73SVivien Didelot 3685fad09c73SVivien Didelot /* Legacy SMI probing will only support chips similar to 88E6085 */ 3686fad09c73SVivien Didelot chip->info = &mv88e6xxx_table[MV88E6085]; 3687fad09c73SVivien Didelot 3688fad09c73SVivien Didelot err = mv88e6xxx_smi_init(chip, bus, sw_addr); 3689fad09c73SVivien Didelot if (err) 3690fad09c73SVivien Didelot goto free; 3691fad09c73SVivien Didelot 3692fad09c73SVivien Didelot err = mv88e6xxx_detect(chip); 3693fad09c73SVivien Didelot if (err) 3694fad09c73SVivien Didelot goto free; 3695fad09c73SVivien Didelot 3696dc30c35bSAndrew Lunn mutex_lock(&chip->reg_lock); 3697dc30c35bSAndrew Lunn err = mv88e6xxx_switch_reset(chip); 3698dc30c35bSAndrew Lunn mutex_unlock(&chip->reg_lock); 3699dc30c35bSAndrew Lunn if (err) 3700dc30c35bSAndrew Lunn goto free; 3701dc30c35bSAndrew Lunn 3702e57e5e77SVivien Didelot mv88e6xxx_phy_init(chip); 3703e57e5e77SVivien Didelot 3704fad09c73SVivien Didelot err = mv88e6xxx_mdio_register(chip, NULL); 3705fad09c73SVivien Didelot if (err) 3706fad09c73SVivien Didelot goto free; 3707fad09c73SVivien Didelot 3708fad09c73SVivien Didelot *priv = chip; 3709fad09c73SVivien Didelot 3710fad09c73SVivien Didelot return chip->info->name; 3711fad09c73SVivien Didelot free: 3712fad09c73SVivien Didelot devm_kfree(dsa_dev, chip); 3713fad09c73SVivien Didelot 3714fad09c73SVivien Didelot return NULL; 3715fad09c73SVivien Didelot } 3716fad09c73SVivien Didelot 37177df8fbddSVivien Didelot static int mv88e6xxx_port_mdb_prepare(struct dsa_switch *ds, int port, 37187df8fbddSVivien Didelot const struct switchdev_obj_port_mdb *mdb, 37197df8fbddSVivien Didelot struct switchdev_trans *trans) 37207df8fbddSVivien Didelot { 37217df8fbddSVivien Didelot /* We don't need any dynamic resource from the kernel (yet), 37227df8fbddSVivien Didelot * so skip the prepare phase. 37237df8fbddSVivien Didelot */ 37247df8fbddSVivien Didelot 37257df8fbddSVivien Didelot return 0; 37267df8fbddSVivien Didelot } 37277df8fbddSVivien Didelot 37287df8fbddSVivien Didelot static void mv88e6xxx_port_mdb_add(struct dsa_switch *ds, int port, 37297df8fbddSVivien Didelot const struct switchdev_obj_port_mdb *mdb, 37307df8fbddSVivien Didelot struct switchdev_trans *trans) 37317df8fbddSVivien Didelot { 373204bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 37337df8fbddSVivien Didelot 37347df8fbddSVivien Didelot mutex_lock(&chip->reg_lock); 37357df8fbddSVivien Didelot if (mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid, 37367df8fbddSVivien Didelot GLOBAL_ATU_DATA_STATE_MC_STATIC)) 37377df8fbddSVivien Didelot netdev_err(ds->ports[port].netdev, "failed to load multicast MAC address\n"); 37387df8fbddSVivien Didelot mutex_unlock(&chip->reg_lock); 37397df8fbddSVivien Didelot } 37407df8fbddSVivien Didelot 37417df8fbddSVivien Didelot static int mv88e6xxx_port_mdb_del(struct dsa_switch *ds, int port, 37427df8fbddSVivien Didelot const struct switchdev_obj_port_mdb *mdb) 37437df8fbddSVivien Didelot { 374404bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 37457df8fbddSVivien Didelot int err; 37467df8fbddSVivien Didelot 37477df8fbddSVivien Didelot mutex_lock(&chip->reg_lock); 37487df8fbddSVivien Didelot err = mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid, 37497df8fbddSVivien Didelot GLOBAL_ATU_DATA_STATE_UNUSED); 37507df8fbddSVivien Didelot mutex_unlock(&chip->reg_lock); 37517df8fbddSVivien Didelot 37527df8fbddSVivien Didelot return err; 37537df8fbddSVivien Didelot } 37547df8fbddSVivien Didelot 37557df8fbddSVivien Didelot static int mv88e6xxx_port_mdb_dump(struct dsa_switch *ds, int port, 37567df8fbddSVivien Didelot struct switchdev_obj_port_mdb *mdb, 37577df8fbddSVivien Didelot int (*cb)(struct switchdev_obj *obj)) 37587df8fbddSVivien Didelot { 375904bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 37607df8fbddSVivien Didelot int err; 37617df8fbddSVivien Didelot 37627df8fbddSVivien Didelot mutex_lock(&chip->reg_lock); 37637df8fbddSVivien Didelot err = mv88e6xxx_port_db_dump(chip, port, &mdb->obj, cb); 37647df8fbddSVivien Didelot mutex_unlock(&chip->reg_lock); 37657df8fbddSVivien Didelot 37667df8fbddSVivien Didelot return err; 37677df8fbddSVivien Didelot } 37687df8fbddSVivien Didelot 37699d490b4eSVivien Didelot static struct dsa_switch_ops mv88e6xxx_switch_ops = { 3770fad09c73SVivien Didelot .probe = mv88e6xxx_drv_probe, 37717b314362SAndrew Lunn .get_tag_protocol = mv88e6xxx_get_tag_protocol, 3772fad09c73SVivien Didelot .setup = mv88e6xxx_setup, 3773fad09c73SVivien Didelot .set_addr = mv88e6xxx_set_addr, 3774fad09c73SVivien Didelot .adjust_link = mv88e6xxx_adjust_link, 3775fad09c73SVivien Didelot .get_strings = mv88e6xxx_get_strings, 3776fad09c73SVivien Didelot .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, 3777fad09c73SVivien Didelot .get_sset_count = mv88e6xxx_get_sset_count, 3778fad09c73SVivien Didelot .set_eee = mv88e6xxx_set_eee, 3779fad09c73SVivien Didelot .get_eee = mv88e6xxx_get_eee, 3780fad09c73SVivien Didelot #ifdef CONFIG_NET_DSA_HWMON 3781fad09c73SVivien Didelot .get_temp = mv88e6xxx_get_temp, 3782fad09c73SVivien Didelot .get_temp_limit = mv88e6xxx_get_temp_limit, 3783fad09c73SVivien Didelot .set_temp_limit = mv88e6xxx_set_temp_limit, 3784fad09c73SVivien Didelot .get_temp_alarm = mv88e6xxx_get_temp_alarm, 3785fad09c73SVivien Didelot #endif 3786fad09c73SVivien Didelot .get_eeprom_len = mv88e6xxx_get_eeprom_len, 3787fad09c73SVivien Didelot .get_eeprom = mv88e6xxx_get_eeprom, 3788fad09c73SVivien Didelot .set_eeprom = mv88e6xxx_set_eeprom, 3789fad09c73SVivien Didelot .get_regs_len = mv88e6xxx_get_regs_len, 3790fad09c73SVivien Didelot .get_regs = mv88e6xxx_get_regs, 37912cfcd964SVivien Didelot .set_ageing_time = mv88e6xxx_set_ageing_time, 3792fad09c73SVivien Didelot .port_bridge_join = mv88e6xxx_port_bridge_join, 3793fad09c73SVivien Didelot .port_bridge_leave = mv88e6xxx_port_bridge_leave, 3794fad09c73SVivien Didelot .port_stp_state_set = mv88e6xxx_port_stp_state_set, 3795749efcb8SVivien Didelot .port_fast_age = mv88e6xxx_port_fast_age, 3796fad09c73SVivien Didelot .port_vlan_filtering = mv88e6xxx_port_vlan_filtering, 3797fad09c73SVivien Didelot .port_vlan_prepare = mv88e6xxx_port_vlan_prepare, 3798fad09c73SVivien Didelot .port_vlan_add = mv88e6xxx_port_vlan_add, 3799fad09c73SVivien Didelot .port_vlan_del = mv88e6xxx_port_vlan_del, 3800fad09c73SVivien Didelot .port_vlan_dump = mv88e6xxx_port_vlan_dump, 3801fad09c73SVivien Didelot .port_fdb_prepare = mv88e6xxx_port_fdb_prepare, 3802fad09c73SVivien Didelot .port_fdb_add = mv88e6xxx_port_fdb_add, 3803fad09c73SVivien Didelot .port_fdb_del = mv88e6xxx_port_fdb_del, 3804fad09c73SVivien Didelot .port_fdb_dump = mv88e6xxx_port_fdb_dump, 38057df8fbddSVivien Didelot .port_mdb_prepare = mv88e6xxx_port_mdb_prepare, 38067df8fbddSVivien Didelot .port_mdb_add = mv88e6xxx_port_mdb_add, 38077df8fbddSVivien Didelot .port_mdb_del = mv88e6xxx_port_mdb_del, 38087df8fbddSVivien Didelot .port_mdb_dump = mv88e6xxx_port_mdb_dump, 3809fad09c73SVivien Didelot }; 3810fad09c73SVivien Didelot 3811fad09c73SVivien Didelot static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip, 3812fad09c73SVivien Didelot struct device_node *np) 3813fad09c73SVivien Didelot { 3814fad09c73SVivien Didelot struct device *dev = chip->dev; 3815fad09c73SVivien Didelot struct dsa_switch *ds; 3816fad09c73SVivien Didelot 3817fad09c73SVivien Didelot ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL); 3818fad09c73SVivien Didelot if (!ds) 3819fad09c73SVivien Didelot return -ENOMEM; 3820fad09c73SVivien Didelot 3821fad09c73SVivien Didelot ds->dev = dev; 3822fad09c73SVivien Didelot ds->priv = chip; 38239d490b4eSVivien Didelot ds->ops = &mv88e6xxx_switch_ops; 3824fad09c73SVivien Didelot 3825fad09c73SVivien Didelot dev_set_drvdata(dev, ds); 3826fad09c73SVivien Didelot 3827fad09c73SVivien Didelot return dsa_register_switch(ds, np); 3828fad09c73SVivien Didelot } 3829fad09c73SVivien Didelot 3830fad09c73SVivien Didelot static void mv88e6xxx_unregister_switch(struct mv88e6xxx_chip *chip) 3831fad09c73SVivien Didelot { 3832fad09c73SVivien Didelot dsa_unregister_switch(chip->ds); 3833fad09c73SVivien Didelot } 3834fad09c73SVivien Didelot 3835fad09c73SVivien Didelot static int mv88e6xxx_probe(struct mdio_device *mdiodev) 3836fad09c73SVivien Didelot { 3837fad09c73SVivien Didelot struct device *dev = &mdiodev->dev; 3838fad09c73SVivien Didelot struct device_node *np = dev->of_node; 3839fad09c73SVivien Didelot const struct mv88e6xxx_info *compat_info; 3840fad09c73SVivien Didelot struct mv88e6xxx_chip *chip; 3841fad09c73SVivien Didelot u32 eeprom_len; 3842fad09c73SVivien Didelot int err; 3843fad09c73SVivien Didelot 3844fad09c73SVivien Didelot compat_info = of_device_get_match_data(dev); 3845fad09c73SVivien Didelot if (!compat_info) 3846fad09c73SVivien Didelot return -EINVAL; 3847fad09c73SVivien Didelot 3848fad09c73SVivien Didelot chip = mv88e6xxx_alloc_chip(dev); 3849fad09c73SVivien Didelot if (!chip) 3850fad09c73SVivien Didelot return -ENOMEM; 3851fad09c73SVivien Didelot 3852fad09c73SVivien Didelot chip->info = compat_info; 3853fad09c73SVivien Didelot 3854fad09c73SVivien Didelot err = mv88e6xxx_smi_init(chip, mdiodev->bus, mdiodev->addr); 3855fad09c73SVivien Didelot if (err) 3856fad09c73SVivien Didelot return err; 3857fad09c73SVivien Didelot 3858fad09c73SVivien Didelot err = mv88e6xxx_detect(chip); 3859fad09c73SVivien Didelot if (err) 3860fad09c73SVivien Didelot return err; 3861fad09c73SVivien Didelot 3862e57e5e77SVivien Didelot mv88e6xxx_phy_init(chip); 3863e57e5e77SVivien Didelot 3864fad09c73SVivien Didelot chip->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS); 3865fad09c73SVivien Didelot if (IS_ERR(chip->reset)) 3866fad09c73SVivien Didelot return PTR_ERR(chip->reset); 3867fad09c73SVivien Didelot 3868ee4dc2e7SVivien Didelot if (chip->info->ops->get_eeprom && 3869fad09c73SVivien Didelot !of_property_read_u32(np, "eeprom-length", &eeprom_len)) 3870fad09c73SVivien Didelot chip->eeprom_len = eeprom_len; 3871fad09c73SVivien Didelot 3872dc30c35bSAndrew Lunn mutex_lock(&chip->reg_lock); 3873dc30c35bSAndrew Lunn err = mv88e6xxx_switch_reset(chip); 3874dc30c35bSAndrew Lunn mutex_unlock(&chip->reg_lock); 3875fad09c73SVivien Didelot if (err) 3876dc30c35bSAndrew Lunn goto out; 3877fad09c73SVivien Didelot 3878dc30c35bSAndrew Lunn chip->irq = of_irq_get(np, 0); 3879dc30c35bSAndrew Lunn if (chip->irq == -EPROBE_DEFER) { 3880dc30c35bSAndrew Lunn err = chip->irq; 3881dc30c35bSAndrew Lunn goto out; 3882fad09c73SVivien Didelot } 3883fad09c73SVivien Didelot 3884dc30c35bSAndrew Lunn if (chip->irq > 0) { 3885dc30c35bSAndrew Lunn /* Has to be performed before the MDIO bus is created, 3886dc30c35bSAndrew Lunn * because the PHYs will link there interrupts to these 3887dc30c35bSAndrew Lunn * interrupt controllers 3888dc30c35bSAndrew Lunn */ 3889dc30c35bSAndrew Lunn mutex_lock(&chip->reg_lock); 3890dc30c35bSAndrew Lunn err = mv88e6xxx_g1_irq_setup(chip); 3891dc30c35bSAndrew Lunn mutex_unlock(&chip->reg_lock); 3892dc30c35bSAndrew Lunn 3893dc30c35bSAndrew Lunn if (err) 3894dc30c35bSAndrew Lunn goto out; 3895dc30c35bSAndrew Lunn 3896dc30c35bSAndrew Lunn if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT)) { 3897dc30c35bSAndrew Lunn err = mv88e6xxx_g2_irq_setup(chip); 3898dc30c35bSAndrew Lunn if (err) 3899dc30c35bSAndrew Lunn goto out_g1_irq; 3900dc30c35bSAndrew Lunn } 3901dc30c35bSAndrew Lunn } 3902dc30c35bSAndrew Lunn 3903dc30c35bSAndrew Lunn err = mv88e6xxx_mdio_register(chip, np); 3904dc30c35bSAndrew Lunn if (err) 3905dc30c35bSAndrew Lunn goto out_g2_irq; 3906dc30c35bSAndrew Lunn 3907dc30c35bSAndrew Lunn err = mv88e6xxx_register_switch(chip, np); 3908dc30c35bSAndrew Lunn if (err) 3909dc30c35bSAndrew Lunn goto out_mdio; 3910dc30c35bSAndrew Lunn 3911fad09c73SVivien Didelot return 0; 3912dc30c35bSAndrew Lunn 3913dc30c35bSAndrew Lunn out_mdio: 3914dc30c35bSAndrew Lunn mv88e6xxx_mdio_unregister(chip); 3915dc30c35bSAndrew Lunn out_g2_irq: 391646712644SAndrew Lunn if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT) && chip->irq > 0) 3917dc30c35bSAndrew Lunn mv88e6xxx_g2_irq_free(chip); 3918dc30c35bSAndrew Lunn out_g1_irq: 391961f7c3f8SAndrew Lunn if (chip->irq > 0) { 392061f7c3f8SAndrew Lunn mutex_lock(&chip->reg_lock); 3921dc30c35bSAndrew Lunn mv88e6xxx_g1_irq_free(chip); 392261f7c3f8SAndrew Lunn mutex_unlock(&chip->reg_lock); 392361f7c3f8SAndrew Lunn } 3924dc30c35bSAndrew Lunn out: 3925dc30c35bSAndrew Lunn return err; 3926fad09c73SVivien Didelot } 3927fad09c73SVivien Didelot 3928fad09c73SVivien Didelot static void mv88e6xxx_remove(struct mdio_device *mdiodev) 3929fad09c73SVivien Didelot { 3930fad09c73SVivien Didelot struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev); 393104bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 3932fad09c73SVivien Didelot 3933930188ceSAndrew Lunn mv88e6xxx_phy_destroy(chip); 3934fad09c73SVivien Didelot mv88e6xxx_unregister_switch(chip); 3935fad09c73SVivien Didelot mv88e6xxx_mdio_unregister(chip); 3936dc30c35bSAndrew Lunn 393746712644SAndrew Lunn if (chip->irq > 0) { 3938dc30c35bSAndrew Lunn if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT)) 3939dc30c35bSAndrew Lunn mv88e6xxx_g2_irq_free(chip); 3940dc30c35bSAndrew Lunn mv88e6xxx_g1_irq_free(chip); 3941fad09c73SVivien Didelot } 394246712644SAndrew Lunn } 3943fad09c73SVivien Didelot 3944fad09c73SVivien Didelot static const struct of_device_id mv88e6xxx_of_match[] = { 3945fad09c73SVivien Didelot { 3946fad09c73SVivien Didelot .compatible = "marvell,mv88e6085", 3947fad09c73SVivien Didelot .data = &mv88e6xxx_table[MV88E6085], 3948fad09c73SVivien Didelot }, 3949fad09c73SVivien Didelot { /* sentinel */ }, 3950fad09c73SVivien Didelot }; 3951fad09c73SVivien Didelot 3952fad09c73SVivien Didelot MODULE_DEVICE_TABLE(of, mv88e6xxx_of_match); 3953fad09c73SVivien Didelot 3954fad09c73SVivien Didelot static struct mdio_driver mv88e6xxx_driver = { 3955fad09c73SVivien Didelot .probe = mv88e6xxx_probe, 3956fad09c73SVivien Didelot .remove = mv88e6xxx_remove, 3957fad09c73SVivien Didelot .mdiodrv.driver = { 3958fad09c73SVivien Didelot .name = "mv88e6085", 3959fad09c73SVivien Didelot .of_match_table = mv88e6xxx_of_match, 3960fad09c73SVivien Didelot }, 3961fad09c73SVivien Didelot }; 3962fad09c73SVivien Didelot 3963fad09c73SVivien Didelot static int __init mv88e6xxx_init(void) 3964fad09c73SVivien Didelot { 39659d490b4eSVivien Didelot register_switch_driver(&mv88e6xxx_switch_ops); 3966fad09c73SVivien Didelot return mdio_driver_register(&mv88e6xxx_driver); 3967fad09c73SVivien Didelot } 3968fad09c73SVivien Didelot module_init(mv88e6xxx_init); 3969fad09c73SVivien Didelot 3970fad09c73SVivien Didelot static void __exit mv88e6xxx_cleanup(void) 3971fad09c73SVivien Didelot { 3972fad09c73SVivien Didelot mdio_driver_unregister(&mv88e6xxx_driver); 39739d490b4eSVivien Didelot unregister_switch_driver(&mv88e6xxx_switch_ops); 3974fad09c73SVivien Didelot } 3975fad09c73SVivien Didelot module_exit(mv88e6xxx_cleanup); 3976fad09c73SVivien Didelot 3977fad09c73SVivien Didelot MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>"); 3978fad09c73SVivien Didelot MODULE_DESCRIPTION("Driver for Marvell 88E6XXX ethernet switch chips"); 3979fad09c73SVivien Didelot MODULE_LICENSE("GPL"); 3980