diff options
Diffstat (limited to 'drivers/net/dsa')
44 files changed, 2099 insertions, 1802 deletions
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig index 3ed5391bb18d..f8c1d73b251d 100644 --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig @@ -37,7 +37,6 @@ config NET_DSA_LANTIQ_GSWIP config NET_DSA_MT7530 tristate "MediaTek MT7530 and MT7531 Ethernet switch support" select NET_DSA_TAG_MTK - select MEDIATEK_GE_PHY imply NET_DSA_MT7530_MDIO imply NET_DSA_MT7530_MMIO help @@ -49,6 +48,7 @@ config NET_DSA_MT7530 config NET_DSA_MT7530_MDIO tristate "MediaTek MT7530 MDIO interface driver" depends on NET_DSA_MT7530 + imply MEDIATEK_GE_PHY select PCS_MTK_LYNXI help This enables support for the MediaTek MT7530 and MT7531 switch @@ -60,6 +60,7 @@ config NET_DSA_MT7530_MMIO tristate "MediaTek MT7530 MMIO interface driver" depends on NET_DSA_MT7530 depends on HAS_IOMEM + imply MEDIATEK_GE_SOC_PHY help This enables support for the built-in Ethernet switch found in the MediaTek MT7988 SoC. diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 3464ce5e7470..4e27dc913cf7 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -1393,12 +1393,6 @@ static void b53_phylink_get_caps(struct dsa_switch *ds, int port, /* Get the implementation specific capabilities */ if (dev->ops->phylink_get_caps) dev->ops->phylink_get_caps(dev, port, config); - - /* This driver does not make use of the speed, duplex, pause or the - * advertisement in its mac_config, so it is safe to mark this driver - * as non-legacy. - */ - config->legacy_pre_march2020 = false; } static struct phylink_pcs *b53_phylink_mac_select_pcs(struct dsa_switch *ds, diff --git a/drivers/net/dsa/b53/b53_mdio.c b/drivers/net/dsa/b53/b53_mdio.c index 8b422b298cd5..4d55d8d18376 100644 --- a/drivers/net/dsa/b53/b53_mdio.c +++ b/drivers/net/dsa/b53/b53_mdio.c @@ -19,6 +19,7 @@ #include <linux/kernel.h> #include <linux/phy.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/delay.h> #include <linux/brcmphy.h> #include <linux/rtnetlink.h> diff --git a/drivers/net/dsa/b53/b53_mmap.c b/drivers/net/dsa/b53/b53_mmap.c index 5db1ed26f03a..5e39641ea887 100644 --- a/drivers/net/dsa/b53/b53_mmap.c +++ b/drivers/net/dsa/b53/b53_mmap.c @@ -19,6 +19,7 @@ #include <linux/bits.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/io.h> #include <linux/platform_device.h> #include <linux/platform_data/b53.h> diff --git a/drivers/net/dsa/b53/b53_serdes.c b/drivers/net/dsa/b53/b53_serdes.c index b0ccebcd3ffa..3f8a491ce885 100644 --- a/drivers/net/dsa/b53/b53_serdes.c +++ b/drivers/net/dsa/b53/b53_serdes.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Northstar Plus switch SerDes/SGMII PHY main logic * diff --git a/drivers/net/dsa/b53/b53_serdes.h b/drivers/net/dsa/b53/b53_serdes.h index ef81f5da5f81..3d367c4df4d9 100644 --- a/drivers/net/dsa/b53/b53_serdes.h +++ b/drivers/net/dsa/b53/b53_serdes.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause */ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* * Northstar Plus switch SerDes/SGMII PHY definitions * diff --git a/drivers/net/dsa/hirschmann/hellcreek.c b/drivers/net/dsa/hirschmann/hellcreek.c index af50001ccdd4..11ef1d7ea229 100644 --- a/drivers/net/dsa/hirschmann/hellcreek.c +++ b/drivers/net/dsa/hirschmann/hellcreek.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: (GPL-2.0 or MIT) +// SPDX-License-Identifier: (GPL-2.0 OR MIT) /* * DSA driver for: * Hirschmann Hellcreek TSN switch. @@ -11,7 +11,6 @@ #include <linux/module.h> #include <linux/device.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/of_mdio.h> #include <linux/platform_device.h> #include <linux/bitops.h> diff --git a/drivers/net/dsa/hirschmann/hellcreek.h b/drivers/net/dsa/hirschmann/hellcreek.h index 4a678f7d61ae..6874cb9dc361 100644 --- a/drivers/net/dsa/hirschmann/hellcreek.h +++ b/drivers/net/dsa/hirschmann/hellcreek.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: (GPL-2.0 or MIT) */ +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ /* * DSA driver for: * Hirschmann Hellcreek TSN switch. diff --git a/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c b/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c index ffd06cf8c44f..bd7aacc71a63 100644 --- a/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c +++ b/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c @@ -298,17 +298,10 @@ static void hellcreek_get_rxts(struct hellcreek *hellcreek, struct sk_buff_head received; unsigned long flags; - /* The latched timestamp belongs to one of the received frames. */ + /* Construct Rx timestamps for all received PTP packets. */ __skb_queue_head_init(&received); - - /* Lock & disable interrupts */ spin_lock_irqsave(&rxq->lock, flags); - - /* Add the reception queue "rxq" to the "received" queue an reintialize - * "rxq". From now on, we deal with "received" not with "rxq" - */ skb_queue_splice_tail_init(rxq, &received); - spin_unlock_irqrestore(&rxq->lock, flags); for (; skb; skb = __skb_dequeue(&received)) { diff --git a/drivers/net/dsa/hirschmann/hellcreek_ptp.c b/drivers/net/dsa/hirschmann/hellcreek_ptp.c index 3e44ccb7db84..5249a1c2a80b 100644 --- a/drivers/net/dsa/hirschmann/hellcreek_ptp.c +++ b/drivers/net/dsa/hirschmann/hellcreek_ptp.c @@ -9,6 +9,7 @@ * Kurt Kanzenbach <kurt@linutronix.de> */ +#include <linux/of.h> #include <linux/ptp_clock_kernel.h> #include "hellcreek.h" #include "hellcreek_ptp.h" diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c index ff76444057d2..ee67adeb2cdb 100644 --- a/drivers/net/dsa/lan9303-core.c +++ b/drivers/net/dsa/lan9303-core.c @@ -8,6 +8,7 @@ #include <linux/regmap.h> #include <linux/mutex.h> #include <linux/mii.h> +#include <linux/of.h> #include <linux/phy.h> #include <linux/if_bridge.h> #include <linux/if_vlan.h> @@ -1290,12 +1291,6 @@ static void lan9303_phylink_get_caps(struct dsa_switch *ds, int port, __set_bit(PHY_INTERFACE_MODE_GMII, config->supported_interfaces); } - - /* This driver does not make use of the speed, duplex, pause or the - * advertisement in its mac_config, so it is safe to mark this driver - * as non-legacy. - */ - config->legacy_pre_march2020 = false; } static void lan9303_phylink_mac_link_up(struct dsa_switch *ds, int port, diff --git a/drivers/net/dsa/microchip/ksz8.h b/drivers/net/dsa/microchip/ksz8.h index e68465fdf6b9..4cea811e73ac 100644 --- a/drivers/net/dsa/microchip/ksz8.h +++ b/drivers/net/dsa/microchip/ksz8.h @@ -48,13 +48,11 @@ int ksz8_port_mirror_add(struct ksz_device *dev, int port, bool ingress, struct netlink_ext_ack *extack); void ksz8_port_mirror_del(struct ksz_device *dev, int port, struct dsa_mall_mirror_tc_entry *mirror); -int ksz8_get_stp_reg(void); void ksz8_get_caps(struct ksz_device *dev, int port, struct phylink_config *config); void ksz8_config_cpu_port(struct dsa_switch *ds); int ksz8_enable_stp_addr(struct ksz_device *dev); int ksz8_reset_switch(struct ksz_device *dev); -int ksz8_switch_detect(struct ksz_device *dev); int ksz8_switch_init(struct ksz_device *dev); void ksz8_switch_exit(struct ksz_device *dev); int ksz8_change_mtu(struct ksz_device *dev, int port, int mtu); diff --git a/drivers/net/dsa/microchip/ksz8863_smi.c b/drivers/net/dsa/microchip/ksz8863_smi.c index fd6e2e69a42a..5711a59e2ac9 100644 --- a/drivers/net/dsa/microchip/ksz8863_smi.c +++ b/drivers/net/dsa/microchip/ksz8863_smi.c @@ -5,6 +5,9 @@ * Copyright (C) 2019 Pengutronix, Michael Grzeschik <kernel@pengutronix.de> */ +#include <linux/mod_devicetable.h> +#include <linux/property.h> + #include "ksz8.h" #include "ksz_common.h" diff --git a/drivers/net/dsa/microchip/ksz9477.h b/drivers/net/dsa/microchip/ksz9477.h index b6f7e3c46e3f..a6f425866a29 100644 --- a/drivers/net/dsa/microchip/ksz9477.h +++ b/drivers/net/dsa/microchip/ksz9477.h @@ -36,7 +36,6 @@ int ksz9477_port_mirror_add(struct ksz_device *dev, int port, bool ingress, struct netlink_ext_ack *extack); void ksz9477_port_mirror_del(struct ksz_device *dev, int port, struct dsa_mall_mirror_tc_entry *mirror); -int ksz9477_get_stp_reg(void); void ksz9477_get_caps(struct ksz_device *dev, int port, struct phylink_config *config); int ksz9477_fdb_dump(struct ksz_device *dev, int port, @@ -54,7 +53,6 @@ void ksz9477_config_cpu_port(struct dsa_switch *ds); int ksz9477_tc_cbs_set_cinc(struct ksz_device *dev, int port, u32 val); int ksz9477_enable_stp_addr(struct ksz_device *dev); int ksz9477_reset_switch(struct ksz_device *dev); -int ksz9477_dsa_init(struct ksz_device *dev); int ksz9477_switch_init(struct ksz_device *dev); void ksz9477_switch_exit(struct ksz_device *dev); void ksz9477_port_queue_split(struct ksz_device *dev, int port); diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 6c0623f88654..6673122266b7 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -18,8 +18,8 @@ #include <linux/if_vlan.h> #include <linux/irq.h> #include <linux/irqdomain.h> +#include <linux/of.h> #include <linux/of_mdio.h> -#include <linux/of_device.h> #include <linux/of_net.h> #include <linux/micrel_phy.h> #include <net/dsa.h> @@ -1619,8 +1619,6 @@ static void ksz_phylink_get_caps(struct dsa_switch *ds, int port, { struct ksz_device *dev = ds->priv; - config->legacy_pre_march2020 = false; - if (dev->info->supports_mii[port]) __set_bit(PHY_INTERFACE_MODE_MII, config->supported_interfaces); diff --git a/drivers/net/dsa/mt7530-mmio.c b/drivers/net/dsa/mt7530-mmio.c index 1a3d4b692f34..0a6a2fe34e64 100644 --- a/drivers/net/dsa/mt7530-mmio.c +++ b/drivers/net/dsa/mt7530-mmio.c @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-only +#include <linux/mod_devicetable.h> #include <linux/module.h> -#include <linux/of_platform.h> +#include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/reset.h> diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index b8bb9f3b3609..035a34b50f31 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -2953,12 +2953,6 @@ static void mt753x_phylink_get_caps(struct dsa_switch *ds, int port, config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000FD; - /* This driver does not make use of the speed, duplex, pause or the - * advertisement in its mac_config, so it is safe to mark this driver - * as non-legacy. - */ - config->legacy_pre_march2020 = false; - priv->info->mac_port_get_caps(ds, port, config); } diff --git a/drivers/net/dsa/mv88e6060.c b/drivers/net/dsa/mv88e6060.c index fdda62d6eb16..294312b58e4f 100644 --- a/drivers/net/dsa/mv88e6060.c +++ b/drivers/net/dsa/mv88e6060.c @@ -247,11 +247,56 @@ mv88e6060_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val) return reg_write(priv, addr, regnum, val); } +static void mv88e6060_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) +{ + unsigned long *interfaces = config->supported_interfaces; + struct mv88e6060_priv *priv = ds->priv; + int addr = REG_PORT(port); + int ret; + + ret = reg_read(priv, addr, PORT_STATUS); + if (ret < 0) { + dev_err(ds->dev, + "port %d: unable to read status register: %pe\n", + port, ERR_PTR(ret)); + return; + } + + /* If the port is configured in SNI mode (acts as a 10Mbps PHY), + * it should have phy-mode = "sni", but that doesn't yet exist, so + * forcibly fail validation until the need arises to introduce it. + */ + if (!(ret & PORT_STATUS_PORTMODE)) { + dev_warn(ds->dev, "port %d: SNI mode not supported\n", port); + return; + } + + config->mac_capabilities = MAC_100 | MAC_10 | MAC_SYM_PAUSE; + + if (port >= 4) { + /* Ports 4 and 5 can support MII, REVMII and REVRMII modes */ + __set_bit(PHY_INTERFACE_MODE_MII, interfaces); + __set_bit(PHY_INTERFACE_MODE_REVMII, interfaces); + __set_bit(PHY_INTERFACE_MODE_REVRMII, interfaces); + } + if (port <= 4) { + /* Ports 0 to 3 have internal PHYs, and port 4 can optionally + * use an internal PHY. + */ + /* Internal PHY */ + __set_bit(PHY_INTERFACE_MODE_INTERNAL, interfaces); + /* Default phylib interface mode */ + __set_bit(PHY_INTERFACE_MODE_GMII, interfaces); + } +} + static const struct dsa_switch_ops mv88e6060_switch_ops = { .get_tag_protocol = mv88e6060_get_tag_protocol, .setup = mv88e6060_setup, .phy_read = mv88e6060_phy_read, .phy_write = mv88e6060_phy_write, + .phylink_get_caps = mv88e6060_phylink_get_caps, }; static int mv88e6060_probe(struct mdio_device *mdiodev) diff --git a/drivers/net/dsa/mv88e6xxx/Makefile b/drivers/net/dsa/mv88e6xxx/Makefile index 1409e691ab77..a9a9651187db 100644 --- a/drivers/net/dsa/mv88e6xxx/Makefile +++ b/drivers/net/dsa/mv88e6xxx/Makefile @@ -9,6 +9,9 @@ mv88e6xxx-objs += global2.o mv88e6xxx-objs += global2_avb.o mv88e6xxx-objs += global2_scratch.o mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_PTP) += hwtstamp.o +mv88e6xxx-objs += pcs-6185.o +mv88e6xxx-objs += pcs-6352.o +mv88e6xxx-objs += pcs-639x.o mv88e6xxx-objs += phy.o mv88e6xxx-objs += port.o mv88e6xxx-objs += port_hidden.o diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 7af2f08a62f1..52a99d8bada0 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -23,7 +23,7 @@ #include <linux/list.h> #include <linux/mdio.h> #include <linux/module.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/of_irq.h> #include <linux/of_mdio.h> #include <linux/platform_data/mv88e6xxx.h> @@ -499,81 +499,6 @@ static int mv88e6xxx_port_ppu_updates(struct mv88e6xxx_chip *chip, int port) return !!(reg & MV88E6XXX_PORT_STS_PHY_DETECT); } -static int mv88e6xxx_serdes_pcs_get_state(struct dsa_switch *ds, int port, - struct phylink_link_state *state) -{ - struct mv88e6xxx_chip *chip = ds->priv; - int lane; - int err; - - mv88e6xxx_reg_lock(chip); - lane = mv88e6xxx_serdes_get_lane(chip, port); - if (lane >= 0 && chip->info->ops->serdes_pcs_get_state) - err = chip->info->ops->serdes_pcs_get_state(chip, port, lane, - state); - else - err = -EOPNOTSUPP; - mv88e6xxx_reg_unlock(chip); - - return err; -} - -static int mv88e6xxx_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port, - unsigned int mode, - phy_interface_t interface, - const unsigned long *advertise) -{ - const struct mv88e6xxx_ops *ops = chip->info->ops; - int lane; - - if (ops->serdes_pcs_config) { - lane = mv88e6xxx_serdes_get_lane(chip, port); - if (lane >= 0) - return ops->serdes_pcs_config(chip, port, lane, mode, - interface, advertise); - } - - return 0; -} - -static void mv88e6xxx_serdes_pcs_an_restart(struct dsa_switch *ds, int port) -{ - struct mv88e6xxx_chip *chip = ds->priv; - const struct mv88e6xxx_ops *ops; - int err = 0; - int lane; - - ops = chip->info->ops; - - if (ops->serdes_pcs_an_restart) { - mv88e6xxx_reg_lock(chip); - lane = mv88e6xxx_serdes_get_lane(chip, port); - if (lane >= 0) - err = ops->serdes_pcs_an_restart(chip, port, lane); - mv88e6xxx_reg_unlock(chip); - - if (err) - dev_err(ds->dev, "p%d: failed to restart AN\n", port); - } -} - -static int mv88e6xxx_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port, - unsigned int mode, - int speed, int duplex) -{ - const struct mv88e6xxx_ops *ops = chip->info->ops; - int lane; - - if (!phylink_autoneg_inband(mode) && ops->serdes_pcs_link_up) { - lane = mv88e6xxx_serdes_get_lane(chip, port); - if (lane >= 0) - return ops->serdes_pcs_link_up(chip, port, lane, - speed, duplex); - } - - return 0; -} - static const u8 mv88e6185_phy_interface_modes[] = { [MV88E6185_PORT_STS_CMODE_GMII_FD] = PHY_INTERFACE_MODE_GMII, [MV88E6185_PORT_STS_CMODE_MII_100_FD_PS] = PHY_INTERFACE_MODE_MII, @@ -853,6 +778,20 @@ static void mv88e6xxx_get_caps(struct dsa_switch *ds, int port, } } +static struct phylink_pcs *mv88e6xxx_mac_select_pcs(struct dsa_switch *ds, + int port, + phy_interface_t interface) +{ + struct mv88e6xxx_chip *chip = ds->priv; + struct phylink_pcs *pcs = ERR_PTR(-EOPNOTSUPP); + + if (chip->info->ops->pcs_ops) + pcs = chip->info->ops->pcs_ops->pcs_select(chip, port, + interface); + + return pcs; +} + static int mv88e6xxx_mac_prepare(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface) { @@ -889,16 +828,6 @@ static void mv88e6xxx_mac_config(struct dsa_switch *ds, int port, state->interface); if (err && err != -EOPNOTSUPP) goto err_unlock; - - err = mv88e6xxx_serdes_pcs_config(chip, port, mode, - state->interface, - state->advertising); - /* FIXME: we should restart negotiation if something changed - - * which is something we get if we convert to using phylinks - * PCS operations. - */ - if (err > 0) - err = 0; } err_unlock: @@ -982,17 +911,6 @@ static void mv88e6xxx_mac_link_up(struct dsa_switch *ds, int port, */ if (!mv88e6xxx_port_ppu_updates(chip, port) || mode == MLO_AN_FIXED) { - /* FIXME: for an automedia port, should we force the link - * down here - what if the link comes up due to "other" media - * while we're bringing the port up, how is the exclusivity - * handled in the Marvell hardware? E.g. port 2 on 88E6390 - * shared between internal PHY and Serdes. - */ - err = mv88e6xxx_serdes_pcs_link_up(chip, port, mode, speed, - duplex); - if (err) - goto error; - if (ops->port_set_speed_duplex) { err = ops->port_set_speed_duplex(chip, port, speed, duplex); @@ -3171,102 +3089,6 @@ static int mv88e6xxx_setup_egress_floods(struct mv88e6xxx_chip *chip, int port) return 0; } -static irqreturn_t mv88e6xxx_serdes_irq_thread_fn(int irq, void *dev_id) -{ - struct mv88e6xxx_port *mvp = dev_id; - struct mv88e6xxx_chip *chip = mvp->chip; - irqreturn_t ret = IRQ_NONE; - int port = mvp->port; - int lane; - - mv88e6xxx_reg_lock(chip); - lane = mv88e6xxx_serdes_get_lane(chip, port); - if (lane >= 0) - ret = mv88e6xxx_serdes_irq_status(chip, port, lane); - mv88e6xxx_reg_unlock(chip); - - return ret; -} - -static int mv88e6xxx_serdes_irq_request(struct mv88e6xxx_chip *chip, int port, - int lane) -{ - struct mv88e6xxx_port *dev_id = &chip->ports[port]; - unsigned int irq; - int err; - - /* Nothing to request if this SERDES port has no IRQ */ - irq = mv88e6xxx_serdes_irq_mapping(chip, port); - if (!irq) - return 0; - - snprintf(dev_id->serdes_irq_name, sizeof(dev_id->serdes_irq_name), - "mv88e6xxx-%s-serdes-%d", dev_name(chip->dev), port); - - /* Requesting the IRQ will trigger IRQ callbacks, so release the lock */ - mv88e6xxx_reg_unlock(chip); - err = request_threaded_irq(irq, NULL, mv88e6xxx_serdes_irq_thread_fn, - IRQF_ONESHOT, dev_id->serdes_irq_name, - dev_id); - mv88e6xxx_reg_lock(chip); - if (err) - return err; - - dev_id->serdes_irq = irq; - - return mv88e6xxx_serdes_irq_enable(chip, port, lane); -} - -static int mv88e6xxx_serdes_irq_free(struct mv88e6xxx_chip *chip, int port, - int lane) -{ - struct mv88e6xxx_port *dev_id = &chip->ports[port]; - unsigned int irq = dev_id->serdes_irq; - int err; - - /* Nothing to free if no IRQ has been requested */ - if (!irq) - return 0; - - err = mv88e6xxx_serdes_irq_disable(chip, port, lane); - - /* Freeing the IRQ will trigger IRQ callbacks, so release the lock */ - mv88e6xxx_reg_unlock(chip); - free_irq(irq, dev_id); - mv88e6xxx_reg_lock(chip); - - dev_id->serdes_irq = 0; - - return err; -} - -static int mv88e6xxx_serdes_power(struct mv88e6xxx_chip *chip, int port, - bool on) -{ - int lane; - int err; - - lane = mv88e6xxx_serdes_get_lane(chip, port); - if (lane < 0) - return 0; - - if (on) { - err = mv88e6xxx_serdes_power_up(chip, port, lane); - if (err) - return err; - - err = mv88e6xxx_serdes_irq_request(chip, port, lane); - } else { - err = mv88e6xxx_serdes_irq_free(chip, port, lane); - if (err) - return err; - - err = mv88e6xxx_serdes_power_down(chip, port, lane); - } - - return err; -} - static int mv88e6xxx_set_egress_port(struct mv88e6xxx_chip *chip, enum mv88e6xxx_egress_direction direction, int port) @@ -3330,56 +3152,17 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) { struct device_node *phy_handle = NULL; struct dsa_switch *ds = chip->ds; - phy_interface_t mode; struct dsa_port *dp; - int tx_amp, speed; + int tx_amp; int err; u16 reg; chip->ports[port].chip = chip; chip->ports[port].port = port; - dp = dsa_to_port(ds, port); - - /* MAC Forcing register: don't force link, speed, duplex or flow control - * state to any particular values on physical ports, but force the CPU - * port and all DSA ports to their maximum bandwidth and full duplex. - */ - if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) { - struct phylink_config pl_config = {}; - unsigned long caps; - - chip->info->ops->phylink_get_caps(chip, port, &pl_config); - - caps = pl_config.mac_capabilities; - - if (chip->info->ops->port_max_speed_mode) - mode = chip->info->ops->port_max_speed_mode(chip, port); - else - mode = PHY_INTERFACE_MODE_NA; - - if (caps & MAC_10000FD) - speed = SPEED_10000; - else if (caps & MAC_5000FD) - speed = SPEED_5000; - else if (caps & MAC_2500FD) - speed = SPEED_2500; - else if (caps & MAC_1000) - speed = SPEED_1000; - else if (caps & MAC_100) - speed = SPEED_100; - else - speed = SPEED_10; - - err = mv88e6xxx_port_setup_mac(chip, port, LINK_FORCED_UP, - speed, DUPLEX_FULL, - PAUSE_OFF, mode); - } else { - err = mv88e6xxx_port_setup_mac(chip, port, LINK_UNFORCED, - SPEED_UNFORCED, DUPLEX_UNFORCED, - PAUSE_ON, - PHY_INTERFACE_MODE_NA); - } + err = mv88e6xxx_port_setup_mac(chip, port, LINK_UNFORCED, + SPEED_UNFORCED, DUPLEX_UNFORCED, + PAUSE_ON, PHY_INTERFACE_MODE_NA); if (err) return err; @@ -3556,6 +3339,7 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) } if (chip->info->ops->serdes_set_tx_amplitude) { + dp = dsa_to_port(ds, port); if (dp) phy_handle = of_parse_phandle(dp->dn, "phy-handle", 0); @@ -3629,29 +3413,6 @@ static int mv88e6xxx_change_mtu(struct dsa_switch *ds, int port, int new_mtu) return ret; } -static int mv88e6xxx_port_enable(struct dsa_switch *ds, int port, - struct phy_device *phydev) -{ - struct mv88e6xxx_chip *chip = ds->priv; - int err; - - mv88e6xxx_reg_lock(chip); - err = mv88e6xxx_serdes_power(chip, port, true); - mv88e6xxx_reg_unlock(chip); - - return err; -} - -static void mv88e6xxx_port_disable(struct dsa_switch *ds, int port) -{ - struct mv88e6xxx_chip *chip = ds->priv; - - mv88e6xxx_reg_lock(chip); - if (mv88e6xxx_serdes_power(chip, port, false)) - dev_err(chip->dev, "failed to power off SERDES\n"); - mv88e6xxx_reg_unlock(chip); -} - static int mv88e6xxx_set_ageing_time(struct dsa_switch *ds, unsigned int ageing_time) { @@ -4114,12 +3875,26 @@ out_mdios: static int mv88e6xxx_port_setup(struct dsa_switch *ds, int port) { + struct mv88e6xxx_chip *chip = ds->priv; + int err; + + if (chip->info->ops->pcs_ops->pcs_init) { + err = chip->info->ops->pcs_ops->pcs_init(chip, port); + if (err) + return err; + } + return mv88e6xxx_setup_devlink_regions_port(ds, port); } static void mv88e6xxx_port_teardown(struct dsa_switch *ds, int port) { + struct mv88e6xxx_chip *chip = ds->priv; + mv88e6xxx_teardown_devlink_regions_port(ds, port); + + if (chip->info->ops->pcs_ops->pcs_teardown) + chip->info->ops->pcs_ops->pcs_teardown(chip, port); } static int mv88e6xxx_get_eeprom_len(struct dsa_switch *ds) @@ -4236,15 +4011,13 @@ static const struct mv88e6xxx_ops mv88e6095_ops = { .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, .mgmt_rsvd2cpu = mv88e6185_g2_mgmt_rsvd2cpu, - .serdes_power = mv88e6185_serdes_power, - .serdes_get_lane = mv88e6185_serdes_get_lane, - .serdes_pcs_get_state = mv88e6185_serdes_pcs_get_state, .ppu_enable = mv88e6185_g1_ppu_enable, .ppu_disable = mv88e6185_g1_ppu_disable, .reset = mv88e6185_g1_reset, .vtu_getnext = mv88e6185_g1_vtu_getnext, .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, .phylink_get_caps = mv88e6095_phylink_get_caps, + .pcs_ops = &mv88e6185_pcs_ops, .set_max_frame_size = mv88e6185_g1_set_max_frame_size, }; @@ -4282,18 +4055,14 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6097_watchdog_ops, .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, - .serdes_power = mv88e6185_serdes_power, - .serdes_get_lane = mv88e6185_serdes_get_lane, - .serdes_pcs_get_state = mv88e6185_serdes_pcs_get_state, .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, - .serdes_irq_enable = mv88e6097_serdes_irq_enable, - .serdes_irq_status = mv88e6097_serdes_irq_status, .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .rmu_disable = mv88e6085_g1_rmu_disable, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .phylink_get_caps = mv88e6095_phylink_get_caps, + .pcs_ops = &mv88e6185_pcs_ops, .stu_getnext = mv88e6352_g1_stu_getnext, .stu_loadpurge = mv88e6352_g1_stu_loadpurge, .set_max_frame_size = mv88e6185_g1_set_max_frame_size, @@ -4429,16 +4198,8 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .stu_getnext = mv88e6352_g1_stu_getnext, .stu_loadpurge = mv88e6352_g1_stu_loadpurge, - .serdes_power = mv88e6390_serdes_power, .serdes_get_lane = mv88e6341_serdes_get_lane, - /* Check status register pause & lpa register */ - .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, - .serdes_pcs_config = mv88e6390_serdes_pcs_config, - .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart, - .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up, .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, - .serdes_irq_enable = mv88e6390_serdes_irq_enable, - .serdes_irq_status = mv88e6390_serdes_irq_status, .gpio_ops = &mv88e6352_gpio_ops, .serdes_get_sset_count = mv88e6390_serdes_get_sset_count, .serdes_get_strings = mv88e6390_serdes_get_strings, @@ -4446,6 +4207,7 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, .serdes_get_regs = mv88e6390_serdes_get_regs, .phylink_get_caps = mv88e6341_phylink_get_caps, + .pcs_ops = &mv88e6390_pcs_ops, }; static const struct mv88e6xxx_ops mv88e6161_ops = { @@ -4626,16 +4388,11 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .stu_getnext = mv88e6352_g1_stu_getnext, .stu_loadpurge = mv88e6352_g1_stu_loadpurge, - .serdes_get_lane = mv88e6352_serdes_get_lane, - .serdes_pcs_get_state = mv88e6352_serdes_pcs_get_state, - .serdes_pcs_config = mv88e6352_serdes_pcs_config, - .serdes_pcs_an_restart = mv88e6352_serdes_pcs_an_restart, - .serdes_pcs_link_up = mv88e6352_serdes_pcs_link_up, - .serdes_power = mv88e6352_serdes_power, .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, .serdes_get_regs = mv88e6352_serdes_get_regs, .gpio_ops = &mv88e6352_gpio_ops, .phylink_get_caps = mv88e6352_phylink_get_caps, + .pcs_ops = &mv88e6352_pcs_ops, }; static const struct mv88e6xxx_ops mv88e6175_ops = { @@ -4731,20 +4488,13 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .stu_getnext = mv88e6352_g1_stu_getnext, .stu_loadpurge = mv88e6352_g1_stu_loadpurge, - .serdes_get_lane = mv88e6352_serdes_get_lane, - .serdes_pcs_get_state = mv88e6352_serdes_pcs_get_state, - .serdes_pcs_config = mv88e6352_serdes_pcs_config, - .serdes_pcs_an_restart = mv88e6352_serdes_pcs_an_restart, - .serdes_pcs_link_up = mv88e6352_serdes_pcs_link_up, - .serdes_power = mv88e6352_serdes_power, .serdes_irq_mapping = mv88e6352_serdes_irq_mapping, - .serdes_irq_enable = mv88e6352_serdes_irq_enable, - .serdes_irq_status = mv88e6352_serdes_irq_status, .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, .serdes_get_regs = mv88e6352_serdes_get_regs, .serdes_set_tx_amplitude = mv88e6352_serdes_set_tx_amplitude, .gpio_ops = &mv88e6352_gpio_ops, .phylink_get_caps = mv88e6352_phylink_get_caps, + .pcs_ops = &mv88e6352_pcs_ops, }; static const struct mv88e6xxx_ops mv88e6185_ops = { @@ -4774,9 +4524,6 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6097_watchdog_ops, .mgmt_rsvd2cpu = mv88e6185_g2_mgmt_rsvd2cpu, - .serdes_power = mv88e6185_serdes_power, - .serdes_get_lane = mv88e6185_serdes_get_lane, - .serdes_pcs_get_state = mv88e6185_serdes_pcs_get_state, .set_cascade_port = mv88e6185_g1_set_cascade_port, .ppu_enable = mv88e6185_g1_ppu_enable, .ppu_disable = mv88e6185_g1_ppu_disable, @@ -4784,6 +4531,7 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { .vtu_getnext = mv88e6185_g1_vtu_getnext, .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, .phylink_get_caps = mv88e6185_phylink_get_caps, + .pcs_ops = &mv88e6185_pcs_ops, .set_max_frame_size = mv88e6185_g1_set_max_frame_size, }; @@ -4834,22 +4582,15 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .stu_getnext = mv88e6390_g1_stu_getnext, .stu_loadpurge = mv88e6390_g1_stu_loadpurge, - .serdes_power = mv88e6390_serdes_power, .serdes_get_lane = mv88e6390_serdes_get_lane, - /* Check status register pause & lpa register */ - .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, - .serdes_pcs_config = mv88e6390_serdes_pcs_config, - .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart, - .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up, .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, - .serdes_irq_enable = mv88e6390_serdes_irq_enable, - .serdes_irq_status = mv88e6390_serdes_irq_status, .serdes_get_strings = mv88e6390_serdes_get_strings, .serdes_get_stats = mv88e6390_serdes_get_stats, .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, .serdes_get_regs = mv88e6390_serdes_get_regs, .gpio_ops = &mv88e6352_gpio_ops, .phylink_get_caps = mv88e6390_phylink_get_caps, + .pcs_ops = &mv88e6390_pcs_ops, }; static const struct mv88e6xxx_ops mv88e6190x_ops = { @@ -4899,22 +4640,15 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .stu_getnext = mv88e6390_g1_stu_getnext, .stu_loadpurge = mv88e6390_g1_stu_loadpurge, - .serdes_power = mv88e6390_serdes_power, .serdes_get_lane = mv88e6390x_serdes_get_lane, - /* Check status register pause & lpa register */ - .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, - .serdes_pcs_config = mv88e6390_serdes_pcs_config, - .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart, - .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up, .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, - .serdes_irq_enable = mv88e6390_serdes_irq_enable, - .serdes_irq_status = mv88e6390_serdes_irq_status, .serdes_get_strings = mv88e6390_serdes_get_strings, .serdes_get_stats = mv88e6390_serdes_get_stats, .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, .serdes_get_regs = mv88e6390_serdes_get_regs, .gpio_ops = &mv88e6352_gpio_ops, .phylink_get_caps = mv88e6390x_phylink_get_caps, + .pcs_ops = &mv88e6390_pcs_ops, }; static const struct mv88e6xxx_ops mv88e6191_ops = { @@ -4962,16 +4696,8 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .stu_getnext = mv88e6390_g1_stu_getnext, .stu_loadpurge = mv88e6390_g1_stu_loadpurge, - .serdes_power = mv88e6390_serdes_power, .serdes_get_lane = mv88e6390_serdes_get_lane, - /* Check status register pause & lpa register */ - .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, - .serdes_pcs_config = mv88e6390_serdes_pcs_config, - .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart, - .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up, .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, - .serdes_irq_enable = mv88e6390_serdes_irq_enable, - .serdes_irq_status = mv88e6390_serdes_irq_status, .serdes_get_strings = mv88e6390_serdes_get_strings, .serdes_get_stats = mv88e6390_serdes_get_stats, .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, @@ -4979,6 +4705,7 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, .phylink_get_caps = mv88e6390_phylink_get_caps, + .pcs_ops = &mv88e6390_pcs_ops, }; static const struct mv88e6xxx_ops mv88e6240_ops = { @@ -5028,15 +4755,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .stu_getnext = mv88e6352_g1_stu_getnext, .stu_loadpurge = mv88e6352_g1_stu_loadpurge, - .serdes_get_lane = mv88e6352_serdes_get_lane, - .serdes_pcs_get_state = mv88e6352_serdes_pcs_get_state, - .serdes_pcs_config = mv88e6352_serdes_pcs_config, - .serdes_pcs_an_restart = mv88e6352_serdes_pcs_an_restart, - .serdes_pcs_link_up = mv88e6352_serdes_pcs_link_up, - .serdes_power = mv88e6352_serdes_power, .serdes_irq_mapping = mv88e6352_serdes_irq_mapping, - .serdes_irq_enable = mv88e6352_serdes_irq_enable, - .serdes_irq_status = mv88e6352_serdes_irq_status, .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, .serdes_get_regs = mv88e6352_serdes_get_regs, .serdes_set_tx_amplitude = mv88e6352_serdes_set_tx_amplitude, @@ -5044,6 +4763,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .avb_ops = &mv88e6352_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, .phylink_get_caps = mv88e6352_phylink_get_caps, + .pcs_ops = &mv88e6352_pcs_ops, }; static const struct mv88e6xxx_ops mv88e6250_ops = { @@ -5135,16 +4855,8 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .stu_getnext = mv88e6390_g1_stu_getnext, .stu_loadpurge = mv88e6390_g1_stu_loadpurge, - .serdes_power = mv88e6390_serdes_power, .serdes_get_lane = mv88e6390_serdes_get_lane, - /* Check status register pause & lpa register */ - .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, - .serdes_pcs_config = mv88e6390_serdes_pcs_config, - .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart, - .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up, .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, - .serdes_irq_enable = mv88e6390_serdes_irq_enable, - .serdes_irq_status = mv88e6390_serdes_irq_status, .serdes_get_strings = mv88e6390_serdes_get_strings, .serdes_get_stats = mv88e6390_serdes_get_stats, .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, @@ -5153,6 +4865,7 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6390_ptp_ops, .phylink_get_caps = mv88e6390_phylink_get_caps, + .pcs_ops = &mv88e6390_pcs_ops, }; static const struct mv88e6xxx_ops mv88e6320_ops = { @@ -5297,16 +5010,8 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .stu_getnext = mv88e6352_g1_stu_getnext, .stu_loadpurge = mv88e6352_g1_stu_loadpurge, - .serdes_power = mv88e6390_serdes_power, .serdes_get_lane = mv88e6341_serdes_get_lane, - /* Check status register pause & lpa register */ - .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, - .serdes_pcs_config = mv88e6390_serdes_pcs_config, - .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart, - .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up, .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, - .serdes_irq_enable = mv88e6390_serdes_irq_enable, - .serdes_irq_status = mv88e6390_serdes_irq_status, .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, @@ -5316,6 +5021,7 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, .serdes_get_regs = mv88e6390_serdes_get_regs, .phylink_get_caps = mv88e6341_phylink_get_caps, + .pcs_ops = &mv88e6390_pcs_ops, }; static const struct mv88e6xxx_ops mv88e6350_ops = { @@ -5459,15 +5165,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .stu_getnext = mv88e6352_g1_stu_getnext, .stu_loadpurge = mv88e6352_g1_stu_loadpurge, - .serdes_get_lane = mv88e6352_serdes_get_lane, - .serdes_pcs_get_state = mv88e6352_serdes_pcs_get_state, - .serdes_pcs_config = mv88e6352_serdes_pcs_config, - .serdes_pcs_an_restart = mv88e6352_serdes_pcs_an_restart, - .serdes_pcs_link_up = mv88e6352_serdes_pcs_link_up, - .serdes_power = mv88e6352_serdes_power, .serdes_irq_mapping = mv88e6352_serdes_irq_mapping, - .serdes_irq_enable = mv88e6352_serdes_irq_enable, - .serdes_irq_status = mv88e6352_serdes_irq_status, .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6352_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, @@ -5478,6 +5176,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .serdes_get_regs = mv88e6352_serdes_get_regs, .serdes_set_tx_amplitude = mv88e6352_serdes_set_tx_amplitude, .phylink_get_caps = mv88e6352_phylink_get_caps, + .pcs_ops = &mv88e6352_pcs_ops, }; static const struct mv88e6xxx_ops mv88e6390_ops = { @@ -5528,16 +5227,8 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .stu_getnext = mv88e6390_g1_stu_getnext, .stu_loadpurge = mv88e6390_g1_stu_loadpurge, - .serdes_power = mv88e6390_serdes_power, .serdes_get_lane = mv88e6390_serdes_get_lane, - /* Check status register pause & lpa register */ - .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, - .serdes_pcs_config = mv88e6390_serdes_pcs_config, - .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart, - .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up, .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, - .serdes_irq_enable = mv88e6390_serdes_irq_enable, - .serdes_irq_status = mv88e6390_serdes_irq_status, .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6390_ptp_ops, @@ -5547,6 +5238,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, .serdes_get_regs = mv88e6390_serdes_get_regs, .phylink_get_caps = mv88e6390_phylink_get_caps, + .pcs_ops = &mv88e6390_pcs_ops, }; static const struct mv88e6xxx_ops mv88e6390x_ops = { @@ -5597,15 +5289,8 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .stu_getnext = mv88e6390_g1_stu_getnext, .stu_loadpurge = mv88e6390_g1_stu_loadpurge, - .serdes_power = mv88e6390_serdes_power, .serdes_get_lane = mv88e6390x_serdes_get_lane, - .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, - .serdes_pcs_config = mv88e6390_serdes_pcs_config, - .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart, - .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up, .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, - .serdes_irq_enable = mv88e6390_serdes_irq_enable, - .serdes_irq_status = mv88e6390_serdes_irq_status, .serdes_get_sset_count = mv88e6390_serdes_get_sset_count, .serdes_get_strings = mv88e6390_serdes_get_strings, .serdes_get_stats = mv88e6390_serdes_get_stats, @@ -5615,11 +5300,11 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6390_ptp_ops, .phylink_get_caps = mv88e6390x_phylink_get_caps, + .pcs_ops = &mv88e6390_pcs_ops, }; static const struct mv88e6xxx_ops mv88e6393x_ops = { /* MV88E6XXX_FAMILY_6393 */ - .setup_errata = mv88e6393x_serdes_setup_errata, .irl_init_all = mv88e6390_g2_irl_init_all, .get_eeprom = mv88e6xxx_g2_get_eeprom8, .set_eeprom = mv88e6xxx_g2_set_eeprom8, @@ -5669,20 +5354,14 @@ static const struct mv88e6xxx_ops mv88e6393x_ops = { .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .stu_getnext = mv88e6390_g1_stu_getnext, .stu_loadpurge = mv88e6390_g1_stu_loadpurge, - .serdes_power = mv88e6393x_serdes_power, .serdes_get_lane = mv88e6393x_serdes_get_lane, - .serdes_pcs_get_state = mv88e6393x_serdes_pcs_get_state, - .serdes_pcs_config = mv88e6390_serdes_pcs_config, - .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart, - .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up, .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, - .serdes_irq_enable = mv88e6393x_serdes_irq_enable, - .serdes_irq_status = mv88e6393x_serdes_irq_status, /* TODO: serdes stats */ .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, .phylink_get_caps = mv88e6393x_phylink_get_caps, + .pcs_ops = &mv88e6393x_pcs_ops, }; static const struct mv88e6xxx_info mv88e6xxx_table[] = { @@ -7114,18 +6793,15 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { .port_setup = mv88e6xxx_port_setup, .port_teardown = mv88e6xxx_port_teardown, .phylink_get_caps = mv88e6xxx_get_caps, - .phylink_mac_link_state = mv88e6xxx_serdes_pcs_get_state, + .phylink_mac_select_pcs = mv88e6xxx_mac_select_pcs, .phylink_mac_prepare = mv88e6xxx_mac_prepare, .phylink_mac_config = mv88e6xxx_mac_config, .phylink_mac_finish = mv88e6xxx_mac_finish, - .phylink_mac_an_restart = mv88e6xxx_serdes_pcs_an_restart, .phylink_mac_link_down = mv88e6xxx_mac_link_down, .phylink_mac_link_up = mv88e6xxx_mac_link_up, .get_strings = mv88e6xxx_get_strings, .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, .get_sset_count = mv88e6xxx_get_sset_count, - .port_enable = mv88e6xxx_port_enable, - .port_disable = mv88e6xxx_port_disable, .port_max_mtu = mv88e6xxx_get_max_mtu, .port_change_mtu = mv88e6xxx_change_mtu, .get_mac_eee = mv88e6xxx_get_mac_eee, diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index 0ad34b2d8913..44383a03ef2f 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -205,6 +205,7 @@ struct mv88e6xxx_irq_ops; struct mv88e6xxx_gpio_ops; struct mv88e6xxx_avb_ops; struct mv88e6xxx_ptp_ops; +struct mv88e6xxx_pcs_ops; struct mv88e6xxx_irq { u16 masked; @@ -285,9 +286,8 @@ struct mv88e6xxx_port { u8 cmode; bool mirror_ingress; bool mirror_egress; - unsigned int serdes_irq; - char serdes_irq_name[64]; struct devlink_region *region; + void *pcs_private; /* MacAuth Bypass control flag */ bool mab; @@ -590,31 +590,12 @@ struct mv88e6xxx_ops { int (*mgmt_rsvd2cpu)(struct mv88e6xxx_chip *chip); - /* Power on/off a SERDES interface */ - int (*serdes_power)(struct mv88e6xxx_chip *chip, int port, int lane, - bool up); - /* SERDES lane mapping */ int (*serdes_get_lane)(struct mv88e6xxx_chip *chip, int port); - int (*serdes_pcs_get_state)(struct mv88e6xxx_chip *chip, int port, - int lane, struct phylink_link_state *state); - int (*serdes_pcs_config)(struct mv88e6xxx_chip *chip, int port, - int lane, unsigned int mode, - phy_interface_t interface, - const unsigned long *advertise); - int (*serdes_pcs_an_restart)(struct mv88e6xxx_chip *chip, int port, - int lane); - int (*serdes_pcs_link_up)(struct mv88e6xxx_chip *chip, int port, - int lane, int speed, int duplex); - /* SERDES interrupt handling */ unsigned int (*serdes_irq_mapping)(struct mv88e6xxx_chip *chip, int port); - int (*serdes_irq_enable)(struct mv88e6xxx_chip *chip, int port, int lane, - bool enable); - irqreturn_t (*serdes_irq_status)(struct mv88e6xxx_chip *chip, int port, - int lane); /* Statistics from the SERDES interface */ int (*serdes_get_sset_count)(struct mv88e6xxx_chip *chip, int port); @@ -664,6 +645,8 @@ struct mv88e6xxx_ops { void (*phylink_get_caps)(struct mv88e6xxx_chip *chip, int port, struct phylink_config *config); + const struct mv88e6xxx_pcs_ops *pcs_ops; + /* Max Frame Size */ int (*set_max_frame_size)(struct mv88e6xxx_chip *chip, int mtu); }; @@ -736,6 +719,14 @@ struct mv88e6xxx_ptp_ops { u32 cc_mult_dem; }; +struct mv88e6xxx_pcs_ops { + int (*pcs_init)(struct mv88e6xxx_chip *chip, int port); + void (*pcs_teardown)(struct mv88e6xxx_chip *chip, int port); + struct phylink_pcs *(*pcs_select)(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode); + +}; + #define STATS_TYPE_PORT BIT(0) #define STATS_TYPE_BANK0 BIT(1) #define STATS_TYPE_BANK1 BIT(2) diff --git a/drivers/net/dsa/mv88e6xxx/pcs-6185.c b/drivers/net/dsa/mv88e6xxx/pcs-6185.c new file mode 100644 index 000000000000..4d677f836807 --- /dev/null +++ b/drivers/net/dsa/mv88e6xxx/pcs-6185.c @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Marvell 88E6185 family SERDES PCS support + * + * Copyright (c) 2008 Marvell Semiconductor + * + * Copyright (c) 2017 Andrew Lunn <andrew@lunn.ch> + */ +#include <linux/phylink.h> + +#include "global2.h" +#include "port.h" +#include "serdes.h" + +struct mv88e6185_pcs { + struct phylink_pcs phylink_pcs; + unsigned int irq; + char name[64]; + + struct mv88e6xxx_chip *chip; + int port; +}; + +static struct mv88e6185_pcs *pcs_to_mv88e6185_pcs(struct phylink_pcs *pcs) +{ + return container_of(pcs, struct mv88e6185_pcs, phylink_pcs); +} + +static irqreturn_t mv88e6185_pcs_handle_irq(int irq, void *dev_id) +{ + struct mv88e6185_pcs *mpcs = dev_id; + struct mv88e6xxx_chip *chip; + irqreturn_t ret = IRQ_NONE; + bool link_up; + u16 status; + int port; + int err; + + chip = mpcs->chip; + port = mpcs->port; + + mv88e6xxx_reg_lock(chip); + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &status); + mv88e6xxx_reg_unlock(chip); + + if (!err) { + link_up = !!(status & MV88E6XXX_PORT_STS_LINK); + + phylink_pcs_change(&mpcs->phylink_pcs, link_up); + + ret = IRQ_HANDLED; + } + + return ret; +} + +static void mv88e6185_pcs_get_state(struct phylink_pcs *pcs, + struct phylink_link_state *state) +{ + struct mv88e6185_pcs *mpcs = pcs_to_mv88e6185_pcs(pcs); + struct mv88e6xxx_chip *chip = mpcs->chip; + int port = mpcs->port; + u16 status; + int err; + + mv88e6xxx_reg_lock(chip); + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &status); + mv88e6xxx_reg_unlock(chip); + + if (err) + status = 0; + + state->link = !!(status & MV88E6XXX_PORT_STS_LINK); + if (state->link) { + state->duplex = status & MV88E6XXX_PORT_STS_DUPLEX ? + DUPLEX_FULL : DUPLEX_HALF; + + switch (status & MV88E6XXX_PORT_STS_SPEED_MASK) { + case MV88E6XXX_PORT_STS_SPEED_1000: + state->speed = SPEED_1000; + break; + + case MV88E6XXX_PORT_STS_SPEED_100: + state->speed = SPEED_100; + break; + + case MV88E6XXX_PORT_STS_SPEED_10: + state->speed = SPEED_10; + break; + + default: + state->link = false; + break; + } + } +} + +static int mv88e6185_pcs_config(struct phylink_pcs *pcs, unsigned int mode, + phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac) +{ + return 0; +} + +static void mv88e6185_pcs_an_restart(struct phylink_pcs *pcs) +{ +} + +static const struct phylink_pcs_ops mv88e6185_phylink_pcs_ops = { + .pcs_get_state = mv88e6185_pcs_get_state, + .pcs_config = mv88e6185_pcs_config, + .pcs_an_restart = mv88e6185_pcs_an_restart, +}; + +static int mv88e6185_pcs_init(struct mv88e6xxx_chip *chip, int port) +{ + struct mv88e6185_pcs *mpcs; + struct device *dev; + unsigned int irq; + int err; + + /* There are no configurable serdes lanes on this switch chip, so + * we use the static cmode configuration to determine whether we + * have a PCS or not. + */ + if (chip->ports[port].cmode != MV88E6185_PORT_STS_CMODE_SERDES && + chip->ports[port].cmode != MV88E6185_PORT_STS_CMODE_1000BASE_X) + return 0; + + dev = chip->dev; + + mpcs = kzalloc(sizeof(*mpcs), GFP_KERNEL); + if (!mpcs) + return -ENOMEM; + + mpcs->chip = chip; + mpcs->port = port; + mpcs->phylink_pcs.ops = &mv88e6185_phylink_pcs_ops; + + irq = mv88e6xxx_serdes_irq_mapping(chip, port); + if (irq) { + snprintf(mpcs->name, sizeof(mpcs->name), + "mv88e6xxx-%s-serdes-%d", dev_name(dev), port); + + err = request_threaded_irq(irq, NULL, mv88e6185_pcs_handle_irq, + IRQF_ONESHOT, mpcs->name, mpcs); + if (err) { + kfree(mpcs); + return err; + } + + mpcs->irq = irq; + } else { + mpcs->phylink_pcs.poll = true; + } + + chip->ports[port].pcs_private = &mpcs->phylink_pcs; + + return 0; +} + +static void mv88e6185_pcs_teardown(struct mv88e6xxx_chip *chip, int port) +{ + struct mv88e6185_pcs *mpcs; + + mpcs = chip->ports[port].pcs_private; + if (!mpcs) + return; + + if (mpcs->irq) + free_irq(mpcs->irq, mpcs); + + kfree(mpcs); + + chip->ports[port].pcs_private = NULL; +} + +static struct phylink_pcs *mv88e6185_pcs_select(struct mv88e6xxx_chip *chip, + int port, + phy_interface_t interface) +{ + return chip->ports[port].pcs_private; +} + +const struct mv88e6xxx_pcs_ops mv88e6185_pcs_ops = { + .pcs_init = mv88e6185_pcs_init, + .pcs_teardown = mv88e6185_pcs_teardown, + .pcs_select = mv88e6185_pcs_select, +}; diff --git a/drivers/net/dsa/mv88e6xxx/pcs-6352.c b/drivers/net/dsa/mv88e6xxx/pcs-6352.c new file mode 100644 index 000000000000..88f624b65470 --- /dev/null +++ b/drivers/net/dsa/mv88e6xxx/pcs-6352.c @@ -0,0 +1,390 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Marvell 88E6352 family SERDES PCS support + * + * Copyright (c) 2008 Marvell Semiconductor + * + * Copyright (c) 2017 Andrew Lunn <andrew@lunn.ch> + */ +#include <linux/phylink.h> + +#include "global2.h" +#include "port.h" +#include "serdes.h" + +/* Definitions from drivers/net/phy/marvell.c, which would be good to reuse. */ +#define MII_M1011_PHY_STATUS 17 +#define MII_M1011_IMASK 18 +#define MII_M1011_IMASK_LINK_CHANGE BIT(10) +#define MII_M1011_IEVENT 19 +#define MII_M1011_IEVENT_LINK_CHANGE BIT(10) +#define MII_MARVELL_PHY_PAGE 22 +#define MII_MARVELL_FIBER_PAGE 1 + +struct marvell_c22_pcs { + struct mdio_device mdio; + struct phylink_pcs phylink_pcs; + unsigned int irq; + char name[64]; + bool (*link_check)(struct marvell_c22_pcs *mpcs); + struct mv88e6xxx_port *port; +}; + +static struct marvell_c22_pcs *pcs_to_marvell_c22_pcs(struct phylink_pcs *pcs) +{ + return container_of(pcs, struct marvell_c22_pcs, phylink_pcs); +} + +static int marvell_c22_pcs_set_fiber_page(struct marvell_c22_pcs *mpcs) +{ + u16 page; + int err; + + mutex_lock(&mpcs->mdio.bus->mdio_lock); + + err = __mdiodev_read(&mpcs->mdio, MII_MARVELL_PHY_PAGE); + if (err < 0) { + dev_err(mpcs->mdio.dev.parent, + "%s: can't read Serdes page register: %pe\n", + mpcs->name, ERR_PTR(err)); + return err; + } + + page = err; + + err = __mdiodev_write(&mpcs->mdio, MII_MARVELL_PHY_PAGE, + MII_MARVELL_FIBER_PAGE); + if (err) { + dev_err(mpcs->mdio.dev.parent, + "%s: can't set Serdes page register: %pe\n", + mpcs->name, ERR_PTR(err)); + return err; + } + + return page; +} + +static int marvell_c22_pcs_restore_page(struct marvell_c22_pcs *mpcs, + int oldpage, int ret) +{ + int err; + + if (oldpage >= 0) { + err = __mdiodev_write(&mpcs->mdio, MII_MARVELL_PHY_PAGE, + oldpage); + if (err) + dev_err(mpcs->mdio.dev.parent, + "%s: can't restore Serdes page register: %pe\n", + mpcs->name, ERR_PTR(err)); + if (!err || ret < 0) + err = ret; + } else { + err = oldpage; + } + mutex_unlock(&mpcs->mdio.bus->mdio_lock); + + return err; +} + +static irqreturn_t marvell_c22_pcs_handle_irq(int irq, void *dev_id) +{ + struct marvell_c22_pcs *mpcs = dev_id; + irqreturn_t status = IRQ_NONE; + int err, oldpage; + + oldpage = marvell_c22_pcs_set_fiber_page(mpcs); + if (oldpage < 0) + goto fail; + + err = __mdiodev_read(&mpcs->mdio, MII_M1011_IEVENT); + if (err >= 0 && err & MII_M1011_IEVENT_LINK_CHANGE) { + phylink_pcs_change(&mpcs->phylink_pcs, true); + status = IRQ_HANDLED; + } + +fail: + marvell_c22_pcs_restore_page(mpcs, oldpage, 0); + + return status; +} + +static int marvell_c22_pcs_modify(struct marvell_c22_pcs *mpcs, u8 reg, + u16 mask, u16 val) +{ + int oldpage, err = 0; + + oldpage = marvell_c22_pcs_set_fiber_page(mpcs); + if (oldpage >= 0) + err = __mdiodev_modify(&mpcs->mdio, reg, mask, val); + + return marvell_c22_pcs_restore_page(mpcs, oldpage, err); +} + +static int marvell_c22_pcs_power(struct marvell_c22_pcs *mpcs, + bool on) +{ + u16 val = on ? 0 : BMCR_PDOWN; + + return marvell_c22_pcs_modify(mpcs, MII_BMCR, BMCR_PDOWN, val); +} + +static int marvell_c22_pcs_control_irq(struct marvell_c22_pcs *mpcs, + bool enable) +{ + u16 val = enable ? MII_M1011_IMASK_LINK_CHANGE : 0; + + return marvell_c22_pcs_modify(mpcs, MII_M1011_IMASK, + MII_M1011_IMASK_LINK_CHANGE, val); +} + +static int marvell_c22_pcs_enable(struct phylink_pcs *pcs) +{ + struct marvell_c22_pcs *mpcs = pcs_to_marvell_c22_pcs(pcs); + int err; + + err = marvell_c22_pcs_power(mpcs, true); + if (err) + return err; + + return marvell_c22_pcs_control_irq(mpcs, !!mpcs->irq); +} + +static void marvell_c22_pcs_disable(struct phylink_pcs *pcs) +{ + struct marvell_c22_pcs *mpcs = pcs_to_marvell_c22_pcs(pcs); + + marvell_c22_pcs_control_irq(mpcs, false); + marvell_c22_pcs_power(mpcs, false); +} + +static void marvell_c22_pcs_get_state(struct phylink_pcs *pcs, + struct phylink_link_state *state) +{ + struct marvell_c22_pcs *mpcs = pcs_to_marvell_c22_pcs(pcs); + int oldpage, bmsr, lpa, status; + + state->link = false; + + if (mpcs->link_check && !mpcs->link_check(mpcs)) + return; + + oldpage = marvell_c22_pcs_set_fiber_page(mpcs); + if (oldpage >= 0) { + bmsr = __mdiodev_read(&mpcs->mdio, MII_BMSR); + lpa = __mdiodev_read(&mpcs->mdio, MII_LPA); + status = __mdiodev_read(&mpcs->mdio, MII_M1011_PHY_STATUS); + } + + if (marvell_c22_pcs_restore_page(mpcs, oldpage, 0) >= 0 && + bmsr >= 0 && lpa >= 0 && status >= 0) + mv88e6xxx_pcs_decode_state(mpcs->mdio.dev.parent, bmsr, lpa, + status, state); +} + +static int marvell_c22_pcs_config(struct phylink_pcs *pcs, + unsigned int neg_mode, + phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac) +{ + struct marvell_c22_pcs *mpcs = pcs_to_marvell_c22_pcs(pcs); + int oldpage, adv, err, ret = 0; + u16 bmcr; + + adv = phylink_mii_c22_pcs_encode_advertisement(interface, advertising); + if (adv < 0) + return 0; + + bmcr = neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED ? BMCR_ANENABLE : 0; + + oldpage = marvell_c22_pcs_set_fiber_page(mpcs); + if (oldpage < 0) + goto restore; + + err = __mdiodev_modify_changed(&mpcs->mdio, MII_ADVERTISE, 0xffff, adv); + ret = err; + if (err < 0) + goto restore; + + err = __mdiodev_modify_changed(&mpcs->mdio, MII_BMCR, BMCR_ANENABLE, + bmcr); + if (err < 0) { + ret = err; + goto restore; + } + + /* If the ANENABLE bit was changed, the PHY will restart negotiation, + * so we don't need to flag a change to trigger its own restart. + */ + if (err) + ret = 0; + +restore: + return marvell_c22_pcs_restore_page(mpcs, oldpage, ret); +} + +static void marvell_c22_pcs_an_restart(struct phylink_pcs *pcs) +{ + struct marvell_c22_pcs *mpcs = pcs_to_marvell_c22_pcs(pcs); + + marvell_c22_pcs_modify(mpcs, MII_BMCR, BMCR_ANRESTART, BMCR_ANRESTART); +} + +static void marvell_c22_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, + phy_interface_t interface, int speed, + int duplex) +{ + struct marvell_c22_pcs *mpcs = pcs_to_marvell_c22_pcs(pcs); + u16 bmcr; + int err; + + if (phylink_autoneg_inband(mode)) + return; + + bmcr = mii_bmcr_encode_fixed(speed, duplex); + + err = marvell_c22_pcs_modify(mpcs, MII_BMCR, BMCR_SPEED100 | + BMCR_FULLDPLX | BMCR_SPEED1000, bmcr); + if (err) + dev_err(mpcs->mdio.dev.parent, + "%s: failed to configure mpcs: %pe\n", mpcs->name, + ERR_PTR(err)); +} + +static const struct phylink_pcs_ops marvell_c22_pcs_ops = { + .pcs_enable = marvell_c22_pcs_enable, + .pcs_disable = marvell_c22_pcs_disable, + .pcs_get_state = marvell_c22_pcs_get_state, + .pcs_config = marvell_c22_pcs_config, + .pcs_an_restart = marvell_c22_pcs_an_restart, + .pcs_link_up = marvell_c22_pcs_link_up, +}; + +static struct marvell_c22_pcs *marvell_c22_pcs_alloc(struct device *dev, + struct mii_bus *bus, + unsigned int addr) +{ + struct marvell_c22_pcs *mpcs; + + mpcs = kzalloc(sizeof(*mpcs), GFP_KERNEL); + if (!mpcs) + return NULL; + + mpcs->mdio.dev.parent = dev; + mpcs->mdio.bus = bus; + mpcs->mdio.addr = addr; + mpcs->phylink_pcs.ops = &marvell_c22_pcs_ops; + mpcs->phylink_pcs.neg_mode = true; + + return mpcs; +} + +static int marvell_c22_pcs_setup_irq(struct marvell_c22_pcs *mpcs, + unsigned int irq) +{ + int err; + + mpcs->phylink_pcs.poll = !irq; + mpcs->irq = irq; + + if (irq) { + err = request_threaded_irq(irq, NULL, + marvell_c22_pcs_handle_irq, + IRQF_ONESHOT, mpcs->name, mpcs); + if (err) + return err; + } + + return 0; +} + +/* mv88e6352 specifics */ + +static bool mv88e6352_pcs_link_check(struct marvell_c22_pcs *mpcs) +{ + struct mv88e6xxx_port *port = mpcs->port; + struct mv88e6xxx_chip *chip = port->chip; + u8 cmode; + + /* Port 4 can be in auto-media mode. Check that the port is + * associated with the mpcs. + */ + mv88e6xxx_reg_lock(chip); + chip->info->ops->port_get_cmode(chip, port->port, &cmode); + mv88e6xxx_reg_unlock(chip); + + return cmode == MV88E6XXX_PORT_STS_CMODE_100BASEX || + cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX || + cmode == MV88E6XXX_PORT_STS_CMODE_SGMII; +} + +static int mv88e6352_pcs_init(struct mv88e6xxx_chip *chip, int port) +{ + struct marvell_c22_pcs *mpcs; + struct mii_bus *bus; + struct device *dev; + unsigned int irq; + int err; + + mv88e6xxx_reg_lock(chip); + err = mv88e6352_g2_scratch_port_has_serdes(chip, port); + mv88e6xxx_reg_unlock(chip); + if (err <= 0) + return err; + + irq = mv88e6xxx_serdes_irq_mapping(chip, port); + bus = mv88e6xxx_default_mdio_bus(chip); + dev = chip->dev; + + mpcs = marvell_c22_pcs_alloc(dev, bus, MV88E6352_ADDR_SERDES); + if (!mpcs) + return -ENOMEM; + + snprintf(mpcs->name, sizeof(mpcs->name), + "mv88e6xxx-%s-serdes-%d", dev_name(dev), port); + + mpcs->link_check = mv88e6352_pcs_link_check; + mpcs->port = &chip->ports[port]; + + err = marvell_c22_pcs_setup_irq(mpcs, irq); + if (err) { + kfree(mpcs); + return err; + } + + chip->ports[port].pcs_private = &mpcs->phylink_pcs; + + return 0; +} + +static void mv88e6352_pcs_teardown(struct mv88e6xxx_chip *chip, int port) +{ + struct marvell_c22_pcs *mpcs; + struct phylink_pcs *pcs; + + pcs = chip->ports[port].pcs_private; + if (!pcs) + return; + + mpcs = pcs_to_marvell_c22_pcs(pcs); + + if (mpcs->irq) + free_irq(mpcs->irq, mpcs); + + kfree(mpcs); + + chip->ports[port].pcs_private = NULL; +} + +static struct phylink_pcs *mv88e6352_pcs_select(struct mv88e6xxx_chip *chip, + int port, + phy_interface_t interface) +{ + return chip->ports[port].pcs_private; +} + +const struct mv88e6xxx_pcs_ops mv88e6352_pcs_ops = { + .pcs_init = mv88e6352_pcs_init, + .pcs_teardown = mv88e6352_pcs_teardown, + .pcs_select = mv88e6352_pcs_select, +}; diff --git a/drivers/net/dsa/mv88e6xxx/pcs-639x.c b/drivers/net/dsa/mv88e6xxx/pcs-639x.c new file mode 100644 index 000000000000..ba373656bfe1 --- /dev/null +++ b/drivers/net/dsa/mv88e6xxx/pcs-639x.c @@ -0,0 +1,943 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Marvell 88E6352 family SERDES PCS support + * + * Copyright (c) 2008 Marvell Semiconductor + * + * Copyright (c) 2017 Andrew Lunn <andrew@lunn.ch> + */ +#include <linux/interrupt.h> +#include <linux/irqdomain.h> +#include <linux/mii.h> + +#include "chip.h" +#include "global2.h" +#include "phy.h" +#include "port.h" +#include "serdes.h" + +struct mv88e639x_pcs { + struct mdio_device mdio; + struct phylink_pcs sgmii_pcs; + struct phylink_pcs xg_pcs; + bool erratum_3_14; + bool supports_5g; + phy_interface_t interface; + unsigned int irq; + char name[64]; + irqreturn_t (*handle_irq)(struct mv88e639x_pcs *mpcs); +}; + +static int mv88e639x_read(struct mv88e639x_pcs *mpcs, u16 regnum, u16 *val) +{ + int err; + + err = mdiodev_c45_read(&mpcs->mdio, MDIO_MMD_PHYXS, regnum); + if (err < 0) + return err; + + *val = err; + + return 0; +} + +static int mv88e639x_write(struct mv88e639x_pcs *mpcs, u16 regnum, u16 val) +{ + return mdiodev_c45_write(&mpcs->mdio, MDIO_MMD_PHYXS, regnum, val); +} + +static int mv88e639x_modify(struct mv88e639x_pcs *mpcs, u16 regnum, u16 mask, + u16 val) +{ + return mdiodev_c45_modify(&mpcs->mdio, MDIO_MMD_PHYXS, regnum, mask, + val); +} + +static int mv88e639x_modify_changed(struct mv88e639x_pcs *mpcs, u16 regnum, + u16 mask, u16 set) +{ + return mdiodev_c45_modify_changed(&mpcs->mdio, MDIO_MMD_PHYXS, regnum, + mask, set); +} + +static struct mv88e639x_pcs * +mv88e639x_pcs_alloc(struct device *dev, struct mii_bus *bus, unsigned int addr, + int port) +{ + struct mv88e639x_pcs *mpcs; + + mpcs = kzalloc(sizeof(*mpcs), GFP_KERNEL); + if (!mpcs) + return NULL; + + mpcs->mdio.dev.parent = dev; + mpcs->mdio.bus = bus; + mpcs->mdio.addr = addr; + + snprintf(mpcs->name, sizeof(mpcs->name), + "mv88e6xxx-%s-serdes-%d", dev_name(dev), port); + + return mpcs; +} + +static irqreturn_t mv88e639x_pcs_handle_irq(int irq, void *dev_id) +{ + struct mv88e639x_pcs *mpcs = dev_id; + irqreturn_t (*handler)(struct mv88e639x_pcs *); + + handler = READ_ONCE(mpcs->handle_irq); + if (!handler) + return IRQ_NONE; + + return handler(mpcs); +} + +static int mv88e639x_pcs_setup_irq(struct mv88e639x_pcs *mpcs, + struct mv88e6xxx_chip *chip, int port) +{ + unsigned int irq; + + irq = mv88e6xxx_serdes_irq_mapping(chip, port); + if (!irq) { + /* Use polling mode */ + mpcs->sgmii_pcs.poll = true; + mpcs->xg_pcs.poll = true; + return 0; + } + + mpcs->irq = irq; + + return request_threaded_irq(irq, NULL, mv88e639x_pcs_handle_irq, + IRQF_ONESHOT, mpcs->name, mpcs); +} + +static void mv88e639x_pcs_teardown(struct mv88e6xxx_chip *chip, int port) +{ + struct mv88e639x_pcs *mpcs = chip->ports[port].pcs_private; + + if (!mpcs) + return; + + if (mpcs->irq) + free_irq(mpcs->irq, mpcs); + + kfree(mpcs); + + chip->ports[port].pcs_private = NULL; +} + +static struct mv88e639x_pcs *sgmii_pcs_to_mv88e639x_pcs(struct phylink_pcs *pcs) +{ + return container_of(pcs, struct mv88e639x_pcs, sgmii_pcs); +} + +static irqreturn_t mv88e639x_sgmii_handle_irq(struct mv88e639x_pcs *mpcs) +{ + u16 int_status; + int err; + + err = mv88e639x_read(mpcs, MV88E6390_SGMII_INT_STATUS, &int_status); + if (err) + return IRQ_NONE; + + if (int_status & (MV88E6390_SGMII_INT_LINK_DOWN | + MV88E6390_SGMII_INT_LINK_UP)) { + phylink_pcs_change(&mpcs->sgmii_pcs, + int_status & MV88E6390_SGMII_INT_LINK_UP); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int mv88e639x_sgmii_pcs_control_irq(struct mv88e639x_pcs *mpcs, + bool enable) +{ + u16 val = 0; + + if (enable) + val |= MV88E6390_SGMII_INT_LINK_DOWN | + MV88E6390_SGMII_INT_LINK_UP; + + return mv88e639x_modify(mpcs, MV88E6390_SGMII_INT_ENABLE, + MV88E6390_SGMII_INT_LINK_DOWN | + MV88E6390_SGMII_INT_LINK_UP, val); +} + +static int mv88e639x_sgmii_pcs_control_pwr(struct mv88e639x_pcs *mpcs, + bool enable) +{ + u16 mask, val; + + if (enable) { + mask = BMCR_RESET | BMCR_LOOPBACK | BMCR_PDOWN; + val = 0; + } else { + mask = val = BMCR_PDOWN; + } + + return mv88e639x_modify(mpcs, MV88E6390_SGMII_BMCR, mask, val); +} + +static int mv88e639x_sgmii_pcs_enable(struct phylink_pcs *pcs) +{ + struct mv88e639x_pcs *mpcs = sgmii_pcs_to_mv88e639x_pcs(pcs); + + /* power enable done in post_config */ + mpcs->handle_irq = mv88e639x_sgmii_handle_irq; + + return mv88e639x_sgmii_pcs_control_irq(mpcs, !!mpcs->irq); +} + +static void mv88e639x_sgmii_pcs_disable(struct phylink_pcs *pcs) +{ + struct mv88e639x_pcs *mpcs = sgmii_pcs_to_mv88e639x_pcs(pcs); + + mv88e639x_sgmii_pcs_control_irq(mpcs, false); + mv88e639x_sgmii_pcs_control_pwr(mpcs, false); +} + +static void mv88e639x_sgmii_pcs_pre_config(struct phylink_pcs *pcs, + phy_interface_t interface) +{ + struct mv88e639x_pcs *mpcs = sgmii_pcs_to_mv88e639x_pcs(pcs); + + mv88e639x_sgmii_pcs_control_pwr(mpcs, false); +} + +static int mv88e6390_erratum_3_14(struct mv88e639x_pcs *mpcs) +{ + const int lanes[] = { MV88E6390_PORT9_LANE0, MV88E6390_PORT9_LANE1, + MV88E6390_PORT9_LANE2, MV88E6390_PORT9_LANE3, + MV88E6390_PORT10_LANE0, MV88E6390_PORT10_LANE1, + MV88E6390_PORT10_LANE2, MV88E6390_PORT10_LANE3 }; + int err, i; + + /* 88e6190x and 88e6390x errata 3.14: + * After chip reset, SERDES reconfiguration or SERDES core + * Software Reset, the SERDES lanes may not be properly aligned + * resulting in CRC errors + */ + + for (i = 0; i < ARRAY_SIZE(lanes); i++) { + err = mdiobus_c45_write(mpcs->mdio.bus, lanes[i], + MDIO_MMD_PHYXS, + 0xf054, 0x400C); + if (err) + return err; + + err = mdiobus_c45_write(mpcs->mdio.bus, lanes[i], + MDIO_MMD_PHYXS, + 0xf054, 0x4000); + if (err) + return err; + } + + return 0; +} + +static int mv88e639x_sgmii_pcs_post_config(struct phylink_pcs *pcs, + phy_interface_t interface) +{ + struct mv88e639x_pcs *mpcs = sgmii_pcs_to_mv88e639x_pcs(pcs); + int err; + + mv88e639x_sgmii_pcs_control_pwr(mpcs, true); + + if (mpcs->erratum_3_14) { + err = mv88e6390_erratum_3_14(mpcs); + if (err) + dev_err(mpcs->mdio.dev.parent, + "failed to apply erratum 3.14: %pe\n", + ERR_PTR(err)); + } + + return 0; +} + +static void mv88e639x_sgmii_pcs_get_state(struct phylink_pcs *pcs, + struct phylink_link_state *state) +{ + struct mv88e639x_pcs *mpcs = sgmii_pcs_to_mv88e639x_pcs(pcs); + u16 bmsr, lpa, status; + int err; + + err = mv88e639x_read(mpcs, MV88E6390_SGMII_BMSR, &bmsr); + if (err) { + dev_err(mpcs->mdio.dev.parent, + "can't read Serdes PHY %s: %pe\n", + "BMSR", ERR_PTR(err)); + state->link = false; + return; + } + + err = mv88e639x_read(mpcs, MV88E6390_SGMII_LPA, &lpa); + if (err) { + dev_err(mpcs->mdio.dev.parent, + "can't read Serdes PHY %s: %pe\n", + "LPA", ERR_PTR(err)); + state->link = false; + return; + } + + err = mv88e639x_read(mpcs, MV88E6390_SGMII_PHY_STATUS, &status); + if (err) { + dev_err(mpcs->mdio.dev.parent, + "can't read Serdes PHY %s: %pe\n", + "status", ERR_PTR(err)); + state->link = false; + return; + } + + mv88e6xxx_pcs_decode_state(mpcs->mdio.dev.parent, bmsr, lpa, status, + state); +} + +static int mv88e639x_sgmii_pcs_config(struct phylink_pcs *pcs, + unsigned int neg_mode, + phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac) +{ + struct mv88e639x_pcs *mpcs = sgmii_pcs_to_mv88e639x_pcs(pcs); + u16 val, bmcr; + bool changed; + int adv, err; + + adv = phylink_mii_c22_pcs_encode_advertisement(interface, advertising); + if (adv < 0) + return 0; + + mpcs->interface = interface; + + err = mv88e639x_modify_changed(mpcs, MV88E6390_SGMII_ADVERTISE, + 0xffff, adv); + if (err < 0) + return err; + + changed = err > 0; + + err = mv88e639x_read(mpcs, MV88E6390_SGMII_BMCR, &val); + if (err) + return err; + + if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) + bmcr = val | BMCR_ANENABLE; + else + bmcr = val & ~BMCR_ANENABLE; + + /* setting ANENABLE triggers a restart of negotiation */ + if (bmcr == val) + return changed; + + return mv88e639x_write(mpcs, MV88E6390_SGMII_BMCR, bmcr); +} + +static void mv88e639x_sgmii_pcs_an_restart(struct phylink_pcs *pcs) +{ + struct mv88e639x_pcs *mpcs = sgmii_pcs_to_mv88e639x_pcs(pcs); + + mv88e639x_modify(mpcs, MV88E6390_SGMII_BMCR, + BMCR_ANRESTART, BMCR_ANRESTART); +} + +static void mv88e639x_sgmii_pcs_link_up(struct phylink_pcs *pcs, + unsigned int mode, + phy_interface_t interface, + int speed, int duplex) +{ + struct mv88e639x_pcs *mpcs = sgmii_pcs_to_mv88e639x_pcs(pcs); + u16 bmcr; + int err; + + if (phylink_autoneg_inband(mode)) + return; + + bmcr = mii_bmcr_encode_fixed(speed, duplex); + + err = mv88e639x_modify(mpcs, MV88E6390_SGMII_BMCR, + BMCR_SPEED1000 | BMCR_SPEED100 | BMCR_FULLDPLX, + bmcr); + if (err) + dev_err(mpcs->mdio.dev.parent, + "can't access Serdes PHY %s: %pe\n", + "BMCR", ERR_PTR(err)); +} + +static const struct phylink_pcs_ops mv88e639x_sgmii_pcs_ops = { + .pcs_enable = mv88e639x_sgmii_pcs_enable, + .pcs_disable = mv88e639x_sgmii_pcs_disable, + .pcs_pre_config = mv88e639x_sgmii_pcs_pre_config, + .pcs_post_config = mv88e639x_sgmii_pcs_post_config, + .pcs_get_state = mv88e639x_sgmii_pcs_get_state, + .pcs_an_restart = mv88e639x_sgmii_pcs_an_restart, + .pcs_config = mv88e639x_sgmii_pcs_config, + .pcs_link_up = mv88e639x_sgmii_pcs_link_up, +}; + +static struct mv88e639x_pcs *xg_pcs_to_mv88e639x_pcs(struct phylink_pcs *pcs) +{ + return container_of(pcs, struct mv88e639x_pcs, xg_pcs); +} + +static int mv88e639x_xg_pcs_enable(struct mv88e639x_pcs *mpcs) +{ + return mv88e639x_modify(mpcs, MV88E6390_10G_CTRL1, + MDIO_CTRL1_RESET | MDIO_PCS_CTRL1_LOOPBACK | + MDIO_CTRL1_LPOWER, 0); +} + +static void mv88e639x_xg_pcs_disable(struct mv88e639x_pcs *mpcs) +{ + mv88e639x_modify(mpcs, MV88E6390_10G_CTRL1, MDIO_CTRL1_LPOWER, + MDIO_CTRL1_LPOWER); +} + +static void mv88e639x_xg_pcs_get_state(struct phylink_pcs *pcs, + struct phylink_link_state *state) +{ + struct mv88e639x_pcs *mpcs = xg_pcs_to_mv88e639x_pcs(pcs); + u16 status; + int err; + + state->link = false; + + err = mv88e639x_read(mpcs, MV88E6390_10G_STAT1, &status); + if (err) { + dev_err(mpcs->mdio.dev.parent, + "can't read Serdes PHY %s: %pe\n", + "STAT1", ERR_PTR(err)); + return; + } + + state->link = !!(status & MDIO_STAT1_LSTATUS); + if (state->link) { + switch (state->interface) { + case PHY_INTERFACE_MODE_5GBASER: + state->speed = SPEED_5000; + break; + + case PHY_INTERFACE_MODE_10GBASER: + case PHY_INTERFACE_MODE_RXAUI: + case PHY_INTERFACE_MODE_XAUI: + state->speed = SPEED_10000; + break; + + default: + state->link = false; + return; + } + + state->duplex = DUPLEX_FULL; + } +} + +static int mv88e639x_xg_pcs_config(struct phylink_pcs *pcs, + unsigned int neg_mode, + phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac) +{ + return 0; +} + +static struct phylink_pcs * +mv88e639x_pcs_select(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode) +{ + struct mv88e639x_pcs *mpcs; + + mpcs = chip->ports[port].pcs_private; + if (!mpcs) + return NULL; + + switch (mode) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: + return &mpcs->sgmii_pcs; + + case PHY_INTERFACE_MODE_5GBASER: + if (!mpcs->supports_5g) + return NULL; + fallthrough; + case PHY_INTERFACE_MODE_10GBASER: + case PHY_INTERFACE_MODE_XAUI: + case PHY_INTERFACE_MODE_RXAUI: + return &mpcs->xg_pcs; + + default: + return NULL; + } +} + +/* Marvell 88E6390 Specific support */ + +static irqreturn_t mv88e6390_xg_handle_irq(struct mv88e639x_pcs *mpcs) +{ + u16 int_status; + int err; + + err = mv88e639x_read(mpcs, MV88E6390_10G_INT_STATUS, &int_status); + if (err) + return IRQ_NONE; + + if (int_status & (MV88E6390_10G_INT_LINK_DOWN | + MV88E6390_10G_INT_LINK_UP)) { + phylink_pcs_change(&mpcs->xg_pcs, + int_status & MV88E6390_10G_INT_LINK_UP); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int mv88e6390_xg_control_irq(struct mv88e639x_pcs *mpcs, bool enable) +{ + u16 val = 0; + + if (enable) + val = MV88E6390_10G_INT_LINK_DOWN | MV88E6390_10G_INT_LINK_UP; + + return mv88e639x_modify(mpcs, MV88E6390_10G_INT_ENABLE, + MV88E6390_10G_INT_LINK_DOWN | + MV88E6390_10G_INT_LINK_UP, val); +} + +static int mv88e6390_xg_pcs_enable(struct phylink_pcs *pcs) +{ + struct mv88e639x_pcs *mpcs = xg_pcs_to_mv88e639x_pcs(pcs); + int err; + + err = mv88e639x_xg_pcs_enable(mpcs); + if (err) + return err; + + mpcs->handle_irq = mv88e6390_xg_handle_irq; + + return mv88e6390_xg_control_irq(mpcs, !!mpcs->irq); +} + +static void mv88e6390_xg_pcs_disable(struct phylink_pcs *pcs) +{ + struct mv88e639x_pcs *mpcs = xg_pcs_to_mv88e639x_pcs(pcs); + + mv88e6390_xg_control_irq(mpcs, false); + mv88e639x_xg_pcs_disable(mpcs); +} + +static const struct phylink_pcs_ops mv88e6390_xg_pcs_ops = { + .pcs_enable = mv88e6390_xg_pcs_enable, + .pcs_disable = mv88e6390_xg_pcs_disable, + .pcs_get_state = mv88e639x_xg_pcs_get_state, + .pcs_config = mv88e639x_xg_pcs_config, +}; + +static int mv88e6390_pcs_enable_checker(struct mv88e639x_pcs *mpcs) +{ + return mv88e639x_modify(mpcs, MV88E6390_PG_CONTROL, + MV88E6390_PG_CONTROL_ENABLE_PC, + MV88E6390_PG_CONTROL_ENABLE_PC); +} + +static int mv88e6390_pcs_init(struct mv88e6xxx_chip *chip, int port) +{ + struct mv88e639x_pcs *mpcs; + struct mii_bus *bus; + struct device *dev; + int lane, err; + + lane = mv88e6xxx_serdes_get_lane(chip, port); + if (lane < 0) + return 0; + + bus = mv88e6xxx_default_mdio_bus(chip); + dev = chip->dev; + + mpcs = mv88e639x_pcs_alloc(dev, bus, lane, port); + if (!mpcs) + return -ENOMEM; + + mpcs->sgmii_pcs.ops = &mv88e639x_sgmii_pcs_ops; + mpcs->sgmii_pcs.neg_mode = true; + mpcs->xg_pcs.ops = &mv88e6390_xg_pcs_ops; + mpcs->xg_pcs.neg_mode = true; + + if (chip->info->prod_num == MV88E6XXX_PORT_SWITCH_ID_PROD_6190X || + chip->info->prod_num == MV88E6XXX_PORT_SWITCH_ID_PROD_6390X) + mpcs->erratum_3_14 = true; + + err = mv88e639x_pcs_setup_irq(mpcs, chip, port); + if (err) + goto err_free; + + /* 6390 and 6390x has the checker, 6393x doesn't appear to? */ + /* This is to enable gathering the statistics. Maybe this + * should call out to a helper? Or we could do this at init time. + */ + err = mv88e6390_pcs_enable_checker(mpcs); + if (err) + goto err_free; + + chip->ports[port].pcs_private = mpcs; + + return 0; + +err_free: + kfree(mpcs); + return err; +} + +const struct mv88e6xxx_pcs_ops mv88e6390_pcs_ops = { + .pcs_init = mv88e6390_pcs_init, + .pcs_teardown = mv88e639x_pcs_teardown, + .pcs_select = mv88e639x_pcs_select, +}; + +/* Marvell 88E6393X Specific support */ + +static int mv88e6393x_power_lane(struct mv88e639x_pcs *mpcs, bool enable) +{ + u16 val = MV88E6393X_SERDES_CTRL1_TX_PDOWN | + MV88E6393X_SERDES_CTRL1_RX_PDOWN; + + return mv88e639x_modify(mpcs, MV88E6393X_SERDES_CTRL1, val, + enable ? 0 : val); +} + +/* mv88e6393x family errata 4.6: + * Cannot clear PwrDn bit on SERDES if device is configured CPU_MGD mode or + * P0_mode is configured for [x]MII. + * Workaround: Set SERDES register 4.F002 bit 5=0 and bit 15=1. + * + * It seems that after this workaround the SERDES is automatically powered up + * (the bit is cleared), so power it down. + */ +static int mv88e6393x_erratum_4_6(struct mv88e639x_pcs *mpcs) +{ + int err; + + err = mv88e639x_modify(mpcs, MV88E6393X_SERDES_POC, + MV88E6393X_SERDES_POC_PDOWN | + MV88E6393X_SERDES_POC_RESET, + MV88E6393X_SERDES_POC_RESET); + if (err) + return err; + + err = mv88e639x_modify(mpcs, MV88E6390_SGMII_BMCR, + BMCR_PDOWN, BMCR_PDOWN); + if (err) + return err; + + err = mv88e639x_sgmii_pcs_control_pwr(mpcs, false); + if (err) + return err; + + return mv88e6393x_power_lane(mpcs, false); +} + +/* mv88e6393x family errata 4.8: + * When a SERDES port is operating in 1000BASE-X or SGMII mode link may not + * come up after hardware reset or software reset of SERDES core. Workaround + * is to write SERDES register 4.F074.14=1 for only those modes and 0 in all + * other modes. + */ +static int mv88e6393x_erratum_4_8(struct mv88e639x_pcs *mpcs) +{ + u16 reg, poc; + int err; + + err = mv88e639x_read(mpcs, MV88E6393X_SERDES_POC, &poc); + if (err) + return err; + + poc &= MV88E6393X_SERDES_POC_PCS_MASK; + if (poc == MV88E6393X_SERDES_POC_PCS_1000BASEX || + poc == MV88E6393X_SERDES_POC_PCS_SGMII_PHY || + poc == MV88E6393X_SERDES_POC_PCS_SGMII_MAC) + reg = MV88E6393X_ERRATA_4_8_BIT; + else + reg = 0; + + return mv88e639x_modify(mpcs, MV88E6393X_ERRATA_4_8_REG, + MV88E6393X_ERRATA_4_8_BIT, reg); +} + +/* mv88e6393x family errata 5.2: + * For optimal signal integrity the following sequence should be applied to + * SERDES operating in 10G mode. These registers only apply to 10G operation + * and have no effect on other speeds. + */ +static int mv88e6393x_erratum_5_2(struct mv88e639x_pcs *mpcs) +{ + static const struct { + u16 dev, reg, val, mask; + } fixes[] = { + { MDIO_MMD_VEND1, 0x8093, 0xcb5a, 0xffff }, + { MDIO_MMD_VEND1, 0x8171, 0x7088, 0xffff }, + { MDIO_MMD_VEND1, 0x80c9, 0x311a, 0xffff }, + { MDIO_MMD_VEND1, 0x80a2, 0x8000, 0xff7f }, + { MDIO_MMD_VEND1, 0x80a9, 0x0000, 0xfff0 }, + { MDIO_MMD_VEND1, 0x80a3, 0x0000, 0xf8ff }, + { MDIO_MMD_PHYXS, MV88E6393X_SERDES_POC, + MV88E6393X_SERDES_POC_RESET, MV88E6393X_SERDES_POC_RESET }, + }; + int err, i; + + for (i = 0; i < ARRAY_SIZE(fixes); ++i) { + err = mdiodev_c45_modify(&mpcs->mdio, fixes[i].dev, + fixes[i].reg, fixes[i].mask, + fixes[i].val); + if (err) + return err; + } + + return 0; +} + +/* Inband AN is broken on Amethyst in 2500base-x mode when set by standard + * mechanism (via cmode). + * We can get around this by configuring the PCS mode to 1000base-x and then + * writing value 0x58 to register 1e.8000. (This must be done while SerDes + * receiver and transmitter are disabled, which is, when this function is + * called.) + * It seem that when we do this configuration to 2500base-x mode (by changing + * PCS mode to 1000base-x and frequency to 3.125 GHz from 1.25 GHz) and then + * configure to sgmii or 1000base-x, the device thinks that it already has + * SerDes at 1.25 GHz and does not change the 1e.8000 register, leaving SerDes + * at 3.125 GHz. + * To avoid this, change PCS mode back to 2500base-x when disabling SerDes from + * 2500base-x mode. + */ +static int mv88e6393x_fix_2500basex_an(struct mv88e639x_pcs *mpcs, bool on) +{ + u16 reg; + int err; + + if (on) + reg = MV88E6393X_SERDES_POC_PCS_1000BASEX | + MV88E6393X_SERDES_POC_AN; + else + reg = MV88E6393X_SERDES_POC_PCS_2500BASEX; + + reg |= MV88E6393X_SERDES_POC_RESET; + + err = mv88e639x_modify(mpcs, MV88E6393X_SERDES_POC, + MV88E6393X_SERDES_POC_PCS_MASK | + MV88E6393X_SERDES_POC_AN | + MV88E6393X_SERDES_POC_RESET, reg); + if (err) + return err; + + return mdiodev_c45_write(&mpcs->mdio, MDIO_MMD_VEND1, 0x8000, 0x58); +} + +static int mv88e6393x_sgmii_apply_2500basex_an(struct mv88e639x_pcs *mpcs, + phy_interface_t interface, + bool enable) +{ + int err; + + if (interface != PHY_INTERFACE_MODE_2500BASEX) + return 0; + + err = mv88e6393x_fix_2500basex_an(mpcs, enable); + if (err) + dev_err(mpcs->mdio.dev.parent, + "failed to %s 2500basex fix: %pe\n", + enable ? "enable" : "disable", ERR_PTR(err)); + + return err; +} + +static void mv88e6393x_sgmii_pcs_disable(struct phylink_pcs *pcs) +{ + struct mv88e639x_pcs *mpcs = sgmii_pcs_to_mv88e639x_pcs(pcs); + + mv88e639x_sgmii_pcs_disable(pcs); + mv88e6393x_power_lane(mpcs, false); + mv88e6393x_sgmii_apply_2500basex_an(mpcs, mpcs->interface, false); +} + +static void mv88e6393x_sgmii_pcs_pre_config(struct phylink_pcs *pcs, + phy_interface_t interface) +{ + struct mv88e639x_pcs *mpcs = sgmii_pcs_to_mv88e639x_pcs(pcs); + + mv88e639x_sgmii_pcs_pre_config(pcs, interface); + mv88e6393x_power_lane(mpcs, false); + mv88e6393x_sgmii_apply_2500basex_an(mpcs, mpcs->interface, false); +} + +static int mv88e6393x_sgmii_pcs_post_config(struct phylink_pcs *pcs, + phy_interface_t interface) +{ + struct mv88e639x_pcs *mpcs = sgmii_pcs_to_mv88e639x_pcs(pcs); + int err; + + err = mv88e6393x_erratum_4_8(mpcs); + if (err) + return err; + + err = mv88e6393x_sgmii_apply_2500basex_an(mpcs, interface, true); + if (err) + return err; + + err = mv88e6393x_power_lane(mpcs, true); + if (err) + return err; + + return mv88e639x_sgmii_pcs_post_config(pcs, interface); +} + +static const struct phylink_pcs_ops mv88e6393x_sgmii_pcs_ops = { + .pcs_enable = mv88e639x_sgmii_pcs_enable, + .pcs_disable = mv88e6393x_sgmii_pcs_disable, + .pcs_pre_config = mv88e6393x_sgmii_pcs_pre_config, + .pcs_post_config = mv88e6393x_sgmii_pcs_post_config, + .pcs_get_state = mv88e639x_sgmii_pcs_get_state, + .pcs_an_restart = mv88e639x_sgmii_pcs_an_restart, + .pcs_config = mv88e639x_sgmii_pcs_config, + .pcs_link_up = mv88e639x_sgmii_pcs_link_up, +}; + +static irqreturn_t mv88e6393x_xg_handle_irq(struct mv88e639x_pcs *mpcs) +{ + u16 int_status, stat1; + bool link_down; + int err; + + err = mv88e639x_read(mpcs, MV88E6393X_10G_INT_STATUS, &int_status); + if (err) + return IRQ_NONE; + + if (int_status & MV88E6393X_10G_INT_LINK_CHANGE) { + err = mv88e639x_read(mpcs, MV88E6390_10G_STAT1, &stat1); + if (err) + return IRQ_NONE; + + link_down = !(stat1 & MDIO_STAT1_LSTATUS); + + phylink_pcs_change(&mpcs->xg_pcs, !link_down); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int mv88e6393x_xg_control_irq(struct mv88e639x_pcs *mpcs, bool enable) +{ + u16 val = 0; + + if (enable) + val = MV88E6393X_10G_INT_LINK_CHANGE; + + return mv88e639x_modify(mpcs, MV88E6393X_10G_INT_ENABLE, + MV88E6393X_10G_INT_LINK_CHANGE, val); +} + +static int mv88e6393x_xg_pcs_enable(struct phylink_pcs *pcs) +{ + struct mv88e639x_pcs *mpcs = xg_pcs_to_mv88e639x_pcs(pcs); + + mpcs->handle_irq = mv88e6393x_xg_handle_irq; + + return mv88e6393x_xg_control_irq(mpcs, !!mpcs->irq); +} + +static void mv88e6393x_xg_pcs_disable(struct phylink_pcs *pcs) +{ + struct mv88e639x_pcs *mpcs = xg_pcs_to_mv88e639x_pcs(pcs); + + mv88e6393x_xg_control_irq(mpcs, false); + mv88e639x_xg_pcs_disable(mpcs); + mv88e6393x_power_lane(mpcs, false); +} + +/* The PCS has to be powered down while CMODE is changed */ +static void mv88e6393x_xg_pcs_pre_config(struct phylink_pcs *pcs, + phy_interface_t interface) +{ + struct mv88e639x_pcs *mpcs = xg_pcs_to_mv88e639x_pcs(pcs); + + mv88e639x_xg_pcs_disable(mpcs); + mv88e6393x_power_lane(mpcs, false); +} + +static int mv88e6393x_xg_pcs_post_config(struct phylink_pcs *pcs, + phy_interface_t interface) +{ + struct mv88e639x_pcs *mpcs = xg_pcs_to_mv88e639x_pcs(pcs); + int err; + + if (interface == PHY_INTERFACE_MODE_10GBASER) { + err = mv88e6393x_erratum_5_2(mpcs); + if (err) + return err; + } + + err = mv88e6393x_power_lane(mpcs, true); + if (err) + return err; + + return mv88e639x_xg_pcs_enable(mpcs); +} + +static const struct phylink_pcs_ops mv88e6393x_xg_pcs_ops = { + .pcs_enable = mv88e6393x_xg_pcs_enable, + .pcs_disable = mv88e6393x_xg_pcs_disable, + .pcs_pre_config = mv88e6393x_xg_pcs_pre_config, + .pcs_post_config = mv88e6393x_xg_pcs_post_config, + .pcs_get_state = mv88e639x_xg_pcs_get_state, + .pcs_config = mv88e639x_xg_pcs_config, +}; + +static int mv88e6393x_pcs_init(struct mv88e6xxx_chip *chip, int port) +{ + struct mv88e639x_pcs *mpcs; + struct mii_bus *bus; + struct device *dev; + int lane, err; + + lane = mv88e6xxx_serdes_get_lane(chip, port); + if (lane < 0) + return 0; + + bus = mv88e6xxx_default_mdio_bus(chip); + dev = chip->dev; + + mpcs = mv88e639x_pcs_alloc(dev, bus, lane, port); + if (!mpcs) + return -ENOMEM; + + mpcs->sgmii_pcs.ops = &mv88e6393x_sgmii_pcs_ops; + mpcs->sgmii_pcs.neg_mode = true; + mpcs->xg_pcs.ops = &mv88e6393x_xg_pcs_ops; + mpcs->xg_pcs.neg_mode = true; + mpcs->supports_5g = true; + + err = mv88e6393x_erratum_4_6(mpcs); + if (err) + goto err_free; + + err = mv88e639x_pcs_setup_irq(mpcs, chip, port); + if (err) + goto err_free; + + chip->ports[port].pcs_private = mpcs; + + return 0; + +err_free: + kfree(mpcs); + return err; +} + +const struct mv88e6xxx_pcs_ops mv88e6393x_pcs_ops = { + .pcs_init = mv88e6393x_pcs_init, + .pcs_teardown = mv88e639x_pcs_teardown, + .pcs_select = mv88e639x_pcs_select, +}; diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index dd66ec902d4c..5394a8cf7bf1 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -524,7 +524,6 @@ static int mv88e6xxx_port_set_cmode(struct mv88e6xxx_chip *chip, int port, phy_interface_t mode, bool force) { u16 cmode; - int lane; u16 reg; int err; @@ -577,19 +576,6 @@ static int mv88e6xxx_port_set_cmode(struct mv88e6xxx_chip *chip, int port, if (cmode == chip->ports[port].cmode && !force) return 0; - lane = mv88e6xxx_serdes_get_lane(chip, port); - if (lane >= 0) { - if (chip->ports[port].serdes_irq) { - err = mv88e6xxx_serdes_irq_disable(chip, port, lane); - if (err) - return err; - } - - err = mv88e6xxx_serdes_power_down(chip, port, lane); - if (err) - return err; - } - chip->ports[port].cmode = 0; if (cmode) { @@ -605,22 +591,6 @@ static int mv88e6xxx_port_set_cmode(struct mv88e6xxx_chip *chip, int port, return err; chip->ports[port].cmode = cmode; - - lane = mv88e6xxx_serdes_get_lane(chip, port); - if (lane == -ENODEV) - return 0; - if (lane < 0) - return lane; - - err = mv88e6xxx_serdes_power_up(chip, port, lane); - if (err) - return err; - - if (chip->ports[port].serdes_irq) { - err = mv88e6xxx_serdes_irq_enable(chip, port, lane); - if (err) - return err; - } } return 0; diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c index 80167d53212f..3b4b42651fa3 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.c +++ b/drivers/net/dsa/mv88e6xxx/serdes.c @@ -39,15 +39,8 @@ static int mv88e6390_serdes_read(struct mv88e6xxx_chip *chip, return mv88e6xxx_phy_read_c45(chip, lane, device, reg, val); } -static int mv88e6390_serdes_write(struct mv88e6xxx_chip *chip, - int lane, int device, int reg, u16 val) -{ - return mv88e6xxx_phy_write_c45(chip, lane, device, reg, val); -} - -static int mv88e6xxx_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, - u16 bmsr, u16 lpa, u16 status, - struct phylink_link_state *state) +int mv88e6xxx_pcs_decode_state(struct device *dev, u16 bmsr, u16 lpa, + u16 status, struct phylink_link_state *state) { state->link = false; @@ -88,7 +81,7 @@ static int mv88e6xxx_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, state->speed = SPEED_10; break; default: - dev_err(chip->dev, "invalid PHY speed\n"); + dev_err(dev, "invalid PHY speed\n"); return -EINVAL; } } else if (state->link && @@ -117,160 +110,6 @@ static int mv88e6xxx_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, return 0; } -int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane, - bool up) -{ - u16 val, new_val; - int err; - - err = mv88e6352_serdes_read(chip, MII_BMCR, &val); - if (err) - return err; - - if (up) - new_val = val & ~BMCR_PDOWN; - else - new_val = val | BMCR_PDOWN; - - if (val != new_val) - err = mv88e6352_serdes_write(chip, MII_BMCR, new_val); - - return err; -} - -int mv88e6352_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port, - int lane, unsigned int mode, - phy_interface_t interface, - const unsigned long *advertise) -{ - u16 adv, bmcr, val; - bool changed; - int err; - - switch (interface) { - case PHY_INTERFACE_MODE_SGMII: - adv = 0x0001; - break; - - case PHY_INTERFACE_MODE_1000BASEX: - adv = linkmode_adv_to_mii_adv_x(advertise, - ETHTOOL_LINK_MODE_1000baseX_Full_BIT); - break; - - default: - return 0; - } - - err = mv88e6352_serdes_read(chip, MII_ADVERTISE, &val); - if (err) - return err; - - changed = val != adv; - if (changed) { - err = mv88e6352_serdes_write(chip, MII_ADVERTISE, adv); - if (err) - return err; - } - - err = mv88e6352_serdes_read(chip, MII_BMCR, &val); - if (err) - return err; - - if (phylink_autoneg_inband(mode)) - bmcr = val | BMCR_ANENABLE; - else - bmcr = val & ~BMCR_ANENABLE; - - if (bmcr == val) - return changed; - - return mv88e6352_serdes_write(chip, MII_BMCR, bmcr); -} - -int mv88e6352_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port, - int lane, struct phylink_link_state *state) -{ - u16 bmsr, lpa, status; - int err; - - err = mv88e6352_serdes_read(chip, MII_BMSR, &bmsr); - if (err) { - dev_err(chip->dev, "can't read Serdes PHY BMSR: %d\n", err); - return err; - } - - err = mv88e6352_serdes_read(chip, 0x11, &status); - if (err) { - dev_err(chip->dev, "can't read Serdes PHY status: %d\n", err); - return err; - } - - err = mv88e6352_serdes_read(chip, MII_LPA, &lpa); - if (err) { - dev_err(chip->dev, "can't read Serdes PHY LPA: %d\n", err); - return err; - } - - return mv88e6xxx_serdes_pcs_get_state(chip, bmsr, lpa, status, state); -} - -int mv88e6352_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port, - int lane) -{ - u16 bmcr; - int err; - - err = mv88e6352_serdes_read(chip, MII_BMCR, &bmcr); - if (err) - return err; - - return mv88e6352_serdes_write(chip, MII_BMCR, bmcr | BMCR_ANRESTART); -} - -int mv88e6352_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port, - int lane, int speed, int duplex) -{ - u16 val, bmcr; - int err; - - err = mv88e6352_serdes_read(chip, MII_BMCR, &val); - if (err) - return err; - - bmcr = val & ~(BMCR_SPEED100 | BMCR_FULLDPLX | BMCR_SPEED1000); - switch (speed) { - case SPEED_1000: - bmcr |= BMCR_SPEED1000; - break; - case SPEED_100: - bmcr |= BMCR_SPEED100; - break; - case SPEED_10: - break; - } - - if (duplex == DUPLEX_FULL) - bmcr |= BMCR_FULLDPLX; - - if (bmcr == val) - return 0; - - return mv88e6352_serdes_write(chip, MII_BMCR, bmcr); -} - -int mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) -{ - u8 cmode = chip->ports[port].cmode; - int lane = -ENODEV; - - if ((cmode == MV88E6XXX_PORT_STS_CMODE_100BASEX) || - (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX) || - (cmode == MV88E6XXX_PORT_STS_CMODE_SGMII)) - lane = 0xff; /* Unused */ - - return lane; -} - struct mv88e6352_serdes_hw_stat { char string[ETH_GSTRING_LEN]; int sizeof_stat; @@ -363,51 +202,6 @@ int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port, return ARRAY_SIZE(mv88e6352_serdes_hw_stats); } -static void mv88e6352_serdes_irq_link(struct mv88e6xxx_chip *chip, int port) -{ - u16 bmsr; - int err; - - /* If the link has dropped, we want to know about it. */ - err = mv88e6352_serdes_read(chip, MII_BMSR, &bmsr); - if (err) { - dev_err(chip->dev, "can't read Serdes BMSR: %d\n", err); - return; - } - - dsa_port_phylink_mac_change(chip->ds, port, !!(bmsr & BMSR_LSTATUS)); -} - -irqreturn_t mv88e6352_serdes_irq_status(struct mv88e6xxx_chip *chip, int port, - int lane) -{ - irqreturn_t ret = IRQ_NONE; - u16 status; - int err; - - err = mv88e6352_serdes_read(chip, MV88E6352_SERDES_INT_STATUS, &status); - if (err) - return ret; - - if (status & MV88E6352_SERDES_INT_LINK_CHANGE) { - ret = IRQ_HANDLED; - mv88e6352_serdes_irq_link(chip, port); - } - - return ret; -} - -int mv88e6352_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, int lane, - bool enable) -{ - u16 val = 0; - - if (enable) - val |= MV88E6352_SERDES_INT_LINK_CHANGE; - - return mv88e6352_serdes_write(chip, MV88E6352_SERDES_INT_ENABLE, val); -} - unsigned int mv88e6352_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port) { return irq_find_mapping(chip->g2_irq.domain, MV88E6352_SERDES_IRQ); @@ -461,115 +255,6 @@ int mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) return lane; } -int mv88e6185_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane, - bool up) -{ - /* The serdes power can't be controlled on this switch chip but we need - * to supply this function to avoid returning -EOPNOTSUPP in - * mv88e6xxx_serdes_power_up/mv88e6xxx_serdes_power_down - */ - return 0; -} - -int mv88e6185_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) -{ - /* There are no configurable serdes lanes on this switch chip but we - * need to return a non-negative lane number so that callers of - * mv88e6xxx_serdes_get_lane() know this is a serdes port. - */ - switch (chip->ports[port].cmode) { - case MV88E6185_PORT_STS_CMODE_SERDES: - case MV88E6185_PORT_STS_CMODE_1000BASE_X: - return 0; - default: - return -ENODEV; - } -} - -int mv88e6185_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port, - int lane, struct phylink_link_state *state) -{ - int err; - u16 status; - - err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &status); - if (err) - return err; - - state->link = !!(status & MV88E6XXX_PORT_STS_LINK); - - if (state->link) { - state->duplex = status & MV88E6XXX_PORT_STS_DUPLEX ? DUPLEX_FULL : DUPLEX_HALF; - - switch (status & MV88E6XXX_PORT_STS_SPEED_MASK) { - case MV88E6XXX_PORT_STS_SPEED_1000: - state->speed = SPEED_1000; - break; - case MV88E6XXX_PORT_STS_SPEED_100: - state->speed = SPEED_100; - break; - case MV88E6XXX_PORT_STS_SPEED_10: - state->speed = SPEED_10; - break; - default: - dev_err(chip->dev, "invalid PHY speed\n"); - return -EINVAL; - } - } else { - state->duplex = DUPLEX_UNKNOWN; - state->speed = SPEED_UNKNOWN; - } - - return 0; -} - -int mv88e6097_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, int lane, - bool enable) -{ - u8 cmode = chip->ports[port].cmode; - - /* The serdes interrupts are enabled in the G2_INT_MASK register. We - * need to return 0 to avoid returning -EOPNOTSUPP in - * mv88e6xxx_serdes_irq_enable/mv88e6xxx_serdes_irq_disable - */ - switch (cmode) { - case MV88E6185_PORT_STS_CMODE_SERDES: - case MV88E6185_PORT_STS_CMODE_1000BASE_X: - return 0; - } - - return -EOPNOTSUPP; -} - -static void mv88e6097_serdes_irq_link(struct mv88e6xxx_chip *chip, int port) -{ - u16 status; - int err; - - err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &status); - if (err) { - dev_err(chip->dev, "can't read port status: %d\n", err); - return; - } - - dsa_port_phylink_mac_change(chip->ds, port, !!(status & MV88E6XXX_PORT_STS_LINK)); -} - -irqreturn_t mv88e6097_serdes_irq_status(struct mv88e6xxx_chip *chip, int port, - int lane) -{ - u8 cmode = chip->ports[port].cmode; - - switch (cmode) { - case MV88E6185_PORT_STS_CMODE_SERDES: - case MV88E6185_PORT_STS_CMODE_1000BASE_X: - mv88e6097_serdes_irq_link(chip, port); - return IRQ_HANDLED; - } - - return IRQ_NONE; -} - int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) { u8 cmode = chip->ports[port].cmode; @@ -690,57 +375,6 @@ int mv88e6393x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) return lane; } -/* Set power up/down for 10GBASE-R and 10GBASE-X4/X2 */ -static int mv88e6390_serdes_power_10g(struct mv88e6xxx_chip *chip, int lane, - bool up) -{ - u16 val, new_val; - int err; - - err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, - MV88E6390_10G_CTRL1, &val); - - if (err) - return err; - - if (up) - new_val = val & ~(MDIO_CTRL1_RESET | - MDIO_PCS_CTRL1_LOOPBACK | - MDIO_CTRL1_LPOWER); - else - new_val = val | MDIO_CTRL1_LPOWER; - - if (val != new_val) - err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, - MV88E6390_10G_CTRL1, new_val); - - return err; -} - -/* Set power up/down for SGMII and 1000Base-X */ -static int mv88e6390_serdes_power_sgmii(struct mv88e6xxx_chip *chip, int lane, - bool up) -{ - u16 val, new_val; - int err; - - err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, - MV88E6390_SGMII_BMCR, &val); - if (err) - return err; - - if (up) - new_val = val & ~(BMCR_RESET | BMCR_LOOPBACK | BMCR_PDOWN); - else - new_val = val | BMCR_PDOWN; - - if (val != new_val) - err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, - MV88E6390_SGMII_BMCR, new_val); - - return err; -} - struct mv88e6390_serdes_hw_stat { char string[ETH_GSTRING_LEN]; int reg; @@ -814,484 +448,6 @@ int mv88e6390_serdes_get_stats(struct mv88e6xxx_chip *chip, int port, return ARRAY_SIZE(mv88e6390_serdes_hw_stats); } -static int mv88e6390_serdes_enable_checker(struct mv88e6xxx_chip *chip, int lane) -{ - u16 reg; - int err; - - err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, - MV88E6390_PG_CONTROL, ®); - if (err) - return err; - - reg |= MV88E6390_PG_CONTROL_ENABLE_PC; - return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, - MV88E6390_PG_CONTROL, reg); -} - -int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane, - bool up) -{ - u8 cmode = chip->ports[port].cmode; - int err; - - switch (cmode) { - case MV88E6XXX_PORT_STS_CMODE_SGMII: - case MV88E6XXX_PORT_STS_CMODE_1000BASEX: - case MV88E6XXX_PORT_STS_CMODE_2500BASEX: - err = mv88e6390_serdes_power_sgmii(chip, lane, up); - break; - case MV88E6XXX_PORT_STS_CMODE_XAUI: - case MV88E6XXX_PORT_STS_CMODE_RXAUI: - err = mv88e6390_serdes_power_10g(chip, lane, up); - break; - default: - err = -EINVAL; - break; - } - - if (!err && up) - err = mv88e6390_serdes_enable_checker(chip, lane); - - return err; -} - -int mv88e6390_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port, - int lane, unsigned int mode, - phy_interface_t interface, - const unsigned long *advertise) -{ - u16 val, bmcr, adv; - bool changed; - int err; - - switch (interface) { - case PHY_INTERFACE_MODE_SGMII: - adv = 0x0001; - break; - - case PHY_INTERFACE_MODE_1000BASEX: - adv = linkmode_adv_to_mii_adv_x(advertise, - ETHTOOL_LINK_MODE_1000baseX_Full_BIT); - break; - - case PHY_INTERFACE_MODE_2500BASEX: - adv = linkmode_adv_to_mii_adv_x(advertise, - ETHTOOL_LINK_MODE_2500baseX_Full_BIT); - break; - - default: - return 0; - } - - err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, - MV88E6390_SGMII_ADVERTISE, &val); - if (err) - return err; - - changed = val != adv; - if (changed) { - err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, - MV88E6390_SGMII_ADVERTISE, adv); - if (err) - return err; - } - - err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, - MV88E6390_SGMII_BMCR, &val); - if (err) - return err; - - if (phylink_autoneg_inband(mode)) - bmcr = val | BMCR_ANENABLE; - else - bmcr = val & ~BMCR_ANENABLE; - - /* setting ANENABLE triggers a restart of negotiation */ - if (bmcr == val) - return changed; - - return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, - MV88E6390_SGMII_BMCR, bmcr); -} - -static int mv88e6390_serdes_pcs_get_state_sgmii(struct mv88e6xxx_chip *chip, - int port, int lane, struct phylink_link_state *state) -{ - u16 bmsr, lpa, status; - int err; - - err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, - MV88E6390_SGMII_BMSR, &bmsr); - if (err) { - dev_err(chip->dev, "can't read Serdes PHY BMSR: %d\n", err); - return err; - } - - err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, - MV88E6390_SGMII_PHY_STATUS, &status); - if (err) { - dev_err(chip->dev, "can't read Serdes PHY status: %d\n", err); - return err; - } - - err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, - MV88E6390_SGMII_LPA, &lpa); - if (err) { - dev_err(chip->dev, "can't read Serdes PHY LPA: %d\n", err); - return err; - } - - return mv88e6xxx_serdes_pcs_get_state(chip, bmsr, lpa, status, state); -} - -static int mv88e6390_serdes_pcs_get_state_10g(struct mv88e6xxx_chip *chip, - int port, int lane, struct phylink_link_state *state) -{ - u16 status; - int err; - - err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, - MV88E6390_10G_STAT1, &status); - if (err) - return err; - - state->link = !!(status & MDIO_STAT1_LSTATUS); - if (state->link) { - state->speed = SPEED_10000; - state->duplex = DUPLEX_FULL; - } - - return 0; -} - -static int mv88e6393x_serdes_pcs_get_state_10g(struct mv88e6xxx_chip *chip, - int port, int lane, - struct phylink_link_state *state) -{ - u16 status; - int err; - - err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, - MV88E6390_10G_STAT1, &status); - if (err) - return err; - - state->link = !!(status & MDIO_STAT1_LSTATUS); - if (state->link) { - if (state->interface == PHY_INTERFACE_MODE_5GBASER) - state->speed = SPEED_5000; - else - state->speed = SPEED_10000; - state->duplex = DUPLEX_FULL; - } - return 0; -} - -/* USXGMII registers for Marvell switch 88e639x are undocumented and this function is based - * on some educated guesses. It appears that there are no status bits related to - * autonegotiation complete or flow control. - */ -static int mv88e639x_serdes_pcs_get_state_usxgmii(struct mv88e6xxx_chip *chip, - int port, int lane, - struct phylink_link_state *state) -{ - u16 status, lp_status; - int err; - - err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, - MV88E6390_USXGMII_PHY_STATUS, &status); - if (err) { - dev_err(chip->dev, "can't read Serdes USXGMII PHY status: %d\n", err); - return err; - } - dev_dbg(chip->dev, "USXGMII PHY status: 0x%x\n", status); - - state->link = !!(status & MDIO_USXGMII_LINK); - state->an_complete = state->link; - - if (state->link) { - err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, - MV88E6390_USXGMII_LP_STATUS, &lp_status); - if (err) { - dev_err(chip->dev, "can't read Serdes USXGMII LP status: %d\n", err); - return err; - } - dev_dbg(chip->dev, "USXGMII LP status: 0x%x\n", lp_status); - /* lp_status appears to include the "link" bit as per USXGMII spec. */ - phylink_decode_usxgmii_word(state, lp_status); - } - return 0; -} - -int mv88e6390_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port, - int lane, struct phylink_link_state *state) -{ - switch (state->interface) { - case PHY_INTERFACE_MODE_SGMII: - case PHY_INTERFACE_MODE_1000BASEX: - case PHY_INTERFACE_MODE_2500BASEX: - return mv88e6390_serdes_pcs_get_state_sgmii(chip, port, lane, - state); - case PHY_INTERFACE_MODE_XAUI: - case PHY_INTERFACE_MODE_RXAUI: - return mv88e6390_serdes_pcs_get_state_10g(chip, port, lane, - state); - - default: - return -EOPNOTSUPP; - } -} - -int mv88e6393x_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port, - int lane, struct phylink_link_state *state) -{ - switch (state->interface) { - case PHY_INTERFACE_MODE_SGMII: - case PHY_INTERFACE_MODE_1000BASEX: - case PHY_INTERFACE_MODE_2500BASEX: - return mv88e6390_serdes_pcs_get_state_sgmii(chip, port, lane, - state); - case PHY_INTERFACE_MODE_5GBASER: - case PHY_INTERFACE_MODE_10GBASER: - return mv88e6393x_serdes_pcs_get_state_10g(chip, port, lane, - state); - case PHY_INTERFACE_MODE_USXGMII: - return mv88e639x_serdes_pcs_get_state_usxgmii(chip, port, lane, - state); - - default: - return -EOPNOTSUPP; - } -} - -int mv88e6390_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port, - int lane) -{ - u16 bmcr; - int err; - - err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, - MV88E6390_SGMII_BMCR, &bmcr); - if (err) - return err; - - return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, - MV88E6390_SGMII_BMCR, - bmcr | BMCR_ANRESTART); -} - -int mv88e6390_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port, - int lane, int speed, int duplex) -{ - u16 val, bmcr; - int err; - - err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, - MV88E6390_SGMII_BMCR, &val); - if (err) - return err; - - bmcr = val & ~(BMCR_SPEED100 | BMCR_FULLDPLX | BMCR_SPEED1000); - switch (speed) { - case SPEED_2500: - case SPEED_1000: - bmcr |= BMCR_SPEED1000; - break; - case SPEED_100: - bmcr |= BMCR_SPEED100; - break; - case SPEED_10: - break; - } - - if (duplex == DUPLEX_FULL) - bmcr |= BMCR_FULLDPLX; - - if (bmcr == val) - return 0; - - return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, - MV88E6390_SGMII_BMCR, bmcr); -} - -static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip, - int port, int lane) -{ - u16 bmsr; - int err; - - /* If the link has dropped, we want to know about it. */ - err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, - MV88E6390_SGMII_BMSR, &bmsr); - if (err) { - dev_err(chip->dev, "can't read Serdes BMSR: %d\n", err); - return; - } - - dsa_port_phylink_mac_change(chip->ds, port, !!(bmsr & BMSR_LSTATUS)); -} - -static void mv88e6393x_serdes_irq_link_10g(struct mv88e6xxx_chip *chip, - int port, u8 lane) -{ - u16 status; - int err; - - /* If the link has dropped, we want to know about it. */ - err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, - MV88E6390_10G_STAT1, &status); - if (err) { - dev_err(chip->dev, "can't read Serdes STAT1: %d\n", err); - return; - } - - dsa_port_phylink_mac_change(chip->ds, port, !!(status & MDIO_STAT1_LSTATUS)); -} - -static int mv88e6390_serdes_irq_enable_sgmii(struct mv88e6xxx_chip *chip, - int lane, bool enable) -{ - u16 val = 0; - - if (enable) - val |= MV88E6390_SGMII_INT_LINK_DOWN | - MV88E6390_SGMII_INT_LINK_UP; - - return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, - MV88E6390_SGMII_INT_ENABLE, val); -} - -int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, int lane, - bool enable) -{ - u8 cmode = chip->ports[port].cmode; - - switch (cmode) { - case MV88E6XXX_PORT_STS_CMODE_SGMII: - case MV88E6XXX_PORT_STS_CMODE_1000BASEX: - case MV88E6XXX_PORT_STS_CMODE_2500BASEX: - return mv88e6390_serdes_irq_enable_sgmii(chip, lane, enable); - } - - return 0; -} - -static int mv88e6390_serdes_irq_status_sgmii(struct mv88e6xxx_chip *chip, - int lane, u16 *status) -{ - int err; - - err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, - MV88E6390_SGMII_INT_STATUS, status); - - return err; -} - -static int mv88e6393x_serdes_irq_enable_10g(struct mv88e6xxx_chip *chip, - u8 lane, bool enable) -{ - u16 val = 0; - - if (enable) - val |= MV88E6393X_10G_INT_LINK_CHANGE; - - return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, - MV88E6393X_10G_INT_ENABLE, val); -} - -int mv88e6393x_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, - int lane, bool enable) -{ - u8 cmode = chip->ports[port].cmode; - - switch (cmode) { - case MV88E6XXX_PORT_STS_CMODE_SGMII: - case MV88E6XXX_PORT_STS_CMODE_1000BASEX: - case MV88E6XXX_PORT_STS_CMODE_2500BASEX: - return mv88e6390_serdes_irq_enable_sgmii(chip, lane, enable); - case MV88E6393X_PORT_STS_CMODE_5GBASER: - case MV88E6393X_PORT_STS_CMODE_10GBASER: - case MV88E6393X_PORT_STS_CMODE_USXGMII: - return mv88e6393x_serdes_irq_enable_10g(chip, lane, enable); - } - - return 0; -} - -static int mv88e6393x_serdes_irq_status_10g(struct mv88e6xxx_chip *chip, - u8 lane, u16 *status) -{ - int err; - - err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, - MV88E6393X_10G_INT_STATUS, status); - - return err; -} - -irqreturn_t mv88e6393x_serdes_irq_status(struct mv88e6xxx_chip *chip, int port, - int lane) -{ - u8 cmode = chip->ports[port].cmode; - irqreturn_t ret = IRQ_NONE; - u16 status; - int err; - - switch (cmode) { - case MV88E6XXX_PORT_STS_CMODE_SGMII: - case MV88E6XXX_PORT_STS_CMODE_1000BASEX: - case MV88E6XXX_PORT_STS_CMODE_2500BASEX: - err = mv88e6390_serdes_irq_status_sgmii(chip, lane, &status); - if (err) - return ret; - if (status & (MV88E6390_SGMII_INT_LINK_DOWN | - MV88E6390_SGMII_INT_LINK_UP)) { - ret = IRQ_HANDLED; - mv88e6390_serdes_irq_link_sgmii(chip, port, lane); - } - break; - case MV88E6393X_PORT_STS_CMODE_5GBASER: - case MV88E6393X_PORT_STS_CMODE_10GBASER: - case MV88E6393X_PORT_STS_CMODE_USXGMII: - err = mv88e6393x_serdes_irq_status_10g(chip, lane, &status); - if (err) - return err; - if (status & MV88E6393X_10G_INT_LINK_CHANGE) { - ret = IRQ_HANDLED; - mv88e6393x_serdes_irq_link_10g(chip, port, lane); - } - break; - } - - return ret; -} - -irqreturn_t mv88e6390_serdes_irq_status(struct mv88e6xxx_chip *chip, int port, - int lane) -{ - u8 cmode = chip->ports[port].cmode; - irqreturn_t ret = IRQ_NONE; - u16 status; - int err; - - switch (cmode) { - case MV88E6XXX_PORT_STS_CMODE_SGMII: - case MV88E6XXX_PORT_STS_CMODE_1000BASEX: - case MV88E6XXX_PORT_STS_CMODE_2500BASEX: - err = mv88e6390_serdes_irq_status_sgmii(chip, lane, &status); - if (err) - return ret; - if (status & (MV88E6390_SGMII_INT_LINK_DOWN | - MV88E6390_SGMII_INT_LINK_UP)) { - ret = IRQ_HANDLED; - mv88e6390_serdes_irq_link_sgmii(chip, port, lane); - } - } - - return ret; -} - unsigned int mv88e6390_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port) { return irq_find_mapping(chip->g2_irq.domain, port); @@ -1390,259 +546,3 @@ int mv88e6352_serdes_set_tx_amplitude(struct mv88e6xxx_chip *chip, int port, return mv88e6352_serdes_write(chip, MV88E6352_SERDES_SPEC_CTRL2, ctrl); } - -static int mv88e6393x_serdes_power_lane(struct mv88e6xxx_chip *chip, int lane, - bool on) -{ - u16 reg; - int err; - - err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, - MV88E6393X_SERDES_CTRL1, ®); - if (err) - return err; - - if (on) - reg &= ~(MV88E6393X_SERDES_CTRL1_TX_PDOWN | - MV88E6393X_SERDES_CTRL1_RX_PDOWN); - else - reg |= MV88E6393X_SERDES_CTRL1_TX_PDOWN | - MV88E6393X_SERDES_CTRL1_RX_PDOWN; - - return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, - MV88E6393X_SERDES_CTRL1, reg); -} - -static int mv88e6393x_serdes_erratum_4_6(struct mv88e6xxx_chip *chip, int lane) -{ - u16 reg; - int err; - - /* mv88e6393x family errata 4.6: - * Cannot clear PwrDn bit on SERDES if device is configured CPU_MGD - * mode or P0_mode is configured for [x]MII. - * Workaround: Set SERDES register 4.F002 bit 5=0 and bit 15=1. - * - * It seems that after this workaround the SERDES is automatically - * powered up (the bit is cleared), so power it down. - */ - err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, - MV88E6393X_SERDES_POC, ®); - if (err) - return err; - - reg &= ~MV88E6393X_SERDES_POC_PDOWN; - reg |= MV88E6393X_SERDES_POC_RESET; - - err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, - MV88E6393X_SERDES_POC, reg); - if (err) - return err; - - err = mv88e6390_serdes_power_sgmii(chip, lane, false); - if (err) - return err; - - return mv88e6393x_serdes_power_lane(chip, lane, false); -} - -int mv88e6393x_serdes_setup_errata(struct mv88e6xxx_chip *chip) -{ - int err; - - err = mv88e6393x_serdes_erratum_4_6(chip, MV88E6393X_PORT0_LANE); - if (err) - return err; - - err = mv88e6393x_serdes_erratum_4_6(chip, MV88E6393X_PORT9_LANE); - if (err) - return err; - - return mv88e6393x_serdes_erratum_4_6(chip, MV88E6393X_PORT10_LANE); -} - -static int mv88e6393x_serdes_erratum_4_8(struct mv88e6xxx_chip *chip, int lane) -{ - u16 reg, pcs; - int err; - - /* mv88e6393x family errata 4.8: - * When a SERDES port is operating in 1000BASE-X or SGMII mode link may - * not come up after hardware reset or software reset of SERDES core. - * Workaround is to write SERDES register 4.F074.14=1 for only those - * modes and 0 in all other modes. - */ - err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, - MV88E6393X_SERDES_POC, &pcs); - if (err) - return err; - - pcs &= MV88E6393X_SERDES_POC_PCS_MASK; - - err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, - MV88E6393X_ERRATA_4_8_REG, ®); - if (err) - return err; - - if (pcs == MV88E6393X_SERDES_POC_PCS_1000BASEX || - pcs == MV88E6393X_SERDES_POC_PCS_SGMII_PHY || - pcs == MV88E6393X_SERDES_POC_PCS_SGMII_MAC) - reg |= MV88E6393X_ERRATA_4_8_BIT; - else - reg &= ~MV88E6393X_ERRATA_4_8_BIT; - - return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, - MV88E6393X_ERRATA_4_8_REG, reg); -} - -static int mv88e6393x_serdes_erratum_5_2(struct mv88e6xxx_chip *chip, int lane, - u8 cmode) -{ - static const struct { - u16 dev, reg, val, mask; - } fixes[] = { - { MDIO_MMD_VEND1, 0x8093, 0xcb5a, 0xffff }, - { MDIO_MMD_VEND1, 0x8171, 0x7088, 0xffff }, - { MDIO_MMD_VEND1, 0x80c9, 0x311a, 0xffff }, - { MDIO_MMD_VEND1, 0x80a2, 0x8000, 0xff7f }, - { MDIO_MMD_VEND1, 0x80a9, 0x0000, 0xfff0 }, - { MDIO_MMD_VEND1, 0x80a3, 0x0000, 0xf8ff }, - { MDIO_MMD_PHYXS, MV88E6393X_SERDES_POC, - MV88E6393X_SERDES_POC_RESET, MV88E6393X_SERDES_POC_RESET }, - }; - int err, i; - u16 reg; - - /* mv88e6393x family errata 5.2: - * For optimal signal integrity the following sequence should be applied - * to SERDES operating in 10G mode. These registers only apply to 10G - * operation and have no effect on other speeds. - */ - if (cmode != MV88E6393X_PORT_STS_CMODE_10GBASER && - cmode != MV88E6393X_PORT_STS_CMODE_USXGMII) - return 0; - - for (i = 0; i < ARRAY_SIZE(fixes); ++i) { - err = mv88e6390_serdes_read(chip, lane, fixes[i].dev, - fixes[i].reg, ®); - if (err) - return err; - - reg &= ~fixes[i].mask; - reg |= fixes[i].val; - - err = mv88e6390_serdes_write(chip, lane, fixes[i].dev, - fixes[i].reg, reg); - if (err) - return err; - } - - return 0; -} - -static int mv88e6393x_serdes_fix_2500basex_an(struct mv88e6xxx_chip *chip, - int lane, u8 cmode, bool on) -{ - u16 reg; - int err; - - if (cmode != MV88E6XXX_PORT_STS_CMODE_2500BASEX) - return 0; - - /* Inband AN is broken on Amethyst in 2500base-x mode when set by - * standard mechanism (via cmode). - * We can get around this by configuring the PCS mode to 1000base-x - * and then writing value 0x58 to register 1e.8000. (This must be done - * while SerDes receiver and transmitter are disabled, which is, when - * this function is called.) - * It seem that when we do this configuration to 2500base-x mode (by - * changing PCS mode to 1000base-x and frequency to 3.125 GHz from - * 1.25 GHz) and then configure to sgmii or 1000base-x, the device - * thinks that it already has SerDes at 1.25 GHz and does not change - * the 1e.8000 register, leaving SerDes at 3.125 GHz. - * To avoid this, change PCS mode back to 2500base-x when disabling - * SerDes from 2500base-x mode. - */ - err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, - MV88E6393X_SERDES_POC, ®); - if (err) - return err; - - reg &= ~(MV88E6393X_SERDES_POC_PCS_MASK | MV88E6393X_SERDES_POC_AN); - if (on) - reg |= MV88E6393X_SERDES_POC_PCS_1000BASEX | - MV88E6393X_SERDES_POC_AN; - else - reg |= MV88E6393X_SERDES_POC_PCS_2500BASEX; - reg |= MV88E6393X_SERDES_POC_RESET; - - err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, - MV88E6393X_SERDES_POC, reg); - if (err) - return err; - - err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_VEND1, 0x8000, 0x58); - if (err) - return err; - - return 0; -} - -int mv88e6393x_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane, - bool on) -{ - u8 cmode = chip->ports[port].cmode; - int err; - - if (port != 0 && port != 9 && port != 10) - return -EOPNOTSUPP; - - if (on) { - err = mv88e6393x_serdes_erratum_4_8(chip, lane); - if (err) - return err; - - err = mv88e6393x_serdes_erratum_5_2(chip, lane, cmode); - if (err) - return err; - - err = mv88e6393x_serdes_fix_2500basex_an(chip, lane, cmode, - true); - if (err) - return err; - - err = mv88e6393x_serdes_power_lane(chip, lane, true); - if (err) - return err; - } - - switch (cmode) { - case MV88E6XXX_PORT_STS_CMODE_SGMII: - case MV88E6XXX_PORT_STS_CMODE_1000BASEX: - case MV88E6XXX_PORT_STS_CMODE_2500BASEX: - err = mv88e6390_serdes_power_sgmii(chip, lane, on); - break; - case MV88E6393X_PORT_STS_CMODE_5GBASER: - case MV88E6393X_PORT_STS_CMODE_10GBASER: - case MV88E6393X_PORT_STS_CMODE_USXGMII: - err = mv88e6390_serdes_power_10g(chip, lane, on); - break; - default: - err = -EINVAL; - break; - } - - if (err) - return err; - - if (!on) { - err = mv88e6393x_serdes_power_lane(chip, lane, false); - if (err) - return err; - - err = mv88e6393x_serdes_fix_2500basex_an(chip, lane, cmode, - false); - } - - return err; -} diff --git a/drivers/net/dsa/mv88e6xxx/serdes.h b/drivers/net/dsa/mv88e6xxx/serdes.h index e245687ddb1d..aac95cab46e3 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.h +++ b/drivers/net/dsa/mv88e6xxx/serdes.h @@ -12,6 +12,8 @@ #include "chip.h" +struct phylink_link_state; + #define MV88E6352_ADDR_SERDES 0x0f #define MV88E6352_SERDES_PAGE_FIBER 0x01 #define MV88E6352_SERDES_IRQ 0x0b @@ -44,6 +46,10 @@ /* 10GBASE-R and 10GBASE-X4/X2 */ #define MV88E6390_10G_CTRL1 (0x1000 + MDIO_CTRL1) #define MV88E6390_10G_STAT1 (0x1000 + MDIO_STAT1) +#define MV88E6390_10G_INT_ENABLE 0x9001 +#define MV88E6390_10G_INT_LINK_DOWN BIT(3) +#define MV88E6390_10G_INT_LINK_UP BIT(2) +#define MV88E6390_10G_INT_STATUS 0x9003 #define MV88E6393X_10G_INT_ENABLE 0x9000 #define MV88E6393X_10G_INT_LINK_CHANGE BIT(2) #define MV88E6393X_10G_INT_STATUS 0x9001 @@ -107,65 +113,17 @@ #define MV88E6393X_ERRATA_4_8_REG 0xF074 #define MV88E6393X_ERRATA_4_8_BIT BIT(14) -int mv88e6185_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); +int mv88e6xxx_pcs_decode_state(struct device *dev, u16 bmsr, u16 lpa, + u16 status, struct phylink_link_state *state); + int mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); -int mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); int mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); int mv88e6393x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); -int mv88e6352_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port, - int lane, unsigned int mode, - phy_interface_t interface, - const unsigned long *advertise); -int mv88e6390_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port, - int lane, unsigned int mode, - phy_interface_t interface, - const unsigned long *advertise); -int mv88e6185_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port, - int lane, struct phylink_link_state *state); -int mv88e6352_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port, - int lane, struct phylink_link_state *state); -int mv88e6390_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port, - int lane, struct phylink_link_state *state); -int mv88e6393x_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port, - int lane, struct phylink_link_state *state); -int mv88e6352_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port, - int lane); -int mv88e6390_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port, - int lane); -int mv88e6352_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port, - int lane, int speed, int duplex); -int mv88e6390_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port, - int lane, int speed, int duplex); unsigned int mv88e6352_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port); unsigned int mv88e6390_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port); -int mv88e6185_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane, - bool up); -int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane, - bool on); -int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane, - bool on); -int mv88e6393x_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane, - bool on); -int mv88e6393x_serdes_setup_errata(struct mv88e6xxx_chip *chip); -int mv88e6097_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, int lane, - bool enable); -int mv88e6352_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, int lane, - bool enable); -int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, int lane, - bool enable); -int mv88e6393x_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, - int lane, bool enable); -irqreturn_t mv88e6097_serdes_irq_status(struct mv88e6xxx_chip *chip, int port, - int lane); -irqreturn_t mv88e6352_serdes_irq_status(struct mv88e6xxx_chip *chip, int port, - int lane); -irqreturn_t mv88e6390_serdes_irq_status(struct mv88e6xxx_chip *chip, int port, - int lane); -irqreturn_t mv88e6393x_serdes_irq_status(struct mv88e6xxx_chip *chip, int port, - int lane); int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port); int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip, int port, uint8_t *data); @@ -195,24 +153,6 @@ static inline int mv88e6xxx_serdes_get_lane(struct mv88e6xxx_chip *chip, return chip->info->ops->serdes_get_lane(chip, port); } -static inline int mv88e6xxx_serdes_power_up(struct mv88e6xxx_chip *chip, - int port, int lane) -{ - if (!chip->info->ops->serdes_power) - return -EOPNOTSUPP; - - return chip->info->ops->serdes_power(chip, port, lane, true); -} - -static inline int mv88e6xxx_serdes_power_down(struct mv88e6xxx_chip *chip, - int port, int lane) -{ - if (!chip->info->ops->serdes_power) - return -EOPNOTSUPP; - - return chip->info->ops->serdes_power(chip, port, lane, false); -} - static inline unsigned int mv88e6xxx_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port) { @@ -222,31 +162,9 @@ mv88e6xxx_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port) return chip->info->ops->serdes_irq_mapping(chip, port); } -static inline int mv88e6xxx_serdes_irq_enable(struct mv88e6xxx_chip *chip, - int port, int lane) -{ - if (!chip->info->ops->serdes_irq_enable) - return -EOPNOTSUPP; - - return chip->info->ops->serdes_irq_enable(chip, port, lane, true); -} - -static inline int mv88e6xxx_serdes_irq_disable(struct mv88e6xxx_chip *chip, - int port, int lane) -{ - if (!chip->info->ops->serdes_irq_enable) - return -EOPNOTSUPP; - - return chip->info->ops->serdes_irq_enable(chip, port, lane, false); -} - -static inline irqreturn_t -mv88e6xxx_serdes_irq_status(struct mv88e6xxx_chip *chip, int port, int lane) -{ - if (!chip->info->ops->serdes_irq_status) - return IRQ_NONE; - - return chip->info->ops->serdes_irq_status(chip, port, lane); -} +extern const struct mv88e6xxx_pcs_ops mv88e6185_pcs_ops; +extern const struct mv88e6xxx_pcs_ops mv88e6352_pcs_ops; +extern const struct mv88e6xxx_pcs_ops mv88e6390_pcs_ops; +extern const struct mv88e6xxx_pcs_ops mv88e6393x_pcs_ops; #endif diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index bef879c6d500..9a3e5ec16972 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -1042,12 +1042,6 @@ static void felix_phylink_get_caps(struct dsa_switch *ds, int port, { struct ocelot *ocelot = ds->priv; - /* This driver does not make use of the speed, duplex, pause or the - * advertisement in its mac_config, so it is safe to mark this driver - * as non-legacy. - */ - config->legacy_pre_march2020 = false; - config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000FD | MAC_2500FD; diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c index f16daa9b1765..3c5509e75a54 100644 --- a/drivers/net/dsa/ocelot/felix_vsc9959.c +++ b/drivers/net/dsa/ocelot/felix_vsc9959.c @@ -16,6 +16,7 @@ #include <net/pkt_sched.h> #include <linux/iopoll.h> #include <linux/mdio.h> +#include <linux/of.h> #include <linux/pci.h> #include <linux/time.h> #include "felix.h" @@ -1748,10 +1749,10 @@ static int vsc9959_stream_identify(struct flow_cls_offload *f, struct flow_dissector *dissector = rule->match.dissector; if (dissector->used_keys & - ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) | - BIT(FLOW_DISSECTOR_KEY_BASIC) | - BIT(FLOW_DISSECTOR_KEY_VLAN) | - BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS))) + ~(BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL) | + BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) | + BIT_ULL(FLOW_DISSECTOR_KEY_VLAN) | + BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS))) return -EOPNOTSUPP; if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c index 15003b2af264..8f912bda120b 100644 --- a/drivers/net/dsa/ocelot/seville_vsc9953.c +++ b/drivers/net/dsa/ocelot/seville_vsc9953.c @@ -2,13 +2,14 @@ /* Distributed Switch Architecture VSC9953 driver * Copyright (C) 2020, Maxim Kochetkov <fido_max@inbox.ru> */ +#include <linux/platform_device.h> #include <linux/types.h> #include <soc/mscc/ocelot_vcap.h> #include <soc/mscc/ocelot_sys.h> #include <soc/mscc/ocelot.h> #include <linux/mdio/mdio-mscc-miim.h> +#include <linux/mod_devicetable.h> #include <linux/of_mdio.h> -#include <linux/of_platform.h> #include <linux/pcs-lynx.h> #include <linux/dsa/ocelot.h> #include <linux/iopoll.h> diff --git a/drivers/net/dsa/qca/ar9331.c b/drivers/net/dsa/qca/ar9331.c index 3b0937031499..8d9d271ac3af 100644 --- a/drivers/net/dsa/qca/ar9331.c +++ b/drivers/net/dsa/qca/ar9331.c @@ -1012,7 +1012,7 @@ static const struct regmap_config ar9331_mdio_regmap_config = { .wr_table = &ar9331_register_set, .rd_table = &ar9331_register_set, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static struct regmap_bus ar9331_sw_bus = { diff --git a/drivers/net/dsa/qca/qca8k-8xxx.c b/drivers/net/dsa/qca/qca8k-8xxx.c index efe9380d4a15..de1dc22cf683 100644 --- a/drivers/net/dsa/qca/qca8k-8xxx.c +++ b/drivers/net/dsa/qca/qca8k-8xxx.c @@ -1400,8 +1400,6 @@ static void qca8k_phylink_get_caps(struct dsa_switch *ds, int port, config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000FD; - - config->legacy_pre_march2020 = false; } static void @@ -1758,11 +1756,52 @@ static int qca8k_connect_tag_protocol(struct dsa_switch *ds, return 0; } +static void qca8k_setup_hol_fixup(struct qca8k_priv *priv, int port) +{ + u32 mask; + + switch (port) { + /* The 2 CPU port and port 5 requires some different + * priority than any other ports. + */ + case 0: + case 5: + case 6: + mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) | + QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) | + QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x4) | + QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x4) | + QCA8K_PORT_HOL_CTRL0_EG_PRI4(0x6) | + QCA8K_PORT_HOL_CTRL0_EG_PRI5(0x8) | + QCA8K_PORT_HOL_CTRL0_EG_PORT(0x1e); + break; + default: + mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) | + QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) | + QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x6) | + QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x8) | + QCA8K_PORT_HOL_CTRL0_EG_PORT(0x19); + } + regmap_write(priv->regmap, QCA8K_REG_PORT_HOL_CTRL0(port), mask); + + mask = QCA8K_PORT_HOL_CTRL1_ING(0x6) | + QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | + QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | + QCA8K_PORT_HOL_CTRL1_WRED_EN; + regmap_update_bits(priv->regmap, QCA8K_REG_PORT_HOL_CTRL1(port), + QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK | + QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | + QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | + QCA8K_PORT_HOL_CTRL1_WRED_EN, + mask); +} + static int qca8k_setup(struct dsa_switch *ds) { struct qca8k_priv *priv = ds->priv; - int cpu_port, ret, i; + struct dsa_port *dp; + int cpu_port, ret; u32 mask; cpu_port = qca8k_find_cpu_port(ds); @@ -1817,27 +1856,27 @@ qca8k_setup(struct dsa_switch *ds) dev_warn(priv->dev, "mib init failed"); /* Initial setup of all ports */ - for (i = 0; i < QCA8K_NUM_PORTS; i++) { + dsa_switch_for_each_port(dp, ds) { /* Disable forwarding by default on all ports */ - ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), + ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(dp->index), QCA8K_PORT_LOOKUP_MEMBER, 0); if (ret) return ret; + } - /* Enable QCA header mode on all cpu ports */ - if (dsa_is_cpu_port(ds, i)) { - ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(i), - FIELD_PREP(QCA8K_PORT_HDR_CTRL_TX_MASK, QCA8K_PORT_HDR_CTRL_ALL) | - FIELD_PREP(QCA8K_PORT_HDR_CTRL_RX_MASK, QCA8K_PORT_HDR_CTRL_ALL)); - if (ret) { - dev_err(priv->dev, "failed enabling QCA header mode"); - return ret; - } + /* Disable MAC by default on all user ports */ + dsa_switch_for_each_user_port(dp, ds) + qca8k_port_set_status(priv, dp->index, 0); + + /* Enable QCA header mode on all cpu ports */ + dsa_switch_for_each_cpu_port(dp, ds) { + ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(dp->index), + FIELD_PREP(QCA8K_PORT_HDR_CTRL_TX_MASK, QCA8K_PORT_HDR_CTRL_ALL) | + FIELD_PREP(QCA8K_PORT_HDR_CTRL_RX_MASK, QCA8K_PORT_HDR_CTRL_ALL)); + if (ret) { + dev_err(priv->dev, "failed enabling QCA header mode on port %d", dp->index); + return ret; } - - /* Disable MAC by default on all user ports */ - if (dsa_is_user_port(ds, i)) - qca8k_port_set_status(priv, i, 0); } /* Forward all unknown frames to CPU port for Linux processing @@ -1852,92 +1891,55 @@ qca8k_setup(struct dsa_switch *ds) if (ret) return ret; + /* CPU port gets connected to all user ports of the switch */ + ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(cpu_port), + QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds)); + if (ret) + return ret; + /* Setup connection between CPU port & user ports - * Configure specific switch configuration for ports + * Individual user ports get connected to CPU port only */ - for (i = 0; i < QCA8K_NUM_PORTS; i++) { - /* CPU port gets connected to all user ports of the switch */ - if (dsa_is_cpu_port(ds, i)) { - ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), - QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds)); - if (ret) - return ret; - } + dsa_switch_for_each_user_port(dp, ds) { + u8 port = dp->index; - /* Individual user ports get connected to CPU port only */ - if (dsa_is_user_port(ds, i)) { - ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), - QCA8K_PORT_LOOKUP_MEMBER, - BIT(cpu_port)); - if (ret) - return ret; - - /* Enable ARP Auto-learning by default */ - ret = regmap_set_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i), - QCA8K_PORT_LOOKUP_LEARN); - if (ret) - return ret; - - /* For port based vlans to work we need to set the - * default egress vid - */ - ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i), - QCA8K_EGREES_VLAN_PORT_MASK(i), - QCA8K_EGREES_VLAN_PORT(i, QCA8K_PORT_VID_DEF)); - if (ret) - return ret; - - ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(i), - QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) | - QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF)); - if (ret) - return ret; - } + ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), + QCA8K_PORT_LOOKUP_MEMBER, + BIT(cpu_port)); + if (ret) + return ret; - /* The port 5 of the qca8337 have some problem in flood condition. The - * original legacy driver had some specific buffer and priority settings - * for the different port suggested by the QCA switch team. Add this - * missing settings to improve switch stability under load condition. - * This problem is limited to qca8337 and other qca8k switch are not affected. + ret = regmap_clear_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port), + QCA8K_PORT_LOOKUP_LEARN); + if (ret) + return ret; + + /* For port based vlans to work we need to set the + * default egress vid */ - if (priv->switch_id == QCA8K_ID_QCA8337) { - switch (i) { - /* The 2 CPU port and port 5 requires some different - * priority than any other ports. - */ - case 0: - case 5: - case 6: - mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) | - QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) | - QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x4) | - QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x4) | - QCA8K_PORT_HOL_CTRL0_EG_PRI4(0x6) | - QCA8K_PORT_HOL_CTRL0_EG_PRI5(0x8) | - QCA8K_PORT_HOL_CTRL0_EG_PORT(0x1e); - break; - default: - mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) | - QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) | - QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x6) | - QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x8) | - QCA8K_PORT_HOL_CTRL0_EG_PORT(0x19); - } - qca8k_write(priv, QCA8K_REG_PORT_HOL_CTRL0(i), mask); - - mask = QCA8K_PORT_HOL_CTRL1_ING(0x6) | - QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | - QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | - QCA8K_PORT_HOL_CTRL1_WRED_EN; - qca8k_rmw(priv, QCA8K_REG_PORT_HOL_CTRL1(i), - QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK | - QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | - QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | - QCA8K_PORT_HOL_CTRL1_WRED_EN, - mask); - } + ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port), + QCA8K_EGREES_VLAN_PORT_MASK(port), + QCA8K_EGREES_VLAN_PORT(port, QCA8K_PORT_VID_DEF)); + if (ret) + return ret; + + ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port), + QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) | + QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF)); + if (ret) + return ret; } + /* The port 5 of the qca8337 have some problem in flood condition. The + * original legacy driver had some specific buffer and priority settings + * for the different port suggested by the QCA switch team. Add this + * missing settings to improve switch stability under load condition. + * This problem is limited to qca8337 and other qca8k switch are not affected. + */ + if (priv->switch_id == QCA8K_ID_QCA8337) + dsa_switch_for_each_available_port(dp, ds) + qca8k_setup_hol_fixup(priv, dp->index); + /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */ if (priv->switch_id == QCA8K_ID_QCA8327) { mask = QCA8K_GLOBAL_FC_GOL_XON_THRES(288) | @@ -1980,6 +1982,8 @@ static const struct dsa_switch_ops qca8k_switch_ops = { .port_change_mtu = qca8k_port_change_mtu, .port_max_mtu = qca8k_port_max_mtu, .port_stp_state_set = qca8k_port_stp_state_set, + .port_pre_bridge_flags = qca8k_port_pre_bridge_flags, + .port_bridge_flags = qca8k_port_bridge_flags, .port_bridge_join = qca8k_port_bridge_join, .port_bridge_leave = qca8k_port_bridge_leave, .port_fast_age = qca8k_port_fast_age, diff --git a/drivers/net/dsa/qca/qca8k-common.c b/drivers/net/dsa/qca/qca8k-common.c index 13b8452ce5b2..fce04ce12cf9 100644 --- a/drivers/net/dsa/qca/qca8k-common.c +++ b/drivers/net/dsa/qca/qca8k-common.c @@ -565,9 +565,26 @@ int qca8k_get_mac_eee(struct dsa_switch *ds, int port, return 0; } +static int qca8k_port_configure_learning(struct dsa_switch *ds, int port, + bool learning) +{ + struct qca8k_priv *priv = ds->priv; + + if (learning) + return regmap_set_bits(priv->regmap, + QCA8K_PORT_LOOKUP_CTRL(port), + QCA8K_PORT_LOOKUP_LEARN); + else + return regmap_clear_bits(priv->regmap, + QCA8K_PORT_LOOKUP_CTRL(port), + QCA8K_PORT_LOOKUP_LEARN); +} + void qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) { + struct dsa_port *dp = dsa_to_port(ds, port); struct qca8k_priv *priv = ds->priv; + bool learning = false; u32 stp_state; switch (state) { @@ -582,8 +599,11 @@ void qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) break; case BR_STATE_LEARNING: stp_state = QCA8K_PORT_LOOKUP_STATE_LEARNING; + learning = dp->learning; break; case BR_STATE_FORWARDING: + learning = dp->learning; + fallthrough; default: stp_state = QCA8K_PORT_LOOKUP_STATE_FORWARD; break; @@ -591,6 +611,34 @@ void qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), QCA8K_PORT_LOOKUP_STATE_MASK, stp_state); + + qca8k_port_configure_learning(ds, port, learning); +} + +int qca8k_port_pre_bridge_flags(struct dsa_switch *ds, int port, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack) +{ + if (flags.mask & ~BR_LEARNING) + return -EINVAL; + + return 0; +} + +int qca8k_port_bridge_flags(struct dsa_switch *ds, int port, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack) +{ + int ret; + + if (flags.mask & BR_LEARNING) { + ret = qca8k_port_configure_learning(ds, port, + flags.val & BR_LEARNING); + if (ret) + return ret; + } + + return 0; } int qca8k_port_bridge_join(struct dsa_switch *ds, int port, diff --git a/drivers/net/dsa/qca/qca8k-leds.c b/drivers/net/dsa/qca/qca8k-leds.c index 1261e0bb21ef..e8c16e76e34b 100644 --- a/drivers/net/dsa/qca/qca8k-leds.c +++ b/drivers/net/dsa/qca/qca8k-leds.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 +#include <linux/property.h> #include <linux/regmap.h> #include <net/dsa.h> diff --git a/drivers/net/dsa/qca/qca8k.h b/drivers/net/dsa/qca/qca8k.h index c5cc8a172d65..8f88b7db384d 100644 --- a/drivers/net/dsa/qca/qca8k.h +++ b/drivers/net/dsa/qca/qca8k.h @@ -522,6 +522,12 @@ int qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e); /* Common bridge function */ void qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state); +int qca8k_port_pre_bridge_flags(struct dsa_switch *ds, int port, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack); +int qca8k_port_bridge_flags(struct dsa_switch *ds, int port, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack); int qca8k_port_bridge_join(struct dsa_switch *ds, int port, struct dsa_bridge bridge, bool *tx_fwd_offload, diff --git a/drivers/net/dsa/realtek/realtek-mdio.c b/drivers/net/dsa/realtek/realtek-mdio.c index 5a8fe707ca25..292e6d087e8b 100644 --- a/drivers/net/dsa/realtek/realtek-mdio.c +++ b/drivers/net/dsa/realtek/realtek-mdio.c @@ -20,7 +20,7 @@ */ #include <linux/module.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/overflow.h> #include <linux/regmap.h> @@ -276,7 +276,7 @@ MODULE_DEVICE_TABLE(of, realtek_mdio_of_match); static struct mdio_driver realtek_mdio_driver = { .mdiodrv.driver = { .name = "realtek-mdio", - .of_match_table = of_match_ptr(realtek_mdio_of_match), + .of_match_table = realtek_mdio_of_match, }, .probe = realtek_mdio_probe, .remove = realtek_mdio_remove, diff --git a/drivers/net/dsa/realtek/realtek-smi.c b/drivers/net/dsa/realtek/realtek-smi.c index 1b447d96b9c4..ff13563059c5 100644 --- a/drivers/net/dsa/realtek/realtek-smi.c +++ b/drivers/net/dsa/realtek/realtek-smi.c @@ -31,7 +31,6 @@ #include <linux/spinlock.h> #include <linux/skbuff.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/of_mdio.h> #include <linux/delay.h> #include <linux/gpio/consumer.h> @@ -557,7 +556,7 @@ MODULE_DEVICE_TABLE(of, realtek_smi_of_match); static struct platform_driver realtek_smi_driver = { .driver = { .name = "realtek-smi", - .of_match_table = of_match_ptr(realtek_smi_of_match), + .of_match_table = realtek_smi_of_match, }, .probe = realtek_smi_probe, .remove = realtek_smi_remove, diff --git a/drivers/net/dsa/realtek/rtl8366rb.c b/drivers/net/dsa/realtek/rtl8366rb.c index 25f88022b9e4..7868ef237f6c 100644 --- a/drivers/net/dsa/realtek/rtl8366rb.c +++ b/drivers/net/dsa/realtek/rtl8366rb.c @@ -1049,6 +1049,32 @@ static enum dsa_tag_protocol rtl8366_get_tag_protocol(struct dsa_switch *ds, return DSA_TAG_PROTO_RTL4_A; } +static void rtl8366rb_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) +{ + unsigned long *interfaces = config->supported_interfaces; + struct realtek_priv *priv = ds->priv; + + if (port == priv->cpu_port) { + __set_bit(PHY_INTERFACE_MODE_MII, interfaces); + __set_bit(PHY_INTERFACE_MODE_GMII, interfaces); + /* REVMII only supports 100M FD */ + __set_bit(PHY_INTERFACE_MODE_REVMII, interfaces); + /* RGMII only supports 1G FD */ + phy_interface_set_rgmii(interfaces); + + config->mac_capabilities = MAC_1000 | MAC_100 | + MAC_SYM_PAUSE; + } else { + /* RSGMII port, but we don't have that, and we don't + * specify in DT, so phylib uses the default of GMII + */ + __set_bit(PHY_INTERFACE_MODE_GMII, interfaces); + config->mac_capabilities = MAC_1000 | MAC_100 | MAC_10 | + MAC_SYM_PAUSE | MAC_ASYM_PAUSE; + } +} + static void rtl8366rb_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface, struct phy_device *phydev, @@ -1796,6 +1822,7 @@ static int rtl8366rb_detect(struct realtek_priv *priv) static const struct dsa_switch_ops rtl8366rb_switch_ops_smi = { .get_tag_protocol = rtl8366_get_tag_protocol, .setup = rtl8366rb_setup, + .phylink_get_caps = rtl8366rb_phylink_get_caps, .phylink_mac_link_up = rtl8366rb_mac_link_up, .phylink_mac_link_down = rtl8366rb_mac_link_down, .get_strings = rtl8366_get_strings, @@ -1821,6 +1848,7 @@ static const struct dsa_switch_ops rtl8366rb_switch_ops_mdio = { .setup = rtl8366rb_setup, .phy_read = rtl8366rb_dsa_phy_read, .phy_write = rtl8366rb_dsa_phy_write, + .phylink_get_caps = rtl8366rb_phylink_get_caps, .phylink_mac_link_up = rtl8366rb_mac_link_up, .phylink_mac_link_down = rtl8366rb_mac_link_down, .get_strings = rtl8366_get_strings, diff --git a/drivers/net/dsa/rzn1_a5psw.c b/drivers/net/dsa/rzn1_a5psw.c index c37d2e537230..2eda10b33f2e 100644 --- a/drivers/net/dsa/rzn1_a5psw.c +++ b/drivers/net/dsa/rzn1_a5psw.c @@ -331,13 +331,9 @@ static void a5psw_flooding_set_resolution(struct a5psw *a5psw, int port, A5PSW_MCAST_DEF_MASK}; int i; - if (set) - a5psw->bridged_ports |= BIT(port); - else - a5psw->bridged_ports &= ~BIT(port); - for (i = 0; i < ARRAY_SIZE(offsets); i++) - a5psw_reg_writel(a5psw, offsets[i], a5psw->bridged_ports); + a5psw_reg_rmw(a5psw, offsets[i], BIT(port), + set ? BIT(port) : 0); } static void a5psw_port_set_standalone(struct a5psw *a5psw, int port, @@ -365,6 +361,8 @@ static int a5psw_port_bridge_join(struct dsa_switch *ds, int port, a5psw->br_dev = bridge.dev; a5psw_port_set_standalone(a5psw, port, false); + a5psw->bridged_ports |= BIT(port); + return 0; } @@ -373,6 +371,8 @@ static void a5psw_port_bridge_leave(struct dsa_switch *ds, int port, { struct a5psw *a5psw = ds->priv; + a5psw->bridged_ports &= ~BIT(port); + a5psw_port_set_standalone(a5psw, port, true); /* No more ports bridged */ @@ -380,9 +380,63 @@ static void a5psw_port_bridge_leave(struct dsa_switch *ds, int port, a5psw->br_dev = NULL; } +static int a5psw_port_pre_bridge_flags(struct dsa_switch *ds, int port, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack) +{ + if (flags.mask & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | + BR_BCAST_FLOOD)) + return -EINVAL; + + return 0; +} + +static int +a5psw_port_bridge_flags(struct dsa_switch *ds, int port, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack) +{ + struct a5psw *a5psw = ds->priv; + u32 val; + + /* If a port is set as standalone, we do not want to be able to + * configure flooding nor learning which would result in joining the + * unique bridge. This can happen when a port leaves the bridge, in + * which case the DSA core will try to "clear" all flags for the + * standalone port (ie enable flooding, disable learning). In that case + * do not fail but do not apply the flags. + */ + if (!(a5psw->bridged_ports & BIT(port))) + return 0; + + if (flags.mask & BR_LEARNING) { + val = flags.val & BR_LEARNING ? 0 : A5PSW_INPUT_LEARN_DIS(port); + a5psw_reg_rmw(a5psw, A5PSW_INPUT_LEARN, + A5PSW_INPUT_LEARN_DIS(port), val); + } + + if (flags.mask & BR_FLOOD) { + val = flags.val & BR_FLOOD ? BIT(port) : 0; + a5psw_reg_rmw(a5psw, A5PSW_UCAST_DEF_MASK, BIT(port), val); + } + + if (flags.mask & BR_MCAST_FLOOD) { + val = flags.val & BR_MCAST_FLOOD ? BIT(port) : 0; + a5psw_reg_rmw(a5psw, A5PSW_MCAST_DEF_MASK, BIT(port), val); + } + + if (flags.mask & BR_BCAST_FLOOD) { + val = flags.val & BR_BCAST_FLOOD ? BIT(port) : 0; + a5psw_reg_rmw(a5psw, A5PSW_BCAST_DEF_MASK, BIT(port), val); + } + + return 0; +} + static void a5psw_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) { bool learning_enabled, rx_enabled, tx_enabled; + struct dsa_port *dp = dsa_to_port(ds, port); struct a5psw *a5psw = ds->priv; switch (state) { @@ -396,12 +450,12 @@ static void a5psw_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) case BR_STATE_LEARNING: rx_enabled = false; tx_enabled = false; - learning_enabled = true; + learning_enabled = dp->learning; break; case BR_STATE_FORWARDING: rx_enabled = true; tx_enabled = true; - learning_enabled = true; + learning_enabled = dp->learning; break; default: dev_err(ds->dev, "invalid STP state: %d\n", state); @@ -585,6 +639,140 @@ out_unlock: return ret; } +static int a5psw_port_vlan_filtering(struct dsa_switch *ds, int port, + bool vlan_filtering, + struct netlink_ext_ack *extack) +{ + u32 mask = BIT(port + A5PSW_VLAN_VERI_SHIFT) | + BIT(port + A5PSW_VLAN_DISC_SHIFT); + u32 val = vlan_filtering ? mask : 0; + struct a5psw *a5psw = ds->priv; + + /* Disable/enable vlan tagging */ + a5psw_reg_rmw(a5psw, A5PSW_VLAN_IN_MODE_ENA, BIT(port), + vlan_filtering ? BIT(port) : 0); + + /* Disable/enable vlan input filtering */ + a5psw_reg_rmw(a5psw, A5PSW_VLAN_VERIFY, mask, val); + + return 0; +} + +static int a5psw_find_vlan_entry(struct a5psw *a5psw, u16 vid) +{ + u32 vlan_res; + int i; + + /* Find vlan for this port */ + for (i = 0; i < A5PSW_VLAN_COUNT; i++) { + vlan_res = a5psw_reg_readl(a5psw, A5PSW_VLAN_RES(i)); + if (FIELD_GET(A5PSW_VLAN_RES_VLANID, vlan_res) == vid) + return i; + } + + return -1; +} + +static int a5psw_new_vlan_res_entry(struct a5psw *a5psw, u16 newvid) +{ + u32 vlan_res; + int i; + + /* Find a free VLAN entry */ + for (i = 0; i < A5PSW_VLAN_COUNT; i++) { + vlan_res = a5psw_reg_readl(a5psw, A5PSW_VLAN_RES(i)); + if (!(FIELD_GET(A5PSW_VLAN_RES_PORTMASK, vlan_res))) { + vlan_res = FIELD_PREP(A5PSW_VLAN_RES_VLANID, newvid); + a5psw_reg_writel(a5psw, A5PSW_VLAN_RES(i), vlan_res); + return i; + } + } + + return -1; +} + +static void a5psw_port_vlan_tagged_cfg(struct a5psw *a5psw, + unsigned int vlan_res_id, int port, + bool set) +{ + u32 mask = A5PSW_VLAN_RES_WR_PORTMASK | A5PSW_VLAN_RES_RD_TAGMASK | + BIT(port); + u32 vlan_res_off = A5PSW_VLAN_RES(vlan_res_id); + u32 val = A5PSW_VLAN_RES_WR_TAGMASK, reg; + + if (set) + val |= BIT(port); + + /* Toggle tag mask read */ + a5psw_reg_writel(a5psw, vlan_res_off, A5PSW_VLAN_RES_RD_TAGMASK); + reg = a5psw_reg_readl(a5psw, vlan_res_off); + a5psw_reg_writel(a5psw, vlan_res_off, A5PSW_VLAN_RES_RD_TAGMASK); + + reg &= ~mask; + reg |= val; + a5psw_reg_writel(a5psw, vlan_res_off, reg); +} + +static void a5psw_port_vlan_cfg(struct a5psw *a5psw, unsigned int vlan_res_id, + int port, bool set) +{ + u32 mask = A5PSW_VLAN_RES_WR_TAGMASK | BIT(port); + u32 reg = A5PSW_VLAN_RES_WR_PORTMASK; + + if (set) + reg |= BIT(port); + + a5psw_reg_rmw(a5psw, A5PSW_VLAN_RES(vlan_res_id), mask, reg); +} + +static int a5psw_port_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack) +{ + bool tagged = !(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED); + bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; + struct a5psw *a5psw = ds->priv; + u16 vid = vlan->vid; + int vlan_res_id; + + vlan_res_id = a5psw_find_vlan_entry(a5psw, vid); + if (vlan_res_id < 0) { + vlan_res_id = a5psw_new_vlan_res_entry(a5psw, vid); + if (vlan_res_id < 0) + return -ENOSPC; + } + + a5psw_port_vlan_cfg(a5psw, vlan_res_id, port, true); + if (tagged) + a5psw_port_vlan_tagged_cfg(a5psw, vlan_res_id, port, true); + + /* Configure port to tag with corresponding VID, but do not enable it + * yet: wait for vlan filtering to be enabled to enable vlan port + * tagging + */ + if (pvid) + a5psw_reg_writel(a5psw, A5PSW_SYSTEM_TAGINFO(port), vid); + + return 0; +} + +static int a5psw_port_vlan_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) +{ + struct a5psw *a5psw = ds->priv; + u16 vid = vlan->vid; + int vlan_res_id; + + vlan_res_id = a5psw_find_vlan_entry(a5psw, vid); + if (vlan_res_id < 0) + return -EINVAL; + + a5psw_port_vlan_cfg(a5psw, vlan_res_id, port, false); + a5psw_port_vlan_tagged_cfg(a5psw, vlan_res_id, port, false); + + return 0; +} + static u64 a5psw_read_stat(struct a5psw *a5psw, u32 offset, int port) { u32 reg_lo, reg_hi; @@ -702,6 +890,27 @@ static void a5psw_get_eth_ctrl_stats(struct dsa_switch *ds, int port, ctrl_stats->MACControlFramesReceived = stat; } +static void a5psw_vlan_setup(struct a5psw *a5psw, int port) +{ + u32 reg; + + /* Enable TAG always mode for the port, this is actually controlled + * by VLAN_IN_MODE_ENA field which will be used for PVID insertion + */ + reg = A5PSW_VLAN_IN_MODE_TAG_ALWAYS; + reg <<= A5PSW_VLAN_IN_MODE_PORT_SHIFT(port); + a5psw_reg_rmw(a5psw, A5PSW_VLAN_IN_MODE, A5PSW_VLAN_IN_MODE_PORT(port), + reg); + + /* Set transparent mode for output frame manipulation, this will depend + * on the VLAN_RES configuration mode + */ + reg = A5PSW_VLAN_OUT_MODE_TRANSPARENT; + reg <<= A5PSW_VLAN_OUT_MODE_PORT_SHIFT(port); + a5psw_reg_rmw(a5psw, A5PSW_VLAN_OUT_MODE, + A5PSW_VLAN_OUT_MODE_PORT(port), reg); +} + static int a5psw_setup(struct dsa_switch *ds) { struct a5psw *a5psw = ds->priv; @@ -776,6 +985,8 @@ static int a5psw_setup(struct dsa_switch *ds) /* Enable standalone mode for user ports */ if (dsa_port_is_user(dp)) a5psw_port_set_standalone(a5psw, port, true); + + a5psw_vlan_setup(a5psw, port); } return 0; @@ -801,8 +1012,13 @@ static const struct dsa_switch_ops a5psw_switch_ops = { .set_ageing_time = a5psw_set_ageing_time, .port_bridge_join = a5psw_port_bridge_join, .port_bridge_leave = a5psw_port_bridge_leave, + .port_pre_bridge_flags = a5psw_port_pre_bridge_flags, + .port_bridge_flags = a5psw_port_bridge_flags, .port_stp_state_set = a5psw_port_stp_state_set, .port_fast_age = a5psw_port_fast_age, + .port_vlan_filtering = a5psw_port_vlan_filtering, + .port_vlan_add = a5psw_port_vlan_add, + .port_vlan_del = a5psw_port_vlan_del, .port_fdb_add = a5psw_port_fdb_add, .port_fdb_del = a5psw_port_fdb_del, .port_fdb_dump = a5psw_port_fdb_dump, @@ -992,6 +1208,8 @@ static int a5psw_probe(struct platform_device *pdev) if (IS_ERR(a5psw->base)) return PTR_ERR(a5psw->base); + a5psw->bridged_ports = BIT(A5PSW_CPU_PORT); + ret = a5psw_pcs_get(a5psw); if (ret) return ret; @@ -1090,7 +1308,7 @@ MODULE_DEVICE_TABLE(of, a5psw_of_mtable); static struct platform_driver a5psw_driver = { .driver = { .name = "rzn1_a5psw", - .of_match_table = of_match_ptr(a5psw_of_mtable), + .of_match_table = a5psw_of_mtable, }, .probe = a5psw_probe, .remove = a5psw_remove, diff --git a/drivers/net/dsa/rzn1_a5psw.h b/drivers/net/dsa/rzn1_a5psw.h index b869192eef3f..d54acedac194 100644 --- a/drivers/net/dsa/rzn1_a5psw.h +++ b/drivers/net/dsa/rzn1_a5psw.h @@ -51,7 +51,9 @@ #define A5PSW_VLAN_IN_MODE_TAG_ALWAYS 0x2 #define A5PSW_VLAN_OUT_MODE 0x2C -#define A5PSW_VLAN_OUT_MODE_PORT(port) (GENMASK(1, 0) << ((port) * 2)) +#define A5PSW_VLAN_OUT_MODE_PORT_SHIFT(port) ((port) * 2) +#define A5PSW_VLAN_OUT_MODE_PORT(port) (GENMASK(1, 0) << \ + A5PSW_VLAN_OUT_MODE_PORT_SHIFT(port)) #define A5PSW_VLAN_OUT_MODE_DIS 0x0 #define A5PSW_VLAN_OUT_MODE_STRIP 0x1 #define A5PSW_VLAN_OUT_MODE_TAG_THROUGH 0x2 @@ -60,7 +62,7 @@ #define A5PSW_VLAN_IN_MODE_ENA 0x30 #define A5PSW_VLAN_TAG_ID 0x34 -#define A5PSW_SYSTEM_TAGINFO(port) (0x200 + A5PSW_PORT_OFFSET(port)) +#define A5PSW_SYSTEM_TAGINFO(port) (0x200 + 4 * (port)) #define A5PSW_AUTH_PORT(port) (0x240 + 4 * (port)) #define A5PSW_AUTH_PORT_AUTHORIZED BIT(0) @@ -69,7 +71,7 @@ #define A5PSW_VLAN_RES_WR_PORTMASK BIT(30) #define A5PSW_VLAN_RES_WR_TAGMASK BIT(29) #define A5PSW_VLAN_RES_RD_TAGMASK BIT(28) -#define A5PSW_VLAN_RES_ID GENMASK(16, 5) +#define A5PSW_VLAN_RES_VLANID GENMASK(16, 5) #define A5PSW_VLAN_RES_PORTMASK GENMASK(4, 0) #define A5PSW_RXMATCH_CONFIG(port) (0x3e80 + 4 * (port)) diff --git a/drivers/net/dsa/sja1105/sja1105_flower.c b/drivers/net/dsa/sja1105/sja1105_flower.c index fad5afe3819c..9e8ca182c722 100644 --- a/drivers/net/dsa/sja1105/sja1105_flower.c +++ b/drivers/net/dsa/sja1105/sja1105_flower.c @@ -205,10 +205,10 @@ static int sja1105_flower_parse_key(struct sja1105_private *priv, u16 pcp = U16_MAX; if (dissector->used_keys & - ~(BIT(FLOW_DISSECTOR_KEY_BASIC) | - BIT(FLOW_DISSECTOR_KEY_CONTROL) | - BIT(FLOW_DISSECTOR_KEY_VLAN) | - BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS))) { + ~(BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) | + BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL) | + BIT_ULL(FLOW_DISSECTOR_KEY_VLAN) | + BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS))) { NL_SET_ERR_MSG_MOD(extack, "Unsupported keys used"); return -EOPNOTSUPP; diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index 3529a565b4aa..331bb1c6676a 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -15,7 +15,6 @@ #include <linux/of.h> #include <linux/of_net.h> #include <linux/of_mdio.h> -#include <linux/of_device.h> #include <linux/pcs/pcs-xpcs.h> #include <linux/netdev_features.h> #include <linux/netdevice.h> @@ -1396,12 +1395,6 @@ static void sja1105_phylink_get_caps(struct dsa_switch *ds, int port, struct sja1105_xmii_params_entry *mii; phy_interface_t phy_mode; - /* This driver does not make use of the speed, duplex, pause or the - * advertisement in its mac_config, so it is safe to mark this driver - * as non-legacy. - */ - config->legacy_pre_march2020 = false; - phy_mode = priv->phy_mode[port]; if (phy_mode == PHY_INTERFACE_MODE_SGMII || phy_mode == PHY_INTERFACE_MODE_2500BASEX) { diff --git a/drivers/net/dsa/vitesse-vsc73xx-core.c b/drivers/net/dsa/vitesse-vsc73xx-core.c index ef1a4a7c47b2..4f09e7438f3b 100644 --- a/drivers/net/dsa/vitesse-vsc73xx-core.c +++ b/drivers/net/dsa/vitesse-vsc73xx-core.c @@ -18,7 +18,6 @@ #include <linux/module.h> #include <linux/device.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/of_mdio.h> #include <linux/bitops.h> #include <linux/if_bridge.h> diff --git a/drivers/net/dsa/xrs700x/xrs700x.c b/drivers/net/dsa/xrs700x/xrs700x.c index fa622639d640..753fef757f11 100644 --- a/drivers/net/dsa/xrs700x/xrs700x.c +++ b/drivers/net/dsa/xrs700x/xrs700x.c @@ -7,7 +7,7 @@ #include <net/dsa.h> #include <linux/etherdevice.h> #include <linux/if_bridge.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/netdev_features.h> #include <linux/if_hsr.h> #include "xrs700x.h" |