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; 416dc30c35bSAndrew Lunn 417dc30c35bSAndrew Lunn for (irq = 0; irq < 16; irq++) { 418dc30c35bSAndrew Lunn virq = irq_find_mapping(chip->g2_irq.domain, irq); 419dc30c35bSAndrew Lunn irq_dispose_mapping(virq); 420dc30c35bSAndrew Lunn } 421dc30c35bSAndrew Lunn 422dc30c35bSAndrew Lunn irq_domain_remove(chip->g2_irq.domain); 423dc30c35bSAndrew Lunn } 424dc30c35bSAndrew Lunn 425dc30c35bSAndrew Lunn static int mv88e6xxx_g1_irq_setup(struct mv88e6xxx_chip *chip) 426dc30c35bSAndrew Lunn { 427dc30c35bSAndrew Lunn int err, irq; 428dc30c35bSAndrew Lunn u16 reg; 429dc30c35bSAndrew Lunn 430dc30c35bSAndrew Lunn chip->g1_irq.nirqs = chip->info->g1_irqs; 431dc30c35bSAndrew Lunn chip->g1_irq.domain = irq_domain_add_simple( 432dc30c35bSAndrew Lunn NULL, chip->g1_irq.nirqs, 0, 433dc30c35bSAndrew Lunn &mv88e6xxx_g1_irq_domain_ops, chip); 434dc30c35bSAndrew Lunn if (!chip->g1_irq.domain) 435dc30c35bSAndrew Lunn return -ENOMEM; 436dc30c35bSAndrew Lunn 437dc30c35bSAndrew Lunn for (irq = 0; irq < chip->g1_irq.nirqs; irq++) 438dc30c35bSAndrew Lunn irq_create_mapping(chip->g1_irq.domain, irq); 439dc30c35bSAndrew Lunn 440dc30c35bSAndrew Lunn chip->g1_irq.chip = mv88e6xxx_g1_irq_chip; 441dc30c35bSAndrew Lunn chip->g1_irq.masked = ~0; 442dc30c35bSAndrew Lunn 443dc30c35bSAndrew Lunn err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, ®); 444dc30c35bSAndrew Lunn if (err) 445dc30c35bSAndrew Lunn goto out; 446dc30c35bSAndrew Lunn 447dc30c35bSAndrew Lunn reg &= ~GENMASK(chip->g1_irq.nirqs, 0); 448dc30c35bSAndrew Lunn 449dc30c35bSAndrew Lunn err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, reg); 450dc30c35bSAndrew Lunn if (err) 451dc30c35bSAndrew Lunn goto out; 452dc30c35bSAndrew Lunn 453dc30c35bSAndrew Lunn /* Reading the interrupt status clears (most of) them */ 454dc30c35bSAndrew Lunn err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, ®); 455dc30c35bSAndrew Lunn if (err) 456dc30c35bSAndrew Lunn goto out; 457dc30c35bSAndrew Lunn 458dc30c35bSAndrew Lunn err = request_threaded_irq(chip->irq, NULL, 459dc30c35bSAndrew Lunn mv88e6xxx_g1_irq_thread_fn, 460dc30c35bSAndrew Lunn IRQF_ONESHOT | IRQF_TRIGGER_FALLING, 461dc30c35bSAndrew Lunn dev_name(chip->dev), chip); 462dc30c35bSAndrew Lunn if (err) 463dc30c35bSAndrew Lunn goto out; 464dc30c35bSAndrew Lunn 465dc30c35bSAndrew Lunn return 0; 466dc30c35bSAndrew Lunn 467dc30c35bSAndrew Lunn out: 468dc30c35bSAndrew Lunn mv88e6xxx_g1_irq_free(chip); 469dc30c35bSAndrew Lunn 470dc30c35bSAndrew Lunn return err; 471dc30c35bSAndrew Lunn } 472dc30c35bSAndrew Lunn 473ec561276SVivien Didelot int mv88e6xxx_wait(struct mv88e6xxx_chip *chip, int addr, int reg, u16 mask) 4742d79af6eSVivien Didelot { 4756441e669SAndrew Lunn int i; 4762d79af6eSVivien Didelot 4776441e669SAndrew Lunn for (i = 0; i < 16; i++) { 4782d79af6eSVivien Didelot u16 val; 4792d79af6eSVivien Didelot int err; 4802d79af6eSVivien Didelot 4812d79af6eSVivien Didelot err = mv88e6xxx_read(chip, addr, reg, &val); 4822d79af6eSVivien Didelot if (err) 4832d79af6eSVivien Didelot return err; 4842d79af6eSVivien Didelot 4852d79af6eSVivien Didelot if (!(val & mask)) 4862d79af6eSVivien Didelot return 0; 4872d79af6eSVivien Didelot 4882d79af6eSVivien Didelot usleep_range(1000, 2000); 4892d79af6eSVivien Didelot } 4902d79af6eSVivien Didelot 49130853553SAndrew Lunn dev_err(chip->dev, "Timeout while waiting for switch\n"); 4922d79af6eSVivien Didelot return -ETIMEDOUT; 4932d79af6eSVivien Didelot } 4942d79af6eSVivien Didelot 495f22ab641SVivien Didelot /* Indirect write to single pointer-data register with an Update bit */ 496ec561276SVivien Didelot int mv88e6xxx_update(struct mv88e6xxx_chip *chip, int addr, int reg, u16 update) 497f22ab641SVivien Didelot { 498f22ab641SVivien Didelot u16 val; 4990f02b4f7SAndrew Lunn int err; 500f22ab641SVivien Didelot 501f22ab641SVivien Didelot /* Wait until the previous operation is completed */ 5020f02b4f7SAndrew Lunn err = mv88e6xxx_wait(chip, addr, reg, BIT(15)); 503f22ab641SVivien Didelot if (err) 504f22ab641SVivien Didelot return err; 505f22ab641SVivien Didelot 506f22ab641SVivien Didelot /* Set the Update bit to trigger a write operation */ 507f22ab641SVivien Didelot val = BIT(15) | update; 508f22ab641SVivien Didelot 509f22ab641SVivien Didelot return mv88e6xxx_write(chip, addr, reg, val); 510f22ab641SVivien Didelot } 511f22ab641SVivien Didelot 512a935c052SVivien Didelot static int mv88e6xxx_ppu_disable(struct mv88e6xxx_chip *chip) 513fad09c73SVivien Didelot { 514fad09c73SVivien Didelot u16 val; 515a935c052SVivien Didelot int i, err; 516fad09c73SVivien Didelot 517a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val); 518fad09c73SVivien Didelot if (err) 519fad09c73SVivien Didelot return err; 520fad09c73SVivien Didelot 521a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, 522a935c052SVivien Didelot val & ~GLOBAL_CONTROL_PPU_ENABLE); 523a935c052SVivien Didelot if (err) 524a935c052SVivien Didelot return err; 525fad09c73SVivien Didelot 5266441e669SAndrew Lunn for (i = 0; i < 16; i++) { 527a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &val); 528a935c052SVivien Didelot if (err) 529a935c052SVivien Didelot return err; 530fad09c73SVivien Didelot 531fad09c73SVivien Didelot usleep_range(1000, 2000); 532a935c052SVivien Didelot if ((val & GLOBAL_STATUS_PPU_MASK) != GLOBAL_STATUS_PPU_POLLING) 533fad09c73SVivien Didelot return 0; 534fad09c73SVivien Didelot } 535fad09c73SVivien Didelot 536fad09c73SVivien Didelot return -ETIMEDOUT; 537fad09c73SVivien Didelot } 538fad09c73SVivien Didelot 539fad09c73SVivien Didelot static int mv88e6xxx_ppu_enable(struct mv88e6xxx_chip *chip) 540fad09c73SVivien Didelot { 541a935c052SVivien Didelot u16 val; 542a935c052SVivien Didelot int i, err; 543fad09c73SVivien Didelot 544a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val); 545a935c052SVivien Didelot if (err) 546a935c052SVivien Didelot return err; 547fad09c73SVivien Didelot 548a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, 549a935c052SVivien Didelot val | GLOBAL_CONTROL_PPU_ENABLE); 550fad09c73SVivien Didelot if (err) 551fad09c73SVivien Didelot return err; 552fad09c73SVivien Didelot 5536441e669SAndrew Lunn for (i = 0; i < 16; i++) { 554a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &val); 555a935c052SVivien Didelot if (err) 556a935c052SVivien Didelot return err; 557fad09c73SVivien Didelot 558fad09c73SVivien Didelot usleep_range(1000, 2000); 559a935c052SVivien Didelot if ((val & GLOBAL_STATUS_PPU_MASK) == GLOBAL_STATUS_PPU_POLLING) 560fad09c73SVivien Didelot return 0; 561fad09c73SVivien Didelot } 562fad09c73SVivien Didelot 563fad09c73SVivien Didelot return -ETIMEDOUT; 564fad09c73SVivien Didelot } 565fad09c73SVivien Didelot 566fad09c73SVivien Didelot static void mv88e6xxx_ppu_reenable_work(struct work_struct *ugly) 567fad09c73SVivien Didelot { 568fad09c73SVivien Didelot struct mv88e6xxx_chip *chip; 569fad09c73SVivien Didelot 570fad09c73SVivien Didelot chip = container_of(ugly, struct mv88e6xxx_chip, ppu_work); 571fad09c73SVivien Didelot 572fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 573fad09c73SVivien Didelot 574fad09c73SVivien Didelot if (mutex_trylock(&chip->ppu_mutex)) { 575fad09c73SVivien Didelot if (mv88e6xxx_ppu_enable(chip) == 0) 576fad09c73SVivien Didelot chip->ppu_disabled = 0; 577fad09c73SVivien Didelot mutex_unlock(&chip->ppu_mutex); 578fad09c73SVivien Didelot } 579fad09c73SVivien Didelot 580fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 581fad09c73SVivien Didelot } 582fad09c73SVivien Didelot 583fad09c73SVivien Didelot static void mv88e6xxx_ppu_reenable_timer(unsigned long _ps) 584fad09c73SVivien Didelot { 585fad09c73SVivien Didelot struct mv88e6xxx_chip *chip = (void *)_ps; 586fad09c73SVivien Didelot 587fad09c73SVivien Didelot schedule_work(&chip->ppu_work); 588fad09c73SVivien Didelot } 589fad09c73SVivien Didelot 590fad09c73SVivien Didelot static int mv88e6xxx_ppu_access_get(struct mv88e6xxx_chip *chip) 591fad09c73SVivien Didelot { 592fad09c73SVivien Didelot int ret; 593fad09c73SVivien Didelot 594fad09c73SVivien Didelot mutex_lock(&chip->ppu_mutex); 595fad09c73SVivien Didelot 596fad09c73SVivien Didelot /* If the PHY polling unit is enabled, disable it so that 597fad09c73SVivien Didelot * we can access the PHY registers. If it was already 598fad09c73SVivien Didelot * disabled, cancel the timer that is going to re-enable 599fad09c73SVivien Didelot * it. 600fad09c73SVivien Didelot */ 601fad09c73SVivien Didelot if (!chip->ppu_disabled) { 602fad09c73SVivien Didelot ret = mv88e6xxx_ppu_disable(chip); 603fad09c73SVivien Didelot if (ret < 0) { 604fad09c73SVivien Didelot mutex_unlock(&chip->ppu_mutex); 605fad09c73SVivien Didelot return ret; 606fad09c73SVivien Didelot } 607fad09c73SVivien Didelot chip->ppu_disabled = 1; 608fad09c73SVivien Didelot } else { 609fad09c73SVivien Didelot del_timer(&chip->ppu_timer); 610fad09c73SVivien Didelot ret = 0; 611fad09c73SVivien Didelot } 612fad09c73SVivien Didelot 613fad09c73SVivien Didelot return ret; 614fad09c73SVivien Didelot } 615fad09c73SVivien Didelot 616fad09c73SVivien Didelot static void mv88e6xxx_ppu_access_put(struct mv88e6xxx_chip *chip) 617fad09c73SVivien Didelot { 618fad09c73SVivien Didelot /* Schedule a timer to re-enable the PHY polling unit. */ 619fad09c73SVivien Didelot mod_timer(&chip->ppu_timer, jiffies + msecs_to_jiffies(10)); 620fad09c73SVivien Didelot mutex_unlock(&chip->ppu_mutex); 621fad09c73SVivien Didelot } 622fad09c73SVivien Didelot 623fad09c73SVivien Didelot static void mv88e6xxx_ppu_state_init(struct mv88e6xxx_chip *chip) 624fad09c73SVivien Didelot { 625fad09c73SVivien Didelot mutex_init(&chip->ppu_mutex); 626fad09c73SVivien Didelot INIT_WORK(&chip->ppu_work, mv88e6xxx_ppu_reenable_work); 62768497a87SWei Yongjun setup_timer(&chip->ppu_timer, mv88e6xxx_ppu_reenable_timer, 62868497a87SWei Yongjun (unsigned long)chip); 629fad09c73SVivien Didelot } 630fad09c73SVivien Didelot 631930188ceSAndrew Lunn static void mv88e6xxx_ppu_state_destroy(struct mv88e6xxx_chip *chip) 632930188ceSAndrew Lunn { 633930188ceSAndrew Lunn del_timer_sync(&chip->ppu_timer); 634930188ceSAndrew Lunn } 635930188ceSAndrew Lunn 636e57e5e77SVivien Didelot static int mv88e6xxx_phy_ppu_read(struct mv88e6xxx_chip *chip, int addr, 637e57e5e77SVivien Didelot int reg, u16 *val) 638fad09c73SVivien Didelot { 639e57e5e77SVivien Didelot int err; 640fad09c73SVivien Didelot 641e57e5e77SVivien Didelot err = mv88e6xxx_ppu_access_get(chip); 642e57e5e77SVivien Didelot if (!err) { 643e57e5e77SVivien Didelot err = mv88e6xxx_read(chip, addr, reg, val); 644fad09c73SVivien Didelot mv88e6xxx_ppu_access_put(chip); 645fad09c73SVivien Didelot } 646fad09c73SVivien Didelot 647e57e5e77SVivien Didelot return err; 648fad09c73SVivien Didelot } 649fad09c73SVivien Didelot 650e57e5e77SVivien Didelot static int mv88e6xxx_phy_ppu_write(struct mv88e6xxx_chip *chip, int addr, 651e57e5e77SVivien Didelot int reg, u16 val) 652fad09c73SVivien Didelot { 653e57e5e77SVivien Didelot int err; 654fad09c73SVivien Didelot 655e57e5e77SVivien Didelot err = mv88e6xxx_ppu_access_get(chip); 656e57e5e77SVivien Didelot if (!err) { 657e57e5e77SVivien Didelot err = mv88e6xxx_write(chip, addr, reg, val); 658fad09c73SVivien Didelot mv88e6xxx_ppu_access_put(chip); 659fad09c73SVivien Didelot } 660fad09c73SVivien Didelot 661e57e5e77SVivien Didelot return err; 662fad09c73SVivien Didelot } 663fad09c73SVivien Didelot 664fad09c73SVivien Didelot static bool mv88e6xxx_6065_family(struct mv88e6xxx_chip *chip) 665fad09c73SVivien Didelot { 666fad09c73SVivien Didelot return chip->info->family == MV88E6XXX_FAMILY_6065; 667fad09c73SVivien Didelot } 668fad09c73SVivien Didelot 669fad09c73SVivien Didelot static bool mv88e6xxx_6095_family(struct mv88e6xxx_chip *chip) 670fad09c73SVivien Didelot { 671fad09c73SVivien Didelot return chip->info->family == MV88E6XXX_FAMILY_6095; 672fad09c73SVivien Didelot } 673fad09c73SVivien Didelot 674fad09c73SVivien Didelot static bool mv88e6xxx_6097_family(struct mv88e6xxx_chip *chip) 675fad09c73SVivien Didelot { 676fad09c73SVivien Didelot return chip->info->family == MV88E6XXX_FAMILY_6097; 677fad09c73SVivien Didelot } 678fad09c73SVivien Didelot 679fad09c73SVivien Didelot static bool mv88e6xxx_6165_family(struct mv88e6xxx_chip *chip) 680fad09c73SVivien Didelot { 681fad09c73SVivien Didelot return chip->info->family == MV88E6XXX_FAMILY_6165; 682fad09c73SVivien Didelot } 683fad09c73SVivien Didelot 684fad09c73SVivien Didelot static bool mv88e6xxx_6185_family(struct mv88e6xxx_chip *chip) 685fad09c73SVivien Didelot { 686fad09c73SVivien Didelot return chip->info->family == MV88E6XXX_FAMILY_6185; 687fad09c73SVivien Didelot } 688fad09c73SVivien Didelot 689fad09c73SVivien Didelot static bool mv88e6xxx_6320_family(struct mv88e6xxx_chip *chip) 690fad09c73SVivien Didelot { 691fad09c73SVivien Didelot return chip->info->family == MV88E6XXX_FAMILY_6320; 692fad09c73SVivien Didelot } 693fad09c73SVivien Didelot 694fad09c73SVivien Didelot static bool mv88e6xxx_6351_family(struct mv88e6xxx_chip *chip) 695fad09c73SVivien Didelot { 696fad09c73SVivien Didelot return chip->info->family == MV88E6XXX_FAMILY_6351; 697fad09c73SVivien Didelot } 698fad09c73SVivien Didelot 699fad09c73SVivien Didelot static bool mv88e6xxx_6352_family(struct mv88e6xxx_chip *chip) 700fad09c73SVivien Didelot { 701fad09c73SVivien Didelot return chip->info->family == MV88E6XXX_FAMILY_6352; 702fad09c73SVivien Didelot } 703fad09c73SVivien Didelot 704fad09c73SVivien Didelot /* We expect the switch to perform auto negotiation if there is a real 705fad09c73SVivien Didelot * phy. However, in the case of a fixed link phy, we force the port 706fad09c73SVivien Didelot * settings from the fixed link settings. 707fad09c73SVivien Didelot */ 708fad09c73SVivien Didelot static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port, 709fad09c73SVivien Didelot struct phy_device *phydev) 710fad09c73SVivien Didelot { 71104bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 7120e7b9925SAndrew Lunn u16 reg; 7130e7b9925SAndrew Lunn int err; 714fad09c73SVivien Didelot 715fad09c73SVivien Didelot if (!phy_is_pseudo_fixed_link(phydev)) 716fad09c73SVivien Didelot return; 717fad09c73SVivien Didelot 718fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 719fad09c73SVivien Didelot 7200e7b9925SAndrew Lunn err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, ®); 7210e7b9925SAndrew Lunn if (err) 722fad09c73SVivien Didelot goto out; 723fad09c73SVivien Didelot 7240e7b9925SAndrew Lunn reg &= ~(PORT_PCS_CTRL_LINK_UP | 725fad09c73SVivien Didelot PORT_PCS_CTRL_FORCE_LINK | 726fad09c73SVivien Didelot PORT_PCS_CTRL_DUPLEX_FULL | 727fad09c73SVivien Didelot PORT_PCS_CTRL_FORCE_DUPLEX | 728fad09c73SVivien Didelot PORT_PCS_CTRL_UNFORCED); 729fad09c73SVivien Didelot 730fad09c73SVivien Didelot reg |= PORT_PCS_CTRL_FORCE_LINK; 731fad09c73SVivien Didelot if (phydev->link) 732fad09c73SVivien Didelot reg |= PORT_PCS_CTRL_LINK_UP; 733fad09c73SVivien Didelot 734fad09c73SVivien Didelot if (mv88e6xxx_6065_family(chip) && phydev->speed > SPEED_100) 735fad09c73SVivien Didelot goto out; 736fad09c73SVivien Didelot 737fad09c73SVivien Didelot switch (phydev->speed) { 738fad09c73SVivien Didelot case SPEED_1000: 739fad09c73SVivien Didelot reg |= PORT_PCS_CTRL_1000; 740fad09c73SVivien Didelot break; 741fad09c73SVivien Didelot case SPEED_100: 742fad09c73SVivien Didelot reg |= PORT_PCS_CTRL_100; 743fad09c73SVivien Didelot break; 744fad09c73SVivien Didelot case SPEED_10: 745fad09c73SVivien Didelot reg |= PORT_PCS_CTRL_10; 746fad09c73SVivien Didelot break; 747fad09c73SVivien Didelot default: 748fad09c73SVivien Didelot pr_info("Unknown speed"); 749fad09c73SVivien Didelot goto out; 750fad09c73SVivien Didelot } 751fad09c73SVivien Didelot 752fad09c73SVivien Didelot reg |= PORT_PCS_CTRL_FORCE_DUPLEX; 753fad09c73SVivien Didelot if (phydev->duplex == DUPLEX_FULL) 754fad09c73SVivien Didelot reg |= PORT_PCS_CTRL_DUPLEX_FULL; 755fad09c73SVivien Didelot 756fad09c73SVivien Didelot if ((mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip)) && 757370b4ffbSVivien Didelot (port >= mv88e6xxx_num_ports(chip) - 2)) { 758fad09c73SVivien Didelot if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) 759fad09c73SVivien Didelot reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK; 760fad09c73SVivien Didelot if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) 761fad09c73SVivien Didelot reg |= PORT_PCS_CTRL_RGMII_DELAY_TXCLK; 762fad09c73SVivien Didelot if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) 763fad09c73SVivien Didelot reg |= (PORT_PCS_CTRL_RGMII_DELAY_RXCLK | 764fad09c73SVivien Didelot PORT_PCS_CTRL_RGMII_DELAY_TXCLK); 765fad09c73SVivien Didelot } 7660e7b9925SAndrew Lunn mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg); 767fad09c73SVivien Didelot 768fad09c73SVivien Didelot out: 769fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 770fad09c73SVivien Didelot } 771fad09c73SVivien Didelot 772fad09c73SVivien Didelot static int _mv88e6xxx_stats_wait(struct mv88e6xxx_chip *chip) 773fad09c73SVivien Didelot { 774a935c052SVivien Didelot u16 val; 775a935c052SVivien Didelot int i, err; 776fad09c73SVivien Didelot 777fad09c73SVivien Didelot for (i = 0; i < 10; i++) { 778a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_OP, &val); 779a935c052SVivien Didelot if ((val & GLOBAL_STATS_OP_BUSY) == 0) 780fad09c73SVivien Didelot return 0; 781fad09c73SVivien Didelot } 782fad09c73SVivien Didelot 783fad09c73SVivien Didelot return -ETIMEDOUT; 784fad09c73SVivien Didelot } 785fad09c73SVivien Didelot 786fad09c73SVivien Didelot static int _mv88e6xxx_stats_snapshot(struct mv88e6xxx_chip *chip, int port) 787fad09c73SVivien Didelot { 788a935c052SVivien Didelot int err; 789fad09c73SVivien Didelot 790fad09c73SVivien Didelot if (mv88e6xxx_6320_family(chip) || mv88e6xxx_6352_family(chip)) 791fad09c73SVivien Didelot port = (port + 1) << 5; 792fad09c73SVivien Didelot 793fad09c73SVivien Didelot /* Snapshot the hardware statistics counters for this port. */ 794a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP, 795fad09c73SVivien Didelot GLOBAL_STATS_OP_CAPTURE_PORT | 796fad09c73SVivien Didelot GLOBAL_STATS_OP_HIST_RX_TX | port); 797a935c052SVivien Didelot if (err) 798a935c052SVivien Didelot return err; 799fad09c73SVivien Didelot 800fad09c73SVivien Didelot /* Wait for the snapshotting to complete. */ 801a935c052SVivien Didelot return _mv88e6xxx_stats_wait(chip); 802fad09c73SVivien Didelot } 803fad09c73SVivien Didelot 804fad09c73SVivien Didelot static void _mv88e6xxx_stats_read(struct mv88e6xxx_chip *chip, 805fad09c73SVivien Didelot int stat, u32 *val) 806fad09c73SVivien Didelot { 807a935c052SVivien Didelot u32 value; 808a935c052SVivien Didelot u16 reg; 809a935c052SVivien Didelot int err; 810fad09c73SVivien Didelot 811fad09c73SVivien Didelot *val = 0; 812fad09c73SVivien Didelot 813a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP, 814fad09c73SVivien Didelot GLOBAL_STATS_OP_READ_CAPTURED | 815fad09c73SVivien Didelot GLOBAL_STATS_OP_HIST_RX_TX | stat); 816a935c052SVivien Didelot if (err) 817fad09c73SVivien Didelot return; 818fad09c73SVivien Didelot 819a935c052SVivien Didelot err = _mv88e6xxx_stats_wait(chip); 820a935c052SVivien Didelot if (err) 821fad09c73SVivien Didelot return; 822fad09c73SVivien Didelot 823a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_COUNTER_32, ®); 824a935c052SVivien Didelot if (err) 825fad09c73SVivien Didelot return; 826fad09c73SVivien Didelot 827a935c052SVivien Didelot value = reg << 16; 828fad09c73SVivien Didelot 829a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_COUNTER_01, ®); 830a935c052SVivien Didelot if (err) 831fad09c73SVivien Didelot return; 832fad09c73SVivien Didelot 833a935c052SVivien Didelot *val = value | reg; 834fad09c73SVivien Didelot } 835fad09c73SVivien Didelot 836fad09c73SVivien Didelot static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = { 837fad09c73SVivien Didelot { "in_good_octets", 8, 0x00, BANK0, }, 838fad09c73SVivien Didelot { "in_bad_octets", 4, 0x02, BANK0, }, 839fad09c73SVivien Didelot { "in_unicast", 4, 0x04, BANK0, }, 840fad09c73SVivien Didelot { "in_broadcasts", 4, 0x06, BANK0, }, 841fad09c73SVivien Didelot { "in_multicasts", 4, 0x07, BANK0, }, 842fad09c73SVivien Didelot { "in_pause", 4, 0x16, BANK0, }, 843fad09c73SVivien Didelot { "in_undersize", 4, 0x18, BANK0, }, 844fad09c73SVivien Didelot { "in_fragments", 4, 0x19, BANK0, }, 845fad09c73SVivien Didelot { "in_oversize", 4, 0x1a, BANK0, }, 846fad09c73SVivien Didelot { "in_jabber", 4, 0x1b, BANK0, }, 847fad09c73SVivien Didelot { "in_rx_error", 4, 0x1c, BANK0, }, 848fad09c73SVivien Didelot { "in_fcs_error", 4, 0x1d, BANK0, }, 849fad09c73SVivien Didelot { "out_octets", 8, 0x0e, BANK0, }, 850fad09c73SVivien Didelot { "out_unicast", 4, 0x10, BANK0, }, 851fad09c73SVivien Didelot { "out_broadcasts", 4, 0x13, BANK0, }, 852fad09c73SVivien Didelot { "out_multicasts", 4, 0x12, BANK0, }, 853fad09c73SVivien Didelot { "out_pause", 4, 0x15, BANK0, }, 854fad09c73SVivien Didelot { "excessive", 4, 0x11, BANK0, }, 855fad09c73SVivien Didelot { "collisions", 4, 0x1e, BANK0, }, 856fad09c73SVivien Didelot { "deferred", 4, 0x05, BANK0, }, 857fad09c73SVivien Didelot { "single", 4, 0x14, BANK0, }, 858fad09c73SVivien Didelot { "multiple", 4, 0x17, BANK0, }, 859fad09c73SVivien Didelot { "out_fcs_error", 4, 0x03, BANK0, }, 860fad09c73SVivien Didelot { "late", 4, 0x1f, BANK0, }, 861fad09c73SVivien Didelot { "hist_64bytes", 4, 0x08, BANK0, }, 862fad09c73SVivien Didelot { "hist_65_127bytes", 4, 0x09, BANK0, }, 863fad09c73SVivien Didelot { "hist_128_255bytes", 4, 0x0a, BANK0, }, 864fad09c73SVivien Didelot { "hist_256_511bytes", 4, 0x0b, BANK0, }, 865fad09c73SVivien Didelot { "hist_512_1023bytes", 4, 0x0c, BANK0, }, 866fad09c73SVivien Didelot { "hist_1024_max_bytes", 4, 0x0d, BANK0, }, 867fad09c73SVivien Didelot { "sw_in_discards", 4, 0x10, PORT, }, 868fad09c73SVivien Didelot { "sw_in_filtered", 2, 0x12, PORT, }, 869fad09c73SVivien Didelot { "sw_out_filtered", 2, 0x13, PORT, }, 870fad09c73SVivien Didelot { "in_discards", 4, 0x00 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 871fad09c73SVivien Didelot { "in_filtered", 4, 0x01 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 872fad09c73SVivien Didelot { "in_accepted", 4, 0x02 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 873fad09c73SVivien Didelot { "in_bad_accepted", 4, 0x03 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 874fad09c73SVivien Didelot { "in_good_avb_class_a", 4, 0x04 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 875fad09c73SVivien Didelot { "in_good_avb_class_b", 4, 0x05 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 876fad09c73SVivien Didelot { "in_bad_avb_class_a", 4, 0x06 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 877fad09c73SVivien Didelot { "in_bad_avb_class_b", 4, 0x07 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 878fad09c73SVivien Didelot { "tcam_counter_0", 4, 0x08 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 879fad09c73SVivien Didelot { "tcam_counter_1", 4, 0x09 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 880fad09c73SVivien Didelot { "tcam_counter_2", 4, 0x0a | GLOBAL_STATS_OP_BANK_1, BANK1, }, 881fad09c73SVivien Didelot { "tcam_counter_3", 4, 0x0b | GLOBAL_STATS_OP_BANK_1, BANK1, }, 882fad09c73SVivien Didelot { "in_da_unknown", 4, 0x0e | GLOBAL_STATS_OP_BANK_1, BANK1, }, 883fad09c73SVivien Didelot { "in_management", 4, 0x0f | GLOBAL_STATS_OP_BANK_1, BANK1, }, 884fad09c73SVivien Didelot { "out_queue_0", 4, 0x10 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 885fad09c73SVivien Didelot { "out_queue_1", 4, 0x11 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 886fad09c73SVivien Didelot { "out_queue_2", 4, 0x12 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 887fad09c73SVivien Didelot { "out_queue_3", 4, 0x13 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 888fad09c73SVivien Didelot { "out_queue_4", 4, 0x14 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 889fad09c73SVivien Didelot { "out_queue_5", 4, 0x15 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 890fad09c73SVivien Didelot { "out_queue_6", 4, 0x16 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 891fad09c73SVivien Didelot { "out_queue_7", 4, 0x17 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 892fad09c73SVivien Didelot { "out_cut_through", 4, 0x18 | GLOBAL_STATS_OP_BANK_1, BANK1, }, 893fad09c73SVivien Didelot { "out_octets_a", 4, 0x1a | GLOBAL_STATS_OP_BANK_1, BANK1, }, 894fad09c73SVivien Didelot { "out_octets_b", 4, 0x1b | GLOBAL_STATS_OP_BANK_1, BANK1, }, 895fad09c73SVivien Didelot { "out_management", 4, 0x1f | GLOBAL_STATS_OP_BANK_1, BANK1, }, 896fad09c73SVivien Didelot }; 897fad09c73SVivien Didelot 898fad09c73SVivien Didelot static bool mv88e6xxx_has_stat(struct mv88e6xxx_chip *chip, 899fad09c73SVivien Didelot struct mv88e6xxx_hw_stat *stat) 900fad09c73SVivien Didelot { 901fad09c73SVivien Didelot switch (stat->type) { 902fad09c73SVivien Didelot case BANK0: 903fad09c73SVivien Didelot return true; 904fad09c73SVivien Didelot case BANK1: 905fad09c73SVivien Didelot return mv88e6xxx_6320_family(chip); 906fad09c73SVivien Didelot case PORT: 907fad09c73SVivien Didelot return mv88e6xxx_6095_family(chip) || 908fad09c73SVivien Didelot mv88e6xxx_6185_family(chip) || 909fad09c73SVivien Didelot mv88e6xxx_6097_family(chip) || 910fad09c73SVivien Didelot mv88e6xxx_6165_family(chip) || 911fad09c73SVivien Didelot mv88e6xxx_6351_family(chip) || 912fad09c73SVivien Didelot mv88e6xxx_6352_family(chip); 913fad09c73SVivien Didelot } 914fad09c73SVivien Didelot return false; 915fad09c73SVivien Didelot } 916fad09c73SVivien Didelot 917fad09c73SVivien Didelot static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_chip *chip, 918fad09c73SVivien Didelot struct mv88e6xxx_hw_stat *s, 919fad09c73SVivien Didelot int port) 920fad09c73SVivien Didelot { 921fad09c73SVivien Didelot u32 low; 922fad09c73SVivien Didelot u32 high = 0; 9230e7b9925SAndrew Lunn int err; 9240e7b9925SAndrew Lunn u16 reg; 925fad09c73SVivien Didelot u64 value; 926fad09c73SVivien Didelot 927fad09c73SVivien Didelot switch (s->type) { 928fad09c73SVivien Didelot case PORT: 9290e7b9925SAndrew Lunn err = mv88e6xxx_port_read(chip, port, s->reg, ®); 9300e7b9925SAndrew Lunn if (err) 931fad09c73SVivien Didelot return UINT64_MAX; 932fad09c73SVivien Didelot 9330e7b9925SAndrew Lunn low = reg; 934fad09c73SVivien Didelot if (s->sizeof_stat == 4) { 9350e7b9925SAndrew Lunn err = mv88e6xxx_port_read(chip, port, s->reg + 1, ®); 9360e7b9925SAndrew Lunn if (err) 937fad09c73SVivien Didelot return UINT64_MAX; 9380e7b9925SAndrew Lunn high = reg; 939fad09c73SVivien Didelot } 940fad09c73SVivien Didelot break; 941fad09c73SVivien Didelot case BANK0: 942fad09c73SVivien Didelot case BANK1: 943fad09c73SVivien Didelot _mv88e6xxx_stats_read(chip, s->reg, &low); 944fad09c73SVivien Didelot if (s->sizeof_stat == 8) 945fad09c73SVivien Didelot _mv88e6xxx_stats_read(chip, s->reg + 1, &high); 946fad09c73SVivien Didelot } 947fad09c73SVivien Didelot value = (((u64)high) << 16) | low; 948fad09c73SVivien Didelot return value; 949fad09c73SVivien Didelot } 950fad09c73SVivien Didelot 951fad09c73SVivien Didelot static void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, 952fad09c73SVivien Didelot uint8_t *data) 953fad09c73SVivien Didelot { 95404bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 955fad09c73SVivien Didelot struct mv88e6xxx_hw_stat *stat; 956fad09c73SVivien Didelot int i, j; 957fad09c73SVivien Didelot 958fad09c73SVivien Didelot for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) { 959fad09c73SVivien Didelot stat = &mv88e6xxx_hw_stats[i]; 960fad09c73SVivien Didelot if (mv88e6xxx_has_stat(chip, stat)) { 961fad09c73SVivien Didelot memcpy(data + j * ETH_GSTRING_LEN, stat->string, 962fad09c73SVivien Didelot ETH_GSTRING_LEN); 963fad09c73SVivien Didelot j++; 964fad09c73SVivien Didelot } 965fad09c73SVivien Didelot } 966fad09c73SVivien Didelot } 967fad09c73SVivien Didelot 968fad09c73SVivien Didelot static int mv88e6xxx_get_sset_count(struct dsa_switch *ds) 969fad09c73SVivien Didelot { 97004bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 971fad09c73SVivien Didelot struct mv88e6xxx_hw_stat *stat; 972fad09c73SVivien Didelot int i, j; 973fad09c73SVivien Didelot 974fad09c73SVivien Didelot for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) { 975fad09c73SVivien Didelot stat = &mv88e6xxx_hw_stats[i]; 976fad09c73SVivien Didelot if (mv88e6xxx_has_stat(chip, stat)) 977fad09c73SVivien Didelot j++; 978fad09c73SVivien Didelot } 979fad09c73SVivien Didelot return j; 980fad09c73SVivien Didelot } 981fad09c73SVivien Didelot 982fad09c73SVivien Didelot static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port, 983fad09c73SVivien Didelot uint64_t *data) 984fad09c73SVivien Didelot { 98504bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 986fad09c73SVivien Didelot struct mv88e6xxx_hw_stat *stat; 987fad09c73SVivien Didelot int ret; 988fad09c73SVivien Didelot int i, j; 989fad09c73SVivien Didelot 990fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 991fad09c73SVivien Didelot 992fad09c73SVivien Didelot ret = _mv88e6xxx_stats_snapshot(chip, port); 993fad09c73SVivien Didelot if (ret < 0) { 994fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 995fad09c73SVivien Didelot return; 996fad09c73SVivien Didelot } 997fad09c73SVivien Didelot for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) { 998fad09c73SVivien Didelot stat = &mv88e6xxx_hw_stats[i]; 999fad09c73SVivien Didelot if (mv88e6xxx_has_stat(chip, stat)) { 1000fad09c73SVivien Didelot data[j] = _mv88e6xxx_get_ethtool_stat(chip, stat, port); 1001fad09c73SVivien Didelot j++; 1002fad09c73SVivien Didelot } 1003fad09c73SVivien Didelot } 1004fad09c73SVivien Didelot 1005fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 1006fad09c73SVivien Didelot } 1007fad09c73SVivien Didelot 1008fad09c73SVivien Didelot static int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port) 1009fad09c73SVivien Didelot { 1010fad09c73SVivien Didelot return 32 * sizeof(u16); 1011fad09c73SVivien Didelot } 1012fad09c73SVivien Didelot 1013fad09c73SVivien Didelot static void mv88e6xxx_get_regs(struct dsa_switch *ds, int port, 1014fad09c73SVivien Didelot struct ethtool_regs *regs, void *_p) 1015fad09c73SVivien Didelot { 101604bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 10170e7b9925SAndrew Lunn int err; 10180e7b9925SAndrew Lunn u16 reg; 1019fad09c73SVivien Didelot u16 *p = _p; 1020fad09c73SVivien Didelot int i; 1021fad09c73SVivien Didelot 1022fad09c73SVivien Didelot regs->version = 0; 1023fad09c73SVivien Didelot 1024fad09c73SVivien Didelot memset(p, 0xff, 32 * sizeof(u16)); 1025fad09c73SVivien Didelot 1026fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 1027fad09c73SVivien Didelot 1028fad09c73SVivien Didelot for (i = 0; i < 32; i++) { 1029fad09c73SVivien Didelot 10300e7b9925SAndrew Lunn err = mv88e6xxx_port_read(chip, port, i, ®); 10310e7b9925SAndrew Lunn if (!err) 10320e7b9925SAndrew Lunn p[i] = reg; 1033fad09c73SVivien Didelot } 1034fad09c73SVivien Didelot 1035fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 1036fad09c73SVivien Didelot } 1037fad09c73SVivien Didelot 1038fad09c73SVivien Didelot static int _mv88e6xxx_atu_wait(struct mv88e6xxx_chip *chip) 1039fad09c73SVivien Didelot { 1040a935c052SVivien Didelot return mv88e6xxx_g1_wait(chip, GLOBAL_ATU_OP, GLOBAL_ATU_OP_BUSY); 1041fad09c73SVivien Didelot } 1042fad09c73SVivien Didelot 1043fad09c73SVivien Didelot static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, 1044fad09c73SVivien Didelot struct ethtool_eee *e) 1045fad09c73SVivien Didelot { 104604bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 10479c93829cSVivien Didelot u16 reg; 10489c93829cSVivien Didelot int err; 1049fad09c73SVivien Didelot 1050fad09c73SVivien Didelot if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEE)) 1051fad09c73SVivien Didelot return -EOPNOTSUPP; 1052fad09c73SVivien Didelot 1053fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 1054fad09c73SVivien Didelot 10559c93829cSVivien Didelot err = mv88e6xxx_phy_read(chip, port, 16, ®); 10569c93829cSVivien Didelot if (err) 1057fad09c73SVivien Didelot goto out; 1058fad09c73SVivien Didelot 1059fad09c73SVivien Didelot e->eee_enabled = !!(reg & 0x0200); 1060fad09c73SVivien Didelot e->tx_lpi_enabled = !!(reg & 0x0100); 1061fad09c73SVivien Didelot 10620e7b9925SAndrew Lunn err = mv88e6xxx_port_read(chip, port, PORT_STATUS, ®); 10639c93829cSVivien Didelot if (err) 1064fad09c73SVivien Didelot goto out; 1065fad09c73SVivien Didelot 1066fad09c73SVivien Didelot e->eee_active = !!(reg & PORT_STATUS_EEE); 1067fad09c73SVivien Didelot out: 1068fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 10699c93829cSVivien Didelot 10709c93829cSVivien Didelot return err; 1071fad09c73SVivien Didelot } 1072fad09c73SVivien Didelot 1073fad09c73SVivien Didelot static int mv88e6xxx_set_eee(struct dsa_switch *ds, int port, 1074fad09c73SVivien Didelot struct phy_device *phydev, struct ethtool_eee *e) 1075fad09c73SVivien Didelot { 107604bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 10779c93829cSVivien Didelot u16 reg; 10789c93829cSVivien Didelot int err; 1079fad09c73SVivien Didelot 1080fad09c73SVivien Didelot if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEE)) 1081fad09c73SVivien Didelot return -EOPNOTSUPP; 1082fad09c73SVivien Didelot 1083fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 1084fad09c73SVivien Didelot 10859c93829cSVivien Didelot err = mv88e6xxx_phy_read(chip, port, 16, ®); 10869c93829cSVivien Didelot if (err) 1087fad09c73SVivien Didelot goto out; 1088fad09c73SVivien Didelot 10899c93829cSVivien Didelot reg &= ~0x0300; 1090fad09c73SVivien Didelot if (e->eee_enabled) 1091fad09c73SVivien Didelot reg |= 0x0200; 1092fad09c73SVivien Didelot if (e->tx_lpi_enabled) 1093fad09c73SVivien Didelot reg |= 0x0100; 1094fad09c73SVivien Didelot 10959c93829cSVivien Didelot err = mv88e6xxx_phy_write(chip, port, 16, reg); 1096fad09c73SVivien Didelot out: 1097fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 1098fad09c73SVivien Didelot 10999c93829cSVivien Didelot return err; 1100fad09c73SVivien Didelot } 1101fad09c73SVivien Didelot 1102fad09c73SVivien Didelot static int _mv88e6xxx_atu_cmd(struct mv88e6xxx_chip *chip, u16 fid, u16 cmd) 1103fad09c73SVivien Didelot { 1104a935c052SVivien Didelot u16 val; 1105a935c052SVivien Didelot int err; 1106fad09c73SVivien Didelot 11076dc10bbcSVivien Didelot if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G1_ATU_FID)) { 1108a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_FID, fid); 1109a935c052SVivien Didelot if (err) 1110a935c052SVivien Didelot return err; 1111fad09c73SVivien Didelot } else if (mv88e6xxx_num_databases(chip) == 256) { 1112fad09c73SVivien Didelot /* ATU DBNum[7:4] are located in ATU Control 15:12 */ 1113a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_CONTROL, &val); 1114a935c052SVivien Didelot if (err) 1115a935c052SVivien Didelot return err; 1116fad09c73SVivien Didelot 1117a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL, 1118a935c052SVivien Didelot (val & 0xfff) | ((fid << 8) & 0xf000)); 1119a935c052SVivien Didelot if (err) 1120a935c052SVivien Didelot return err; 1121fad09c73SVivien Didelot 1122fad09c73SVivien Didelot /* ATU DBNum[3:0] are located in ATU Operation 3:0 */ 1123fad09c73SVivien Didelot cmd |= fid & 0xf; 1124fad09c73SVivien Didelot } 1125fad09c73SVivien Didelot 1126a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_OP, cmd); 1127a935c052SVivien Didelot if (err) 1128a935c052SVivien Didelot return err; 1129fad09c73SVivien Didelot 1130fad09c73SVivien Didelot return _mv88e6xxx_atu_wait(chip); 1131fad09c73SVivien Didelot } 1132fad09c73SVivien Didelot 1133fad09c73SVivien Didelot static int _mv88e6xxx_atu_data_write(struct mv88e6xxx_chip *chip, 1134fad09c73SVivien Didelot struct mv88e6xxx_atu_entry *entry) 1135fad09c73SVivien Didelot { 1136fad09c73SVivien Didelot u16 data = entry->state & GLOBAL_ATU_DATA_STATE_MASK; 1137fad09c73SVivien Didelot 1138fad09c73SVivien Didelot if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) { 1139fad09c73SVivien Didelot unsigned int mask, shift; 1140fad09c73SVivien Didelot 1141fad09c73SVivien Didelot if (entry->trunk) { 1142fad09c73SVivien Didelot data |= GLOBAL_ATU_DATA_TRUNK; 1143fad09c73SVivien Didelot mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK; 1144fad09c73SVivien Didelot shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT; 1145fad09c73SVivien Didelot } else { 1146fad09c73SVivien Didelot mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK; 1147fad09c73SVivien Didelot shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT; 1148fad09c73SVivien Didelot } 1149fad09c73SVivien Didelot 1150fad09c73SVivien Didelot data |= (entry->portv_trunkid << shift) & mask; 1151fad09c73SVivien Didelot } 1152fad09c73SVivien Didelot 1153a935c052SVivien Didelot return mv88e6xxx_g1_write(chip, GLOBAL_ATU_DATA, data); 1154fad09c73SVivien Didelot } 1155fad09c73SVivien Didelot 1156fad09c73SVivien Didelot static int _mv88e6xxx_atu_flush_move(struct mv88e6xxx_chip *chip, 1157fad09c73SVivien Didelot struct mv88e6xxx_atu_entry *entry, 1158fad09c73SVivien Didelot bool static_too) 1159fad09c73SVivien Didelot { 1160fad09c73SVivien Didelot int op; 1161fad09c73SVivien Didelot int err; 1162fad09c73SVivien Didelot 1163fad09c73SVivien Didelot err = _mv88e6xxx_atu_wait(chip); 1164fad09c73SVivien Didelot if (err) 1165fad09c73SVivien Didelot return err; 1166fad09c73SVivien Didelot 1167fad09c73SVivien Didelot err = _mv88e6xxx_atu_data_write(chip, entry); 1168fad09c73SVivien Didelot if (err) 1169fad09c73SVivien Didelot return err; 1170fad09c73SVivien Didelot 1171fad09c73SVivien Didelot if (entry->fid) { 1172fad09c73SVivien Didelot op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB : 1173fad09c73SVivien Didelot GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB; 1174fad09c73SVivien Didelot } else { 1175fad09c73SVivien Didelot op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL : 1176fad09c73SVivien Didelot GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC; 1177fad09c73SVivien Didelot } 1178fad09c73SVivien Didelot 1179fad09c73SVivien Didelot return _mv88e6xxx_atu_cmd(chip, entry->fid, op); 1180fad09c73SVivien Didelot } 1181fad09c73SVivien Didelot 1182fad09c73SVivien Didelot static int _mv88e6xxx_atu_flush(struct mv88e6xxx_chip *chip, 1183fad09c73SVivien Didelot u16 fid, bool static_too) 1184fad09c73SVivien Didelot { 1185fad09c73SVivien Didelot struct mv88e6xxx_atu_entry entry = { 1186fad09c73SVivien Didelot .fid = fid, 1187fad09c73SVivien Didelot .state = 0, /* EntryState bits must be 0 */ 1188fad09c73SVivien Didelot }; 1189fad09c73SVivien Didelot 1190fad09c73SVivien Didelot return _mv88e6xxx_atu_flush_move(chip, &entry, static_too); 1191fad09c73SVivien Didelot } 1192fad09c73SVivien Didelot 1193fad09c73SVivien Didelot static int _mv88e6xxx_atu_move(struct mv88e6xxx_chip *chip, u16 fid, 1194fad09c73SVivien Didelot int from_port, int to_port, bool static_too) 1195fad09c73SVivien Didelot { 1196fad09c73SVivien Didelot struct mv88e6xxx_atu_entry entry = { 1197fad09c73SVivien Didelot .trunk = false, 1198fad09c73SVivien Didelot .fid = fid, 1199fad09c73SVivien Didelot }; 1200fad09c73SVivien Didelot 1201fad09c73SVivien Didelot /* EntryState bits must be 0xF */ 1202fad09c73SVivien Didelot entry.state = GLOBAL_ATU_DATA_STATE_MASK; 1203fad09c73SVivien Didelot 1204fad09c73SVivien Didelot /* ToPort and FromPort are respectively in PortVec bits 7:4 and 3:0 */ 1205fad09c73SVivien Didelot entry.portv_trunkid = (to_port & 0x0f) << 4; 1206fad09c73SVivien Didelot entry.portv_trunkid |= from_port & 0x0f; 1207fad09c73SVivien Didelot 1208fad09c73SVivien Didelot return _mv88e6xxx_atu_flush_move(chip, &entry, static_too); 1209fad09c73SVivien Didelot } 1210fad09c73SVivien Didelot 1211fad09c73SVivien Didelot static int _mv88e6xxx_atu_remove(struct mv88e6xxx_chip *chip, u16 fid, 1212fad09c73SVivien Didelot int port, bool static_too) 1213fad09c73SVivien Didelot { 1214fad09c73SVivien Didelot /* Destination port 0xF means remove the entries */ 1215fad09c73SVivien Didelot return _mv88e6xxx_atu_move(chip, fid, port, 0x0f, static_too); 1216fad09c73SVivien Didelot } 1217fad09c73SVivien Didelot 1218fad09c73SVivien Didelot static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_chip *chip, int port) 1219fad09c73SVivien Didelot { 1220fad09c73SVivien Didelot struct net_device *bridge = chip->ports[port].bridge_dev; 1221370b4ffbSVivien Didelot const u16 mask = (1 << mv88e6xxx_num_ports(chip)) - 1; 1222fad09c73SVivien Didelot struct dsa_switch *ds = chip->ds; 1223fad09c73SVivien Didelot u16 output_ports = 0; 12240e7b9925SAndrew Lunn u16 reg; 12250e7b9925SAndrew Lunn int err; 1226fad09c73SVivien Didelot int i; 1227fad09c73SVivien Didelot 1228fad09c73SVivien Didelot /* allow CPU port or DSA link(s) to send frames to every port */ 1229fad09c73SVivien Didelot if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) { 1230fad09c73SVivien Didelot output_ports = mask; 1231fad09c73SVivien Didelot } else { 1232370b4ffbSVivien Didelot for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { 1233fad09c73SVivien Didelot /* allow sending frames to every group member */ 1234fad09c73SVivien Didelot if (bridge && chip->ports[i].bridge_dev == bridge) 1235fad09c73SVivien Didelot output_ports |= BIT(i); 1236fad09c73SVivien Didelot 1237fad09c73SVivien Didelot /* allow sending frames to CPU port and DSA link(s) */ 1238fad09c73SVivien Didelot if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i)) 1239fad09c73SVivien Didelot output_ports |= BIT(i); 1240fad09c73SVivien Didelot } 1241fad09c73SVivien Didelot } 1242fad09c73SVivien Didelot 1243fad09c73SVivien Didelot /* prevent frames from going back out of the port they came in on */ 1244fad09c73SVivien Didelot output_ports &= ~BIT(port); 1245fad09c73SVivien Didelot 12460e7b9925SAndrew Lunn err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, ®); 12470e7b9925SAndrew Lunn if (err) 12480e7b9925SAndrew Lunn return err; 1249fad09c73SVivien Didelot 1250fad09c73SVivien Didelot reg &= ~mask; 1251fad09c73SVivien Didelot reg |= output_ports & mask; 1252fad09c73SVivien Didelot 12530e7b9925SAndrew Lunn return mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg); 1254fad09c73SVivien Didelot } 1255fad09c73SVivien Didelot 1256fad09c73SVivien Didelot static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port, 1257fad09c73SVivien Didelot u8 state) 1258fad09c73SVivien Didelot { 125904bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 1260fad09c73SVivien Didelot int stp_state; 1261fad09c73SVivien Didelot int err; 1262fad09c73SVivien Didelot 1263fad09c73SVivien Didelot switch (state) { 1264fad09c73SVivien Didelot case BR_STATE_DISABLED: 1265fad09c73SVivien Didelot stp_state = PORT_CONTROL_STATE_DISABLED; 1266fad09c73SVivien Didelot break; 1267fad09c73SVivien Didelot case BR_STATE_BLOCKING: 1268fad09c73SVivien Didelot case BR_STATE_LISTENING: 1269fad09c73SVivien Didelot stp_state = PORT_CONTROL_STATE_BLOCKING; 1270fad09c73SVivien Didelot break; 1271fad09c73SVivien Didelot case BR_STATE_LEARNING: 1272fad09c73SVivien Didelot stp_state = PORT_CONTROL_STATE_LEARNING; 1273fad09c73SVivien Didelot break; 1274fad09c73SVivien Didelot case BR_STATE_FORWARDING: 1275fad09c73SVivien Didelot default: 1276fad09c73SVivien Didelot stp_state = PORT_CONTROL_STATE_FORWARDING; 1277fad09c73SVivien Didelot break; 1278fad09c73SVivien Didelot } 1279fad09c73SVivien Didelot 1280fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 1281e28def33SVivien Didelot err = mv88e6xxx_port_set_state(chip, port, stp_state); 1282fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 1283fad09c73SVivien Didelot 1284fad09c73SVivien Didelot if (err) 1285e28def33SVivien Didelot netdev_err(ds->ports[port].netdev, "failed to update state\n"); 1286fad09c73SVivien Didelot } 1287fad09c73SVivien Didelot 1288749efcb8SVivien Didelot static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port) 1289749efcb8SVivien Didelot { 1290749efcb8SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 1291749efcb8SVivien Didelot int err; 1292749efcb8SVivien Didelot 1293749efcb8SVivien Didelot mutex_lock(&chip->reg_lock); 1294749efcb8SVivien Didelot err = _mv88e6xxx_atu_remove(chip, 0, port, false); 1295749efcb8SVivien Didelot mutex_unlock(&chip->reg_lock); 1296749efcb8SVivien Didelot 1297749efcb8SVivien Didelot if (err) 1298749efcb8SVivien Didelot netdev_err(ds->ports[port].netdev, "failed to flush ATU\n"); 1299749efcb8SVivien Didelot } 1300749efcb8SVivien Didelot 1301fad09c73SVivien Didelot static int _mv88e6xxx_port_pvid(struct mv88e6xxx_chip *chip, int port, 1302fad09c73SVivien Didelot u16 *new, u16 *old) 1303fad09c73SVivien Didelot { 1304fad09c73SVivien Didelot struct dsa_switch *ds = chip->ds; 13050e7b9925SAndrew Lunn u16 pvid, reg; 13060e7b9925SAndrew Lunn int err; 1307fad09c73SVivien Didelot 13080e7b9925SAndrew Lunn err = mv88e6xxx_port_read(chip, port, PORT_DEFAULT_VLAN, ®); 13090e7b9925SAndrew Lunn if (err) 13100e7b9925SAndrew Lunn return err; 1311fad09c73SVivien Didelot 13120e7b9925SAndrew Lunn pvid = reg & PORT_DEFAULT_VLAN_MASK; 1313fad09c73SVivien Didelot 1314fad09c73SVivien Didelot if (new) { 13150e7b9925SAndrew Lunn reg &= ~PORT_DEFAULT_VLAN_MASK; 13160e7b9925SAndrew Lunn reg |= *new & PORT_DEFAULT_VLAN_MASK; 1317fad09c73SVivien Didelot 13180e7b9925SAndrew Lunn err = mv88e6xxx_port_write(chip, port, PORT_DEFAULT_VLAN, reg); 13190e7b9925SAndrew Lunn if (err) 13200e7b9925SAndrew Lunn return err; 1321fad09c73SVivien Didelot 1322fad09c73SVivien Didelot netdev_dbg(ds->ports[port].netdev, 1323fad09c73SVivien Didelot "DefaultVID %d (was %d)\n", *new, pvid); 1324fad09c73SVivien Didelot } 1325fad09c73SVivien Didelot 1326fad09c73SVivien Didelot if (old) 1327fad09c73SVivien Didelot *old = pvid; 1328fad09c73SVivien Didelot 1329fad09c73SVivien Didelot return 0; 1330fad09c73SVivien Didelot } 1331fad09c73SVivien Didelot 1332fad09c73SVivien Didelot static int _mv88e6xxx_port_pvid_get(struct mv88e6xxx_chip *chip, 1333fad09c73SVivien Didelot int port, u16 *pvid) 1334fad09c73SVivien Didelot { 1335fad09c73SVivien Didelot return _mv88e6xxx_port_pvid(chip, port, NULL, pvid); 1336fad09c73SVivien Didelot } 1337fad09c73SVivien Didelot 1338fad09c73SVivien Didelot static int _mv88e6xxx_port_pvid_set(struct mv88e6xxx_chip *chip, 1339fad09c73SVivien Didelot int port, u16 pvid) 1340fad09c73SVivien Didelot { 1341fad09c73SVivien Didelot return _mv88e6xxx_port_pvid(chip, port, &pvid, NULL); 1342fad09c73SVivien Didelot } 1343fad09c73SVivien Didelot 1344fad09c73SVivien Didelot static int _mv88e6xxx_vtu_wait(struct mv88e6xxx_chip *chip) 1345fad09c73SVivien Didelot { 1346a935c052SVivien Didelot return mv88e6xxx_g1_wait(chip, GLOBAL_VTU_OP, GLOBAL_VTU_OP_BUSY); 1347fad09c73SVivien Didelot } 1348fad09c73SVivien Didelot 1349fad09c73SVivien Didelot static int _mv88e6xxx_vtu_cmd(struct mv88e6xxx_chip *chip, u16 op) 1350fad09c73SVivien Didelot { 1351a935c052SVivien Didelot int err; 1352fad09c73SVivien Didelot 1353a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_OP, op); 1354a935c052SVivien Didelot if (err) 1355a935c052SVivien Didelot return err; 1356fad09c73SVivien Didelot 1357fad09c73SVivien Didelot return _mv88e6xxx_vtu_wait(chip); 1358fad09c73SVivien Didelot } 1359fad09c73SVivien Didelot 1360fad09c73SVivien Didelot static int _mv88e6xxx_vtu_stu_flush(struct mv88e6xxx_chip *chip) 1361fad09c73SVivien Didelot { 1362fad09c73SVivien Didelot int ret; 1363fad09c73SVivien Didelot 1364fad09c73SVivien Didelot ret = _mv88e6xxx_vtu_wait(chip); 1365fad09c73SVivien Didelot if (ret < 0) 1366fad09c73SVivien Didelot return ret; 1367fad09c73SVivien Didelot 1368fad09c73SVivien Didelot return _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_FLUSH_ALL); 1369fad09c73SVivien Didelot } 1370fad09c73SVivien Didelot 1371fad09c73SVivien Didelot static int _mv88e6xxx_vtu_stu_data_read(struct mv88e6xxx_chip *chip, 1372b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry *entry, 1373fad09c73SVivien Didelot unsigned int nibble_offset) 1374fad09c73SVivien Didelot { 1375fad09c73SVivien Didelot u16 regs[3]; 1376a935c052SVivien Didelot int i, err; 1377fad09c73SVivien Didelot 1378fad09c73SVivien Didelot for (i = 0; i < 3; ++i) { 1379a935c052SVivien Didelot u16 *reg = ®s[i]; 1380fad09c73SVivien Didelot 1381a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_DATA_0_3 + i, reg); 1382a935c052SVivien Didelot if (err) 1383a935c052SVivien Didelot return err; 1384fad09c73SVivien Didelot } 1385fad09c73SVivien Didelot 1386370b4ffbSVivien Didelot for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { 1387fad09c73SVivien Didelot unsigned int shift = (i % 4) * 4 + nibble_offset; 1388fad09c73SVivien Didelot u16 reg = regs[i / 4]; 1389fad09c73SVivien Didelot 1390fad09c73SVivien Didelot entry->data[i] = (reg >> shift) & GLOBAL_VTU_STU_DATA_MASK; 1391fad09c73SVivien Didelot } 1392fad09c73SVivien Didelot 1393fad09c73SVivien Didelot return 0; 1394fad09c73SVivien Didelot } 1395fad09c73SVivien Didelot 1396fad09c73SVivien Didelot static int mv88e6xxx_vtu_data_read(struct mv88e6xxx_chip *chip, 1397b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry *entry) 1398fad09c73SVivien Didelot { 1399fad09c73SVivien Didelot return _mv88e6xxx_vtu_stu_data_read(chip, entry, 0); 1400fad09c73SVivien Didelot } 1401fad09c73SVivien Didelot 1402fad09c73SVivien Didelot static int mv88e6xxx_stu_data_read(struct mv88e6xxx_chip *chip, 1403b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry *entry) 1404fad09c73SVivien Didelot { 1405fad09c73SVivien Didelot return _mv88e6xxx_vtu_stu_data_read(chip, entry, 2); 1406fad09c73SVivien Didelot } 1407fad09c73SVivien Didelot 1408fad09c73SVivien Didelot static int _mv88e6xxx_vtu_stu_data_write(struct mv88e6xxx_chip *chip, 1409b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry *entry, 1410fad09c73SVivien Didelot unsigned int nibble_offset) 1411fad09c73SVivien Didelot { 1412fad09c73SVivien Didelot u16 regs[3] = { 0 }; 1413a935c052SVivien Didelot int i, err; 1414fad09c73SVivien Didelot 1415370b4ffbSVivien Didelot for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { 1416fad09c73SVivien Didelot unsigned int shift = (i % 4) * 4 + nibble_offset; 1417fad09c73SVivien Didelot u8 data = entry->data[i]; 1418fad09c73SVivien Didelot 1419fad09c73SVivien Didelot regs[i / 4] |= (data & GLOBAL_VTU_STU_DATA_MASK) << shift; 1420fad09c73SVivien Didelot } 1421fad09c73SVivien Didelot 1422fad09c73SVivien Didelot for (i = 0; i < 3; ++i) { 1423a935c052SVivien Didelot u16 reg = regs[i]; 1424a935c052SVivien Didelot 1425a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_DATA_0_3 + i, reg); 1426a935c052SVivien Didelot if (err) 1427a935c052SVivien Didelot return err; 1428fad09c73SVivien Didelot } 1429fad09c73SVivien Didelot 1430fad09c73SVivien Didelot return 0; 1431fad09c73SVivien Didelot } 1432fad09c73SVivien Didelot 1433fad09c73SVivien Didelot static int mv88e6xxx_vtu_data_write(struct mv88e6xxx_chip *chip, 1434b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry *entry) 1435fad09c73SVivien Didelot { 1436fad09c73SVivien Didelot return _mv88e6xxx_vtu_stu_data_write(chip, entry, 0); 1437fad09c73SVivien Didelot } 1438fad09c73SVivien Didelot 1439fad09c73SVivien Didelot static int mv88e6xxx_stu_data_write(struct mv88e6xxx_chip *chip, 1440b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry *entry) 1441fad09c73SVivien Didelot { 1442fad09c73SVivien Didelot return _mv88e6xxx_vtu_stu_data_write(chip, entry, 2); 1443fad09c73SVivien Didelot } 1444fad09c73SVivien Didelot 1445fad09c73SVivien Didelot static int _mv88e6xxx_vtu_vid_write(struct mv88e6xxx_chip *chip, u16 vid) 1446fad09c73SVivien Didelot { 1447a935c052SVivien Didelot return mv88e6xxx_g1_write(chip, GLOBAL_VTU_VID, 1448fad09c73SVivien Didelot vid & GLOBAL_VTU_VID_MASK); 1449fad09c73SVivien Didelot } 1450fad09c73SVivien Didelot 1451fad09c73SVivien Didelot static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_chip *chip, 1452b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry *entry) 1453fad09c73SVivien Didelot { 1454b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry next = { 0 }; 1455a935c052SVivien Didelot u16 val; 1456a935c052SVivien Didelot int err; 1457fad09c73SVivien Didelot 1458a935c052SVivien Didelot err = _mv88e6xxx_vtu_wait(chip); 1459a935c052SVivien Didelot if (err) 1460a935c052SVivien Didelot return err; 1461fad09c73SVivien Didelot 1462a935c052SVivien Didelot err = _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_VTU_GET_NEXT); 1463a935c052SVivien Didelot if (err) 1464a935c052SVivien Didelot return err; 1465fad09c73SVivien Didelot 1466a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_VID, &val); 1467a935c052SVivien Didelot if (err) 1468a935c052SVivien Didelot return err; 1469fad09c73SVivien Didelot 1470a935c052SVivien Didelot next.vid = val & GLOBAL_VTU_VID_MASK; 1471a935c052SVivien Didelot next.valid = !!(val & GLOBAL_VTU_VID_VALID); 1472fad09c73SVivien Didelot 1473fad09c73SVivien Didelot if (next.valid) { 1474a935c052SVivien Didelot err = mv88e6xxx_vtu_data_read(chip, &next); 1475a935c052SVivien Didelot if (err) 1476a935c052SVivien Didelot return err; 1477fad09c73SVivien Didelot 14786dc10bbcSVivien Didelot if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G1_VTU_FID)) { 1479a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_FID, &val); 1480a935c052SVivien Didelot if (err) 1481a935c052SVivien Didelot return err; 1482fad09c73SVivien Didelot 1483a935c052SVivien Didelot next.fid = val & GLOBAL_VTU_FID_MASK; 1484fad09c73SVivien Didelot } else if (mv88e6xxx_num_databases(chip) == 256) { 1485fad09c73SVivien Didelot /* VTU DBNum[7:4] are located in VTU Operation 11:8, and 1486fad09c73SVivien Didelot * VTU DBNum[3:0] are located in VTU Operation 3:0 1487fad09c73SVivien Didelot */ 1488a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_OP, &val); 1489a935c052SVivien Didelot if (err) 1490a935c052SVivien Didelot return err; 1491fad09c73SVivien Didelot 1492a935c052SVivien Didelot next.fid = (val & 0xf00) >> 4; 1493a935c052SVivien Didelot next.fid |= val & 0xf; 1494fad09c73SVivien Didelot } 1495fad09c73SVivien Didelot 1496fad09c73SVivien Didelot if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_STU)) { 1497a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_SID, &val); 1498a935c052SVivien Didelot if (err) 1499a935c052SVivien Didelot return err; 1500fad09c73SVivien Didelot 1501a935c052SVivien Didelot next.sid = val & GLOBAL_VTU_SID_MASK; 1502fad09c73SVivien Didelot } 1503fad09c73SVivien Didelot } 1504fad09c73SVivien Didelot 1505fad09c73SVivien Didelot *entry = next; 1506fad09c73SVivien Didelot return 0; 1507fad09c73SVivien Didelot } 1508fad09c73SVivien Didelot 1509fad09c73SVivien Didelot static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port, 1510fad09c73SVivien Didelot struct switchdev_obj_port_vlan *vlan, 1511fad09c73SVivien Didelot int (*cb)(struct switchdev_obj *obj)) 1512fad09c73SVivien Didelot { 151304bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 1514b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry next; 1515fad09c73SVivien Didelot u16 pvid; 1516fad09c73SVivien Didelot int err; 1517fad09c73SVivien Didelot 1518fad09c73SVivien Didelot if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU)) 1519fad09c73SVivien Didelot return -EOPNOTSUPP; 1520fad09c73SVivien Didelot 1521fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 1522fad09c73SVivien Didelot 1523fad09c73SVivien Didelot err = _mv88e6xxx_port_pvid_get(chip, port, &pvid); 1524fad09c73SVivien Didelot if (err) 1525fad09c73SVivien Didelot goto unlock; 1526fad09c73SVivien Didelot 1527fad09c73SVivien Didelot err = _mv88e6xxx_vtu_vid_write(chip, GLOBAL_VTU_VID_MASK); 1528fad09c73SVivien Didelot if (err) 1529fad09c73SVivien Didelot goto unlock; 1530fad09c73SVivien Didelot 1531fad09c73SVivien Didelot do { 1532fad09c73SVivien Didelot err = _mv88e6xxx_vtu_getnext(chip, &next); 1533fad09c73SVivien Didelot if (err) 1534fad09c73SVivien Didelot break; 1535fad09c73SVivien Didelot 1536fad09c73SVivien Didelot if (!next.valid) 1537fad09c73SVivien Didelot break; 1538fad09c73SVivien Didelot 1539fad09c73SVivien Didelot if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) 1540fad09c73SVivien Didelot continue; 1541fad09c73SVivien Didelot 1542fad09c73SVivien Didelot /* reinit and dump this VLAN obj */ 1543fad09c73SVivien Didelot vlan->vid_begin = next.vid; 1544fad09c73SVivien Didelot vlan->vid_end = next.vid; 1545fad09c73SVivien Didelot vlan->flags = 0; 1546fad09c73SVivien Didelot 1547fad09c73SVivien Didelot if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED) 1548fad09c73SVivien Didelot vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED; 1549fad09c73SVivien Didelot 1550fad09c73SVivien Didelot if (next.vid == pvid) 1551fad09c73SVivien Didelot vlan->flags |= BRIDGE_VLAN_INFO_PVID; 1552fad09c73SVivien Didelot 1553fad09c73SVivien Didelot err = cb(&vlan->obj); 1554fad09c73SVivien Didelot if (err) 1555fad09c73SVivien Didelot break; 1556fad09c73SVivien Didelot } while (next.vid < GLOBAL_VTU_VID_MASK); 1557fad09c73SVivien Didelot 1558fad09c73SVivien Didelot unlock: 1559fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 1560fad09c73SVivien Didelot 1561fad09c73SVivien Didelot return err; 1562fad09c73SVivien Didelot } 1563fad09c73SVivien Didelot 1564fad09c73SVivien Didelot static int _mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_chip *chip, 1565b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry *entry) 1566fad09c73SVivien Didelot { 1567fad09c73SVivien Didelot u16 op = GLOBAL_VTU_OP_VTU_LOAD_PURGE; 1568fad09c73SVivien Didelot u16 reg = 0; 1569a935c052SVivien Didelot int err; 1570fad09c73SVivien Didelot 1571a935c052SVivien Didelot err = _mv88e6xxx_vtu_wait(chip); 1572a935c052SVivien Didelot if (err) 1573a935c052SVivien Didelot return err; 1574fad09c73SVivien Didelot 1575fad09c73SVivien Didelot if (!entry->valid) 1576fad09c73SVivien Didelot goto loadpurge; 1577fad09c73SVivien Didelot 1578fad09c73SVivien Didelot /* Write port member tags */ 1579a935c052SVivien Didelot err = mv88e6xxx_vtu_data_write(chip, entry); 1580a935c052SVivien Didelot if (err) 1581a935c052SVivien Didelot return err; 1582fad09c73SVivien Didelot 1583fad09c73SVivien Didelot if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_STU)) { 1584fad09c73SVivien Didelot reg = entry->sid & GLOBAL_VTU_SID_MASK; 1585a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_SID, reg); 1586a935c052SVivien Didelot if (err) 1587a935c052SVivien Didelot return err; 1588fad09c73SVivien Didelot } 1589fad09c73SVivien Didelot 15906dc10bbcSVivien Didelot if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G1_VTU_FID)) { 1591fad09c73SVivien Didelot reg = entry->fid & GLOBAL_VTU_FID_MASK; 1592a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_FID, reg); 1593a935c052SVivien Didelot if (err) 1594a935c052SVivien Didelot return err; 1595fad09c73SVivien Didelot } else if (mv88e6xxx_num_databases(chip) == 256) { 1596fad09c73SVivien Didelot /* VTU DBNum[7:4] are located in VTU Operation 11:8, and 1597fad09c73SVivien Didelot * VTU DBNum[3:0] are located in VTU Operation 3:0 1598fad09c73SVivien Didelot */ 1599fad09c73SVivien Didelot op |= (entry->fid & 0xf0) << 8; 1600fad09c73SVivien Didelot op |= entry->fid & 0xf; 1601fad09c73SVivien Didelot } 1602fad09c73SVivien Didelot 1603fad09c73SVivien Didelot reg = GLOBAL_VTU_VID_VALID; 1604fad09c73SVivien Didelot loadpurge: 1605fad09c73SVivien Didelot reg |= entry->vid & GLOBAL_VTU_VID_MASK; 1606a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_VID, reg); 1607a935c052SVivien Didelot if (err) 1608a935c052SVivien Didelot return err; 1609fad09c73SVivien Didelot 1610fad09c73SVivien Didelot return _mv88e6xxx_vtu_cmd(chip, op); 1611fad09c73SVivien Didelot } 1612fad09c73SVivien Didelot 1613fad09c73SVivien Didelot static int _mv88e6xxx_stu_getnext(struct mv88e6xxx_chip *chip, u8 sid, 1614b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry *entry) 1615fad09c73SVivien Didelot { 1616b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry next = { 0 }; 1617a935c052SVivien Didelot u16 val; 1618a935c052SVivien Didelot int err; 1619fad09c73SVivien Didelot 1620a935c052SVivien Didelot err = _mv88e6xxx_vtu_wait(chip); 1621a935c052SVivien Didelot if (err) 1622a935c052SVivien Didelot return err; 1623fad09c73SVivien Didelot 1624a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_SID, 1625fad09c73SVivien Didelot sid & GLOBAL_VTU_SID_MASK); 1626a935c052SVivien Didelot if (err) 1627a935c052SVivien Didelot return err; 1628fad09c73SVivien Didelot 1629a935c052SVivien Didelot err = _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_STU_GET_NEXT); 1630a935c052SVivien Didelot if (err) 1631a935c052SVivien Didelot return err; 1632fad09c73SVivien Didelot 1633a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_SID, &val); 1634a935c052SVivien Didelot if (err) 1635a935c052SVivien Didelot return err; 1636fad09c73SVivien Didelot 1637a935c052SVivien Didelot next.sid = val & GLOBAL_VTU_SID_MASK; 1638fad09c73SVivien Didelot 1639a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_VID, &val); 1640a935c052SVivien Didelot if (err) 1641a935c052SVivien Didelot return err; 1642fad09c73SVivien Didelot 1643a935c052SVivien Didelot next.valid = !!(val & GLOBAL_VTU_VID_VALID); 1644fad09c73SVivien Didelot 1645fad09c73SVivien Didelot if (next.valid) { 1646a935c052SVivien Didelot err = mv88e6xxx_stu_data_read(chip, &next); 1647a935c052SVivien Didelot if (err) 1648a935c052SVivien Didelot return err; 1649fad09c73SVivien Didelot } 1650fad09c73SVivien Didelot 1651fad09c73SVivien Didelot *entry = next; 1652fad09c73SVivien Didelot return 0; 1653fad09c73SVivien Didelot } 1654fad09c73SVivien Didelot 1655fad09c73SVivien Didelot static int _mv88e6xxx_stu_loadpurge(struct mv88e6xxx_chip *chip, 1656b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry *entry) 1657fad09c73SVivien Didelot { 1658fad09c73SVivien Didelot u16 reg = 0; 1659a935c052SVivien Didelot int err; 1660fad09c73SVivien Didelot 1661a935c052SVivien Didelot err = _mv88e6xxx_vtu_wait(chip); 1662a935c052SVivien Didelot if (err) 1663a935c052SVivien Didelot return err; 1664fad09c73SVivien Didelot 1665fad09c73SVivien Didelot if (!entry->valid) 1666fad09c73SVivien Didelot goto loadpurge; 1667fad09c73SVivien Didelot 1668fad09c73SVivien Didelot /* Write port states */ 1669a935c052SVivien Didelot err = mv88e6xxx_stu_data_write(chip, entry); 1670a935c052SVivien Didelot if (err) 1671a935c052SVivien Didelot return err; 1672fad09c73SVivien Didelot 1673fad09c73SVivien Didelot reg = GLOBAL_VTU_VID_VALID; 1674fad09c73SVivien Didelot loadpurge: 1675a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_VID, reg); 1676a935c052SVivien Didelot if (err) 1677a935c052SVivien Didelot return err; 1678fad09c73SVivien Didelot 1679fad09c73SVivien Didelot reg = entry->sid & GLOBAL_VTU_SID_MASK; 1680a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_SID, reg); 1681a935c052SVivien Didelot if (err) 1682a935c052SVivien Didelot return err; 1683fad09c73SVivien Didelot 1684fad09c73SVivien Didelot return _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_STU_LOAD_PURGE); 1685fad09c73SVivien Didelot } 1686fad09c73SVivien Didelot 1687fad09c73SVivien Didelot static int _mv88e6xxx_port_fid(struct mv88e6xxx_chip *chip, int port, 1688fad09c73SVivien Didelot u16 *new, u16 *old) 1689fad09c73SVivien Didelot { 1690fad09c73SVivien Didelot struct dsa_switch *ds = chip->ds; 1691fad09c73SVivien Didelot u16 upper_mask; 1692fad09c73SVivien Didelot u16 fid; 16930e7b9925SAndrew Lunn u16 reg; 16940e7b9925SAndrew Lunn int err; 1695fad09c73SVivien Didelot 1696fad09c73SVivien Didelot if (mv88e6xxx_num_databases(chip) == 4096) 1697fad09c73SVivien Didelot upper_mask = 0xff; 1698fad09c73SVivien Didelot else if (mv88e6xxx_num_databases(chip) == 256) 1699fad09c73SVivien Didelot upper_mask = 0xf; 1700fad09c73SVivien Didelot else 1701fad09c73SVivien Didelot return -EOPNOTSUPP; 1702fad09c73SVivien Didelot 1703fad09c73SVivien Didelot /* Port's default FID bits 3:0 are located in reg 0x06, offset 12 */ 17040e7b9925SAndrew Lunn err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, ®); 17050e7b9925SAndrew Lunn if (err) 17060e7b9925SAndrew Lunn return err; 1707fad09c73SVivien Didelot 17080e7b9925SAndrew Lunn fid = (reg & PORT_BASE_VLAN_FID_3_0_MASK) >> 12; 1709fad09c73SVivien Didelot 1710fad09c73SVivien Didelot if (new) { 17110e7b9925SAndrew Lunn reg &= ~PORT_BASE_VLAN_FID_3_0_MASK; 17120e7b9925SAndrew Lunn reg |= (*new << 12) & PORT_BASE_VLAN_FID_3_0_MASK; 1713fad09c73SVivien Didelot 17140e7b9925SAndrew Lunn err = mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg); 17150e7b9925SAndrew Lunn if (err) 17160e7b9925SAndrew Lunn return err; 1717fad09c73SVivien Didelot } 1718fad09c73SVivien Didelot 1719fad09c73SVivien Didelot /* Port's default FID bits 11:4 are located in reg 0x05, offset 0 */ 17200e7b9925SAndrew Lunn err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, ®); 17210e7b9925SAndrew Lunn if (err) 17220e7b9925SAndrew Lunn return err; 1723fad09c73SVivien Didelot 17240e7b9925SAndrew Lunn fid |= (reg & upper_mask) << 4; 1725fad09c73SVivien Didelot 1726fad09c73SVivien Didelot if (new) { 17270e7b9925SAndrew Lunn reg &= ~upper_mask; 17280e7b9925SAndrew Lunn reg |= (*new >> 4) & upper_mask; 1729fad09c73SVivien Didelot 17300e7b9925SAndrew Lunn err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_1, reg); 17310e7b9925SAndrew Lunn if (err) 17320e7b9925SAndrew Lunn return err; 1733fad09c73SVivien Didelot 1734fad09c73SVivien Didelot netdev_dbg(ds->ports[port].netdev, 1735fad09c73SVivien Didelot "FID %d (was %d)\n", *new, fid); 1736fad09c73SVivien Didelot } 1737fad09c73SVivien Didelot 1738fad09c73SVivien Didelot if (old) 1739fad09c73SVivien Didelot *old = fid; 1740fad09c73SVivien Didelot 1741fad09c73SVivien Didelot return 0; 1742fad09c73SVivien Didelot } 1743fad09c73SVivien Didelot 1744fad09c73SVivien Didelot static int _mv88e6xxx_port_fid_get(struct mv88e6xxx_chip *chip, 1745fad09c73SVivien Didelot int port, u16 *fid) 1746fad09c73SVivien Didelot { 1747fad09c73SVivien Didelot return _mv88e6xxx_port_fid(chip, port, NULL, fid); 1748fad09c73SVivien Didelot } 1749fad09c73SVivien Didelot 1750fad09c73SVivien Didelot static int _mv88e6xxx_port_fid_set(struct mv88e6xxx_chip *chip, 1751fad09c73SVivien Didelot int port, u16 fid) 1752fad09c73SVivien Didelot { 1753fad09c73SVivien Didelot return _mv88e6xxx_port_fid(chip, port, &fid, NULL); 1754fad09c73SVivien Didelot } 1755fad09c73SVivien Didelot 1756fad09c73SVivien Didelot static int _mv88e6xxx_fid_new(struct mv88e6xxx_chip *chip, u16 *fid) 1757fad09c73SVivien Didelot { 1758fad09c73SVivien Didelot DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID); 1759b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry vlan; 1760fad09c73SVivien Didelot int i, err; 1761fad09c73SVivien Didelot 1762fad09c73SVivien Didelot bitmap_zero(fid_bitmap, MV88E6XXX_N_FID); 1763fad09c73SVivien Didelot 1764fad09c73SVivien Didelot /* Set every FID bit used by the (un)bridged ports */ 1765370b4ffbSVivien Didelot for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { 1766fad09c73SVivien Didelot err = _mv88e6xxx_port_fid_get(chip, i, fid); 1767fad09c73SVivien Didelot if (err) 1768fad09c73SVivien Didelot return err; 1769fad09c73SVivien Didelot 1770fad09c73SVivien Didelot set_bit(*fid, fid_bitmap); 1771fad09c73SVivien Didelot } 1772fad09c73SVivien Didelot 1773fad09c73SVivien Didelot /* Set every FID bit used by the VLAN entries */ 1774fad09c73SVivien Didelot err = _mv88e6xxx_vtu_vid_write(chip, GLOBAL_VTU_VID_MASK); 1775fad09c73SVivien Didelot if (err) 1776fad09c73SVivien Didelot return err; 1777fad09c73SVivien Didelot 1778fad09c73SVivien Didelot do { 1779fad09c73SVivien Didelot err = _mv88e6xxx_vtu_getnext(chip, &vlan); 1780fad09c73SVivien Didelot if (err) 1781fad09c73SVivien Didelot return err; 1782fad09c73SVivien Didelot 1783fad09c73SVivien Didelot if (!vlan.valid) 1784fad09c73SVivien Didelot break; 1785fad09c73SVivien Didelot 1786fad09c73SVivien Didelot set_bit(vlan.fid, fid_bitmap); 1787fad09c73SVivien Didelot } while (vlan.vid < GLOBAL_VTU_VID_MASK); 1788fad09c73SVivien Didelot 1789fad09c73SVivien Didelot /* The reset value 0x000 is used to indicate that multiple address 1790fad09c73SVivien Didelot * databases are not needed. Return the next positive available. 1791fad09c73SVivien Didelot */ 1792fad09c73SVivien Didelot *fid = find_next_zero_bit(fid_bitmap, MV88E6XXX_N_FID, 1); 1793fad09c73SVivien Didelot if (unlikely(*fid >= mv88e6xxx_num_databases(chip))) 1794fad09c73SVivien Didelot return -ENOSPC; 1795fad09c73SVivien Didelot 1796fad09c73SVivien Didelot /* Clear the database */ 1797fad09c73SVivien Didelot return _mv88e6xxx_atu_flush(chip, *fid, true); 1798fad09c73SVivien Didelot } 1799fad09c73SVivien Didelot 1800fad09c73SVivien Didelot static int _mv88e6xxx_vtu_new(struct mv88e6xxx_chip *chip, u16 vid, 1801b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry *entry) 1802fad09c73SVivien Didelot { 1803fad09c73SVivien Didelot struct dsa_switch *ds = chip->ds; 1804b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry vlan = { 1805fad09c73SVivien Didelot .valid = true, 1806fad09c73SVivien Didelot .vid = vid, 1807fad09c73SVivien Didelot }; 1808fad09c73SVivien Didelot int i, err; 1809fad09c73SVivien Didelot 1810fad09c73SVivien Didelot err = _mv88e6xxx_fid_new(chip, &vlan.fid); 1811fad09c73SVivien Didelot if (err) 1812fad09c73SVivien Didelot return err; 1813fad09c73SVivien Didelot 1814fad09c73SVivien Didelot /* exclude all ports except the CPU and DSA ports */ 1815370b4ffbSVivien Didelot for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) 1816fad09c73SVivien Didelot vlan.data[i] = dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i) 1817fad09c73SVivien Didelot ? GLOBAL_VTU_DATA_MEMBER_TAG_UNMODIFIED 1818fad09c73SVivien Didelot : GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER; 1819fad09c73SVivien Didelot 1820fad09c73SVivien Didelot if (mv88e6xxx_6097_family(chip) || mv88e6xxx_6165_family(chip) || 1821fad09c73SVivien Didelot mv88e6xxx_6351_family(chip) || mv88e6xxx_6352_family(chip)) { 1822b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry vstp; 1823fad09c73SVivien Didelot 1824fad09c73SVivien Didelot /* Adding a VTU entry requires a valid STU entry. As VSTP is not 1825fad09c73SVivien Didelot * implemented, only one STU entry is needed to cover all VTU 1826fad09c73SVivien Didelot * entries. Thus, validate the SID 0. 1827fad09c73SVivien Didelot */ 1828fad09c73SVivien Didelot vlan.sid = 0; 1829fad09c73SVivien Didelot err = _mv88e6xxx_stu_getnext(chip, GLOBAL_VTU_SID_MASK, &vstp); 1830fad09c73SVivien Didelot if (err) 1831fad09c73SVivien Didelot return err; 1832fad09c73SVivien Didelot 1833fad09c73SVivien Didelot if (vstp.sid != vlan.sid || !vstp.valid) { 1834fad09c73SVivien Didelot memset(&vstp, 0, sizeof(vstp)); 1835fad09c73SVivien Didelot vstp.valid = true; 1836fad09c73SVivien Didelot vstp.sid = vlan.sid; 1837fad09c73SVivien Didelot 1838fad09c73SVivien Didelot err = _mv88e6xxx_stu_loadpurge(chip, &vstp); 1839fad09c73SVivien Didelot if (err) 1840fad09c73SVivien Didelot return err; 1841fad09c73SVivien Didelot } 1842fad09c73SVivien Didelot } 1843fad09c73SVivien Didelot 1844fad09c73SVivien Didelot *entry = vlan; 1845fad09c73SVivien Didelot return 0; 1846fad09c73SVivien Didelot } 1847fad09c73SVivien Didelot 1848fad09c73SVivien Didelot static int _mv88e6xxx_vtu_get(struct mv88e6xxx_chip *chip, u16 vid, 1849b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry *entry, bool creat) 1850fad09c73SVivien Didelot { 1851fad09c73SVivien Didelot int err; 1852fad09c73SVivien Didelot 1853fad09c73SVivien Didelot if (!vid) 1854fad09c73SVivien Didelot return -EINVAL; 1855fad09c73SVivien Didelot 1856fad09c73SVivien Didelot err = _mv88e6xxx_vtu_vid_write(chip, vid - 1); 1857fad09c73SVivien Didelot if (err) 1858fad09c73SVivien Didelot return err; 1859fad09c73SVivien Didelot 1860fad09c73SVivien Didelot err = _mv88e6xxx_vtu_getnext(chip, entry); 1861fad09c73SVivien Didelot if (err) 1862fad09c73SVivien Didelot return err; 1863fad09c73SVivien Didelot 1864fad09c73SVivien Didelot if (entry->vid != vid || !entry->valid) { 1865fad09c73SVivien Didelot if (!creat) 1866fad09c73SVivien Didelot return -EOPNOTSUPP; 1867fad09c73SVivien Didelot /* -ENOENT would've been more appropriate, but switchdev expects 1868fad09c73SVivien Didelot * -EOPNOTSUPP to inform bridge about an eventual software VLAN. 1869fad09c73SVivien Didelot */ 1870fad09c73SVivien Didelot 1871fad09c73SVivien Didelot err = _mv88e6xxx_vtu_new(chip, vid, entry); 1872fad09c73SVivien Didelot } 1873fad09c73SVivien Didelot 1874fad09c73SVivien Didelot return err; 1875fad09c73SVivien Didelot } 1876fad09c73SVivien Didelot 1877fad09c73SVivien Didelot static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port, 1878fad09c73SVivien Didelot u16 vid_begin, u16 vid_end) 1879fad09c73SVivien Didelot { 188004bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 1881b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry vlan; 1882fad09c73SVivien Didelot int i, err; 1883fad09c73SVivien Didelot 1884fad09c73SVivien Didelot if (!vid_begin) 1885fad09c73SVivien Didelot return -EOPNOTSUPP; 1886fad09c73SVivien Didelot 1887fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 1888fad09c73SVivien Didelot 1889fad09c73SVivien Didelot err = _mv88e6xxx_vtu_vid_write(chip, vid_begin - 1); 1890fad09c73SVivien Didelot if (err) 1891fad09c73SVivien Didelot goto unlock; 1892fad09c73SVivien Didelot 1893fad09c73SVivien Didelot do { 1894fad09c73SVivien Didelot err = _mv88e6xxx_vtu_getnext(chip, &vlan); 1895fad09c73SVivien Didelot if (err) 1896fad09c73SVivien Didelot goto unlock; 1897fad09c73SVivien Didelot 1898fad09c73SVivien Didelot if (!vlan.valid) 1899fad09c73SVivien Didelot break; 1900fad09c73SVivien Didelot 1901fad09c73SVivien Didelot if (vlan.vid > vid_end) 1902fad09c73SVivien Didelot break; 1903fad09c73SVivien Didelot 1904370b4ffbSVivien Didelot for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { 1905fad09c73SVivien Didelot if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i)) 1906fad09c73SVivien Didelot continue; 1907fad09c73SVivien Didelot 1908fad09c73SVivien Didelot if (vlan.data[i] == 1909fad09c73SVivien Didelot GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) 1910fad09c73SVivien Didelot continue; 1911fad09c73SVivien Didelot 1912fad09c73SVivien Didelot if (chip->ports[i].bridge_dev == 1913fad09c73SVivien Didelot chip->ports[port].bridge_dev) 1914fad09c73SVivien Didelot break; /* same bridge, check next VLAN */ 1915fad09c73SVivien Didelot 1916fad09c73SVivien Didelot netdev_warn(ds->ports[port].netdev, 1917fad09c73SVivien Didelot "hardware VLAN %d already used by %s\n", 1918fad09c73SVivien Didelot vlan.vid, 1919fad09c73SVivien Didelot netdev_name(chip->ports[i].bridge_dev)); 1920fad09c73SVivien Didelot err = -EOPNOTSUPP; 1921fad09c73SVivien Didelot goto unlock; 1922fad09c73SVivien Didelot } 1923fad09c73SVivien Didelot } while (vlan.vid < vid_end); 1924fad09c73SVivien Didelot 1925fad09c73SVivien Didelot unlock: 1926fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 1927fad09c73SVivien Didelot 1928fad09c73SVivien Didelot return err; 1929fad09c73SVivien Didelot } 1930fad09c73SVivien Didelot 1931fad09c73SVivien Didelot static const char * const mv88e6xxx_port_8021q_mode_names[] = { 1932fad09c73SVivien Didelot [PORT_CONTROL_2_8021Q_DISABLED] = "Disabled", 1933fad09c73SVivien Didelot [PORT_CONTROL_2_8021Q_FALLBACK] = "Fallback", 1934fad09c73SVivien Didelot [PORT_CONTROL_2_8021Q_CHECK] = "Check", 1935fad09c73SVivien Didelot [PORT_CONTROL_2_8021Q_SECURE] = "Secure", 1936fad09c73SVivien Didelot }; 1937fad09c73SVivien Didelot 1938fad09c73SVivien Didelot static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port, 1939fad09c73SVivien Didelot bool vlan_filtering) 1940fad09c73SVivien Didelot { 194104bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 1942fad09c73SVivien Didelot u16 old, new = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE : 1943fad09c73SVivien Didelot PORT_CONTROL_2_8021Q_DISABLED; 19440e7b9925SAndrew Lunn u16 reg; 19450e7b9925SAndrew Lunn int err; 1946fad09c73SVivien Didelot 1947fad09c73SVivien Didelot if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU)) 1948fad09c73SVivien Didelot return -EOPNOTSUPP; 1949fad09c73SVivien Didelot 1950fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 1951fad09c73SVivien Didelot 19520e7b9925SAndrew Lunn err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, ®); 19530e7b9925SAndrew Lunn if (err) 1954fad09c73SVivien Didelot goto unlock; 1955fad09c73SVivien Didelot 19560e7b9925SAndrew Lunn old = reg & PORT_CONTROL_2_8021Q_MASK; 1957fad09c73SVivien Didelot 1958fad09c73SVivien Didelot if (new != old) { 19590e7b9925SAndrew Lunn reg &= ~PORT_CONTROL_2_8021Q_MASK; 19600e7b9925SAndrew Lunn reg |= new & PORT_CONTROL_2_8021Q_MASK; 1961fad09c73SVivien Didelot 19620e7b9925SAndrew Lunn err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg); 19630e7b9925SAndrew Lunn if (err) 1964fad09c73SVivien Didelot goto unlock; 1965fad09c73SVivien Didelot 1966fad09c73SVivien Didelot netdev_dbg(ds->ports[port].netdev, "802.1Q Mode %s (was %s)\n", 1967fad09c73SVivien Didelot mv88e6xxx_port_8021q_mode_names[new], 1968fad09c73SVivien Didelot mv88e6xxx_port_8021q_mode_names[old]); 1969fad09c73SVivien Didelot } 1970fad09c73SVivien Didelot 19710e7b9925SAndrew Lunn err = 0; 1972fad09c73SVivien Didelot unlock: 1973fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 1974fad09c73SVivien Didelot 19750e7b9925SAndrew Lunn return err; 1976fad09c73SVivien Didelot } 1977fad09c73SVivien Didelot 1978fad09c73SVivien Didelot static int 1979fad09c73SVivien Didelot mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port, 1980fad09c73SVivien Didelot const struct switchdev_obj_port_vlan *vlan, 1981fad09c73SVivien Didelot struct switchdev_trans *trans) 1982fad09c73SVivien Didelot { 198304bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 1984fad09c73SVivien Didelot int err; 1985fad09c73SVivien Didelot 1986fad09c73SVivien Didelot if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU)) 1987fad09c73SVivien Didelot return -EOPNOTSUPP; 1988fad09c73SVivien Didelot 1989fad09c73SVivien Didelot /* If the requested port doesn't belong to the same bridge as the VLAN 1990fad09c73SVivien Didelot * members, do not support it (yet) and fallback to software VLAN. 1991fad09c73SVivien Didelot */ 1992fad09c73SVivien Didelot err = mv88e6xxx_port_check_hw_vlan(ds, port, vlan->vid_begin, 1993fad09c73SVivien Didelot vlan->vid_end); 1994fad09c73SVivien Didelot if (err) 1995fad09c73SVivien Didelot return err; 1996fad09c73SVivien Didelot 1997fad09c73SVivien Didelot /* We don't need any dynamic resource from the kernel (yet), 1998fad09c73SVivien Didelot * so skip the prepare phase. 1999fad09c73SVivien Didelot */ 2000fad09c73SVivien Didelot return 0; 2001fad09c73SVivien Didelot } 2002fad09c73SVivien Didelot 2003fad09c73SVivien Didelot static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_chip *chip, int port, 2004fad09c73SVivien Didelot u16 vid, bool untagged) 2005fad09c73SVivien Didelot { 2006b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry vlan; 2007fad09c73SVivien Didelot int err; 2008fad09c73SVivien Didelot 2009fad09c73SVivien Didelot err = _mv88e6xxx_vtu_get(chip, vid, &vlan, true); 2010fad09c73SVivien Didelot if (err) 2011fad09c73SVivien Didelot return err; 2012fad09c73SVivien Didelot 2013fad09c73SVivien Didelot vlan.data[port] = untagged ? 2014fad09c73SVivien Didelot GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED : 2015fad09c73SVivien Didelot GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED; 2016fad09c73SVivien Didelot 2017fad09c73SVivien Didelot return _mv88e6xxx_vtu_loadpurge(chip, &vlan); 2018fad09c73SVivien Didelot } 2019fad09c73SVivien Didelot 2020fad09c73SVivien Didelot static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, 2021fad09c73SVivien Didelot const struct switchdev_obj_port_vlan *vlan, 2022fad09c73SVivien Didelot struct switchdev_trans *trans) 2023fad09c73SVivien Didelot { 202404bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 2025fad09c73SVivien Didelot bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; 2026fad09c73SVivien Didelot bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; 2027fad09c73SVivien Didelot u16 vid; 2028fad09c73SVivien Didelot 2029fad09c73SVivien Didelot if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU)) 2030fad09c73SVivien Didelot return; 2031fad09c73SVivien Didelot 2032fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 2033fad09c73SVivien Didelot 2034fad09c73SVivien Didelot for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) 2035fad09c73SVivien Didelot if (_mv88e6xxx_port_vlan_add(chip, port, vid, untagged)) 2036fad09c73SVivien Didelot netdev_err(ds->ports[port].netdev, 2037fad09c73SVivien Didelot "failed to add VLAN %d%c\n", 2038fad09c73SVivien Didelot vid, untagged ? 'u' : 't'); 2039fad09c73SVivien Didelot 2040fad09c73SVivien Didelot if (pvid && _mv88e6xxx_port_pvid_set(chip, port, vlan->vid_end)) 2041fad09c73SVivien Didelot netdev_err(ds->ports[port].netdev, "failed to set PVID %d\n", 2042fad09c73SVivien Didelot vlan->vid_end); 2043fad09c73SVivien Didelot 2044fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 2045fad09c73SVivien Didelot } 2046fad09c73SVivien Didelot 2047fad09c73SVivien Didelot static int _mv88e6xxx_port_vlan_del(struct mv88e6xxx_chip *chip, 2048fad09c73SVivien Didelot int port, u16 vid) 2049fad09c73SVivien Didelot { 2050fad09c73SVivien Didelot struct dsa_switch *ds = chip->ds; 2051b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry vlan; 2052fad09c73SVivien Didelot int i, err; 2053fad09c73SVivien Didelot 2054fad09c73SVivien Didelot err = _mv88e6xxx_vtu_get(chip, vid, &vlan, false); 2055fad09c73SVivien Didelot if (err) 2056fad09c73SVivien Didelot return err; 2057fad09c73SVivien Didelot 2058fad09c73SVivien Didelot /* Tell switchdev if this VLAN is handled in software */ 2059fad09c73SVivien Didelot if (vlan.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) 2060fad09c73SVivien Didelot return -EOPNOTSUPP; 2061fad09c73SVivien Didelot 2062fad09c73SVivien Didelot vlan.data[port] = GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER; 2063fad09c73SVivien Didelot 2064fad09c73SVivien Didelot /* keep the VLAN unless all ports are excluded */ 2065fad09c73SVivien Didelot vlan.valid = false; 2066370b4ffbSVivien Didelot for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { 2067fad09c73SVivien Didelot if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i)) 2068fad09c73SVivien Didelot continue; 2069fad09c73SVivien Didelot 2070fad09c73SVivien Didelot if (vlan.data[i] != GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) { 2071fad09c73SVivien Didelot vlan.valid = true; 2072fad09c73SVivien Didelot break; 2073fad09c73SVivien Didelot } 2074fad09c73SVivien Didelot } 2075fad09c73SVivien Didelot 2076fad09c73SVivien Didelot err = _mv88e6xxx_vtu_loadpurge(chip, &vlan); 2077fad09c73SVivien Didelot if (err) 2078fad09c73SVivien Didelot return err; 2079fad09c73SVivien Didelot 2080fad09c73SVivien Didelot return _mv88e6xxx_atu_remove(chip, vlan.fid, port, false); 2081fad09c73SVivien Didelot } 2082fad09c73SVivien Didelot 2083fad09c73SVivien Didelot static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, 2084fad09c73SVivien Didelot const struct switchdev_obj_port_vlan *vlan) 2085fad09c73SVivien Didelot { 208604bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 2087fad09c73SVivien Didelot u16 pvid, vid; 2088fad09c73SVivien Didelot int err = 0; 2089fad09c73SVivien Didelot 2090fad09c73SVivien Didelot if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU)) 2091fad09c73SVivien Didelot return -EOPNOTSUPP; 2092fad09c73SVivien Didelot 2093fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 2094fad09c73SVivien Didelot 2095fad09c73SVivien Didelot err = _mv88e6xxx_port_pvid_get(chip, port, &pvid); 2096fad09c73SVivien Didelot if (err) 2097fad09c73SVivien Didelot goto unlock; 2098fad09c73SVivien Didelot 2099fad09c73SVivien Didelot for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { 2100fad09c73SVivien Didelot err = _mv88e6xxx_port_vlan_del(chip, port, vid); 2101fad09c73SVivien Didelot if (err) 2102fad09c73SVivien Didelot goto unlock; 2103fad09c73SVivien Didelot 2104fad09c73SVivien Didelot if (vid == pvid) { 2105fad09c73SVivien Didelot err = _mv88e6xxx_port_pvid_set(chip, port, 0); 2106fad09c73SVivien Didelot if (err) 2107fad09c73SVivien Didelot goto unlock; 2108fad09c73SVivien Didelot } 2109fad09c73SVivien Didelot } 2110fad09c73SVivien Didelot 2111fad09c73SVivien Didelot unlock: 2112fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 2113fad09c73SVivien Didelot 2114fad09c73SVivien Didelot return err; 2115fad09c73SVivien Didelot } 2116fad09c73SVivien Didelot 2117fad09c73SVivien Didelot static int _mv88e6xxx_atu_mac_write(struct mv88e6xxx_chip *chip, 2118fad09c73SVivien Didelot const unsigned char *addr) 2119fad09c73SVivien Didelot { 2120a935c052SVivien Didelot int i, err; 2121fad09c73SVivien Didelot 2122fad09c73SVivien Didelot for (i = 0; i < 3; i++) { 2123a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_MAC_01 + i, 2124fad09c73SVivien Didelot (addr[i * 2] << 8) | addr[i * 2 + 1]); 2125a935c052SVivien Didelot if (err) 2126a935c052SVivien Didelot return err; 2127fad09c73SVivien Didelot } 2128fad09c73SVivien Didelot 2129fad09c73SVivien Didelot return 0; 2130fad09c73SVivien Didelot } 2131fad09c73SVivien Didelot 2132fad09c73SVivien Didelot static int _mv88e6xxx_atu_mac_read(struct mv88e6xxx_chip *chip, 2133fad09c73SVivien Didelot unsigned char *addr) 2134fad09c73SVivien Didelot { 2135a935c052SVivien Didelot u16 val; 2136a935c052SVivien Didelot int i, err; 2137fad09c73SVivien Didelot 2138fad09c73SVivien Didelot for (i = 0; i < 3; i++) { 2139a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_MAC_01 + i, &val); 2140a935c052SVivien Didelot if (err) 2141a935c052SVivien Didelot return err; 2142a935c052SVivien Didelot 2143a935c052SVivien Didelot addr[i * 2] = val >> 8; 2144a935c052SVivien Didelot addr[i * 2 + 1] = val & 0xff; 2145fad09c73SVivien Didelot } 2146fad09c73SVivien Didelot 2147fad09c73SVivien Didelot return 0; 2148fad09c73SVivien Didelot } 2149fad09c73SVivien Didelot 2150fad09c73SVivien Didelot static int _mv88e6xxx_atu_load(struct mv88e6xxx_chip *chip, 2151fad09c73SVivien Didelot struct mv88e6xxx_atu_entry *entry) 2152fad09c73SVivien Didelot { 2153fad09c73SVivien Didelot int ret; 2154fad09c73SVivien Didelot 2155fad09c73SVivien Didelot ret = _mv88e6xxx_atu_wait(chip); 2156fad09c73SVivien Didelot if (ret < 0) 2157fad09c73SVivien Didelot return ret; 2158fad09c73SVivien Didelot 2159fad09c73SVivien Didelot ret = _mv88e6xxx_atu_mac_write(chip, entry->mac); 2160fad09c73SVivien Didelot if (ret < 0) 2161fad09c73SVivien Didelot return ret; 2162fad09c73SVivien Didelot 2163fad09c73SVivien Didelot ret = _mv88e6xxx_atu_data_write(chip, entry); 2164fad09c73SVivien Didelot if (ret < 0) 2165fad09c73SVivien Didelot return ret; 2166fad09c73SVivien Didelot 2167fad09c73SVivien Didelot return _mv88e6xxx_atu_cmd(chip, entry->fid, GLOBAL_ATU_OP_LOAD_DB); 2168fad09c73SVivien Didelot } 2169fad09c73SVivien Didelot 217088472939SVivien Didelot static int _mv88e6xxx_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid, 217188472939SVivien Didelot struct mv88e6xxx_atu_entry *entry); 217288472939SVivien Didelot 217388472939SVivien Didelot static int mv88e6xxx_atu_get(struct mv88e6xxx_chip *chip, int fid, 217488472939SVivien Didelot const u8 *addr, struct mv88e6xxx_atu_entry *entry) 217588472939SVivien Didelot { 217688472939SVivien Didelot struct mv88e6xxx_atu_entry next; 217788472939SVivien Didelot int err; 217888472939SVivien Didelot 217988472939SVivien Didelot eth_broadcast_addr(next.mac); 218088472939SVivien Didelot 218188472939SVivien Didelot err = _mv88e6xxx_atu_mac_write(chip, next.mac); 218288472939SVivien Didelot if (err) 218388472939SVivien Didelot return err; 218488472939SVivien Didelot 218588472939SVivien Didelot do { 218688472939SVivien Didelot err = _mv88e6xxx_atu_getnext(chip, fid, &next); 218788472939SVivien Didelot if (err) 218888472939SVivien Didelot return err; 218988472939SVivien Didelot 219088472939SVivien Didelot if (next.state == GLOBAL_ATU_DATA_STATE_UNUSED) 219188472939SVivien Didelot break; 219288472939SVivien Didelot 219388472939SVivien Didelot if (ether_addr_equal(next.mac, addr)) { 219488472939SVivien Didelot *entry = next; 219588472939SVivien Didelot return 0; 219688472939SVivien Didelot } 219788472939SVivien Didelot } while (!is_broadcast_ether_addr(next.mac)); 219888472939SVivien Didelot 219988472939SVivien Didelot memset(entry, 0, sizeof(*entry)); 220088472939SVivien Didelot entry->fid = fid; 220188472939SVivien Didelot ether_addr_copy(entry->mac, addr); 220288472939SVivien Didelot 220388472939SVivien Didelot return 0; 220488472939SVivien Didelot } 220588472939SVivien Didelot 220683dabd1fSVivien Didelot static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port, 2207fad09c73SVivien Didelot const unsigned char *addr, u16 vid, 2208fad09c73SVivien Didelot u8 state) 2209fad09c73SVivien Didelot { 2210b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry vlan; 221188472939SVivien Didelot struct mv88e6xxx_atu_entry entry; 2212fad09c73SVivien Didelot int err; 2213fad09c73SVivien Didelot 2214fad09c73SVivien Didelot /* Null VLAN ID corresponds to the port private database */ 2215fad09c73SVivien Didelot if (vid == 0) 2216fad09c73SVivien Didelot err = _mv88e6xxx_port_fid_get(chip, port, &vlan.fid); 2217fad09c73SVivien Didelot else 2218fad09c73SVivien Didelot err = _mv88e6xxx_vtu_get(chip, vid, &vlan, false); 2219fad09c73SVivien Didelot if (err) 2220fad09c73SVivien Didelot return err; 2221fad09c73SVivien Didelot 222288472939SVivien Didelot err = mv88e6xxx_atu_get(chip, vlan.fid, addr, &entry); 222388472939SVivien Didelot if (err) 222488472939SVivien Didelot return err; 222588472939SVivien Didelot 222688472939SVivien Didelot /* Purge the ATU entry only if no port is using it anymore */ 222788472939SVivien Didelot if (state == GLOBAL_ATU_DATA_STATE_UNUSED) { 222888472939SVivien Didelot entry.portv_trunkid &= ~BIT(port); 222988472939SVivien Didelot if (!entry.portv_trunkid) 223088472939SVivien Didelot entry.state = GLOBAL_ATU_DATA_STATE_UNUSED; 223188472939SVivien Didelot } else { 223288472939SVivien Didelot entry.portv_trunkid |= BIT(port); 2233fad09c73SVivien Didelot entry.state = state; 2234fad09c73SVivien Didelot } 2235fad09c73SVivien Didelot 2236fad09c73SVivien Didelot return _mv88e6xxx_atu_load(chip, &entry); 2237fad09c73SVivien Didelot } 2238fad09c73SVivien Didelot 2239fad09c73SVivien Didelot static int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port, 2240fad09c73SVivien Didelot const struct switchdev_obj_port_fdb *fdb, 2241fad09c73SVivien Didelot struct switchdev_trans *trans) 2242fad09c73SVivien Didelot { 2243fad09c73SVivien Didelot /* We don't need any dynamic resource from the kernel (yet), 2244fad09c73SVivien Didelot * so skip the prepare phase. 2245fad09c73SVivien Didelot */ 2246fad09c73SVivien Didelot return 0; 2247fad09c73SVivien Didelot } 2248fad09c73SVivien Didelot 2249fad09c73SVivien Didelot static void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, 2250fad09c73SVivien Didelot const struct switchdev_obj_port_fdb *fdb, 2251fad09c73SVivien Didelot struct switchdev_trans *trans) 2252fad09c73SVivien Didelot { 225304bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 2254fad09c73SVivien Didelot 2255fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 225683dabd1fSVivien Didelot if (mv88e6xxx_port_db_load_purge(chip, port, fdb->addr, fdb->vid, 225783dabd1fSVivien Didelot GLOBAL_ATU_DATA_STATE_UC_STATIC)) 225883dabd1fSVivien Didelot netdev_err(ds->ports[port].netdev, "failed to load unicast MAC address\n"); 2259fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 2260fad09c73SVivien Didelot } 2261fad09c73SVivien Didelot 2262fad09c73SVivien Didelot static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, 2263fad09c73SVivien Didelot const struct switchdev_obj_port_fdb *fdb) 2264fad09c73SVivien Didelot { 226504bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 226683dabd1fSVivien Didelot int err; 2267fad09c73SVivien Didelot 2268fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 226983dabd1fSVivien Didelot err = mv88e6xxx_port_db_load_purge(chip, port, fdb->addr, fdb->vid, 2270fad09c73SVivien Didelot GLOBAL_ATU_DATA_STATE_UNUSED); 2271fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 2272fad09c73SVivien Didelot 227383dabd1fSVivien Didelot return err; 2274fad09c73SVivien Didelot } 2275fad09c73SVivien Didelot 2276fad09c73SVivien Didelot static int _mv88e6xxx_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid, 2277fad09c73SVivien Didelot struct mv88e6xxx_atu_entry *entry) 2278fad09c73SVivien Didelot { 2279fad09c73SVivien Didelot struct mv88e6xxx_atu_entry next = { 0 }; 2280a935c052SVivien Didelot u16 val; 2281a935c052SVivien Didelot int err; 2282fad09c73SVivien Didelot 2283fad09c73SVivien Didelot next.fid = fid; 2284fad09c73SVivien Didelot 2285a935c052SVivien Didelot err = _mv88e6xxx_atu_wait(chip); 2286a935c052SVivien Didelot if (err) 2287a935c052SVivien Didelot return err; 2288fad09c73SVivien Didelot 2289a935c052SVivien Didelot err = _mv88e6xxx_atu_cmd(chip, fid, GLOBAL_ATU_OP_GET_NEXT_DB); 2290a935c052SVivien Didelot if (err) 2291a935c052SVivien Didelot return err; 2292fad09c73SVivien Didelot 2293a935c052SVivien Didelot err = _mv88e6xxx_atu_mac_read(chip, next.mac); 2294a935c052SVivien Didelot if (err) 2295a935c052SVivien Didelot return err; 2296fad09c73SVivien Didelot 2297a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_DATA, &val); 2298a935c052SVivien Didelot if (err) 2299a935c052SVivien Didelot return err; 2300fad09c73SVivien Didelot 2301a935c052SVivien Didelot next.state = val & GLOBAL_ATU_DATA_STATE_MASK; 2302fad09c73SVivien Didelot if (next.state != GLOBAL_ATU_DATA_STATE_UNUSED) { 2303fad09c73SVivien Didelot unsigned int mask, shift; 2304fad09c73SVivien Didelot 2305a935c052SVivien Didelot if (val & GLOBAL_ATU_DATA_TRUNK) { 2306fad09c73SVivien Didelot next.trunk = true; 2307fad09c73SVivien Didelot mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK; 2308fad09c73SVivien Didelot shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT; 2309fad09c73SVivien Didelot } else { 2310fad09c73SVivien Didelot next.trunk = false; 2311fad09c73SVivien Didelot mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK; 2312fad09c73SVivien Didelot shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT; 2313fad09c73SVivien Didelot } 2314fad09c73SVivien Didelot 2315a935c052SVivien Didelot next.portv_trunkid = (val & mask) >> shift; 2316fad09c73SVivien Didelot } 2317fad09c73SVivien Didelot 2318fad09c73SVivien Didelot *entry = next; 2319fad09c73SVivien Didelot return 0; 2320fad09c73SVivien Didelot } 2321fad09c73SVivien Didelot 232283dabd1fSVivien Didelot static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip, 2323fad09c73SVivien Didelot u16 fid, u16 vid, int port, 232483dabd1fSVivien Didelot struct switchdev_obj *obj, 2325fad09c73SVivien Didelot int (*cb)(struct switchdev_obj *obj)) 2326fad09c73SVivien Didelot { 2327fad09c73SVivien Didelot struct mv88e6xxx_atu_entry addr = { 2328fad09c73SVivien Didelot .mac = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, 2329fad09c73SVivien Didelot }; 2330fad09c73SVivien Didelot int err; 2331fad09c73SVivien Didelot 2332fad09c73SVivien Didelot err = _mv88e6xxx_atu_mac_write(chip, addr.mac); 2333fad09c73SVivien Didelot if (err) 2334fad09c73SVivien Didelot return err; 2335fad09c73SVivien Didelot 2336fad09c73SVivien Didelot do { 2337fad09c73SVivien Didelot err = _mv88e6xxx_atu_getnext(chip, fid, &addr); 2338fad09c73SVivien Didelot if (err) 233983dabd1fSVivien Didelot return err; 2340fad09c73SVivien Didelot 2341fad09c73SVivien Didelot if (addr.state == GLOBAL_ATU_DATA_STATE_UNUSED) 2342fad09c73SVivien Didelot break; 2343fad09c73SVivien Didelot 234483dabd1fSVivien Didelot if (addr.trunk || (addr.portv_trunkid & BIT(port)) == 0) 234583dabd1fSVivien Didelot continue; 2346fad09c73SVivien Didelot 234783dabd1fSVivien Didelot if (obj->id == SWITCHDEV_OBJ_ID_PORT_FDB) { 234883dabd1fSVivien Didelot struct switchdev_obj_port_fdb *fdb; 234983dabd1fSVivien Didelot 235083dabd1fSVivien Didelot if (!is_unicast_ether_addr(addr.mac)) 235183dabd1fSVivien Didelot continue; 235283dabd1fSVivien Didelot 235383dabd1fSVivien Didelot fdb = SWITCHDEV_OBJ_PORT_FDB(obj); 2354fad09c73SVivien Didelot fdb->vid = vid; 2355fad09c73SVivien Didelot ether_addr_copy(fdb->addr, addr.mac); 235683dabd1fSVivien Didelot if (addr.state == GLOBAL_ATU_DATA_STATE_UC_STATIC) 235783dabd1fSVivien Didelot fdb->ndm_state = NUD_NOARP; 235883dabd1fSVivien Didelot else 235983dabd1fSVivien Didelot fdb->ndm_state = NUD_REACHABLE; 23607df8fbddSVivien Didelot } else if (obj->id == SWITCHDEV_OBJ_ID_PORT_MDB) { 23617df8fbddSVivien Didelot struct switchdev_obj_port_mdb *mdb; 23627df8fbddSVivien Didelot 23637df8fbddSVivien Didelot if (!is_multicast_ether_addr(addr.mac)) 23647df8fbddSVivien Didelot continue; 23657df8fbddSVivien Didelot 23667df8fbddSVivien Didelot mdb = SWITCHDEV_OBJ_PORT_MDB(obj); 23677df8fbddSVivien Didelot mdb->vid = vid; 23687df8fbddSVivien Didelot ether_addr_copy(mdb->addr, addr.mac); 236983dabd1fSVivien Didelot } else { 237083dabd1fSVivien Didelot return -EOPNOTSUPP; 2371fad09c73SVivien Didelot } 237283dabd1fSVivien Didelot 237383dabd1fSVivien Didelot err = cb(obj); 237483dabd1fSVivien Didelot if (err) 237583dabd1fSVivien Didelot return err; 2376fad09c73SVivien Didelot } while (!is_broadcast_ether_addr(addr.mac)); 2377fad09c73SVivien Didelot 2378fad09c73SVivien Didelot return err; 2379fad09c73SVivien Didelot } 2380fad09c73SVivien Didelot 238183dabd1fSVivien Didelot static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port, 238283dabd1fSVivien Didelot struct switchdev_obj *obj, 238383dabd1fSVivien Didelot int (*cb)(struct switchdev_obj *obj)) 238483dabd1fSVivien Didelot { 2385b4e47c0fSVivien Didelot struct mv88e6xxx_vtu_entry vlan = { 238683dabd1fSVivien Didelot .vid = GLOBAL_VTU_VID_MASK, /* all ones */ 238783dabd1fSVivien Didelot }; 238883dabd1fSVivien Didelot u16 fid; 238983dabd1fSVivien Didelot int err; 239083dabd1fSVivien Didelot 239183dabd1fSVivien Didelot /* Dump port's default Filtering Information Database (VLAN ID 0) */ 239283dabd1fSVivien Didelot err = _mv88e6xxx_port_fid_get(chip, port, &fid); 239383dabd1fSVivien Didelot if (err) 239483dabd1fSVivien Didelot return err; 239583dabd1fSVivien Didelot 239683dabd1fSVivien Didelot err = mv88e6xxx_port_db_dump_fid(chip, fid, 0, port, obj, cb); 239783dabd1fSVivien Didelot if (err) 239883dabd1fSVivien Didelot return err; 239983dabd1fSVivien Didelot 240083dabd1fSVivien Didelot /* Dump VLANs' Filtering Information Databases */ 240183dabd1fSVivien Didelot err = _mv88e6xxx_vtu_vid_write(chip, vlan.vid); 240283dabd1fSVivien Didelot if (err) 240383dabd1fSVivien Didelot return err; 240483dabd1fSVivien Didelot 240583dabd1fSVivien Didelot do { 240683dabd1fSVivien Didelot err = _mv88e6xxx_vtu_getnext(chip, &vlan); 240783dabd1fSVivien Didelot if (err) 240883dabd1fSVivien Didelot return err; 240983dabd1fSVivien Didelot 241083dabd1fSVivien Didelot if (!vlan.valid) 241183dabd1fSVivien Didelot break; 241283dabd1fSVivien Didelot 241383dabd1fSVivien Didelot err = mv88e6xxx_port_db_dump_fid(chip, vlan.fid, vlan.vid, port, 241483dabd1fSVivien Didelot obj, cb); 241583dabd1fSVivien Didelot if (err) 241683dabd1fSVivien Didelot return err; 241783dabd1fSVivien Didelot } while (vlan.vid < GLOBAL_VTU_VID_MASK); 241883dabd1fSVivien Didelot 241983dabd1fSVivien Didelot return err; 242083dabd1fSVivien Didelot } 242183dabd1fSVivien Didelot 2422fad09c73SVivien Didelot static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port, 2423fad09c73SVivien Didelot struct switchdev_obj_port_fdb *fdb, 2424fad09c73SVivien Didelot int (*cb)(struct switchdev_obj *obj)) 2425fad09c73SVivien Didelot { 242604bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 2427fad09c73SVivien Didelot int err; 2428fad09c73SVivien Didelot 2429fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 243083dabd1fSVivien Didelot err = mv88e6xxx_port_db_dump(chip, port, &fdb->obj, cb); 2431fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 2432fad09c73SVivien Didelot 2433fad09c73SVivien Didelot return err; 2434fad09c73SVivien Didelot } 2435fad09c73SVivien Didelot 2436fad09c73SVivien Didelot static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port, 2437fad09c73SVivien Didelot struct net_device *bridge) 2438fad09c73SVivien Didelot { 243904bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 2440fad09c73SVivien Didelot int i, err = 0; 2441fad09c73SVivien Didelot 2442fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 2443fad09c73SVivien Didelot 2444fad09c73SVivien Didelot /* Assign the bridge and remap each port's VLANTable */ 2445fad09c73SVivien Didelot chip->ports[port].bridge_dev = bridge; 2446fad09c73SVivien Didelot 2447370b4ffbSVivien Didelot for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { 2448fad09c73SVivien Didelot if (chip->ports[i].bridge_dev == bridge) { 2449fad09c73SVivien Didelot err = _mv88e6xxx_port_based_vlan_map(chip, i); 2450fad09c73SVivien Didelot if (err) 2451fad09c73SVivien Didelot break; 2452fad09c73SVivien Didelot } 2453fad09c73SVivien Didelot } 2454fad09c73SVivien Didelot 2455fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 2456fad09c73SVivien Didelot 2457fad09c73SVivien Didelot return err; 2458fad09c73SVivien Didelot } 2459fad09c73SVivien Didelot 2460fad09c73SVivien Didelot static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port) 2461fad09c73SVivien Didelot { 246204bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 2463fad09c73SVivien Didelot struct net_device *bridge = chip->ports[port].bridge_dev; 2464fad09c73SVivien Didelot int i; 2465fad09c73SVivien Didelot 2466fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 2467fad09c73SVivien Didelot 2468fad09c73SVivien Didelot /* Unassign the bridge and remap each port's VLANTable */ 2469fad09c73SVivien Didelot chip->ports[port].bridge_dev = NULL; 2470fad09c73SVivien Didelot 2471370b4ffbSVivien Didelot for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) 2472fad09c73SVivien Didelot if (i == port || chip->ports[i].bridge_dev == bridge) 2473fad09c73SVivien Didelot if (_mv88e6xxx_port_based_vlan_map(chip, i)) 2474fad09c73SVivien Didelot netdev_warn(ds->ports[i].netdev, 2475fad09c73SVivien Didelot "failed to remap\n"); 2476fad09c73SVivien Didelot 2477fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 2478fad09c73SVivien Didelot } 2479fad09c73SVivien Didelot 2480fad09c73SVivien Didelot static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip) 2481fad09c73SVivien Didelot { 2482fad09c73SVivien Didelot bool ppu_active = mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU_ACTIVE); 2483fad09c73SVivien Didelot u16 is_reset = (ppu_active ? 0x8800 : 0xc800); 2484fad09c73SVivien Didelot struct gpio_desc *gpiod = chip->reset; 2485fad09c73SVivien Didelot unsigned long timeout; 24860e7b9925SAndrew Lunn u16 reg; 2487a935c052SVivien Didelot int err; 2488fad09c73SVivien Didelot int i; 2489fad09c73SVivien Didelot 2490fad09c73SVivien Didelot /* Set all ports to the disabled state. */ 2491370b4ffbSVivien Didelot for (i = 0; i < mv88e6xxx_num_ports(chip); i++) { 2492e28def33SVivien Didelot err = mv88e6xxx_port_set_state(chip, i, 2493e28def33SVivien Didelot PORT_CONTROL_STATE_DISABLED); 24940e7b9925SAndrew Lunn if (err) 24950e7b9925SAndrew Lunn return err; 2496fad09c73SVivien Didelot } 2497fad09c73SVivien Didelot 2498fad09c73SVivien Didelot /* Wait for transmit queues to drain. */ 2499fad09c73SVivien Didelot usleep_range(2000, 4000); 2500fad09c73SVivien Didelot 2501fad09c73SVivien Didelot /* If there is a gpio connected to the reset pin, toggle it */ 2502fad09c73SVivien Didelot if (gpiod) { 2503fad09c73SVivien Didelot gpiod_set_value_cansleep(gpiod, 1); 2504fad09c73SVivien Didelot usleep_range(10000, 20000); 2505fad09c73SVivien Didelot gpiod_set_value_cansleep(gpiod, 0); 2506fad09c73SVivien Didelot usleep_range(10000, 20000); 2507fad09c73SVivien Didelot } 2508fad09c73SVivien Didelot 2509fad09c73SVivien Didelot /* Reset the switch. Keep the PPU active if requested. The PPU 2510fad09c73SVivien Didelot * needs to be active to support indirect phy register access 2511fad09c73SVivien Didelot * through global registers 0x18 and 0x19. 2512fad09c73SVivien Didelot */ 2513fad09c73SVivien Didelot if (ppu_active) 2514a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, 0x04, 0xc000); 2515fad09c73SVivien Didelot else 2516a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, 0x04, 0xc400); 25170e7b9925SAndrew Lunn if (err) 25180e7b9925SAndrew Lunn return err; 2519fad09c73SVivien Didelot 2520fad09c73SVivien Didelot /* Wait up to one second for reset to complete. */ 2521fad09c73SVivien Didelot timeout = jiffies + 1 * HZ; 2522fad09c73SVivien Didelot while (time_before(jiffies, timeout)) { 2523a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, 0x00, ®); 2524a935c052SVivien Didelot if (err) 2525a935c052SVivien Didelot return err; 2526fad09c73SVivien Didelot 2527a935c052SVivien Didelot if ((reg & is_reset) == is_reset) 2528fad09c73SVivien Didelot break; 2529fad09c73SVivien Didelot usleep_range(1000, 2000); 2530fad09c73SVivien Didelot } 2531fad09c73SVivien Didelot if (time_after(jiffies, timeout)) 25320e7b9925SAndrew Lunn err = -ETIMEDOUT; 2533fad09c73SVivien Didelot else 25340e7b9925SAndrew Lunn err = 0; 2535fad09c73SVivien Didelot 25360e7b9925SAndrew Lunn return err; 2537fad09c73SVivien Didelot } 2538fad09c73SVivien Didelot 253909cb7dfdSVivien Didelot static int mv88e6xxx_serdes_power_on(struct mv88e6xxx_chip *chip) 2540fad09c73SVivien Didelot { 254109cb7dfdSVivien Didelot u16 val; 254209cb7dfdSVivien Didelot int err; 2543fad09c73SVivien Didelot 254409cb7dfdSVivien Didelot /* Clear Power Down bit */ 254509cb7dfdSVivien Didelot err = mv88e6xxx_serdes_read(chip, MII_BMCR, &val); 254609cb7dfdSVivien Didelot if (err) 254709cb7dfdSVivien Didelot return err; 2548fad09c73SVivien Didelot 254909cb7dfdSVivien Didelot if (val & BMCR_PDOWN) { 255009cb7dfdSVivien Didelot val &= ~BMCR_PDOWN; 255109cb7dfdSVivien Didelot err = mv88e6xxx_serdes_write(chip, MII_BMCR, val); 2552fad09c73SVivien Didelot } 2553fad09c73SVivien Didelot 255409cb7dfdSVivien Didelot return err; 2555fad09c73SVivien Didelot } 2556fad09c73SVivien Didelot 2557fad09c73SVivien Didelot static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) 2558fad09c73SVivien Didelot { 2559fad09c73SVivien Didelot struct dsa_switch *ds = chip->ds; 25600e7b9925SAndrew Lunn int err; 2561fad09c73SVivien Didelot u16 reg; 2562fad09c73SVivien Didelot 2563fad09c73SVivien Didelot if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) || 2564fad09c73SVivien Didelot mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) || 2565fad09c73SVivien Didelot mv88e6xxx_6185_family(chip) || mv88e6xxx_6095_family(chip) || 2566fad09c73SVivien Didelot mv88e6xxx_6065_family(chip) || mv88e6xxx_6320_family(chip)) { 2567fad09c73SVivien Didelot /* MAC Forcing register: don't force link, speed, 2568fad09c73SVivien Didelot * duplex or flow control state to any particular 2569fad09c73SVivien Didelot * values on physical ports, but force the CPU port 2570fad09c73SVivien Didelot * and all DSA ports to their maximum bandwidth and 2571fad09c73SVivien Didelot * full duplex. 2572fad09c73SVivien Didelot */ 25730e7b9925SAndrew Lunn err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, ®); 2574fad09c73SVivien Didelot if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) { 2575fad09c73SVivien Didelot reg &= ~PORT_PCS_CTRL_UNFORCED; 2576fad09c73SVivien Didelot reg |= PORT_PCS_CTRL_FORCE_LINK | 2577fad09c73SVivien Didelot PORT_PCS_CTRL_LINK_UP | 2578fad09c73SVivien Didelot PORT_PCS_CTRL_DUPLEX_FULL | 2579fad09c73SVivien Didelot PORT_PCS_CTRL_FORCE_DUPLEX; 2580fad09c73SVivien Didelot if (mv88e6xxx_6065_family(chip)) 2581fad09c73SVivien Didelot reg |= PORT_PCS_CTRL_100; 2582fad09c73SVivien Didelot else 2583fad09c73SVivien Didelot reg |= PORT_PCS_CTRL_1000; 2584fad09c73SVivien Didelot } else { 2585fad09c73SVivien Didelot reg |= PORT_PCS_CTRL_UNFORCED; 2586fad09c73SVivien Didelot } 2587fad09c73SVivien Didelot 25880e7b9925SAndrew Lunn err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg); 25890e7b9925SAndrew Lunn if (err) 25900e7b9925SAndrew Lunn return err; 2591fad09c73SVivien Didelot } 2592fad09c73SVivien Didelot 2593fad09c73SVivien Didelot /* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock, 2594fad09c73SVivien Didelot * disable Header mode, enable IGMP/MLD snooping, disable VLAN 2595fad09c73SVivien Didelot * tunneling, determine priority by looking at 802.1p and IP 2596fad09c73SVivien Didelot * priority fields (IP prio has precedence), and set STP state 2597fad09c73SVivien Didelot * to Forwarding. 2598fad09c73SVivien Didelot * 2599fad09c73SVivien Didelot * If this is the CPU link, use DSA or EDSA tagging depending 2600fad09c73SVivien Didelot * on which tagging mode was configured. 2601fad09c73SVivien Didelot * 2602fad09c73SVivien Didelot * If this is a link to another switch, use DSA tagging mode. 2603fad09c73SVivien Didelot * 2604fad09c73SVivien Didelot * If this is the upstream port for this switch, enable 2605fad09c73SVivien Didelot * forwarding of unknown unicasts and multicasts. 2606fad09c73SVivien Didelot */ 2607fad09c73SVivien Didelot reg = 0; 2608fad09c73SVivien Didelot if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) || 2609fad09c73SVivien Didelot mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) || 2610fad09c73SVivien Didelot mv88e6xxx_6095_family(chip) || mv88e6xxx_6065_family(chip) || 2611fad09c73SVivien Didelot mv88e6xxx_6185_family(chip) || mv88e6xxx_6320_family(chip)) 2612fad09c73SVivien Didelot reg = PORT_CONTROL_IGMP_MLD_SNOOP | 2613fad09c73SVivien Didelot PORT_CONTROL_USE_TAG | PORT_CONTROL_USE_IP | 2614fad09c73SVivien Didelot PORT_CONTROL_STATE_FORWARDING; 2615fad09c73SVivien Didelot if (dsa_is_cpu_port(ds, port)) { 26162bbb33beSAndrew Lunn if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EDSA)) 2617fad09c73SVivien Didelot reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA | 2618fad09c73SVivien Didelot PORT_CONTROL_FORWARD_UNKNOWN_MC; 26192bbb33beSAndrew Lunn else 26202bbb33beSAndrew Lunn reg |= PORT_CONTROL_DSA_TAG; 2621f027e0ccSJamie Lentin reg |= PORT_CONTROL_EGRESS_ADD_TAG | 2622f027e0ccSJamie Lentin PORT_CONTROL_FORWARD_UNKNOWN; 2623fad09c73SVivien Didelot } 2624fad09c73SVivien Didelot if (dsa_is_dsa_port(ds, port)) { 2625fad09c73SVivien Didelot if (mv88e6xxx_6095_family(chip) || 2626fad09c73SVivien Didelot mv88e6xxx_6185_family(chip)) 2627fad09c73SVivien Didelot reg |= PORT_CONTROL_DSA_TAG; 2628fad09c73SVivien Didelot if (mv88e6xxx_6352_family(chip) || 2629fad09c73SVivien Didelot mv88e6xxx_6351_family(chip) || 2630fad09c73SVivien Didelot mv88e6xxx_6165_family(chip) || 2631fad09c73SVivien Didelot mv88e6xxx_6097_family(chip) || 2632fad09c73SVivien Didelot mv88e6xxx_6320_family(chip)) { 2633fad09c73SVivien Didelot reg |= PORT_CONTROL_FRAME_MODE_DSA; 2634fad09c73SVivien Didelot } 2635fad09c73SVivien Didelot 2636fad09c73SVivien Didelot if (port == dsa_upstream_port(ds)) 2637fad09c73SVivien Didelot reg |= PORT_CONTROL_FORWARD_UNKNOWN | 2638fad09c73SVivien Didelot PORT_CONTROL_FORWARD_UNKNOWN_MC; 2639fad09c73SVivien Didelot } 2640fad09c73SVivien Didelot if (reg) { 26410e7b9925SAndrew Lunn err = mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg); 26420e7b9925SAndrew Lunn if (err) 26430e7b9925SAndrew Lunn return err; 2644fad09c73SVivien Didelot } 2645fad09c73SVivien Didelot 2646fad09c73SVivien Didelot /* If this port is connected to a SerDes, make sure the SerDes is not 2647fad09c73SVivien Didelot * powered down. 2648fad09c73SVivien Didelot */ 264909cb7dfdSVivien Didelot if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_SERDES)) { 26500e7b9925SAndrew Lunn err = mv88e6xxx_port_read(chip, port, PORT_STATUS, ®); 26510e7b9925SAndrew Lunn if (err) 26520e7b9925SAndrew Lunn return err; 26530e7b9925SAndrew Lunn reg &= PORT_STATUS_CMODE_MASK; 26540e7b9925SAndrew Lunn if ((reg == PORT_STATUS_CMODE_100BASE_X) || 26550e7b9925SAndrew Lunn (reg == PORT_STATUS_CMODE_1000BASE_X) || 26560e7b9925SAndrew Lunn (reg == PORT_STATUS_CMODE_SGMII)) { 26570e7b9925SAndrew Lunn err = mv88e6xxx_serdes_power_on(chip); 26580e7b9925SAndrew Lunn if (err < 0) 26590e7b9925SAndrew Lunn return err; 2660fad09c73SVivien Didelot } 2661fad09c73SVivien Didelot } 2662fad09c73SVivien Didelot 2663fad09c73SVivien Didelot /* Port Control 2: don't force a good FCS, set the maximum frame size to 2664fad09c73SVivien Didelot * 10240 bytes, disable 802.1q tags checking, don't discard tagged or 2665fad09c73SVivien Didelot * untagged frames on this port, do a destination address lookup on all 2666fad09c73SVivien Didelot * received packets as usual, disable ARP mirroring and don't send a 2667fad09c73SVivien Didelot * copy of all transmitted/received frames on this port to the CPU. 2668fad09c73SVivien Didelot */ 2669fad09c73SVivien Didelot reg = 0; 2670fad09c73SVivien Didelot if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) || 2671fad09c73SVivien Didelot mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) || 2672fad09c73SVivien Didelot mv88e6xxx_6095_family(chip) || mv88e6xxx_6320_family(chip) || 2673fad09c73SVivien Didelot mv88e6xxx_6185_family(chip)) 2674fad09c73SVivien Didelot reg = PORT_CONTROL_2_MAP_DA; 2675fad09c73SVivien Didelot 2676fad09c73SVivien Didelot if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) || 2677fad09c73SVivien Didelot mv88e6xxx_6165_family(chip) || mv88e6xxx_6320_family(chip)) 2678fad09c73SVivien Didelot reg |= PORT_CONTROL_2_JUMBO_10240; 2679fad09c73SVivien Didelot 2680fad09c73SVivien Didelot if (mv88e6xxx_6095_family(chip) || mv88e6xxx_6185_family(chip)) { 2681fad09c73SVivien Didelot /* Set the upstream port this port should use */ 2682fad09c73SVivien Didelot reg |= dsa_upstream_port(ds); 2683fad09c73SVivien Didelot /* enable forwarding of unknown multicast addresses to 2684fad09c73SVivien Didelot * the upstream port 2685fad09c73SVivien Didelot */ 2686fad09c73SVivien Didelot if (port == dsa_upstream_port(ds)) 2687fad09c73SVivien Didelot reg |= PORT_CONTROL_2_FORWARD_UNKNOWN; 2688fad09c73SVivien Didelot } 2689fad09c73SVivien Didelot 2690fad09c73SVivien Didelot reg |= PORT_CONTROL_2_8021Q_DISABLED; 2691fad09c73SVivien Didelot 2692fad09c73SVivien Didelot if (reg) { 26930e7b9925SAndrew Lunn err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg); 26940e7b9925SAndrew Lunn if (err) 26950e7b9925SAndrew Lunn return err; 2696fad09c73SVivien Didelot } 2697fad09c73SVivien Didelot 2698fad09c73SVivien Didelot /* Port Association Vector: when learning source addresses 2699fad09c73SVivien Didelot * of packets, add the address to the address database using 2700fad09c73SVivien Didelot * a port bitmap that has only the bit for this port set and 2701fad09c73SVivien Didelot * the other bits clear. 2702fad09c73SVivien Didelot */ 2703fad09c73SVivien Didelot reg = 1 << port; 2704fad09c73SVivien Didelot /* Disable learning for CPU port */ 2705fad09c73SVivien Didelot if (dsa_is_cpu_port(ds, port)) 2706fad09c73SVivien Didelot reg = 0; 2707fad09c73SVivien Didelot 27080e7b9925SAndrew Lunn err = mv88e6xxx_port_write(chip, port, PORT_ASSOC_VECTOR, reg); 27090e7b9925SAndrew Lunn if (err) 27100e7b9925SAndrew Lunn return err; 2711fad09c73SVivien Didelot 2712fad09c73SVivien Didelot /* Egress rate control 2: disable egress rate control. */ 27130e7b9925SAndrew Lunn err = mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL_2, 0x0000); 27140e7b9925SAndrew Lunn if (err) 27150e7b9925SAndrew Lunn return err; 2716fad09c73SVivien Didelot 2717fad09c73SVivien Didelot if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) || 2718fad09c73SVivien Didelot mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) || 2719fad09c73SVivien Didelot mv88e6xxx_6320_family(chip)) { 2720fad09c73SVivien Didelot /* Do not limit the period of time that this port can 2721fad09c73SVivien Didelot * be paused for by the remote end or the period of 2722fad09c73SVivien Didelot * time that this port can pause the remote end. 2723fad09c73SVivien Didelot */ 27240e7b9925SAndrew Lunn err = mv88e6xxx_port_write(chip, port, PORT_PAUSE_CTRL, 0x0000); 27250e7b9925SAndrew Lunn if (err) 27260e7b9925SAndrew Lunn return err; 2727fad09c73SVivien Didelot 2728fad09c73SVivien Didelot /* Port ATU control: disable limiting the number of 2729fad09c73SVivien Didelot * address database entries that this port is allowed 2730fad09c73SVivien Didelot * to use. 2731fad09c73SVivien Didelot */ 27320e7b9925SAndrew Lunn err = mv88e6xxx_port_write(chip, port, PORT_ATU_CONTROL, 27330e7b9925SAndrew Lunn 0x0000); 2734fad09c73SVivien Didelot /* Priority Override: disable DA, SA and VTU priority 2735fad09c73SVivien Didelot * override. 2736fad09c73SVivien Didelot */ 27370e7b9925SAndrew Lunn err = mv88e6xxx_port_write(chip, port, PORT_PRI_OVERRIDE, 27380e7b9925SAndrew Lunn 0x0000); 27390e7b9925SAndrew Lunn if (err) 27400e7b9925SAndrew Lunn return err; 2741fad09c73SVivien Didelot 2742fad09c73SVivien Didelot /* Port Ethertype: use the Ethertype DSA Ethertype 2743fad09c73SVivien Didelot * value. 2744fad09c73SVivien Didelot */ 27452bbb33beSAndrew Lunn if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EDSA)) { 27460e7b9925SAndrew Lunn err = mv88e6xxx_port_write(chip, port, PORT_ETH_TYPE, 27470e7b9925SAndrew Lunn ETH_P_EDSA); 27480e7b9925SAndrew Lunn if (err) 27490e7b9925SAndrew Lunn return err; 27502bbb33beSAndrew Lunn } 27512bbb33beSAndrew Lunn 2752fad09c73SVivien Didelot /* Tag Remap: use an identity 802.1p prio -> switch 2753fad09c73SVivien Didelot * prio mapping. 2754fad09c73SVivien Didelot */ 27550e7b9925SAndrew Lunn err = mv88e6xxx_port_write(chip, port, PORT_TAG_REGMAP_0123, 27560e7b9925SAndrew Lunn 0x3210); 27570e7b9925SAndrew Lunn if (err) 27580e7b9925SAndrew Lunn return err; 2759fad09c73SVivien Didelot 2760fad09c73SVivien Didelot /* Tag Remap 2: use an identity 802.1p prio -> switch 2761fad09c73SVivien Didelot * prio mapping. 2762fad09c73SVivien Didelot */ 27630e7b9925SAndrew Lunn err = mv88e6xxx_port_write(chip, port, PORT_TAG_REGMAP_4567, 27640e7b9925SAndrew Lunn 0x7654); 27650e7b9925SAndrew Lunn if (err) 27660e7b9925SAndrew Lunn return err; 2767fad09c73SVivien Didelot } 2768fad09c73SVivien Didelot 27691bc261faSJamie Lentin /* Rate Control: disable ingress rate limiting. */ 2770fad09c73SVivien Didelot if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) || 2771fad09c73SVivien Didelot mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) || 2772fad09c73SVivien Didelot mv88e6xxx_6320_family(chip)) { 27730e7b9925SAndrew Lunn err = mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL, 27740e7b9925SAndrew Lunn 0x0001); 27750e7b9925SAndrew Lunn if (err) 27760e7b9925SAndrew Lunn return err; 27771bc261faSJamie Lentin } else if (mv88e6xxx_6185_family(chip) || mv88e6xxx_6095_family(chip)) { 27780e7b9925SAndrew Lunn err = mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL, 27790e7b9925SAndrew Lunn 0x0000); 27800e7b9925SAndrew Lunn if (err) 27810e7b9925SAndrew Lunn return err; 2782fad09c73SVivien Didelot } 2783fad09c73SVivien Didelot 2784fad09c73SVivien Didelot /* Port Control 1: disable trunking, disable sending 2785fad09c73SVivien Didelot * learning messages to this port. 2786fad09c73SVivien Didelot */ 27870e7b9925SAndrew Lunn err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_1, 0x0000); 27880e7b9925SAndrew Lunn if (err) 27890e7b9925SAndrew Lunn return err; 2790fad09c73SVivien Didelot 2791fad09c73SVivien Didelot /* Port based VLAN map: give each port the same default address 2792fad09c73SVivien Didelot * database, and allow bidirectional communication between the 2793fad09c73SVivien Didelot * CPU and DSA port(s), and the other ports. 2794fad09c73SVivien Didelot */ 27950e7b9925SAndrew Lunn err = _mv88e6xxx_port_fid_set(chip, port, 0); 27960e7b9925SAndrew Lunn if (err) 27970e7b9925SAndrew Lunn return err; 2798fad09c73SVivien Didelot 27990e7b9925SAndrew Lunn err = _mv88e6xxx_port_based_vlan_map(chip, port); 28000e7b9925SAndrew Lunn if (err) 28010e7b9925SAndrew Lunn return err; 2802fad09c73SVivien Didelot 2803fad09c73SVivien Didelot /* Default VLAN ID and priority: don't set a default VLAN 2804fad09c73SVivien Didelot * ID, and set the default packet priority to zero. 2805fad09c73SVivien Didelot */ 28060e7b9925SAndrew Lunn return mv88e6xxx_port_write(chip, port, PORT_DEFAULT_VLAN, 0x0000); 2807fad09c73SVivien Didelot } 2808fad09c73SVivien Didelot 2809aa0938c6SWei Yongjun static int mv88e6xxx_g1_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr) 28103b4caa1bSVivien Didelot { 28113b4caa1bSVivien Didelot int err; 28123b4caa1bSVivien Didelot 2813a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_MAC_01, (addr[0] << 8) | addr[1]); 28143b4caa1bSVivien Didelot if (err) 28153b4caa1bSVivien Didelot return err; 28163b4caa1bSVivien Didelot 2817a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_MAC_23, (addr[2] << 8) | addr[3]); 28183b4caa1bSVivien Didelot if (err) 28193b4caa1bSVivien Didelot return err; 28203b4caa1bSVivien Didelot 2821a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_MAC_45, (addr[4] << 8) | addr[5]); 2822a935c052SVivien Didelot if (err) 2823a935c052SVivien Didelot return err; 2824a935c052SVivien Didelot 2825a935c052SVivien Didelot return 0; 28263b4caa1bSVivien Didelot } 28273b4caa1bSVivien Didelot 2828acddbd21SVivien Didelot static int mv88e6xxx_g1_set_age_time(struct mv88e6xxx_chip *chip, 2829acddbd21SVivien Didelot unsigned int msecs) 2830acddbd21SVivien Didelot { 2831acddbd21SVivien Didelot const unsigned int coeff = chip->info->age_time_coeff; 2832acddbd21SVivien Didelot const unsigned int min = 0x01 * coeff; 2833acddbd21SVivien Didelot const unsigned int max = 0xff * coeff; 2834acddbd21SVivien Didelot u8 age_time; 2835acddbd21SVivien Didelot u16 val; 2836acddbd21SVivien Didelot int err; 2837acddbd21SVivien Didelot 2838acddbd21SVivien Didelot if (msecs < min || msecs > max) 2839acddbd21SVivien Didelot return -ERANGE; 2840acddbd21SVivien Didelot 2841acddbd21SVivien Didelot /* Round to nearest multiple of coeff */ 2842acddbd21SVivien Didelot age_time = (msecs + coeff / 2) / coeff; 2843acddbd21SVivien Didelot 2844a935c052SVivien Didelot err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_CONTROL, &val); 2845acddbd21SVivien Didelot if (err) 2846acddbd21SVivien Didelot return err; 2847acddbd21SVivien Didelot 2848acddbd21SVivien Didelot /* AgeTime is 11:4 bits */ 2849acddbd21SVivien Didelot val &= ~0xff0; 2850acddbd21SVivien Didelot val |= age_time << 4; 2851acddbd21SVivien Didelot 2852a935c052SVivien Didelot return mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL, val); 2853acddbd21SVivien Didelot } 2854acddbd21SVivien Didelot 28552cfcd964SVivien Didelot static int mv88e6xxx_set_ageing_time(struct dsa_switch *ds, 28562cfcd964SVivien Didelot unsigned int ageing_time) 28572cfcd964SVivien Didelot { 285804bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 28592cfcd964SVivien Didelot int err; 28602cfcd964SVivien Didelot 28612cfcd964SVivien Didelot mutex_lock(&chip->reg_lock); 28622cfcd964SVivien Didelot err = mv88e6xxx_g1_set_age_time(chip, ageing_time); 28632cfcd964SVivien Didelot mutex_unlock(&chip->reg_lock); 28642cfcd964SVivien Didelot 28652cfcd964SVivien Didelot return err; 28662cfcd964SVivien Didelot } 28672cfcd964SVivien Didelot 28689729934cSVivien Didelot static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip) 2869fad09c73SVivien Didelot { 2870fad09c73SVivien Didelot struct dsa_switch *ds = chip->ds; 2871fad09c73SVivien Didelot u32 upstream_port = dsa_upstream_port(ds); 2872fad09c73SVivien Didelot u16 reg; 2873fad09c73SVivien Didelot int err; 2874fad09c73SVivien Didelot 2875fad09c73SVivien Didelot /* Enable the PHY Polling Unit if present, don't discard any packets, 2876fad09c73SVivien Didelot * and mask all interrupt sources. 2877fad09c73SVivien Didelot */ 2878dc30c35bSAndrew Lunn err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, ®); 2879dc30c35bSAndrew Lunn if (err < 0) 2880dc30c35bSAndrew Lunn return err; 2881dc30c35bSAndrew Lunn 2882dc30c35bSAndrew Lunn reg &= ~GLOBAL_CONTROL_PPU_ENABLE; 2883fad09c73SVivien Didelot if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU) || 2884fad09c73SVivien Didelot mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU_ACTIVE)) 2885fad09c73SVivien Didelot reg |= GLOBAL_CONTROL_PPU_ENABLE; 2886fad09c73SVivien Didelot 2887a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, reg); 2888fad09c73SVivien Didelot if (err) 2889fad09c73SVivien Didelot return err; 2890fad09c73SVivien Didelot 2891fad09c73SVivien Didelot /* Configure the upstream port, and configure it as the port to which 2892fad09c73SVivien Didelot * ingress and egress and ARP monitor frames are to be sent. 2893fad09c73SVivien Didelot */ 2894fad09c73SVivien Didelot reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT | 2895fad09c73SVivien Didelot upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT | 2896fad09c73SVivien Didelot upstream_port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT; 2897a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_MONITOR_CONTROL, reg); 2898fad09c73SVivien Didelot if (err) 2899fad09c73SVivien Didelot return err; 2900fad09c73SVivien Didelot 2901fad09c73SVivien Didelot /* Disable remote management, and set the switch's DSA device number. */ 2902a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL_2, 2903fad09c73SVivien Didelot GLOBAL_CONTROL_2_MULTIPLE_CASCADE | 2904fad09c73SVivien Didelot (ds->index & 0x1f)); 2905fad09c73SVivien Didelot if (err) 2906fad09c73SVivien Didelot return err; 2907fad09c73SVivien Didelot 2908acddbd21SVivien Didelot /* Clear all the VTU and STU entries */ 2909acddbd21SVivien Didelot err = _mv88e6xxx_vtu_stu_flush(chip); 2910acddbd21SVivien Didelot if (err < 0) 2911acddbd21SVivien Didelot return err; 2912acddbd21SVivien Didelot 2913fad09c73SVivien Didelot /* Set the default address aging time to 5 minutes, and 2914fad09c73SVivien Didelot * enable address learn messages to be sent to all message 2915fad09c73SVivien Didelot * ports. 2916fad09c73SVivien Didelot */ 2917a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL, 2918acddbd21SVivien Didelot GLOBAL_ATU_CONTROL_LEARN2ALL); 2919fad09c73SVivien Didelot if (err) 2920fad09c73SVivien Didelot return err; 2921fad09c73SVivien Didelot 2922acddbd21SVivien Didelot err = mv88e6xxx_g1_set_age_time(chip, 300000); 2923acddbd21SVivien Didelot if (err) 29249729934cSVivien Didelot return err; 29259729934cSVivien Didelot 29269729934cSVivien Didelot /* Clear all ATU entries */ 29279729934cSVivien Didelot err = _mv88e6xxx_atu_flush(chip, 0, true); 29289729934cSVivien Didelot if (err) 29299729934cSVivien Didelot return err; 29309729934cSVivien Didelot 2931fad09c73SVivien Didelot /* Configure the IP ToS mapping registers. */ 2932a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_0, 0x0000); 2933fad09c73SVivien Didelot if (err) 2934fad09c73SVivien Didelot return err; 2935a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_1, 0x0000); 2936fad09c73SVivien Didelot if (err) 2937fad09c73SVivien Didelot return err; 2938a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_2, 0x5555); 2939fad09c73SVivien Didelot if (err) 2940fad09c73SVivien Didelot return err; 2941a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_3, 0x5555); 2942fad09c73SVivien Didelot if (err) 2943fad09c73SVivien Didelot return err; 2944a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_4, 0xaaaa); 2945fad09c73SVivien Didelot if (err) 2946fad09c73SVivien Didelot return err; 2947a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_5, 0xaaaa); 2948fad09c73SVivien Didelot if (err) 2949fad09c73SVivien Didelot return err; 2950a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_6, 0xffff); 2951fad09c73SVivien Didelot if (err) 2952fad09c73SVivien Didelot return err; 2953a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_7, 0xffff); 2954fad09c73SVivien Didelot if (err) 2955fad09c73SVivien Didelot return err; 2956fad09c73SVivien Didelot 2957fad09c73SVivien Didelot /* Configure the IEEE 802.1p priority mapping register. */ 2958a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_IEEE_PRI, 0xfa41); 2959fad09c73SVivien Didelot if (err) 2960fad09c73SVivien Didelot return err; 2961fad09c73SVivien Didelot 29629729934cSVivien Didelot /* Clear the statistics counters for all ports */ 2963a935c052SVivien Didelot err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP, 29649729934cSVivien Didelot GLOBAL_STATS_OP_FLUSH_ALL); 29659729934cSVivien Didelot if (err) 29669729934cSVivien Didelot return err; 29679729934cSVivien Didelot 29689729934cSVivien Didelot /* Wait for the flush to complete. */ 29699729934cSVivien Didelot err = _mv88e6xxx_stats_wait(chip); 29709729934cSVivien Didelot if (err) 29719729934cSVivien Didelot return err; 29729729934cSVivien Didelot 29739729934cSVivien Didelot return 0; 29749729934cSVivien Didelot } 29759729934cSVivien Didelot 2976fad09c73SVivien Didelot static int mv88e6xxx_setup(struct dsa_switch *ds) 2977fad09c73SVivien Didelot { 297804bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 2979fad09c73SVivien Didelot int err; 2980fad09c73SVivien Didelot int i; 2981fad09c73SVivien Didelot 2982fad09c73SVivien Didelot chip->ds = ds; 2983fad09c73SVivien Didelot ds->slave_mii_bus = chip->mdio_bus; 2984fad09c73SVivien Didelot 2985fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 2986fad09c73SVivien Didelot 29879729934cSVivien Didelot /* Setup Switch Port Registers */ 2988370b4ffbSVivien Didelot for (i = 0; i < mv88e6xxx_num_ports(chip); i++) { 29899729934cSVivien Didelot err = mv88e6xxx_setup_port(chip, i); 29909729934cSVivien Didelot if (err) 29919729934cSVivien Didelot goto unlock; 29929729934cSVivien Didelot } 29939729934cSVivien Didelot 29949729934cSVivien Didelot /* Setup Switch Global 1 Registers */ 29959729934cSVivien Didelot err = mv88e6xxx_g1_setup(chip); 2996fad09c73SVivien Didelot if (err) 2997fad09c73SVivien Didelot goto unlock; 2998fad09c73SVivien Didelot 29999729934cSVivien Didelot /* Setup Switch Global 2 Registers */ 30009729934cSVivien Didelot if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_GLOBAL2)) { 30019729934cSVivien Didelot err = mv88e6xxx_g2_setup(chip); 3002fad09c73SVivien Didelot if (err) 3003fad09c73SVivien Didelot goto unlock; 3004fad09c73SVivien Didelot } 3005fad09c73SVivien Didelot 3006fad09c73SVivien Didelot unlock: 3007fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 3008fad09c73SVivien Didelot 3009fad09c73SVivien Didelot return err; 3010fad09c73SVivien Didelot } 3011fad09c73SVivien Didelot 30123b4caa1bSVivien Didelot static int mv88e6xxx_set_addr(struct dsa_switch *ds, u8 *addr) 30133b4caa1bSVivien Didelot { 301404bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 30153b4caa1bSVivien Didelot int err; 30163b4caa1bSVivien Didelot 3017b073d4e2SVivien Didelot if (!chip->info->ops->set_switch_mac) 3018b073d4e2SVivien Didelot return -EOPNOTSUPP; 3019b073d4e2SVivien Didelot 30203b4caa1bSVivien Didelot mutex_lock(&chip->reg_lock); 3021b073d4e2SVivien Didelot err = chip->info->ops->set_switch_mac(chip, addr); 30223b4caa1bSVivien Didelot mutex_unlock(&chip->reg_lock); 30233b4caa1bSVivien Didelot 30243b4caa1bSVivien Didelot return err; 30253b4caa1bSVivien Didelot } 30263b4caa1bSVivien Didelot 3027e57e5e77SVivien Didelot static int mv88e6xxx_mdio_read(struct mii_bus *bus, int phy, int reg) 3028fad09c73SVivien Didelot { 3029fad09c73SVivien Didelot struct mv88e6xxx_chip *chip = bus->priv; 3030e57e5e77SVivien Didelot u16 val; 3031e57e5e77SVivien Didelot int err; 3032fad09c73SVivien Didelot 3033370b4ffbSVivien Didelot if (phy >= mv88e6xxx_num_ports(chip)) 3034fad09c73SVivien Didelot return 0xffff; 3035fad09c73SVivien Didelot 3036fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 3037e57e5e77SVivien Didelot err = mv88e6xxx_phy_read(chip, phy, reg, &val); 3038fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 3039e57e5e77SVivien Didelot 3040e57e5e77SVivien Didelot return err ? err : val; 3041fad09c73SVivien Didelot } 3042fad09c73SVivien Didelot 3043e57e5e77SVivien Didelot static int mv88e6xxx_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val) 3044fad09c73SVivien Didelot { 3045fad09c73SVivien Didelot struct mv88e6xxx_chip *chip = bus->priv; 3046e57e5e77SVivien Didelot int err; 3047fad09c73SVivien Didelot 3048370b4ffbSVivien Didelot if (phy >= mv88e6xxx_num_ports(chip)) 3049fad09c73SVivien Didelot return 0xffff; 3050fad09c73SVivien Didelot 3051fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 3052e57e5e77SVivien Didelot err = mv88e6xxx_phy_write(chip, phy, reg, val); 3053fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 3054e57e5e77SVivien Didelot 3055e57e5e77SVivien Didelot return err; 3056fad09c73SVivien Didelot } 3057fad09c73SVivien Didelot 3058fad09c73SVivien Didelot static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip, 3059fad09c73SVivien Didelot struct device_node *np) 3060fad09c73SVivien Didelot { 3061fad09c73SVivien Didelot static int index; 3062fad09c73SVivien Didelot struct mii_bus *bus; 3063fad09c73SVivien Didelot int err; 3064fad09c73SVivien Didelot 3065fad09c73SVivien Didelot if (np) 3066fad09c73SVivien Didelot chip->mdio_np = of_get_child_by_name(np, "mdio"); 3067fad09c73SVivien Didelot 3068fad09c73SVivien Didelot bus = devm_mdiobus_alloc(chip->dev); 3069fad09c73SVivien Didelot if (!bus) 3070fad09c73SVivien Didelot return -ENOMEM; 3071fad09c73SVivien Didelot 3072fad09c73SVivien Didelot bus->priv = (void *)chip; 3073fad09c73SVivien Didelot if (np) { 3074fad09c73SVivien Didelot bus->name = np->full_name; 3075fad09c73SVivien Didelot snprintf(bus->id, MII_BUS_ID_SIZE, "%s", np->full_name); 3076fad09c73SVivien Didelot } else { 3077fad09c73SVivien Didelot bus->name = "mv88e6xxx SMI"; 3078fad09c73SVivien Didelot snprintf(bus->id, MII_BUS_ID_SIZE, "mv88e6xxx-%d", index++); 3079fad09c73SVivien Didelot } 3080fad09c73SVivien Didelot 3081fad09c73SVivien Didelot bus->read = mv88e6xxx_mdio_read; 3082fad09c73SVivien Didelot bus->write = mv88e6xxx_mdio_write; 3083fad09c73SVivien Didelot bus->parent = chip->dev; 3084fad09c73SVivien Didelot 3085fad09c73SVivien Didelot if (chip->mdio_np) 3086fad09c73SVivien Didelot err = of_mdiobus_register(bus, chip->mdio_np); 3087fad09c73SVivien Didelot else 3088fad09c73SVivien Didelot err = mdiobus_register(bus); 3089fad09c73SVivien Didelot if (err) { 3090fad09c73SVivien Didelot dev_err(chip->dev, "Cannot register MDIO bus (%d)\n", err); 3091fad09c73SVivien Didelot goto out; 3092fad09c73SVivien Didelot } 3093fad09c73SVivien Didelot chip->mdio_bus = bus; 3094fad09c73SVivien Didelot 3095fad09c73SVivien Didelot return 0; 3096fad09c73SVivien Didelot 3097fad09c73SVivien Didelot out: 3098fad09c73SVivien Didelot if (chip->mdio_np) 3099fad09c73SVivien Didelot of_node_put(chip->mdio_np); 3100fad09c73SVivien Didelot 3101fad09c73SVivien Didelot return err; 3102fad09c73SVivien Didelot } 3103fad09c73SVivien Didelot 3104fad09c73SVivien Didelot static void mv88e6xxx_mdio_unregister(struct mv88e6xxx_chip *chip) 3105fad09c73SVivien Didelot 3106fad09c73SVivien Didelot { 3107fad09c73SVivien Didelot struct mii_bus *bus = chip->mdio_bus; 3108fad09c73SVivien Didelot 3109fad09c73SVivien Didelot mdiobus_unregister(bus); 3110fad09c73SVivien Didelot 3111fad09c73SVivien Didelot if (chip->mdio_np) 3112fad09c73SVivien Didelot of_node_put(chip->mdio_np); 3113fad09c73SVivien Didelot } 3114fad09c73SVivien Didelot 3115fad09c73SVivien Didelot #ifdef CONFIG_NET_DSA_HWMON 3116fad09c73SVivien Didelot 3117fad09c73SVivien Didelot static int mv88e61xx_get_temp(struct dsa_switch *ds, int *temp) 3118fad09c73SVivien Didelot { 311904bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 31209c93829cSVivien Didelot u16 val; 3121fad09c73SVivien Didelot int ret; 3122fad09c73SVivien Didelot 3123fad09c73SVivien Didelot *temp = 0; 3124fad09c73SVivien Didelot 3125fad09c73SVivien Didelot mutex_lock(&chip->reg_lock); 3126fad09c73SVivien Didelot 31279c93829cSVivien Didelot ret = mv88e6xxx_phy_write(chip, 0x0, 0x16, 0x6); 3128fad09c73SVivien Didelot if (ret < 0) 3129fad09c73SVivien Didelot goto error; 3130fad09c73SVivien Didelot 3131fad09c73SVivien Didelot /* Enable temperature sensor */ 31329c93829cSVivien Didelot ret = mv88e6xxx_phy_read(chip, 0x0, 0x1a, &val); 3133fad09c73SVivien Didelot if (ret < 0) 3134fad09c73SVivien Didelot goto error; 3135fad09c73SVivien Didelot 31369c93829cSVivien Didelot ret = mv88e6xxx_phy_write(chip, 0x0, 0x1a, val | (1 << 5)); 3137fad09c73SVivien Didelot if (ret < 0) 3138fad09c73SVivien Didelot goto error; 3139fad09c73SVivien Didelot 3140fad09c73SVivien Didelot /* Wait for temperature to stabilize */ 3141fad09c73SVivien Didelot usleep_range(10000, 12000); 3142fad09c73SVivien Didelot 31439c93829cSVivien Didelot ret = mv88e6xxx_phy_read(chip, 0x0, 0x1a, &val); 31449c93829cSVivien Didelot if (ret < 0) 3145fad09c73SVivien Didelot goto error; 3146fad09c73SVivien Didelot 3147fad09c73SVivien Didelot /* Disable temperature sensor */ 31489c93829cSVivien Didelot ret = mv88e6xxx_phy_write(chip, 0x0, 0x1a, val & ~(1 << 5)); 3149fad09c73SVivien Didelot if (ret < 0) 3150fad09c73SVivien Didelot goto error; 3151fad09c73SVivien Didelot 3152fad09c73SVivien Didelot *temp = ((val & 0x1f) - 5) * 5; 3153fad09c73SVivien Didelot 3154fad09c73SVivien Didelot error: 31559c93829cSVivien Didelot mv88e6xxx_phy_write(chip, 0x0, 0x16, 0x0); 3156fad09c73SVivien Didelot mutex_unlock(&chip->reg_lock); 3157fad09c73SVivien Didelot return ret; 3158fad09c73SVivien Didelot } 3159fad09c73SVivien Didelot 3160fad09c73SVivien Didelot static int mv88e63xx_get_temp(struct dsa_switch *ds, int *temp) 3161fad09c73SVivien Didelot { 316204bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 3163fad09c73SVivien Didelot int phy = mv88e6xxx_6320_family(chip) ? 3 : 0; 31649c93829cSVivien Didelot u16 val; 3165fad09c73SVivien Didelot int ret; 3166fad09c73SVivien Didelot 3167fad09c73SVivien Didelot *temp = 0; 3168fad09c73SVivien Didelot 31699c93829cSVivien Didelot mutex_lock(&chip->reg_lock); 31709c93829cSVivien Didelot ret = mv88e6xxx_phy_page_read(chip, phy, 6, 27, &val); 31719c93829cSVivien Didelot mutex_unlock(&chip->reg_lock); 3172fad09c73SVivien Didelot if (ret < 0) 3173fad09c73SVivien Didelot return ret; 3174fad09c73SVivien Didelot 31759c93829cSVivien Didelot *temp = (val & 0xff) - 25; 3176fad09c73SVivien Didelot 3177fad09c73SVivien Didelot return 0; 3178fad09c73SVivien Didelot } 3179fad09c73SVivien Didelot 3180fad09c73SVivien Didelot static int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp) 3181fad09c73SVivien Didelot { 318204bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 3183fad09c73SVivien Didelot 3184fad09c73SVivien Didelot if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP)) 3185fad09c73SVivien Didelot return -EOPNOTSUPP; 3186fad09c73SVivien Didelot 3187fad09c73SVivien Didelot if (mv88e6xxx_6320_family(chip) || mv88e6xxx_6352_family(chip)) 3188fad09c73SVivien Didelot return mv88e63xx_get_temp(ds, temp); 3189fad09c73SVivien Didelot 3190fad09c73SVivien Didelot return mv88e61xx_get_temp(ds, temp); 3191fad09c73SVivien Didelot } 3192fad09c73SVivien Didelot 3193fad09c73SVivien Didelot static int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp) 3194fad09c73SVivien Didelot { 319504bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 3196fad09c73SVivien Didelot int phy = mv88e6xxx_6320_family(chip) ? 3 : 0; 31979c93829cSVivien Didelot u16 val; 3198fad09c73SVivien Didelot int ret; 3199fad09c73SVivien Didelot 3200fad09c73SVivien Didelot if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT)) 3201fad09c73SVivien Didelot return -EOPNOTSUPP; 3202fad09c73SVivien Didelot 3203fad09c73SVivien Didelot *temp = 0; 3204fad09c73SVivien Didelot 32059c93829cSVivien Didelot mutex_lock(&chip->reg_lock); 32069c93829cSVivien Didelot ret = mv88e6xxx_phy_page_read(chip, phy, 6, 26, &val); 32079c93829cSVivien Didelot mutex_unlock(&chip->reg_lock); 3208fad09c73SVivien Didelot if (ret < 0) 3209fad09c73SVivien Didelot return ret; 3210fad09c73SVivien Didelot 32119c93829cSVivien Didelot *temp = (((val >> 8) & 0x1f) * 5) - 25; 3212fad09c73SVivien Didelot 3213fad09c73SVivien Didelot return 0; 3214fad09c73SVivien Didelot } 3215fad09c73SVivien Didelot 3216fad09c73SVivien Didelot static int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp) 3217fad09c73SVivien Didelot { 321804bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 3219fad09c73SVivien Didelot int phy = mv88e6xxx_6320_family(chip) ? 3 : 0; 32209c93829cSVivien Didelot u16 val; 32219c93829cSVivien Didelot int err; 3222fad09c73SVivien Didelot 3223fad09c73SVivien Didelot if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT)) 3224fad09c73SVivien Didelot return -EOPNOTSUPP; 3225fad09c73SVivien Didelot 32269c93829cSVivien Didelot mutex_lock(&chip->reg_lock); 32279c93829cSVivien Didelot err = mv88e6xxx_phy_page_read(chip, phy, 6, 26, &val); 32289c93829cSVivien Didelot if (err) 32299c93829cSVivien Didelot goto unlock; 3230fad09c73SVivien Didelot temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f); 32319c93829cSVivien Didelot err = mv88e6xxx_phy_page_write(chip, phy, 6, 26, 32329c93829cSVivien Didelot (val & 0xe0ff) | (temp << 8)); 32339c93829cSVivien Didelot unlock: 32349c93829cSVivien Didelot mutex_unlock(&chip->reg_lock); 32359c93829cSVivien Didelot 32369c93829cSVivien Didelot return err; 3237fad09c73SVivien Didelot } 3238fad09c73SVivien Didelot 3239fad09c73SVivien Didelot static int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm) 3240fad09c73SVivien Didelot { 324104bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 3242fad09c73SVivien Didelot int phy = mv88e6xxx_6320_family(chip) ? 3 : 0; 32439c93829cSVivien Didelot u16 val; 3244fad09c73SVivien Didelot int ret; 3245fad09c73SVivien Didelot 3246fad09c73SVivien Didelot if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT)) 3247fad09c73SVivien Didelot return -EOPNOTSUPP; 3248fad09c73SVivien Didelot 3249fad09c73SVivien Didelot *alarm = false; 3250fad09c73SVivien Didelot 32519c93829cSVivien Didelot mutex_lock(&chip->reg_lock); 32529c93829cSVivien Didelot ret = mv88e6xxx_phy_page_read(chip, phy, 6, 26, &val); 32539c93829cSVivien Didelot mutex_unlock(&chip->reg_lock); 3254fad09c73SVivien Didelot if (ret < 0) 3255fad09c73SVivien Didelot return ret; 3256fad09c73SVivien Didelot 32579c93829cSVivien Didelot *alarm = !!(val & 0x40); 3258fad09c73SVivien Didelot 3259fad09c73SVivien Didelot return 0; 3260fad09c73SVivien Didelot } 3261fad09c73SVivien Didelot #endif /* CONFIG_NET_DSA_HWMON */ 3262fad09c73SVivien Didelot 3263855b1932SVivien Didelot static int mv88e6xxx_get_eeprom_len(struct dsa_switch *ds) 3264855b1932SVivien Didelot { 326504bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 3266855b1932SVivien Didelot 3267855b1932SVivien Didelot return chip->eeprom_len; 3268855b1932SVivien Didelot } 3269855b1932SVivien Didelot 3270855b1932SVivien Didelot static int mv88e6xxx_get_eeprom(struct dsa_switch *ds, 3271855b1932SVivien Didelot struct ethtool_eeprom *eeprom, u8 *data) 3272855b1932SVivien Didelot { 327304bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 3274855b1932SVivien Didelot int err; 3275855b1932SVivien Didelot 3276ee4dc2e7SVivien Didelot if (!chip->info->ops->get_eeprom) 3277ee4dc2e7SVivien Didelot return -EOPNOTSUPP; 3278ee4dc2e7SVivien Didelot 3279855b1932SVivien Didelot mutex_lock(&chip->reg_lock); 3280ee4dc2e7SVivien Didelot err = chip->info->ops->get_eeprom(chip, eeprom, data); 3281855b1932SVivien Didelot mutex_unlock(&chip->reg_lock); 3282855b1932SVivien Didelot 3283855b1932SVivien Didelot if (err) 3284855b1932SVivien Didelot return err; 3285855b1932SVivien Didelot 3286855b1932SVivien Didelot eeprom->magic = 0xc3ec4951; 3287855b1932SVivien Didelot 3288855b1932SVivien Didelot return 0; 3289855b1932SVivien Didelot } 3290855b1932SVivien Didelot 3291855b1932SVivien Didelot static int mv88e6xxx_set_eeprom(struct dsa_switch *ds, 3292855b1932SVivien Didelot struct ethtool_eeprom *eeprom, u8 *data) 3293855b1932SVivien Didelot { 329404bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 3295855b1932SVivien Didelot int err; 3296855b1932SVivien Didelot 3297ee4dc2e7SVivien Didelot if (!chip->info->ops->set_eeprom) 3298ee4dc2e7SVivien Didelot return -EOPNOTSUPP; 3299ee4dc2e7SVivien Didelot 3300855b1932SVivien Didelot if (eeprom->magic != 0xc3ec4951) 3301855b1932SVivien Didelot return -EINVAL; 3302855b1932SVivien Didelot 3303855b1932SVivien Didelot mutex_lock(&chip->reg_lock); 3304ee4dc2e7SVivien Didelot err = chip->info->ops->set_eeprom(chip, eeprom, data); 3305855b1932SVivien Didelot mutex_unlock(&chip->reg_lock); 3306855b1932SVivien Didelot 3307855b1932SVivien Didelot return err; 3308855b1932SVivien Didelot } 3309855b1932SVivien Didelot 3310b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6085_ops = { 3311b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g1_set_switch_mac, 3312b3469dd8SVivien Didelot .phy_read = mv88e6xxx_phy_ppu_read, 3313b3469dd8SVivien Didelot .phy_write = mv88e6xxx_phy_ppu_write, 3314b3469dd8SVivien Didelot }; 3315b3469dd8SVivien Didelot 3316b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6095_ops = { 3317b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g1_set_switch_mac, 3318b3469dd8SVivien Didelot .phy_read = mv88e6xxx_phy_ppu_read, 3319b3469dd8SVivien Didelot .phy_write = mv88e6xxx_phy_ppu_write, 3320b3469dd8SVivien Didelot }; 3321b3469dd8SVivien Didelot 3322b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6123_ops = { 3323b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g2_set_switch_mac, 3324b3469dd8SVivien Didelot .phy_read = mv88e6xxx_read, 3325b3469dd8SVivien Didelot .phy_write = mv88e6xxx_write, 3326b3469dd8SVivien Didelot }; 3327b3469dd8SVivien Didelot 3328b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6131_ops = { 3329b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g1_set_switch_mac, 3330b3469dd8SVivien Didelot .phy_read = mv88e6xxx_phy_ppu_read, 3331b3469dd8SVivien Didelot .phy_write = mv88e6xxx_phy_ppu_write, 3332b3469dd8SVivien Didelot }; 3333b3469dd8SVivien Didelot 3334b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6161_ops = { 3335b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g2_set_switch_mac, 3336b3469dd8SVivien Didelot .phy_read = mv88e6xxx_read, 3337b3469dd8SVivien Didelot .phy_write = mv88e6xxx_write, 3338b3469dd8SVivien Didelot }; 3339b3469dd8SVivien Didelot 3340b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6165_ops = { 3341b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g2_set_switch_mac, 3342b3469dd8SVivien Didelot .phy_read = mv88e6xxx_read, 3343b3469dd8SVivien Didelot .phy_write = mv88e6xxx_write, 3344b3469dd8SVivien Didelot }; 3345b3469dd8SVivien Didelot 3346b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6171_ops = { 3347b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g2_set_switch_mac, 3348b3469dd8SVivien Didelot .phy_read = mv88e6xxx_g2_smi_phy_read, 3349b3469dd8SVivien Didelot .phy_write = mv88e6xxx_g2_smi_phy_write, 3350b3469dd8SVivien Didelot }; 3351b3469dd8SVivien Didelot 3352b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6172_ops = { 3353ee4dc2e7SVivien Didelot .get_eeprom = mv88e6xxx_g2_get_eeprom16, 3354ee4dc2e7SVivien Didelot .set_eeprom = mv88e6xxx_g2_set_eeprom16, 3355b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g2_set_switch_mac, 3356b3469dd8SVivien Didelot .phy_read = mv88e6xxx_g2_smi_phy_read, 3357b3469dd8SVivien Didelot .phy_write = mv88e6xxx_g2_smi_phy_write, 3358b3469dd8SVivien Didelot }; 3359b3469dd8SVivien Didelot 3360b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6175_ops = { 3361b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g2_set_switch_mac, 3362b3469dd8SVivien Didelot .phy_read = mv88e6xxx_g2_smi_phy_read, 3363b3469dd8SVivien Didelot .phy_write = mv88e6xxx_g2_smi_phy_write, 3364b3469dd8SVivien Didelot }; 3365b3469dd8SVivien Didelot 3366b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6176_ops = { 3367ee4dc2e7SVivien Didelot .get_eeprom = mv88e6xxx_g2_get_eeprom16, 3368ee4dc2e7SVivien Didelot .set_eeprom = mv88e6xxx_g2_set_eeprom16, 3369b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g2_set_switch_mac, 3370b3469dd8SVivien Didelot .phy_read = mv88e6xxx_g2_smi_phy_read, 3371b3469dd8SVivien Didelot .phy_write = mv88e6xxx_g2_smi_phy_write, 3372b3469dd8SVivien Didelot }; 3373b3469dd8SVivien Didelot 3374b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6185_ops = { 3375b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g1_set_switch_mac, 3376b3469dd8SVivien Didelot .phy_read = mv88e6xxx_phy_ppu_read, 3377b3469dd8SVivien Didelot .phy_write = mv88e6xxx_phy_ppu_write, 3378b3469dd8SVivien Didelot }; 3379b3469dd8SVivien Didelot 3380b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6240_ops = { 3381ee4dc2e7SVivien Didelot .get_eeprom = mv88e6xxx_g2_get_eeprom16, 3382ee4dc2e7SVivien Didelot .set_eeprom = mv88e6xxx_g2_set_eeprom16, 3383b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g2_set_switch_mac, 3384b3469dd8SVivien Didelot .phy_read = mv88e6xxx_g2_smi_phy_read, 3385b3469dd8SVivien Didelot .phy_write = mv88e6xxx_g2_smi_phy_write, 3386b3469dd8SVivien Didelot }; 3387b3469dd8SVivien Didelot 3388b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6320_ops = { 3389ee4dc2e7SVivien Didelot .get_eeprom = mv88e6xxx_g2_get_eeprom16, 3390ee4dc2e7SVivien Didelot .set_eeprom = mv88e6xxx_g2_set_eeprom16, 3391b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g2_set_switch_mac, 3392b3469dd8SVivien Didelot .phy_read = mv88e6xxx_g2_smi_phy_read, 3393b3469dd8SVivien Didelot .phy_write = mv88e6xxx_g2_smi_phy_write, 3394b3469dd8SVivien Didelot }; 3395b3469dd8SVivien Didelot 3396b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6321_ops = { 3397ee4dc2e7SVivien Didelot .get_eeprom = mv88e6xxx_g2_get_eeprom16, 3398ee4dc2e7SVivien Didelot .set_eeprom = mv88e6xxx_g2_set_eeprom16, 3399b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g2_set_switch_mac, 3400b3469dd8SVivien Didelot .phy_read = mv88e6xxx_g2_smi_phy_read, 3401b3469dd8SVivien Didelot .phy_write = mv88e6xxx_g2_smi_phy_write, 3402b3469dd8SVivien Didelot }; 3403b3469dd8SVivien Didelot 3404b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6350_ops = { 3405b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g2_set_switch_mac, 3406b3469dd8SVivien Didelot .phy_read = mv88e6xxx_g2_smi_phy_read, 3407b3469dd8SVivien Didelot .phy_write = mv88e6xxx_g2_smi_phy_write, 3408b3469dd8SVivien Didelot }; 3409b3469dd8SVivien Didelot 3410b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6351_ops = { 3411b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g2_set_switch_mac, 3412b3469dd8SVivien Didelot .phy_read = mv88e6xxx_g2_smi_phy_read, 3413b3469dd8SVivien Didelot .phy_write = mv88e6xxx_g2_smi_phy_write, 3414b3469dd8SVivien Didelot }; 3415b3469dd8SVivien Didelot 3416b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6352_ops = { 3417ee4dc2e7SVivien Didelot .get_eeprom = mv88e6xxx_g2_get_eeprom16, 3418ee4dc2e7SVivien Didelot .set_eeprom = mv88e6xxx_g2_set_eeprom16, 3419b073d4e2SVivien Didelot .set_switch_mac = mv88e6xxx_g2_set_switch_mac, 3420b3469dd8SVivien Didelot .phy_read = mv88e6xxx_g2_smi_phy_read, 3421b3469dd8SVivien Didelot .phy_write = mv88e6xxx_g2_smi_phy_write, 3422b3469dd8SVivien Didelot }; 3423b3469dd8SVivien Didelot 3424fad09c73SVivien Didelot static const struct mv88e6xxx_info mv88e6xxx_table[] = { 3425fad09c73SVivien Didelot [MV88E6085] = { 3426fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6085, 3427fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6097, 3428fad09c73SVivien Didelot .name = "Marvell 88E6085", 3429fad09c73SVivien Didelot .num_databases = 4096, 3430fad09c73SVivien Didelot .num_ports = 10, 3431fad09c73SVivien Didelot .port_base_addr = 0x10, 3432a935c052SVivien Didelot .global1_addr = 0x1b, 3433acddbd21SVivien Didelot .age_time_coeff = 15000, 3434dc30c35bSAndrew Lunn .g1_irqs = 8, 3435fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6097, 3436b3469dd8SVivien Didelot .ops = &mv88e6085_ops, 3437fad09c73SVivien Didelot }, 3438fad09c73SVivien Didelot 3439fad09c73SVivien Didelot [MV88E6095] = { 3440fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6095, 3441fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6095, 3442fad09c73SVivien Didelot .name = "Marvell 88E6095/88E6095F", 3443fad09c73SVivien Didelot .num_databases = 256, 3444fad09c73SVivien Didelot .num_ports = 11, 3445fad09c73SVivien Didelot .port_base_addr = 0x10, 3446a935c052SVivien Didelot .global1_addr = 0x1b, 3447acddbd21SVivien Didelot .age_time_coeff = 15000, 3448dc30c35bSAndrew Lunn .g1_irqs = 8, 3449fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6095, 3450b3469dd8SVivien Didelot .ops = &mv88e6095_ops, 3451fad09c73SVivien Didelot }, 3452fad09c73SVivien Didelot 3453fad09c73SVivien Didelot [MV88E6123] = { 3454fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6123, 3455fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6165, 3456fad09c73SVivien Didelot .name = "Marvell 88E6123", 3457fad09c73SVivien Didelot .num_databases = 4096, 3458fad09c73SVivien Didelot .num_ports = 3, 3459fad09c73SVivien Didelot .port_base_addr = 0x10, 3460a935c052SVivien Didelot .global1_addr = 0x1b, 3461acddbd21SVivien Didelot .age_time_coeff = 15000, 3462dc30c35bSAndrew Lunn .g1_irqs = 9, 3463fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6165, 3464b3469dd8SVivien Didelot .ops = &mv88e6123_ops, 3465fad09c73SVivien Didelot }, 3466fad09c73SVivien Didelot 3467fad09c73SVivien Didelot [MV88E6131] = { 3468fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6131, 3469fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6185, 3470fad09c73SVivien Didelot .name = "Marvell 88E6131", 3471fad09c73SVivien Didelot .num_databases = 256, 3472fad09c73SVivien Didelot .num_ports = 8, 3473fad09c73SVivien Didelot .port_base_addr = 0x10, 3474a935c052SVivien Didelot .global1_addr = 0x1b, 3475acddbd21SVivien Didelot .age_time_coeff = 15000, 3476dc30c35bSAndrew Lunn .g1_irqs = 9, 3477fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6185, 3478b3469dd8SVivien Didelot .ops = &mv88e6131_ops, 3479fad09c73SVivien Didelot }, 3480fad09c73SVivien Didelot 3481fad09c73SVivien Didelot [MV88E6161] = { 3482fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6161, 3483fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6165, 3484fad09c73SVivien Didelot .name = "Marvell 88E6161", 3485fad09c73SVivien Didelot .num_databases = 4096, 3486fad09c73SVivien Didelot .num_ports = 6, 3487fad09c73SVivien Didelot .port_base_addr = 0x10, 3488a935c052SVivien Didelot .global1_addr = 0x1b, 3489acddbd21SVivien Didelot .age_time_coeff = 15000, 3490dc30c35bSAndrew Lunn .g1_irqs = 9, 3491fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6165, 3492b3469dd8SVivien Didelot .ops = &mv88e6161_ops, 3493fad09c73SVivien Didelot }, 3494fad09c73SVivien Didelot 3495fad09c73SVivien Didelot [MV88E6165] = { 3496fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6165, 3497fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6165, 3498fad09c73SVivien Didelot .name = "Marvell 88E6165", 3499fad09c73SVivien Didelot .num_databases = 4096, 3500fad09c73SVivien Didelot .num_ports = 6, 3501fad09c73SVivien Didelot .port_base_addr = 0x10, 3502a935c052SVivien Didelot .global1_addr = 0x1b, 3503acddbd21SVivien Didelot .age_time_coeff = 15000, 3504dc30c35bSAndrew Lunn .g1_irqs = 9, 3505fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6165, 3506b3469dd8SVivien Didelot .ops = &mv88e6165_ops, 3507fad09c73SVivien Didelot }, 3508fad09c73SVivien Didelot 3509fad09c73SVivien Didelot [MV88E6171] = { 3510fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6171, 3511fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6351, 3512fad09c73SVivien Didelot .name = "Marvell 88E6171", 3513fad09c73SVivien Didelot .num_databases = 4096, 3514fad09c73SVivien Didelot .num_ports = 7, 3515fad09c73SVivien Didelot .port_base_addr = 0x10, 3516a935c052SVivien Didelot .global1_addr = 0x1b, 3517acddbd21SVivien Didelot .age_time_coeff = 15000, 3518dc30c35bSAndrew Lunn .g1_irqs = 9, 3519fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6351, 3520b3469dd8SVivien Didelot .ops = &mv88e6171_ops, 3521fad09c73SVivien Didelot }, 3522fad09c73SVivien Didelot 3523fad09c73SVivien Didelot [MV88E6172] = { 3524fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6172, 3525fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6352, 3526fad09c73SVivien Didelot .name = "Marvell 88E6172", 3527fad09c73SVivien Didelot .num_databases = 4096, 3528fad09c73SVivien Didelot .num_ports = 7, 3529fad09c73SVivien Didelot .port_base_addr = 0x10, 3530a935c052SVivien Didelot .global1_addr = 0x1b, 3531acddbd21SVivien Didelot .age_time_coeff = 15000, 3532dc30c35bSAndrew Lunn .g1_irqs = 9, 3533fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6352, 3534b3469dd8SVivien Didelot .ops = &mv88e6172_ops, 3535fad09c73SVivien Didelot }, 3536fad09c73SVivien Didelot 3537fad09c73SVivien Didelot [MV88E6175] = { 3538fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6175, 3539fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6351, 3540fad09c73SVivien Didelot .name = "Marvell 88E6175", 3541fad09c73SVivien Didelot .num_databases = 4096, 3542fad09c73SVivien Didelot .num_ports = 7, 3543fad09c73SVivien Didelot .port_base_addr = 0x10, 3544a935c052SVivien Didelot .global1_addr = 0x1b, 3545acddbd21SVivien Didelot .age_time_coeff = 15000, 3546dc30c35bSAndrew Lunn .g1_irqs = 9, 3547fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6351, 3548b3469dd8SVivien Didelot .ops = &mv88e6175_ops, 3549fad09c73SVivien Didelot }, 3550fad09c73SVivien Didelot 3551fad09c73SVivien Didelot [MV88E6176] = { 3552fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6176, 3553fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6352, 3554fad09c73SVivien Didelot .name = "Marvell 88E6176", 3555fad09c73SVivien Didelot .num_databases = 4096, 3556fad09c73SVivien Didelot .num_ports = 7, 3557fad09c73SVivien Didelot .port_base_addr = 0x10, 3558a935c052SVivien Didelot .global1_addr = 0x1b, 3559acddbd21SVivien Didelot .age_time_coeff = 15000, 3560dc30c35bSAndrew Lunn .g1_irqs = 9, 3561fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6352, 3562b3469dd8SVivien Didelot .ops = &mv88e6176_ops, 3563fad09c73SVivien Didelot }, 3564fad09c73SVivien Didelot 3565fad09c73SVivien Didelot [MV88E6185] = { 3566fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6185, 3567fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6185, 3568fad09c73SVivien Didelot .name = "Marvell 88E6185", 3569fad09c73SVivien Didelot .num_databases = 256, 3570fad09c73SVivien Didelot .num_ports = 10, 3571fad09c73SVivien Didelot .port_base_addr = 0x10, 3572a935c052SVivien Didelot .global1_addr = 0x1b, 3573acddbd21SVivien Didelot .age_time_coeff = 15000, 3574dc30c35bSAndrew Lunn .g1_irqs = 8, 3575fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6185, 3576b3469dd8SVivien Didelot .ops = &mv88e6185_ops, 3577fad09c73SVivien Didelot }, 3578fad09c73SVivien Didelot 3579fad09c73SVivien Didelot [MV88E6240] = { 3580fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6240, 3581fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6352, 3582fad09c73SVivien Didelot .name = "Marvell 88E6240", 3583fad09c73SVivien Didelot .num_databases = 4096, 3584fad09c73SVivien Didelot .num_ports = 7, 3585fad09c73SVivien Didelot .port_base_addr = 0x10, 3586a935c052SVivien Didelot .global1_addr = 0x1b, 3587acddbd21SVivien Didelot .age_time_coeff = 15000, 3588dc30c35bSAndrew Lunn .g1_irqs = 9, 3589fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6352, 3590b3469dd8SVivien Didelot .ops = &mv88e6240_ops, 3591fad09c73SVivien Didelot }, 3592fad09c73SVivien Didelot 3593fad09c73SVivien Didelot [MV88E6320] = { 3594fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6320, 3595fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6320, 3596fad09c73SVivien Didelot .name = "Marvell 88E6320", 3597fad09c73SVivien Didelot .num_databases = 4096, 3598fad09c73SVivien Didelot .num_ports = 7, 3599fad09c73SVivien Didelot .port_base_addr = 0x10, 3600a935c052SVivien Didelot .global1_addr = 0x1b, 3601acddbd21SVivien Didelot .age_time_coeff = 15000, 3602dc30c35bSAndrew Lunn .g1_irqs = 8, 3603fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6320, 3604b3469dd8SVivien Didelot .ops = &mv88e6320_ops, 3605fad09c73SVivien Didelot }, 3606fad09c73SVivien Didelot 3607fad09c73SVivien Didelot [MV88E6321] = { 3608fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6321, 3609fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6320, 3610fad09c73SVivien Didelot .name = "Marvell 88E6321", 3611fad09c73SVivien Didelot .num_databases = 4096, 3612fad09c73SVivien Didelot .num_ports = 7, 3613fad09c73SVivien Didelot .port_base_addr = 0x10, 3614a935c052SVivien Didelot .global1_addr = 0x1b, 3615acddbd21SVivien Didelot .age_time_coeff = 15000, 3616dc30c35bSAndrew Lunn .g1_irqs = 8, 3617fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6320, 3618b3469dd8SVivien Didelot .ops = &mv88e6321_ops, 3619fad09c73SVivien Didelot }, 3620fad09c73SVivien Didelot 3621fad09c73SVivien Didelot [MV88E6350] = { 3622fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6350, 3623fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6351, 3624fad09c73SVivien Didelot .name = "Marvell 88E6350", 3625fad09c73SVivien Didelot .num_databases = 4096, 3626fad09c73SVivien Didelot .num_ports = 7, 3627fad09c73SVivien Didelot .port_base_addr = 0x10, 3628a935c052SVivien Didelot .global1_addr = 0x1b, 3629acddbd21SVivien Didelot .age_time_coeff = 15000, 3630dc30c35bSAndrew Lunn .g1_irqs = 9, 3631fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6351, 3632b3469dd8SVivien Didelot .ops = &mv88e6350_ops, 3633fad09c73SVivien Didelot }, 3634fad09c73SVivien Didelot 3635fad09c73SVivien Didelot [MV88E6351] = { 3636fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6351, 3637fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6351, 3638fad09c73SVivien Didelot .name = "Marvell 88E6351", 3639fad09c73SVivien Didelot .num_databases = 4096, 3640fad09c73SVivien Didelot .num_ports = 7, 3641fad09c73SVivien Didelot .port_base_addr = 0x10, 3642a935c052SVivien Didelot .global1_addr = 0x1b, 3643acddbd21SVivien Didelot .age_time_coeff = 15000, 3644dc30c35bSAndrew Lunn .g1_irqs = 9, 3645fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6351, 3646b3469dd8SVivien Didelot .ops = &mv88e6351_ops, 3647fad09c73SVivien Didelot }, 3648fad09c73SVivien Didelot 3649fad09c73SVivien Didelot [MV88E6352] = { 3650fad09c73SVivien Didelot .prod_num = PORT_SWITCH_ID_PROD_NUM_6352, 3651fad09c73SVivien Didelot .family = MV88E6XXX_FAMILY_6352, 3652fad09c73SVivien Didelot .name = "Marvell 88E6352", 3653fad09c73SVivien Didelot .num_databases = 4096, 3654fad09c73SVivien Didelot .num_ports = 7, 3655fad09c73SVivien Didelot .port_base_addr = 0x10, 3656a935c052SVivien Didelot .global1_addr = 0x1b, 3657acddbd21SVivien Didelot .age_time_coeff = 15000, 3658dc30c35bSAndrew Lunn .g1_irqs = 9, 3659fad09c73SVivien Didelot .flags = MV88E6XXX_FLAGS_FAMILY_6352, 3660b3469dd8SVivien Didelot .ops = &mv88e6352_ops, 3661fad09c73SVivien Didelot }, 3662fad09c73SVivien Didelot }; 3663fad09c73SVivien Didelot 3664fad09c73SVivien Didelot static const struct mv88e6xxx_info *mv88e6xxx_lookup_info(unsigned int prod_num) 3665fad09c73SVivien Didelot { 3666fad09c73SVivien Didelot int i; 3667fad09c73SVivien Didelot 3668fad09c73SVivien Didelot for (i = 0; i < ARRAY_SIZE(mv88e6xxx_table); ++i) 3669fad09c73SVivien Didelot if (mv88e6xxx_table[i].prod_num == prod_num) 3670fad09c73SVivien Didelot return &mv88e6xxx_table[i]; 3671fad09c73SVivien Didelot 3672fad09c73SVivien Didelot return NULL; 3673fad09c73SVivien Didelot } 3674fad09c73SVivien Didelot 3675fad09c73SVivien Didelot static int mv88e6xxx_detect(struct mv88e6xxx_chip *chip) 3676fad09c73SVivien Didelot { 3677fad09c73SVivien Didelot const struct mv88e6xxx_info *info; 36788f6345b2SVivien Didelot unsigned int prod_num, rev; 36798f6345b2SVivien Didelot u16 id; 36808f6345b2SVivien Didelot int err; 3681fad09c73SVivien Didelot 36828f6345b2SVivien Didelot mutex_lock(&chip->reg_lock); 36838f6345b2SVivien Didelot err = mv88e6xxx_port_read(chip, 0, PORT_SWITCH_ID, &id); 36848f6345b2SVivien Didelot mutex_unlock(&chip->reg_lock); 36858f6345b2SVivien Didelot if (err) 36868f6345b2SVivien Didelot return err; 3687fad09c73SVivien Didelot 3688fad09c73SVivien Didelot prod_num = (id & 0xfff0) >> 4; 3689fad09c73SVivien Didelot rev = id & 0x000f; 3690fad09c73SVivien Didelot 3691fad09c73SVivien Didelot info = mv88e6xxx_lookup_info(prod_num); 3692fad09c73SVivien Didelot if (!info) 3693fad09c73SVivien Didelot return -ENODEV; 3694fad09c73SVivien Didelot 3695fad09c73SVivien Didelot /* Update the compatible info with the probed one */ 3696fad09c73SVivien Didelot chip->info = info; 3697fad09c73SVivien Didelot 3698ca070c10SVivien Didelot err = mv88e6xxx_g2_require(chip); 3699ca070c10SVivien Didelot if (err) 3700ca070c10SVivien Didelot return err; 3701ca070c10SVivien Didelot 3702fad09c73SVivien Didelot dev_info(chip->dev, "switch 0x%x detected: %s, revision %u\n", 3703fad09c73SVivien Didelot chip->info->prod_num, chip->info->name, rev); 3704fad09c73SVivien Didelot 3705fad09c73SVivien Didelot return 0; 3706fad09c73SVivien Didelot } 3707fad09c73SVivien Didelot 3708fad09c73SVivien Didelot static struct mv88e6xxx_chip *mv88e6xxx_alloc_chip(struct device *dev) 3709fad09c73SVivien Didelot { 3710fad09c73SVivien Didelot struct mv88e6xxx_chip *chip; 3711fad09c73SVivien Didelot 3712fad09c73SVivien Didelot chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); 3713fad09c73SVivien Didelot if (!chip) 3714fad09c73SVivien Didelot return NULL; 3715fad09c73SVivien Didelot 3716fad09c73SVivien Didelot chip->dev = dev; 3717fad09c73SVivien Didelot 3718fad09c73SVivien Didelot mutex_init(&chip->reg_lock); 3719fad09c73SVivien Didelot 3720fad09c73SVivien Didelot return chip; 3721fad09c73SVivien Didelot } 3722fad09c73SVivien Didelot 3723e57e5e77SVivien Didelot static void mv88e6xxx_phy_init(struct mv88e6xxx_chip *chip) 3724e57e5e77SVivien Didelot { 3725b3469dd8SVivien Didelot if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU)) 3726e57e5e77SVivien Didelot mv88e6xxx_ppu_state_init(chip); 3727e57e5e77SVivien Didelot } 3728e57e5e77SVivien Didelot 3729930188ceSAndrew Lunn static void mv88e6xxx_phy_destroy(struct mv88e6xxx_chip *chip) 3730930188ceSAndrew Lunn { 3731b3469dd8SVivien Didelot if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU)) 3732930188ceSAndrew Lunn mv88e6xxx_ppu_state_destroy(chip); 3733930188ceSAndrew Lunn } 3734930188ceSAndrew Lunn 3735fad09c73SVivien Didelot static int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip, 3736fad09c73SVivien Didelot struct mii_bus *bus, int sw_addr) 3737fad09c73SVivien Didelot { 3738fad09c73SVivien Didelot /* ADDR[0] pin is unavailable externally and considered zero */ 3739fad09c73SVivien Didelot if (sw_addr & 0x1) 3740fad09c73SVivien Didelot return -EINVAL; 3741fad09c73SVivien Didelot 3742fad09c73SVivien Didelot if (sw_addr == 0) 3743fad09c73SVivien Didelot chip->smi_ops = &mv88e6xxx_smi_single_chip_ops; 3744a0ffff24SVivien Didelot else if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_MULTI_CHIP)) 3745fad09c73SVivien Didelot chip->smi_ops = &mv88e6xxx_smi_multi_chip_ops; 3746fad09c73SVivien Didelot else 3747fad09c73SVivien Didelot return -EINVAL; 3748fad09c73SVivien Didelot 3749fad09c73SVivien Didelot chip->bus = bus; 3750fad09c73SVivien Didelot chip->sw_addr = sw_addr; 3751fad09c73SVivien Didelot 3752fad09c73SVivien Didelot return 0; 3753fad09c73SVivien Didelot } 3754fad09c73SVivien Didelot 37557b314362SAndrew Lunn static enum dsa_tag_protocol mv88e6xxx_get_tag_protocol(struct dsa_switch *ds) 37567b314362SAndrew Lunn { 375704bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 37582bbb33beSAndrew Lunn 37592bbb33beSAndrew Lunn if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EDSA)) 37607b314362SAndrew Lunn return DSA_TAG_PROTO_EDSA; 37612bbb33beSAndrew Lunn 37622bbb33beSAndrew Lunn return DSA_TAG_PROTO_DSA; 37637b314362SAndrew Lunn } 37647b314362SAndrew Lunn 3765fad09c73SVivien Didelot static const char *mv88e6xxx_drv_probe(struct device *dsa_dev, 3766fad09c73SVivien Didelot struct device *host_dev, int sw_addr, 3767fad09c73SVivien Didelot void **priv) 3768fad09c73SVivien Didelot { 3769fad09c73SVivien Didelot struct mv88e6xxx_chip *chip; 3770fad09c73SVivien Didelot struct mii_bus *bus; 3771fad09c73SVivien Didelot int err; 3772fad09c73SVivien Didelot 3773fad09c73SVivien Didelot bus = dsa_host_dev_to_mii_bus(host_dev); 3774fad09c73SVivien Didelot if (!bus) 3775fad09c73SVivien Didelot return NULL; 3776fad09c73SVivien Didelot 3777fad09c73SVivien Didelot chip = mv88e6xxx_alloc_chip(dsa_dev); 3778fad09c73SVivien Didelot if (!chip) 3779fad09c73SVivien Didelot return NULL; 3780fad09c73SVivien Didelot 3781fad09c73SVivien Didelot /* Legacy SMI probing will only support chips similar to 88E6085 */ 3782fad09c73SVivien Didelot chip->info = &mv88e6xxx_table[MV88E6085]; 3783fad09c73SVivien Didelot 3784fad09c73SVivien Didelot err = mv88e6xxx_smi_init(chip, bus, sw_addr); 3785fad09c73SVivien Didelot if (err) 3786fad09c73SVivien Didelot goto free; 3787fad09c73SVivien Didelot 3788fad09c73SVivien Didelot err = mv88e6xxx_detect(chip); 3789fad09c73SVivien Didelot if (err) 3790fad09c73SVivien Didelot goto free; 3791fad09c73SVivien Didelot 3792dc30c35bSAndrew Lunn mutex_lock(&chip->reg_lock); 3793dc30c35bSAndrew Lunn err = mv88e6xxx_switch_reset(chip); 3794dc30c35bSAndrew Lunn mutex_unlock(&chip->reg_lock); 3795dc30c35bSAndrew Lunn if (err) 3796dc30c35bSAndrew Lunn goto free; 3797dc30c35bSAndrew Lunn 3798e57e5e77SVivien Didelot mv88e6xxx_phy_init(chip); 3799e57e5e77SVivien Didelot 3800fad09c73SVivien Didelot err = mv88e6xxx_mdio_register(chip, NULL); 3801fad09c73SVivien Didelot if (err) 3802fad09c73SVivien Didelot goto free; 3803fad09c73SVivien Didelot 3804fad09c73SVivien Didelot *priv = chip; 3805fad09c73SVivien Didelot 3806fad09c73SVivien Didelot return chip->info->name; 3807fad09c73SVivien Didelot free: 3808fad09c73SVivien Didelot devm_kfree(dsa_dev, chip); 3809fad09c73SVivien Didelot 3810fad09c73SVivien Didelot return NULL; 3811fad09c73SVivien Didelot } 3812fad09c73SVivien Didelot 38137df8fbddSVivien Didelot static int mv88e6xxx_port_mdb_prepare(struct dsa_switch *ds, int port, 38147df8fbddSVivien Didelot const struct switchdev_obj_port_mdb *mdb, 38157df8fbddSVivien Didelot struct switchdev_trans *trans) 38167df8fbddSVivien Didelot { 38177df8fbddSVivien Didelot /* We don't need any dynamic resource from the kernel (yet), 38187df8fbddSVivien Didelot * so skip the prepare phase. 38197df8fbddSVivien Didelot */ 38207df8fbddSVivien Didelot 38217df8fbddSVivien Didelot return 0; 38227df8fbddSVivien Didelot } 38237df8fbddSVivien Didelot 38247df8fbddSVivien Didelot static void mv88e6xxx_port_mdb_add(struct dsa_switch *ds, int port, 38257df8fbddSVivien Didelot const struct switchdev_obj_port_mdb *mdb, 38267df8fbddSVivien Didelot struct switchdev_trans *trans) 38277df8fbddSVivien Didelot { 382804bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 38297df8fbddSVivien Didelot 38307df8fbddSVivien Didelot mutex_lock(&chip->reg_lock); 38317df8fbddSVivien Didelot if (mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid, 38327df8fbddSVivien Didelot GLOBAL_ATU_DATA_STATE_MC_STATIC)) 38337df8fbddSVivien Didelot netdev_err(ds->ports[port].netdev, "failed to load multicast MAC address\n"); 38347df8fbddSVivien Didelot mutex_unlock(&chip->reg_lock); 38357df8fbddSVivien Didelot } 38367df8fbddSVivien Didelot 38377df8fbddSVivien Didelot static int mv88e6xxx_port_mdb_del(struct dsa_switch *ds, int port, 38387df8fbddSVivien Didelot const struct switchdev_obj_port_mdb *mdb) 38397df8fbddSVivien Didelot { 384004bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 38417df8fbddSVivien Didelot int err; 38427df8fbddSVivien Didelot 38437df8fbddSVivien Didelot mutex_lock(&chip->reg_lock); 38447df8fbddSVivien Didelot err = mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid, 38457df8fbddSVivien Didelot GLOBAL_ATU_DATA_STATE_UNUSED); 38467df8fbddSVivien Didelot mutex_unlock(&chip->reg_lock); 38477df8fbddSVivien Didelot 38487df8fbddSVivien Didelot return err; 38497df8fbddSVivien Didelot } 38507df8fbddSVivien Didelot 38517df8fbddSVivien Didelot static int mv88e6xxx_port_mdb_dump(struct dsa_switch *ds, int port, 38527df8fbddSVivien Didelot struct switchdev_obj_port_mdb *mdb, 38537df8fbddSVivien Didelot int (*cb)(struct switchdev_obj *obj)) 38547df8fbddSVivien Didelot { 385504bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 38567df8fbddSVivien Didelot int err; 38577df8fbddSVivien Didelot 38587df8fbddSVivien Didelot mutex_lock(&chip->reg_lock); 38597df8fbddSVivien Didelot err = mv88e6xxx_port_db_dump(chip, port, &mdb->obj, cb); 38607df8fbddSVivien Didelot mutex_unlock(&chip->reg_lock); 38617df8fbddSVivien Didelot 38627df8fbddSVivien Didelot return err; 38637df8fbddSVivien Didelot } 38647df8fbddSVivien Didelot 38659d490b4eSVivien Didelot static struct dsa_switch_ops mv88e6xxx_switch_ops = { 3866fad09c73SVivien Didelot .probe = mv88e6xxx_drv_probe, 38677b314362SAndrew Lunn .get_tag_protocol = mv88e6xxx_get_tag_protocol, 3868fad09c73SVivien Didelot .setup = mv88e6xxx_setup, 3869fad09c73SVivien Didelot .set_addr = mv88e6xxx_set_addr, 3870fad09c73SVivien Didelot .adjust_link = mv88e6xxx_adjust_link, 3871fad09c73SVivien Didelot .get_strings = mv88e6xxx_get_strings, 3872fad09c73SVivien Didelot .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, 3873fad09c73SVivien Didelot .get_sset_count = mv88e6xxx_get_sset_count, 3874fad09c73SVivien Didelot .set_eee = mv88e6xxx_set_eee, 3875fad09c73SVivien Didelot .get_eee = mv88e6xxx_get_eee, 3876fad09c73SVivien Didelot #ifdef CONFIG_NET_DSA_HWMON 3877fad09c73SVivien Didelot .get_temp = mv88e6xxx_get_temp, 3878fad09c73SVivien Didelot .get_temp_limit = mv88e6xxx_get_temp_limit, 3879fad09c73SVivien Didelot .set_temp_limit = mv88e6xxx_set_temp_limit, 3880fad09c73SVivien Didelot .get_temp_alarm = mv88e6xxx_get_temp_alarm, 3881fad09c73SVivien Didelot #endif 3882fad09c73SVivien Didelot .get_eeprom_len = mv88e6xxx_get_eeprom_len, 3883fad09c73SVivien Didelot .get_eeprom = mv88e6xxx_get_eeprom, 3884fad09c73SVivien Didelot .set_eeprom = mv88e6xxx_set_eeprom, 3885fad09c73SVivien Didelot .get_regs_len = mv88e6xxx_get_regs_len, 3886fad09c73SVivien Didelot .get_regs = mv88e6xxx_get_regs, 38872cfcd964SVivien Didelot .set_ageing_time = mv88e6xxx_set_ageing_time, 3888fad09c73SVivien Didelot .port_bridge_join = mv88e6xxx_port_bridge_join, 3889fad09c73SVivien Didelot .port_bridge_leave = mv88e6xxx_port_bridge_leave, 3890fad09c73SVivien Didelot .port_stp_state_set = mv88e6xxx_port_stp_state_set, 3891749efcb8SVivien Didelot .port_fast_age = mv88e6xxx_port_fast_age, 3892fad09c73SVivien Didelot .port_vlan_filtering = mv88e6xxx_port_vlan_filtering, 3893fad09c73SVivien Didelot .port_vlan_prepare = mv88e6xxx_port_vlan_prepare, 3894fad09c73SVivien Didelot .port_vlan_add = mv88e6xxx_port_vlan_add, 3895fad09c73SVivien Didelot .port_vlan_del = mv88e6xxx_port_vlan_del, 3896fad09c73SVivien Didelot .port_vlan_dump = mv88e6xxx_port_vlan_dump, 3897fad09c73SVivien Didelot .port_fdb_prepare = mv88e6xxx_port_fdb_prepare, 3898fad09c73SVivien Didelot .port_fdb_add = mv88e6xxx_port_fdb_add, 3899fad09c73SVivien Didelot .port_fdb_del = mv88e6xxx_port_fdb_del, 3900fad09c73SVivien Didelot .port_fdb_dump = mv88e6xxx_port_fdb_dump, 39017df8fbddSVivien Didelot .port_mdb_prepare = mv88e6xxx_port_mdb_prepare, 39027df8fbddSVivien Didelot .port_mdb_add = mv88e6xxx_port_mdb_add, 39037df8fbddSVivien Didelot .port_mdb_del = mv88e6xxx_port_mdb_del, 39047df8fbddSVivien Didelot .port_mdb_dump = mv88e6xxx_port_mdb_dump, 3905fad09c73SVivien Didelot }; 3906fad09c73SVivien Didelot 3907fad09c73SVivien Didelot static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip, 3908fad09c73SVivien Didelot struct device_node *np) 3909fad09c73SVivien Didelot { 3910fad09c73SVivien Didelot struct device *dev = chip->dev; 3911fad09c73SVivien Didelot struct dsa_switch *ds; 3912fad09c73SVivien Didelot 3913fad09c73SVivien Didelot ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL); 3914fad09c73SVivien Didelot if (!ds) 3915fad09c73SVivien Didelot return -ENOMEM; 3916fad09c73SVivien Didelot 3917fad09c73SVivien Didelot ds->dev = dev; 3918fad09c73SVivien Didelot ds->priv = chip; 39199d490b4eSVivien Didelot ds->ops = &mv88e6xxx_switch_ops; 3920fad09c73SVivien Didelot 3921fad09c73SVivien Didelot dev_set_drvdata(dev, ds); 3922fad09c73SVivien Didelot 3923fad09c73SVivien Didelot return dsa_register_switch(ds, np); 3924fad09c73SVivien Didelot } 3925fad09c73SVivien Didelot 3926fad09c73SVivien Didelot static void mv88e6xxx_unregister_switch(struct mv88e6xxx_chip *chip) 3927fad09c73SVivien Didelot { 3928fad09c73SVivien Didelot dsa_unregister_switch(chip->ds); 3929fad09c73SVivien Didelot } 3930fad09c73SVivien Didelot 3931fad09c73SVivien Didelot static int mv88e6xxx_probe(struct mdio_device *mdiodev) 3932fad09c73SVivien Didelot { 3933fad09c73SVivien Didelot struct device *dev = &mdiodev->dev; 3934fad09c73SVivien Didelot struct device_node *np = dev->of_node; 3935fad09c73SVivien Didelot const struct mv88e6xxx_info *compat_info; 3936fad09c73SVivien Didelot struct mv88e6xxx_chip *chip; 3937fad09c73SVivien Didelot u32 eeprom_len; 3938fad09c73SVivien Didelot int err; 3939fad09c73SVivien Didelot 3940fad09c73SVivien Didelot compat_info = of_device_get_match_data(dev); 3941fad09c73SVivien Didelot if (!compat_info) 3942fad09c73SVivien Didelot return -EINVAL; 3943fad09c73SVivien Didelot 3944fad09c73SVivien Didelot chip = mv88e6xxx_alloc_chip(dev); 3945fad09c73SVivien Didelot if (!chip) 3946fad09c73SVivien Didelot return -ENOMEM; 3947fad09c73SVivien Didelot 3948fad09c73SVivien Didelot chip->info = compat_info; 3949fad09c73SVivien Didelot 3950fad09c73SVivien Didelot err = mv88e6xxx_smi_init(chip, mdiodev->bus, mdiodev->addr); 3951fad09c73SVivien Didelot if (err) 3952fad09c73SVivien Didelot return err; 3953fad09c73SVivien Didelot 3954fad09c73SVivien Didelot err = mv88e6xxx_detect(chip); 3955fad09c73SVivien Didelot if (err) 3956fad09c73SVivien Didelot return err; 3957fad09c73SVivien Didelot 3958e57e5e77SVivien Didelot mv88e6xxx_phy_init(chip); 3959e57e5e77SVivien Didelot 3960fad09c73SVivien Didelot chip->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS); 3961fad09c73SVivien Didelot if (IS_ERR(chip->reset)) 3962fad09c73SVivien Didelot return PTR_ERR(chip->reset); 3963fad09c73SVivien Didelot 3964ee4dc2e7SVivien Didelot if (chip->info->ops->get_eeprom && 3965fad09c73SVivien Didelot !of_property_read_u32(np, "eeprom-length", &eeprom_len)) 3966fad09c73SVivien Didelot chip->eeprom_len = eeprom_len; 3967fad09c73SVivien Didelot 3968dc30c35bSAndrew Lunn mutex_lock(&chip->reg_lock); 3969dc30c35bSAndrew Lunn err = mv88e6xxx_switch_reset(chip); 3970dc30c35bSAndrew Lunn mutex_unlock(&chip->reg_lock); 3971fad09c73SVivien Didelot if (err) 3972dc30c35bSAndrew Lunn goto out; 3973fad09c73SVivien Didelot 3974dc30c35bSAndrew Lunn chip->irq = of_irq_get(np, 0); 3975dc30c35bSAndrew Lunn if (chip->irq == -EPROBE_DEFER) { 3976dc30c35bSAndrew Lunn err = chip->irq; 3977dc30c35bSAndrew Lunn goto out; 3978fad09c73SVivien Didelot } 3979fad09c73SVivien Didelot 3980dc30c35bSAndrew Lunn if (chip->irq > 0) { 3981dc30c35bSAndrew Lunn /* Has to be performed before the MDIO bus is created, 3982dc30c35bSAndrew Lunn * because the PHYs will link there interrupts to these 3983dc30c35bSAndrew Lunn * interrupt controllers 3984dc30c35bSAndrew Lunn */ 3985dc30c35bSAndrew Lunn mutex_lock(&chip->reg_lock); 3986dc30c35bSAndrew Lunn err = mv88e6xxx_g1_irq_setup(chip); 3987dc30c35bSAndrew Lunn mutex_unlock(&chip->reg_lock); 3988dc30c35bSAndrew Lunn 3989dc30c35bSAndrew Lunn if (err) 3990dc30c35bSAndrew Lunn goto out; 3991dc30c35bSAndrew Lunn 3992dc30c35bSAndrew Lunn if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT)) { 3993dc30c35bSAndrew Lunn err = mv88e6xxx_g2_irq_setup(chip); 3994dc30c35bSAndrew Lunn if (err) 3995dc30c35bSAndrew Lunn goto out_g1_irq; 3996dc30c35bSAndrew Lunn } 3997dc30c35bSAndrew Lunn } 3998dc30c35bSAndrew Lunn 3999dc30c35bSAndrew Lunn err = mv88e6xxx_mdio_register(chip, np); 4000dc30c35bSAndrew Lunn if (err) 4001dc30c35bSAndrew Lunn goto out_g2_irq; 4002dc30c35bSAndrew Lunn 4003dc30c35bSAndrew Lunn err = mv88e6xxx_register_switch(chip, np); 4004dc30c35bSAndrew Lunn if (err) 4005dc30c35bSAndrew Lunn goto out_mdio; 4006dc30c35bSAndrew Lunn 4007fad09c73SVivien Didelot return 0; 4008dc30c35bSAndrew Lunn 4009dc30c35bSAndrew Lunn out_mdio: 4010dc30c35bSAndrew Lunn mv88e6xxx_mdio_unregister(chip); 4011dc30c35bSAndrew Lunn out_g2_irq: 4012dc30c35bSAndrew Lunn if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT)) 4013dc30c35bSAndrew Lunn mv88e6xxx_g2_irq_free(chip); 4014dc30c35bSAndrew Lunn out_g1_irq: 4015dc30c35bSAndrew Lunn mv88e6xxx_g1_irq_free(chip); 4016dc30c35bSAndrew Lunn out: 4017dc30c35bSAndrew Lunn return err; 4018fad09c73SVivien Didelot } 4019fad09c73SVivien Didelot 4020fad09c73SVivien Didelot static void mv88e6xxx_remove(struct mdio_device *mdiodev) 4021fad09c73SVivien Didelot { 4022fad09c73SVivien Didelot struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev); 402304bed143SVivien Didelot struct mv88e6xxx_chip *chip = ds->priv; 4024fad09c73SVivien Didelot 4025930188ceSAndrew Lunn mv88e6xxx_phy_destroy(chip); 4026fad09c73SVivien Didelot mv88e6xxx_unregister_switch(chip); 4027fad09c73SVivien Didelot mv88e6xxx_mdio_unregister(chip); 4028dc30c35bSAndrew Lunn 4029dc30c35bSAndrew Lunn if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT)) 4030dc30c35bSAndrew Lunn mv88e6xxx_g2_irq_free(chip); 4031dc30c35bSAndrew Lunn mv88e6xxx_g1_irq_free(chip); 4032fad09c73SVivien Didelot } 4033fad09c73SVivien Didelot 4034fad09c73SVivien Didelot static const struct of_device_id mv88e6xxx_of_match[] = { 4035fad09c73SVivien Didelot { 4036fad09c73SVivien Didelot .compatible = "marvell,mv88e6085", 4037fad09c73SVivien Didelot .data = &mv88e6xxx_table[MV88E6085], 4038fad09c73SVivien Didelot }, 4039fad09c73SVivien Didelot { /* sentinel */ }, 4040fad09c73SVivien Didelot }; 4041fad09c73SVivien Didelot 4042fad09c73SVivien Didelot MODULE_DEVICE_TABLE(of, mv88e6xxx_of_match); 4043fad09c73SVivien Didelot 4044fad09c73SVivien Didelot static struct mdio_driver mv88e6xxx_driver = { 4045fad09c73SVivien Didelot .probe = mv88e6xxx_probe, 4046fad09c73SVivien Didelot .remove = mv88e6xxx_remove, 4047fad09c73SVivien Didelot .mdiodrv.driver = { 4048fad09c73SVivien Didelot .name = "mv88e6085", 4049fad09c73SVivien Didelot .of_match_table = mv88e6xxx_of_match, 4050fad09c73SVivien Didelot }, 4051fad09c73SVivien Didelot }; 4052fad09c73SVivien Didelot 4053fad09c73SVivien Didelot static int __init mv88e6xxx_init(void) 4054fad09c73SVivien Didelot { 40559d490b4eSVivien Didelot register_switch_driver(&mv88e6xxx_switch_ops); 4056fad09c73SVivien Didelot return mdio_driver_register(&mv88e6xxx_driver); 4057fad09c73SVivien Didelot } 4058fad09c73SVivien Didelot module_init(mv88e6xxx_init); 4059fad09c73SVivien Didelot 4060fad09c73SVivien Didelot static void __exit mv88e6xxx_cleanup(void) 4061fad09c73SVivien Didelot { 4062fad09c73SVivien Didelot mdio_driver_unregister(&mv88e6xxx_driver); 40639d490b4eSVivien Didelot unregister_switch_driver(&mv88e6xxx_switch_ops); 4064fad09c73SVivien Didelot } 4065fad09c73SVivien Didelot module_exit(mv88e6xxx_cleanup); 4066fad09c73SVivien Didelot 4067fad09c73SVivien Didelot MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>"); 4068fad09c73SVivien Didelot MODULE_DESCRIPTION("Driver for Marvell 88E6XXX ethernet switch chips"); 4069fad09c73SVivien Didelot MODULE_LICENSE("GPL"); 4070