diff options
Diffstat (limited to 'drivers/net/dsa')
30 files changed, 1577 insertions, 1668 deletions
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/hirschmann/hellcreek.c b/drivers/net/dsa/hirschmann/hellcreek.c index af50001ccdd4..720f4e4ed0b0 100644 --- a/drivers/net/dsa/hirschmann/hellcreek.c +++ b/drivers/net/dsa/hirschmann/hellcreek.c @@ -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_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/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/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index b18cd170ec06..2414c1f8bc2d 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> @@ -1624,8 +1624,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 38b3c6dda386..8fbda739c1b3 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -2949,12 +2949,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/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 c7d51a539451..bce9c9e43752 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); @@ -3163,102 +3081,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) @@ -3322,56 +3144,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; @@ -3548,6 +3331,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); @@ -3621,29 +3405,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) { @@ -4106,12 +3867,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) @@ -4228,15 +4003,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, }; @@ -4274,18 +4047,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, @@ -4421,16 +4190,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, @@ -4438,6 +4199,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 = { @@ -4618,16 +4380,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 = { @@ -4723,20 +4480,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 = { @@ -4766,9 +4516,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, @@ -4776,6 +4523,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, }; @@ -4826,22 +4574,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 = { @@ -4891,22 +4632,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 = { @@ -4954,16 +4688,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, @@ -4971,6 +4697,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 = { @@ -5020,15 +4747,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, @@ -5036,6 +4755,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 = { @@ -5127,16 +4847,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, @@ -5145,6 +4857,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 = { @@ -5289,16 +5002,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, @@ -5308,6 +5013,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 = { @@ -5451,15 +5157,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, @@ -5470,6 +5168,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 = { @@ -5520,16 +5219,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, @@ -5539,6 +5230,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 = { @@ -5589,15 +5281,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, @@ -5607,11 +5292,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, @@ -5661,20 +5346,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[] = { @@ -7106,18 +6785,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..98dd49dac421 --- /dev/null +++ b/drivers/net/dsa/mv88e6xxx/pcs-639x.c @@ -0,0 +1,898 @@ +// 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 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 mv88e639x_sgmii_pcs_post_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, true); + + 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; + + 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 8da46d284e35..fd7eb4a52918 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 1c113957fcf4..dce3548dcd05 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" 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..47b9f5bf98fb 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 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/realtek/realtek-mdio.c b/drivers/net/dsa/realtek/realtek-mdio.c index 5a8fe707ca25..4310e7793e58 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> diff --git a/drivers/net/dsa/realtek/realtek-smi.c b/drivers/net/dsa/realtek/realtek-smi.c index 1b447d96b9c4..c2bd8bb6c9c2 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> 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" |