diff options
Diffstat (limited to 'drivers/net/phy')
29 files changed, 3211 insertions, 583 deletions
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 8dc461f7574b..9dabe03a668c 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -38,6 +38,7 @@ config MDIO_BCM_IPROC tristate "Broadcom iProc MDIO bus controller" depends on ARCH_BCM_IPROC || COMPILE_TEST depends on HAS_IOMEM && OF_MDIO + default ARCH_BCM_IPROC help This module provides a driver for the MDIO busses found in the Broadcom iProc SoC's. @@ -324,6 +325,12 @@ config BROADCOM_PHY Currently supports the BCM5411, BCM5421, BCM5461, BCM54616S, BCM5464, BCM5481, BCM54810 and BCM5482 PHYs. +config BCM84881_PHY + bool "Broadcom BCM84881 PHY" + depends on PHYLIB=y + ---help--- + Support the Broadcom BCM84881 PHY. + config CICADA_PHY tristate "Cicada PHYs" ---help--- @@ -340,9 +347,10 @@ config DAVICOM_PHY Currently supports dm9161e and dm9131 config DP83822_PHY - tristate "Texas Instruments DP83822/825 PHYs" + tristate "Texas Instruments DP83822/825/826 PHYs" ---help--- - Supports the DP83822 and DP83825I PHYs. + Supports the DP83822, DP83825I, DP83825CM, DP83825CS, DP83825S, + DP83826C and DP83826NC PHYs. config DP83TC811_PHY tristate "Texas Instruments DP83TC811 PHY" @@ -431,6 +439,9 @@ config MICROCHIP_T1_PHY config MICROSEMI_PHY tristate "Microsemi PHYs" + depends on MACSEC || MACSEC=n + select CRYPTO_AES + select CRYPTO_ECB ---help--- Currently supports VSC8514, VSC8530, VSC8531, VSC8540 and VSC8541 PHYs diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index b433ec3bf9a6..fe5badf13b65 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -43,6 +43,8 @@ obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o obj-$(CONFIG_MDIO_THUNDER) += mdio-thunder.o obj-$(CONFIG_MDIO_XGENE) += mdio-xgene.o +obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += mii_timestamper.o + obj-$(CONFIG_SFP) += sfp.o sfp-obj-$(CONFIG_SFP) += sfp-bus.o obj-y += $(sfp-obj-y) $(sfp-obj-m) @@ -62,6 +64,7 @@ obj-$(CONFIG_BCM87XX_PHY) += bcm87xx.o obj-$(CONFIG_BCM_CYGNUS_PHY) += bcm-cygnus.o obj-$(CONFIG_BCM_NET_PHYLIB) += bcm-phy-lib.o obj-$(CONFIG_BROADCOM_PHY) += broadcom.o +obj-$(CONFIG_BCM84881_PHY) += bcm84881.o obj-$(CONFIG_CICADA_PHY) += cicada.o obj-$(CONFIG_CORTINA_PHY) += cortina.o obj-$(CONFIG_DAVICOM_PHY) += davicom.o diff --git a/drivers/net/phy/adin.c b/drivers/net/phy/adin.c index cf5a391c93e6..c7eabe4382fb 100644 --- a/drivers/net/phy/adin.c +++ b/drivers/net/phy/adin.c @@ -145,7 +145,7 @@ struct adin_clause45_mmd_map { u16 adin_regnum; }; -static struct adin_clause45_mmd_map adin_clause45_mmd_map[] = { +static const struct adin_clause45_mmd_map adin_clause45_mmd_map[] = { { MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE, ADIN1300_EEE_CAP_REG }, { MDIO_MMD_AN, MDIO_AN_EEE_LPABLE, ADIN1300_EEE_LPABLE_REG }, { MDIO_MMD_AN, MDIO_AN_EEE_ADV, ADIN1300_EEE_ADV_REG }, @@ -159,7 +159,7 @@ struct adin_hw_stat { u16 reg2; }; -static struct adin_hw_stat adin_hw_stats[] = { +static const struct adin_hw_stat adin_hw_stats[] = { { "total_frames_checked_count", 0x940A, 0x940B }, /* hi + lo */ { "length_error_frames_count", 0x940C }, { "alignment_error_frames_count", 0x940D }, @@ -456,7 +456,7 @@ static int adin_phy_config_intr(struct phy_device *phydev) static int adin_cl45_to_adin_reg(struct phy_device *phydev, int devad, u16 cl45_regnum) { - struct adin_clause45_mmd_map *m; + const struct adin_clause45_mmd_map *m; int i; if (devad == MDIO_MMD_VEND1) @@ -625,7 +625,7 @@ static int adin_soft_reset(struct phy_device *phydev) if (rc < 0) return rc; - msleep(10); + msleep(20); /* If we get a read error something may be wrong */ rc = phy_read_mmd(phydev, MDIO_MMD_VEND1, @@ -650,7 +650,7 @@ static void adin_get_strings(struct phy_device *phydev, u8 *data) } static int adin_read_mmd_stat_regs(struct phy_device *phydev, - struct adin_hw_stat *stat, + const struct adin_hw_stat *stat, u32 *val) { int ret; @@ -676,7 +676,7 @@ static int adin_read_mmd_stat_regs(struct phy_device *phydev, static u64 adin_get_stat(struct phy_device *phydev, int i) { - struct adin_hw_stat *stat = &adin_hw_stats[i]; + const struct adin_hw_stat *stat = &adin_hw_stats[i]; struct adin_priv *priv = phydev->priv; u32 val; int ret; diff --git a/drivers/net/phy/aquantia_main.c b/drivers/net/phy/aquantia_main.c index 975789d9349d..31927b2c7d5a 100644 --- a/drivers/net/phy/aquantia_main.c +++ b/drivers/net/phy/aquantia_main.c @@ -358,9 +358,11 @@ static int aqr107_read_status(struct phy_device *phydev) switch (FIELD_GET(MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK, val)) { case MDIO_PHYXS_VEND_IF_STATUS_TYPE_KR: - case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XFI: phydev->interface = PHY_INTERFACE_MODE_10GKR; break; + case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XFI: + phydev->interface = PHY_INTERFACE_MODE_10GBASER; + break; case MDIO_PHYXS_VEND_IF_STATUS_TYPE_USXGMII: phydev->interface = PHY_INTERFACE_MODE_USXGMII; break; @@ -493,7 +495,8 @@ static int aqr107_config_init(struct phy_device *phydev) phydev->interface != PHY_INTERFACE_MODE_2500BASEX && phydev->interface != PHY_INTERFACE_MODE_XGMII && phydev->interface != PHY_INTERFACE_MODE_USXGMII && - phydev->interface != PHY_INTERFACE_MODE_10GKR) + phydev->interface != PHY_INTERFACE_MODE_10GKR && + phydev->interface != PHY_INTERFACE_MODE_10GBASER) return -ENODEV; WARN(phydev->interface == PHY_INTERFACE_MODE_XGMII, diff --git a/drivers/net/phy/bcm84881.c b/drivers/net/phy/bcm84881.c new file mode 100644 index 000000000000..14d55a77eb28 --- /dev/null +++ b/drivers/net/phy/bcm84881.c @@ -0,0 +1,269 @@ +// SPDX-License-Identifier: GPL-2.0 +// Broadcom BCM84881 NBASE-T PHY driver, as found on a SFP+ module. +// Copyright (C) 2019 Russell King, Deep Blue Solutions Ltd. +// +// Like the Marvell 88x3310, the Broadcom 84881 changes its host-side +// interface according to the operating speed between 10GBASE-R, +// 2500BASE-X and SGMII (but unlike the 88x3310, without the control +// word). +// +// This driver only supports those aspects of the PHY that I'm able to +// observe and test with the SFP+ module, which is an incomplete subset +// of what this PHY is able to support. For example, I only assume it +// supports a single lane Serdes connection, but it may be that the PHY +// is able to support more than that. +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/phy.h> + +enum { + MDIO_AN_C22 = 0xffe0, +}; + +static int bcm84881_wait_init(struct phy_device *phydev) +{ + unsigned int tries = 20; + int ret, val; + + do { + val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1); + if (val < 0) { + ret = val; + break; + } + if (!(val & MDIO_CTRL1_RESET)) { + ret = 0; + break; + } + if (!--tries) { + ret = -ETIMEDOUT; + break; + } + msleep(100); + } while (1); + + if (ret) + phydev_err(phydev, "%s failed: %d\n", __func__, ret); + + return ret; +} + +static int bcm84881_config_init(struct phy_device *phydev) +{ + switch (phydev->interface) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_2500BASEX: + case PHY_INTERFACE_MODE_10GBASER: + break; + default: + return -ENODEV; + } + return 0; +} + +static int bcm84881_probe(struct phy_device *phydev) +{ + /* This driver requires PMAPMD and AN blocks */ + const u32 mmd_mask = MDIO_DEVS_PMAPMD | MDIO_DEVS_AN; + + if (!phydev->is_c45 || + (phydev->c45_ids.devices_in_package & mmd_mask) != mmd_mask) + return -ENODEV; + + return 0; +} + +static int bcm84881_get_features(struct phy_device *phydev) +{ + int ret; + + ret = genphy_c45_pma_read_abilities(phydev); + if (ret) + return ret; + + /* Although the PHY sets bit 1.11.8, it does not support 10M modes */ + linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, + phydev->supported); + linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, + phydev->supported); + + return 0; +} + +static int bcm84881_config_aneg(struct phy_device *phydev) +{ + bool changed = false; + u32 adv; + int ret; + + /* Wait for the PHY to finish initialising, otherwise our + * advertisement may be overwritten. + */ + ret = bcm84881_wait_init(phydev); + if (ret) + return ret; + + /* We don't support manual MDI control */ + phydev->mdix_ctrl = ETH_TP_MDI_AUTO; + + /* disabled autoneg doesn't seem to work with this PHY */ + if (phydev->autoneg == AUTONEG_DISABLE) + return -EINVAL; + + ret = genphy_c45_an_config_aneg(phydev); + if (ret < 0) + return ret; + if (ret > 0) + changed = true; + + adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising); + ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, + MDIO_AN_C22 + MII_CTRL1000, + ADVERTISE_1000FULL | ADVERTISE_1000HALF, + adv); + if (ret < 0) + return ret; + if (ret > 0) + changed = true; + + return genphy_c45_check_and_restart_aneg(phydev, changed); +} + +static int bcm84881_aneg_done(struct phy_device *phydev) +{ + int bmsr, val; + + val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); + if (val < 0) + return val; + + bmsr = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_C22 + MII_BMSR); + if (bmsr < 0) + return val; + + return !!(val & MDIO_AN_STAT1_COMPLETE) && + !!(bmsr & BMSR_ANEGCOMPLETE); +} + +static int bcm84881_read_status(struct phy_device *phydev) +{ + unsigned int mode; + int bmsr, val; + + val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1); + if (val < 0) + return val; + + if (val & MDIO_AN_CTRL1_RESTART) { + phydev->link = 0; + return 0; + } + + val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); + if (val < 0) + return val; + + bmsr = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_C22 + MII_BMSR); + if (bmsr < 0) + return val; + + phydev->autoneg_complete = !!(val & MDIO_AN_STAT1_COMPLETE) && + !!(bmsr & BMSR_ANEGCOMPLETE); + phydev->link = !!(val & MDIO_STAT1_LSTATUS) && + !!(bmsr & BMSR_LSTATUS); + if (phydev->autoneg == AUTONEG_ENABLE && !phydev->autoneg_complete) + phydev->link = false; + + if (!phydev->link) + return 0; + + linkmode_zero(phydev->lp_advertising); + phydev->speed = SPEED_UNKNOWN; + phydev->duplex = DUPLEX_UNKNOWN; + phydev->pause = 0; + phydev->asym_pause = 0; + phydev->mdix = 0; + + if (phydev->autoneg_complete) { + val = genphy_c45_read_lpa(phydev); + if (val < 0) + return val; + + val = phy_read_mmd(phydev, MDIO_MMD_AN, + MDIO_AN_C22 + MII_STAT1000); + if (val < 0) + return val; + + mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, val); + + if (phydev->autoneg == AUTONEG_ENABLE) + phy_resolve_aneg_linkmode(phydev); + } + + if (phydev->autoneg == AUTONEG_DISABLE) { + /* disabled autoneg doesn't seem to work, so force the link + * down. + */ + phydev->link = 0; + return 0; + } + + /* Set the host link mode - we set the phy interface mode and + * the speed according to this register so that downshift works. + * We leave the duplex setting as per the resolution from the + * above. + */ + val = phy_read_mmd(phydev, MDIO_MMD_VEND1, 0x4011); + mode = (val & 0x1e) >> 1; + if (mode == 1 || mode == 2) + phydev->interface = PHY_INTERFACE_MODE_SGMII; + else if (mode == 3) + phydev->interface = PHY_INTERFACE_MODE_10GBASER; + else if (mode == 4) + phydev->interface = PHY_INTERFACE_MODE_2500BASEX; + switch (mode & 7) { + case 1: + phydev->speed = SPEED_100; + break; + case 2: + phydev->speed = SPEED_1000; + break; + case 3: + phydev->speed = SPEED_10000; + break; + case 4: + phydev->speed = SPEED_2500; + break; + case 5: + phydev->speed = SPEED_5000; + break; + } + + return genphy_c45_read_mdix(phydev); +} + +static struct phy_driver bcm84881_drivers[] = { + { + .phy_id = 0xae025150, + .phy_id_mask = 0xfffffff0, + .name = "Broadcom BCM84881", + .config_init = bcm84881_config_init, + .probe = bcm84881_probe, + .get_features = bcm84881_get_features, + .config_aneg = bcm84881_config_aneg, + .aneg_done = bcm84881_aneg_done, + .read_status = bcm84881_read_status, + }, +}; + +module_phy_driver(bcm84881_drivers); + +/* FIXME: module auto-loading for Clause 45 PHYs seems non-functional */ +static struct mdio_device_id __maybe_unused bcm84881_tbl[] = { + { 0xae025150, 0xfffffff0 }, + { }, +}; +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("Broadcom BCM84881 PHY driver"); +MODULE_DEVICE_TABLE(mdio, bcm84881_tbl); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c index 8f241b57fcf6..ac72a324fcd1 100644 --- a/drivers/net/phy/dp83640.c +++ b/drivers/net/phy/dp83640.c @@ -98,6 +98,7 @@ struct dp83640_private { struct list_head list; struct dp83640_clock *clock; struct phy_device *phydev; + struct mii_timestamper mii_ts; struct delayed_work ts_work; int hwts_tx_en; int hwts_rx_en; @@ -1131,96 +1132,6 @@ static void dp83640_clock_put(struct dp83640_clock *clock) mutex_unlock(&clock->clock_lock); } -static int dp83640_probe(struct phy_device *phydev) -{ - struct dp83640_clock *clock; - struct dp83640_private *dp83640; - int err = -ENOMEM, i; - - if (phydev->mdio.addr == BROADCAST_ADDR) - return 0; - - clock = dp83640_clock_get_bus(phydev->mdio.bus); - if (!clock) - goto no_clock; - - dp83640 = kzalloc(sizeof(struct dp83640_private), GFP_KERNEL); - if (!dp83640) - goto no_memory; - - dp83640->phydev = phydev; - INIT_DELAYED_WORK(&dp83640->ts_work, rx_timestamp_work); - - INIT_LIST_HEAD(&dp83640->rxts); - INIT_LIST_HEAD(&dp83640->rxpool); - for (i = 0; i < MAX_RXTS; i++) - list_add(&dp83640->rx_pool_data[i].list, &dp83640->rxpool); - - phydev->priv = dp83640; - - spin_lock_init(&dp83640->rx_lock); - skb_queue_head_init(&dp83640->rx_queue); - skb_queue_head_init(&dp83640->tx_queue); - - dp83640->clock = clock; - - if (choose_this_phy(clock, phydev)) { - clock->chosen = dp83640; - clock->ptp_clock = ptp_clock_register(&clock->caps, - &phydev->mdio.dev); - if (IS_ERR(clock->ptp_clock)) { - err = PTR_ERR(clock->ptp_clock); - goto no_register; - } - } else - list_add_tail(&dp83640->list, &clock->phylist); - - dp83640_clock_put(clock); - return 0; - -no_register: - clock->chosen = NULL; - kfree(dp83640); -no_memory: - dp83640_clock_put(clock); -no_clock: - return err; -} - -static void dp83640_remove(struct phy_device *phydev) -{ - struct dp83640_clock *clock; - struct list_head *this, *next; - struct dp83640_private *tmp, *dp83640 = phydev->priv; - - if (phydev->mdio.addr == BROADCAST_ADDR) - return; - - enable_status_frames(phydev, false); - cancel_delayed_work_sync(&dp83640->ts_work); - - skb_queue_purge(&dp83640->rx_queue); - skb_queue_purge(&dp83640->tx_queue); - - clock = dp83640_clock_get(dp83640->clock); - - if (dp83640 == clock->chosen) { - ptp_clock_unregister(clock->ptp_clock); - clock->chosen = NULL; - } else { - list_for_each_safe(this, next, &clock->phylist) { - tmp = list_entry(this, struct dp83640_private, list); - if (tmp == dp83640) { - list_del_init(&tmp->list); - break; - } - } - } - - dp83640_clock_put(clock); - kfree(dp83640); -} - static int dp83640_soft_reset(struct phy_device *phydev) { int ret; @@ -1319,9 +1230,10 @@ static int dp83640_config_intr(struct phy_device *phydev) } } -static int dp83640_hwtstamp(struct phy_device *phydev, struct ifreq *ifr) +static int dp83640_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr) { - struct dp83640_private *dp83640 = phydev->priv; + struct dp83640_private *dp83640 = + container_of(mii_ts, struct dp83640_private, mii_ts); struct hwtstamp_config cfg; u16 txcfg0, rxcfg0; @@ -1397,8 +1309,8 @@ static int dp83640_hwtstamp(struct phy_device *phydev, struct ifreq *ifr) mutex_lock(&dp83640->clock->extreg_lock); - ext_write(0, phydev, PAGE5, PTP_TXCFG0, txcfg0); - ext_write(0, phydev, PAGE5, PTP_RXCFG0, rxcfg0); + ext_write(0, dp83640->phydev, PAGE5, PTP_TXCFG0, txcfg0); + ext_write(0, dp83640->phydev, PAGE5, PTP_RXCFG0, rxcfg0); mutex_unlock(&dp83640->clock->extreg_lock); @@ -1428,10 +1340,11 @@ static void rx_timestamp_work(struct work_struct *work) schedule_delayed_work(&dp83640->ts_work, SKB_TIMESTAMP_TIMEOUT); } -static bool dp83640_rxtstamp(struct phy_device *phydev, +static bool dp83640_rxtstamp(struct mii_timestamper *mii_ts, struct sk_buff *skb, int type) { - struct dp83640_private *dp83640 = phydev->priv; + struct dp83640_private *dp83640 = + container_of(mii_ts, struct dp83640_private, mii_ts); struct dp83640_skb_info *skb_info = (struct dp83640_skb_info *)skb->cb; struct list_head *this, *next; struct rxts *rxts; @@ -1477,11 +1390,12 @@ static bool dp83640_rxtstamp(struct phy_device *phydev, return true; } -static void dp83640_txtstamp(struct phy_device *phydev, +static void dp83640_txtstamp(struct mii_timestamper *mii_ts, struct sk_buff *skb, int type) { struct dp83640_skb_info *skb_info = (struct dp83640_skb_info *)skb->cb; - struct dp83640_private *dp83640 = phydev->priv; + struct dp83640_private *dp83640 = + container_of(mii_ts, struct dp83640_private, mii_ts); switch (dp83640->hwts_tx_en) { @@ -1504,9 +1418,11 @@ static void dp83640_txtstamp(struct phy_device *phydev, } } -static int dp83640_ts_info(struct phy_device *dev, struct ethtool_ts_info *info) +static int dp83640_ts_info(struct mii_timestamper *mii_ts, + struct ethtool_ts_info *info) { - struct dp83640_private *dp83640 = dev->priv; + struct dp83640_private *dp83640 = + container_of(mii_ts, struct dp83640_private, mii_ts); info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE | @@ -1526,6 +1442,103 @@ static int dp83640_ts_info(struct phy_device *dev, struct ethtool_ts_info *info) return 0; } +static int dp83640_probe(struct phy_device *phydev) +{ + struct dp83640_clock *clock; + struct dp83640_private *dp83640; + int err = -ENOMEM, i; + + if (phydev->mdio.addr == BROADCAST_ADDR) + return 0; + + clock = dp83640_clock_get_bus(phydev->mdio.bus); + if (!clock) + goto no_clock; + + dp83640 = kzalloc(sizeof(struct dp83640_private), GFP_KERNEL); + if (!dp83640) + goto no_memory; + + dp83640->phydev = phydev; + dp83640->mii_ts.rxtstamp = dp83640_rxtstamp; + dp83640->mii_ts.txtstamp = dp83640_txtstamp; + dp83640->mii_ts.hwtstamp = dp83640_hwtstamp; + dp83640->mii_ts.ts_info = dp83640_ts_info; + + INIT_DELAYED_WORK(&dp83640->ts_work, rx_timestamp_work); + INIT_LIST_HEAD(&dp83640->rxts); + INIT_LIST_HEAD(&dp83640->rxpool); + for (i = 0; i < MAX_RXTS; i++) + list_add(&dp83640->rx_pool_data[i].list, &dp83640->rxpool); + + phydev->mii_ts = &dp83640->mii_ts; + phydev->priv = dp83640; + + spin_lock_init(&dp83640->rx_lock); + skb_queue_head_init(&dp83640->rx_queue); + skb_queue_head_init(&dp83640->tx_queue); + + dp83640->clock = clock; + + if (choose_this_phy(clock, phydev)) { + clock->chosen = dp83640; + clock->ptp_clock = ptp_clock_register(&clock->caps, + &phydev->mdio.dev); + if (IS_ERR(clock->ptp_clock)) { + err = PTR_ERR(clock->ptp_clock); + goto no_register; + } + } else + list_add_tail(&dp83640->list, &clock->phylist); + + dp83640_clock_put(clock); + return 0; + +no_register: + clock->chosen = NULL; + kfree(dp83640); +no_memory: + dp83640_clock_put(clock); +no_clock: + return err; +} + +static void dp83640_remove(struct phy_device *phydev) +{ + struct dp83640_clock *clock; + struct list_head *this, *next; + struct dp83640_private *tmp, *dp83640 = phydev->priv; + + if (phydev->mdio.addr == BROADCAST_ADDR) + return; + + phydev->mii_ts = NULL; + + enable_status_frames(phydev, false); + cancel_delayed_work_sync(&dp83640->ts_work); + + skb_queue_purge(&dp83640->rx_queue); + skb_queue_purge(&dp83640->tx_queue); + + clock = dp83640_clock_get(dp83640->clock); + + if (dp83640 == clock->chosen) { + ptp_clock_unregister(clock->ptp_clock); + clock->chosen = NULL; + } else { + list_for_each_safe(this, next, &clock->phylist) { + tmp = list_entry(this, struct dp83640_private, list); + if (tmp == dp83640) { + list_del_init(&tmp->list); + break; + } + } + } + + dp83640_clock_put(clock); + kfree(dp83640); +} + static struct phy_driver dp83640_driver = { .phy_id = DP83640_PHY_ID, .phy_id_mask = 0xfffffff0, @@ -1537,10 +1550,6 @@ static struct phy_driver dp83640_driver = { .config_init = dp83640_config_init, .ack_interrupt = dp83640_ack_interrupt, .config_intr = dp83640_config_intr, - .ts_info = dp83640_ts_info, - .hwtstamp = dp83640_hwtstamp, - .rxtstamp = dp83640_rxtstamp, - .txtstamp = dp83640_txtstamp, }; static int __init dp83640_init(void) diff --git a/drivers/net/phy/dp83822.c b/drivers/net/phy/dp83822.c index 8a4b1d167ce2..fe9aa3ad52a7 100644 --- a/drivers/net/phy/dp83822.c +++ b/drivers/net/phy/dp83822.c @@ -1,6 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* - * Driver for the Texas Instruments DP83822 PHY +/* Driver for the Texas Instruments DP83822, DP83825 and DP83826 PHYs. * * Copyright (C) 2017 Texas Instruments Inc. */ @@ -15,7 +14,12 @@ #include <linux/netdevice.h> #define DP83822_PHY_ID 0x2000a240 +#define DP83825S_PHY_ID 0x2000a140 #define DP83825I_PHY_ID 0x2000a150 +#define DP83825CM_PHY_ID 0x2000a160 +#define DP83825CS_PHY_ID 0x2000a170 +#define DP83826C_PHY_ID 0x2000a130 +#define DP83826NC_PHY_ID 0x2000a110 #define DP83822_DEVADDR 0x1f @@ -319,12 +323,22 @@ static int dp83822_resume(struct phy_device *phydev) static struct phy_driver dp83822_driver[] = { DP83822_PHY_DRIVER(DP83822_PHY_ID, "TI DP83822"), DP83822_PHY_DRIVER(DP83825I_PHY_ID, "TI DP83825I"), + DP83822_PHY_DRIVER(DP83826C_PHY_ID, "TI DP83826C"), + DP83822_PHY_DRIVER(DP83826NC_PHY_ID, "TI DP83826NC"), + DP83822_PHY_DRIVER(DP83825S_PHY_ID, "TI DP83825S"), + DP83822_PHY_DRIVER(DP83825CM_PHY_ID, "TI DP83825M"), + DP83822_PHY_DRIVER(DP83825CS_PHY_ID, "TI DP83825CS"), }; module_phy_driver(dp83822_driver); static struct mdio_device_id __maybe_unused dp83822_tbl[] = { { DP83822_PHY_ID, 0xfffffff0 }, { DP83825I_PHY_ID, 0xfffffff0 }, + { DP83826C_PHY_ID, 0xfffffff0 }, + { DP83826NC_PHY_ID, 0xfffffff0 }, + { DP83825S_PHY_ID, 0xfffffff0 }, + { DP83825CM_PHY_ID, 0xfffffff0 }, + { DP83825CS_PHY_ID, 0xfffffff0 }, { }, }; MODULE_DEVICE_TABLE(mdio, dp83822_tbl); diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c index 01cf71358359..967f57ed0b65 100644 --- a/drivers/net/phy/dp83867.c +++ b/drivers/net/phy/dp83867.c @@ -93,9 +93,11 @@ #define DP83867_STRAP_STS2_CLK_SKEW_NONE BIT(2) /* PHY CTRL bits */ -#define DP83867_PHYCR_FIFO_DEPTH_SHIFT 14 +#define DP83867_PHYCR_TX_FIFO_DEPTH_SHIFT 14 +#define DP83867_PHYCR_RX_FIFO_DEPTH_SHIFT 12 #define DP83867_PHYCR_FIFO_DEPTH_MAX 0x03 -#define DP83867_PHYCR_FIFO_DEPTH_MASK GENMASK(15, 14) +#define DP83867_PHYCR_TX_FIFO_DEPTH_MASK GENMASK(15, 14) +#define DP83867_PHYCR_RX_FIFO_DEPTH_MASK GENMASK(13, 12) #define DP83867_PHYCR_RESERVED_MASK BIT(11) #define DP83867_PHYCR_FORCE_LINK_GOOD BIT(10) @@ -132,7 +134,8 @@ enum { struct dp83867_private { u32 rx_id_delay; u32 tx_id_delay; - u32 fifo_depth; + u32 tx_fifo_depth; + u32 rx_fifo_depth; int io_impedance; int port_mirroring; bool rxctrl_strap_quirk; @@ -409,18 +412,32 @@ static int dp83867_of_init(struct phy_device *phydev) dp83867->port_mirroring = DP83867_PORT_MIRROING_DIS; ret = of_property_read_u32(of_node, "ti,fifo-depth", - &dp83867->fifo_depth); + &dp83867->tx_fifo_depth); if (ret) { - phydev_err(phydev, - "ti,fifo-depth property is required\n"); - return ret; + ret = of_property_read_u32(of_node, "tx-fifo-depth", + &dp83867->tx_fifo_depth); + if (ret) + dp83867->tx_fifo_depth = + DP83867_PHYCR_FIFO_DEPTH_4_B_NIB; } - if (dp83867->fifo_depth > DP83867_PHYCR_FIFO_DEPTH_MAX) { - phydev_err(phydev, - "ti,fifo-depth value %u out of range\n", - dp83867->fifo_depth); + + if (dp83867->tx_fifo_depth > DP83867_PHYCR_FIFO_DEPTH_MAX) { + phydev_err(phydev, "tx-fifo-depth value %u out of range\n", + dp83867->tx_fifo_depth); + return -EINVAL; + } + + ret = of_property_read_u32(of_node, "rx-fifo-depth", + &dp83867->rx_fifo_depth); + if (ret) + dp83867->rx_fifo_depth = DP83867_PHYCR_FIFO_DEPTH_4_B_NIB; + + if (dp83867->rx_fifo_depth > DP83867_PHYCR_FIFO_DEPTH_MAX) { + phydev_err(phydev, "rx-fifo-depth value %u out of range\n", + dp83867->rx_fifo_depth); return -EINVAL; } + return 0; } #else @@ -459,12 +476,31 @@ static int dp83867_config_init(struct phy_device *phydev) phy_clear_bits_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4, BIT(7)); + if (phy_interface_is_rgmii(phydev) || + phydev->interface == PHY_INTERFACE_MODE_SGMII) { + val = phy_read(phydev, MII_DP83867_PHYCTRL); + if (val < 0) + return val; + + val &= ~DP83867_PHYCR_TX_FIFO_DEPTH_MASK; + val |= (dp83867->tx_fifo_depth << + DP83867_PHYCR_TX_FIFO_DEPTH_SHIFT); + + if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { + val &= ~DP83867_PHYCR_RX_FIFO_DEPTH_MASK; + val |= (dp83867->rx_fifo_depth << + DP83867_PHYCR_RX_FIFO_DEPTH_SHIFT); + } + + ret = phy_write(phydev, MII_DP83867_PHYCTRL, val); + if (ret) + return ret; + } + if (phy_interface_is_rgmii(phydev)) { val = phy_read(phydev, MII_DP83867_PHYCTRL); if (val < 0) return val; - val &= ~DP83867_PHYCR_FIFO_DEPTH_MASK; - val |= (dp83867->fifo_depth << DP83867_PHYCR_FIFO_DEPTH_SHIFT); /* The code below checks if "port mirroring" N/A MODE4 has been * enabled during power on bootstrap. diff --git a/drivers/net/phy/dp83869.c b/drivers/net/phy/dp83869.c index 93021904c5e4..7996a4aea8d2 100644 --- a/drivers/net/phy/dp83869.c +++ b/drivers/net/phy/dp83869.c @@ -334,7 +334,7 @@ static int dp83869_configure_mode(struct phy_device *phydev, break; default: return -EINVAL; - }; + } return ret; } diff --git a/drivers/net/phy/fixed_phy.c b/drivers/net/phy/fixed_phy.c index 7c5265fd2b94..4a3d34f40cb9 100644 --- a/drivers/net/phy/fixed_phy.c +++ b/drivers/net/phy/fixed_phy.c @@ -210,18 +210,15 @@ static struct gpio_desc *fixed_phy_get_gpiod(struct device_node *np) * Linux device associated with it, we simply have obtain * the GPIO descriptor from the device tree like this. */ - gpiod = gpiod_get_from_of_node(fixed_link_node, "link-gpios", 0, - GPIOD_IN, "mdio"); - of_node_put(fixed_link_node); - if (IS_ERR(gpiod)) { - if (PTR_ERR(gpiod) == -EPROBE_DEFER) - return gpiod; - + gpiod = fwnode_gpiod_get_index(of_fwnode_handle(fixed_link_node), + "link", 0, GPIOD_IN, "mdio"); + if (IS_ERR(gpiod) && PTR_ERR(gpiod) != -EPROBE_DEFER) { if (PTR_ERR(gpiod) != -ENOENT) pr_err("error getting GPIO for fixed link %pOF, proceed without\n", fixed_link_node); gpiod = NULL; } + of_node_put(fixed_link_node); return gpiod; } diff --git a/drivers/net/phy/lxt.c b/drivers/net/phy/lxt.c index 356bd6472f49..fec58ad69e02 100644 --- a/drivers/net/phy/lxt.c +++ b/drivers/net/phy/lxt.c @@ -190,27 +190,11 @@ static int lxt973a2_read_status(struct phy_device *phydev) phydev->duplex = DUPLEX_FULL; } - if (phydev->duplex == DUPLEX_FULL) { - phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0; - phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0; - } + phy_resolve_aneg_pause(phydev); } else { - int bmcr = phy_read(phydev, MII_BMCR); - - if (bmcr < 0) - return bmcr; - - if (bmcr & BMCR_FULLDPLX) - phydev->duplex = DUPLEX_FULL; - else - phydev->duplex = DUPLEX_HALF; - - if (bmcr & BMCR_SPEED1000) - phydev->speed = SPEED_1000; - else if (bmcr & BMCR_SPEED100) - phydev->speed = SPEED_100; - else - phydev->speed = SPEED_10; + err = genphy_read_status_fixed(phydev); + if (err < 0) + return err; phydev->pause = phydev->asym_pause = 0; linkmode_zero(phydev->lp_advertising); diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index b1fbd1937328..28e33ece4ce1 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -162,19 +162,9 @@ #define MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII 0x1 /* SGMII to copper */ #define MII_88E1510_GEN_CTRL_REG_1_RESET 0x8000 /* Soft reset */ -#define LPA_FIBER_1000HALF 0x40 -#define LPA_FIBER_1000FULL 0x20 - #define LPA_PAUSE_FIBER 0x180 #define LPA_PAUSE_ASYM_FIBER 0x100 -#define ADVERTISE_FIBER_1000HALF 0x40 -#define ADVERTISE_FIBER_1000FULL 0x20 - -#define ADVERTISE_PAUSE_FIBER 0x180 -#define ADVERTISE_PAUSE_ASYM_FIBER 0x100 - -#define REGISTER_LINK_STATUS 0x400 #define NB_FIBER_STATS 1 MODULE_DESCRIPTION("Marvell PHY driver"); @@ -497,16 +487,15 @@ static inline u32 linkmode_adv_to_fiber_adv_t(unsigned long *advertise) u32 result = 0; if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, advertise)) - result |= ADVERTISE_FIBER_1000HALF; + result |= ADVERTISE_1000XHALF; if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, advertise)) - result |= ADVERTISE_FIBER_1000FULL; + result |= ADVERTISE_1000XFULL; if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, advertise) && linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertise)) - result |= LPA_PAUSE_ASYM_FIBER; + result |= ADVERTISE_1000XPSE_ASYM; else if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertise)) - result |= (ADVERTISE_PAUSE_FIBER - & (~ADVERTISE_PAUSE_ASYM_FIBER)); + result |= ADVERTISE_1000XPAUSE; return result; } @@ -524,7 +513,7 @@ static int marvell_config_aneg_fiber(struct phy_device *phydev) { int changed = 0; int err; - int adv, oldadv; + u16 adv; if (phydev->autoneg != AUTONEG_ENABLE) return genphy_setup_forced(phydev); @@ -533,44 +522,19 @@ static int marvell_config_aneg_fiber(struct phy_device *phydev) linkmode_and(phydev->advertising, phydev->advertising, phydev->supported); - /* Setup fiber advertisement */ - adv = phy_read(phydev, MII_ADVERTISE); - if (adv < 0) - return adv; - - oldadv = adv; - adv &= ~(ADVERTISE_FIBER_1000HALF | ADVERTISE_FIBER_1000FULL - | LPA_PAUSE_FIBER); - adv |= linkmode_adv_to_fiber_adv_t(phydev->advertising); - - if (adv != oldadv) { - err = phy_write(phydev, MII_ADVERTISE, adv); - if (err < 0) - return err; + adv = linkmode_adv_to_fiber_adv_t(phydev->advertising); + /* Setup fiber advertisement */ + err = phy_modify_changed(phydev, MII_ADVERTISE, + ADVERTISE_1000XHALF | ADVERTISE_1000XFULL | + ADVERTISE_1000XPAUSE | ADVERTISE_1000XPSE_ASYM, + adv); + if (err < 0) + return err; + if (err > 0) changed = 1; - } - - if (changed == 0) { - /* Advertisement hasn't changed, but maybe aneg was never on to - * begin with? Or maybe phy was isolated? - */ - int ctl = phy_read(phydev, MII_BMCR); - - if (ctl < 0) - return ctl; - - if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE)) - changed = 1; /* do restart aneg */ - } - /* Only restart aneg if we are advertising something different - * than we were before. - */ - if (changed > 0) - changed = genphy_restart_aneg(phydev); - - return changed; + return genphy_check_and_restart_aneg(phydev, changed); } static int m88e1510_config_aneg(struct phy_device *phydev) @@ -1302,93 +1266,29 @@ static int m88e6390_config_aneg(struct phy_device *phydev) static void fiber_lpa_mod_linkmode_lpa_t(unsigned long *advertising, u32 lpa) { linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, - advertising, lpa & LPA_FIBER_1000HALF); + advertising, lpa & LPA_1000XHALF); linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, - advertising, lpa & LPA_FIBER_1000FULL); -} - -/** - * marvell_update_link - update link status in real time in @phydev - * @phydev: target phy_device struct - * - * Description: Update the value in phydev->link to reflect the - * current link value. - */ -static int marvell_update_link(struct phy_device *phydev, int fiber) -{ - int status; - - /* Use the generic register for copper link, or specific - * register for fiber case - */ - if (fiber) { - status = phy_read(phydev, MII_M1011_PHY_STATUS); - if (status < 0) - return status; - - if ((status & REGISTER_LINK_STATUS) == 0) - phydev->link = 0; - else - phydev->link = 1; - } else { - return genphy_update_link(phydev); - } - - return 0; + advertising, lpa & LPA_1000XFULL); } static int marvell_read_status_page_an(struct phy_device *phydev, - int fiber) + int fiber, int status) { - int status; int lpa; - int lpagb; - - status = phy_read(phydev, MII_M1011_PHY_STATUS); - if (status < 0) - return status; - - lpa = phy_read(phydev, MII_LPA); - if (lpa < 0) - return lpa; - - lpagb = phy_read(phydev, MII_STAT1000); - if (lpagb < 0) - return lpagb; - - if (status & MII_M1011_PHY_STATUS_FULLDUPLEX) - phydev->duplex = DUPLEX_FULL; - else - phydev->duplex = DUPLEX_HALF; - - status = status & MII_M1011_PHY_STATUS_SPD_MASK; - phydev->pause = 0; - phydev->asym_pause = 0; - - switch (status) { - case MII_M1011_PHY_STATUS_1000: - phydev->speed = SPEED_1000; - break; - - case MII_M1011_PHY_STATUS_100: - phydev->speed = SPEED_100; - break; - - default: - phydev->speed = SPEED_10; - break; - } + int err; if (!fiber) { - mii_lpa_to_linkmode_lpa_t(phydev->lp_advertising, lpa); - mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, lpagb); + err = genphy_read_lpa(phydev); + if (err < 0) + return err; - if (phydev->duplex == DUPLEX_FULL) { - phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0; - phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0; - } + phy_resolve_aneg_pause(phydev); } else { + lpa = phy_read(phydev, MII_LPA); + if (lpa < 0) + return lpa; + /* The fiber link is only 1000M capable */ fiber_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, lpa); @@ -1405,31 +1305,25 @@ static int marvell_read_status_page_an(struct phy_device *phydev, } } } - return 0; -} -static int marvell_read_status_page_fixed(struct phy_device *phydev) -{ - int bmcr = phy_read(phydev, MII_BMCR); - - if (bmcr < 0) - return bmcr; - - if (bmcr & BMCR_FULLDPLX) + if (status & MII_M1011_PHY_STATUS_FULLDUPLEX) phydev->duplex = DUPLEX_FULL; else phydev->duplex = DUPLEX_HALF; - if (bmcr & BMCR_SPEED1000) + switch (status & MII_M1011_PHY_STATUS_SPD_MASK) { + case MII_M1011_PHY_STATUS_1000: phydev->speed = SPEED_1000; - else if (bmcr & BMCR_SPEED100) + break; + + case MII_M1011_PHY_STATUS_100: phydev->speed = SPEED_100; - else - phydev->speed = SPEED_10; + break; - phydev->pause = 0; - phydev->asym_pause = 0; - linkmode_zero(phydev->lp_advertising); + default: + phydev->speed = SPEED_10; + break; + } return 0; } @@ -1444,25 +1338,38 @@ static int marvell_read_status_page_fixed(struct phy_device *phydev) */ static int marvell_read_status_page(struct phy_device *phydev, int page) { + int status; int fiber; int err; - /* Detect and update the link, but return if there - * was an error + status = phy_read(phydev, MII_M1011_PHY_STATUS); + if (status < 0) + return status; + + /* Use the generic register for copper link status, + * and the PHY status register for fiber link status. */ + if (page == MII_MARVELL_FIBER_PAGE) { + phydev->link = !!(status & MII_M1011_PHY_STATUS_LINK); + } else { + err = genphy_update_link(phydev); + if (err) + return err; + } + if (page == MII_MARVELL_FIBER_PAGE) fiber = 1; else fiber = 0; - err = marvell_update_link(phydev, fiber); - if (err) - return err; + linkmode_zero(phydev->lp_advertising); + phydev->pause = 0; + phydev->asym_pause = 0; if (phydev->autoneg == AUTONEG_ENABLE) - err = marvell_read_status_page_an(phydev, fiber); + err = marvell_read_status_page_an(phydev, fiber, status); else - err = marvell_read_status_page_fixed(phydev); + err = genphy_read_status_fixed(phydev); return err; } diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c index 1bf13017d288..64c9f3bba2cd 100644 --- a/drivers/net/phy/marvell10g.c +++ b/drivers/net/phy/marvell10g.c @@ -214,9 +214,9 @@ static int mv3310_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) phy_interface_t iface; sfp_parse_support(phydev->sfp_bus, id, support); - iface = sfp_select_interface(phydev->sfp_bus, id, support); + iface = sfp_select_interface(phydev->sfp_bus, support); - if (iface != PHY_INTERFACE_MODE_10GKR) { + if (iface != PHY_INTERFACE_MODE_10GBASER) { dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n"); return -EINVAL; } @@ -304,7 +304,7 @@ static int mv3310_config_init(struct phy_device *phydev) phydev->interface != PHY_INTERFACE_MODE_2500BASEX && phydev->interface != PHY_INTERFACE_MODE_XAUI && phydev->interface != PHY_INTERFACE_MODE_RXAUI && - phydev->interface != PHY_INTERFACE_MODE_10GKR) + phydev->interface != PHY_INTERFACE_MODE_10GBASER) return -ENODEV; return 0; @@ -386,16 +386,17 @@ static void mv3310_update_interface(struct phy_device *phydev) { if ((phydev->interface == PHY_INTERFACE_MODE_SGMII || phydev->interface == PHY_INTERFACE_MODE_2500BASEX || - phydev->interface == PHY_INTERFACE_MODE_10GKR) && phydev->link) { + phydev->interface == PHY_INTERFACE_MODE_10GBASER) && + phydev->link) { /* The PHY automatically switches its serdes interface (and - * active PHYXS instance) between Cisco SGMII, 10GBase-KR and + * active PHYXS instance) between Cisco SGMII, 10GBase-R and * 2500BaseX modes according to the speed. Florian suggests * setting phydev->interface to communicate this to the MAC. * Only do this if we are already in one of the above modes. */ switch (phydev->speed) { case SPEED_10000: - phydev->interface = PHY_INTERFACE_MODE_10GKR; + phydev->interface = PHY_INTERFACE_MODE_10GBASER; break; case SPEED_2500: phydev->interface = PHY_INTERFACE_MODE_2500BASEX; diff --git a/drivers/net/phy/mdio-i2c.c b/drivers/net/phy/mdio-i2c.c index 0dce67672548..0746e2cc39ae 100644 --- a/drivers/net/phy/mdio-i2c.c +++ b/drivers/net/phy/mdio-i2c.c @@ -33,17 +33,24 @@ static int i2c_mii_read(struct mii_bus *bus, int phy_id, int reg) { struct i2c_adapter *i2c = bus->priv; struct i2c_msg msgs[2]; - u8 data[2], dev_addr = reg; + u8 addr[3], data[2], *p; int bus_addr, ret; if (!i2c_mii_valid_phy_id(phy_id)) return 0xffff; + p = addr; + if (reg & MII_ADDR_C45) { + *p++ = 0x20 | ((reg >> 16) & 31); + *p++ = reg >> 8; + } + *p++ = reg; + bus_addr = i2c_mii_phy_addr(phy_id); msgs[0].addr = bus_addr; msgs[0].flags = 0; - msgs[0].len = 1; - msgs[0].buf = &dev_addr; + msgs[0].len = p - addr; + msgs[0].buf = addr; msgs[1].addr = bus_addr; msgs[1].flags = I2C_M_RD; msgs[1].len = sizeof(data); @@ -61,18 +68,23 @@ static int i2c_mii_write(struct mii_bus *bus, int phy_id, int reg, u16 val) struct i2c_adapter *i2c = bus->priv; struct i2c_msg msg; int ret; - u8 data[3]; + u8 data[5], *p; if (!i2c_mii_valid_phy_id(phy_id)) return 0; - data[0] = reg; - data[1] = val >> 8; - data[2] = val; + p = data; + if (reg & MII_ADDR_C45) { + *p++ = (reg >> 16) & 31; + *p++ = reg >> 8; + } + *p++ = reg; + *p++ = val >> 8; + *p++ = val; msg.addr = i2c_mii_phy_addr(phy_id); msg.flags = 0; - msg.len = 3; + msg.len = p - data; msg.buf = data; ret = i2c_transfer(i2c, &msg, 1); diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 229e480179ff..9bb9f37f21dc 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -59,17 +59,11 @@ static int mdiobus_register_gpiod(struct mdio_device *mdiodev) static int mdiobus_register_reset(struct mdio_device *mdiodev) { - struct reset_control *reset = NULL; - - if (mdiodev->dev.of_node) - reset = of_reset_control_get_exclusive(mdiodev->dev.of_node, - "phy"); - if (IS_ERR(reset)) { - if (PTR_ERR(reset) == -ENOENT || PTR_ERR(reset) == -ENOTSUPP) - reset = NULL; - else - return PTR_ERR(reset); - } + struct reset_control *reset; + + reset = reset_control_get_optional_exclusive(&mdiodev->dev, "phy"); + if (IS_ERR(reset)) + return PTR_ERR(reset); mdiodev->reset_ctrl = reset; @@ -164,9 +158,11 @@ struct mii_bus *mdiobus_alloc_size(size_t size) if (size) bus->priv = (void *)bus + aligned_size; - /* Initialise the interrupts to polling */ - for (i = 0; i < PHY_MAX_ADDR; i++) + /* Initialise the interrupts to polling and 64-bit seqcounts */ + for (i = 0; i < PHY_MAX_ADDR; i++) { bus->irq[i] = PHY_POLL; + u64_stats_init(&bus->stats[i].syncp); + } return bus; } @@ -255,9 +251,215 @@ static void mdiobus_release(struct device *d) kfree(bus); } +struct mdio_bus_stat_attr { + int addr; + unsigned int field_offset; +}; + +static u64 mdio_bus_get_stat(struct mdio_bus_stats *s, unsigned int offset) +{ + const char *p = (const char *)s + offset; + unsigned int start; + u64 val = 0; + + do { + start = u64_stats_fetch_begin(&s->syncp); + val = u64_stats_read((const u64_stats_t *)p); + } while (u64_stats_fetch_retry(&s->syncp, start)); + + return val; +} + +static u64 mdio_bus_get_global_stat(struct mii_bus *bus, unsigned int offset) +{ + unsigned int i; + u64 val = 0; + + for (i = 0; i < PHY_MAX_ADDR; i++) + val += mdio_bus_get_stat(&bus->stats[i], offset); + + return val; +} + +static ssize_t mdio_bus_stat_field_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct mii_bus *bus = to_mii_bus(dev); + struct mdio_bus_stat_attr *sattr; + struct dev_ext_attribute *eattr; + u64 val; + + eattr = container_of(attr, struct dev_ext_attribute, attr); + sattr = eattr->var; + + if (sattr->addr < 0) + val = mdio_bus_get_global_stat(bus, sattr->field_offset); + else + val = mdio_bus_get_stat(&bus->stats[sattr->addr], + sattr->field_offset); + + return sprintf(buf, "%llu\n", val); +} + +static ssize_t mdio_bus_device_stat_field_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct mdio_device *mdiodev = to_mdio_device(dev); + struct mii_bus *bus = mdiodev->bus; + struct mdio_bus_stat_attr *sattr; + struct dev_ext_attribute *eattr; + int addr = mdiodev->addr; + u64 val; + + eattr = container_of(attr, struct dev_ext_attribute, attr); + sattr = eattr->var; + + val = mdio_bus_get_stat(&bus->stats[addr], sattr->field_offset); + + return sprintf(buf, "%llu\n", val); +} + +#define MDIO_BUS_STATS_ATTR_DECL(field, file) \ +static struct dev_ext_attribute dev_attr_mdio_bus_##field = { \ + .attr = { .attr = { .name = file, .mode = 0444 }, \ + .show = mdio_bus_stat_field_show, \ + }, \ + .var = &((struct mdio_bus_stat_attr) { \ + -1, offsetof(struct mdio_bus_stats, field) \ + }), \ +}; \ +static struct dev_ext_attribute dev_attr_mdio_bus_device_##field = { \ + .attr = { .attr = { .name = file, .mode = 0444 }, \ + .show = mdio_bus_device_stat_field_show, \ + }, \ + .var = &((struct mdio_bus_stat_attr) { \ + -1, offsetof(struct mdio_bus_stats, field) \ + }), \ +}; + +#define MDIO_BUS_STATS_ATTR(field) \ + MDIO_BUS_STATS_ATTR_DECL(field, __stringify(field)) + +MDIO_BUS_STATS_ATTR(transfers); +MDIO_BUS_STATS_ATTR(errors); +MDIO_BUS_STATS_ATTR(writes); +MDIO_BUS_STATS_ATTR(reads); + +#define MDIO_BUS_STATS_ADDR_ATTR_DECL(field, addr, file) \ +static struct dev_ext_attribute dev_attr_mdio_bus_addr_##field##_##addr = { \ + .attr = { .attr = { .name = file, .mode = 0444 }, \ + .show = mdio_bus_stat_field_show, \ + }, \ + .var = &((struct mdio_bus_stat_attr) { \ + addr, offsetof(struct mdio_bus_stats, field) \ + }), \ +} + +#define MDIO_BUS_STATS_ADDR_ATTR(field, addr) \ + MDIO_BUS_STATS_ADDR_ATTR_DECL(field, addr, \ + __stringify(field) "_" __stringify(addr)) + +#define MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(addr) \ + MDIO_BUS_STATS_ADDR_ATTR(transfers, addr); \ + MDIO_BUS_STATS_ADDR_ATTR(errors, addr); \ + MDIO_BUS_STATS_ADDR_ATTR(writes, addr); \ + MDIO_BUS_STATS_ADDR_ATTR(reads, addr) \ + +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(0); +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(1); +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(2); +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(3); +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(4); +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(5); +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(6); +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(7); +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(8); +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(9); +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(10); +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(11); +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(12); +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(13); +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(14); +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(15); +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(16); +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(17); +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(18); +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(19); +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(20); +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(21); +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(22); +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(23); +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(24); +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(25); +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(26); +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(27); +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(28); +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(29); +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(30); +MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(31); + +#define MDIO_BUS_STATS_ADDR_ATTR_GROUP(addr) \ + &dev_attr_mdio_bus_addr_transfers_##addr.attr.attr, \ + &dev_attr_mdio_bus_addr_errors_##addr.attr.attr, \ + &dev_attr_mdio_bus_addr_writes_##addr.attr.attr, \ + &dev_attr_mdio_bus_addr_reads_##addr.attr.attr \ + +static struct attribute *mdio_bus_statistics_attrs[] = { + &dev_attr_mdio_bus_transfers.attr.attr, + &dev_attr_mdio_bus_errors.attr.attr, + &dev_attr_mdio_bus_writes.attr.attr, + &dev_attr_mdio_bus_reads.attr.attr, + MDIO_BUS_STATS_ADDR_ATTR_GROUP(0), + MDIO_BUS_STATS_ADDR_ATTR_GROUP(1), + MDIO_BUS_STATS_ADDR_ATTR_GROUP(2), + MDIO_BUS_STATS_ADDR_ATTR_GROUP(3), + MDIO_BUS_STATS_ADDR_ATTR_GROUP(4), + MDIO_BUS_STATS_ADDR_ATTR_GROUP(5), + MDIO_BUS_STATS_ADDR_ATTR_GROUP(6), + MDIO_BUS_STATS_ADDR_ATTR_GROUP(7), + MDIO_BUS_STATS_ADDR_ATTR_GROUP(8), + MDIO_BUS_STATS_ADDR_ATTR_GROUP(9), + MDIO_BUS_STATS_ADDR_ATTR_GROUP(10), + MDIO_BUS_STATS_ADDR_ATTR_GROUP(11), + MDIO_BUS_STATS_ADDR_ATTR_GROUP(12), + MDIO_BUS_STATS_ADDR_ATTR_GROUP(13), + MDIO_BUS_STATS_ADDR_ATTR_GROUP(14), + MDIO_BUS_STATS_ADDR_ATTR_GROUP(15), + MDIO_BUS_STATS_ADDR_ATTR_GROUP(16), + MDIO_BUS_STATS_ADDR_ATTR_GROUP(17), + MDIO_BUS_STATS_ADDR_ATTR_GROUP(18), + MDIO_BUS_STATS_ADDR_ATTR_GROUP(19), + MDIO_BUS_STATS_ADDR_ATTR_GROUP(20), + MDIO_BUS_STATS_ADDR_ATTR_GROUP(21), + MDIO_BUS_STATS_ADDR_ATTR_GROUP(22), + MDIO_BUS_STATS_ADDR_ATTR_GROUP(23), + MDIO_BUS_STATS_ADDR_ATTR_GROUP(24), + MDIO_BUS_STATS_ADDR_ATTR_GROUP(25), + MDIO_BUS_STATS_ADDR_ATTR_GROUP(26), + MDIO_BUS_STATS_ADDR_ATTR_GROUP(27), + MDIO_BUS_STATS_ADDR_ATTR_GROUP(28), + MDIO_BUS_STATS_ADDR_ATTR_GROUP(29), + MDIO_BUS_STATS_ADDR_ATTR_GROUP(30), + MDIO_BUS_STATS_ADDR_ATTR_GROUP(31), + NULL, +}; + +static const struct attribute_group mdio_bus_statistics_group = { + .name = "statistics", + .attrs = mdio_bus_statistics_attrs, +}; + +static const struct attribute_group *mdio_bus_groups[] = { + &mdio_bus_statistics_group, + NULL, +}; + static struct class mdio_bus_class = { .name = "mdio_bus", .dev_release = mdiobus_release, + .dev_groups = mdio_bus_groups, }; #if IS_ENABLED(CONFIG_OF_MDIO) @@ -536,6 +738,24 @@ struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr) } EXPORT_SYMBOL(mdiobus_scan); +static void mdiobus_stats_acct(struct mdio_bus_stats *stats, bool op, int ret) +{ + u64_stats_update_begin(&stats->syncp); + + u64_stats_inc(&stats->transfers); + if (ret < 0) { + u64_stats_inc(&stats->errors); + goto out; + } + + if (op) + u64_stats_inc(&stats->reads); + else + u64_stats_inc(&stats->writes); +out: + u64_stats_update_end(&stats->syncp); +} + /** * __mdiobus_read - Unlocked version of the mdiobus_read function * @bus: the mii_bus struct @@ -555,6 +775,7 @@ int __mdiobus_read(struct mii_bus *bus, int addr, u32 regnum) retval = bus->read(bus, addr, regnum); trace_mdio_access(bus, 1, addr, regnum, retval, retval); + mdiobus_stats_acct(&bus->stats[addr], true, retval); return retval; } @@ -580,6 +801,7 @@ int __mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val) err = bus->write(bus, addr, regnum, val); trace_mdio_access(bus, 0, addr, regnum, val, err); + mdiobus_stats_acct(&bus->stats[addr], false, err); return err; } @@ -725,8 +947,27 @@ static int mdio_uevent(struct device *dev, struct kobj_uevent_env *env) return 0; } +static struct attribute *mdio_bus_device_statistics_attrs[] = { + &dev_attr_mdio_bus_device_transfers.attr.attr, + &dev_attr_mdio_bus_device_errors.attr.attr, + &dev_attr_mdio_bus_device_writes.attr.attr, + &dev_attr_mdio_bus_device_reads.attr.attr, + NULL, +}; + +static const struct attribute_group mdio_bus_device_statistics_group = { + .name = "statistics", + .attrs = mdio_bus_device_statistics_attrs, +}; + +static const struct attribute_group *mdio_bus_dev_groups[] = { + &mdio_bus_device_statistics_group, + NULL, +}; + struct bus_type mdio_bus_type = { .name = "mdio_bus", + .dev_groups = mdio_bus_dev_groups, .match = mdio_bus_match, .uevent = mdio_uevent, }; diff --git a/drivers/net/phy/mii_timestamper.c b/drivers/net/phy/mii_timestamper.c new file mode 100644 index 000000000000..2f12c5d901df --- /dev/null +++ b/drivers/net/phy/mii_timestamper.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Support for generic time stamping devices on MII buses. +// Copyright (C) 2018 Richard Cochran <richardcochran@gmail.com> +// + +#include <linux/mii_timestamper.h> + +static LIST_HEAD(mii_timestamping_devices); +static DEFINE_MUTEX(tstamping_devices_lock); + +struct mii_timestamping_desc { + struct list_head list; + struct mii_timestamping_ctrl *ctrl; + struct device *device; +}; + +/** + * register_mii_tstamp_controller() - registers an MII time stamping device. + * + * @device: The device to be registered. + * @ctrl: Pointer to device's control interface. + * + * Returns zero on success or non-zero on failure. + */ +int register_mii_tstamp_controller(struct device *device, + struct mii_timestamping_ctrl *ctrl) +{ + struct mii_timestamping_desc *desc; + + desc = kzalloc(sizeof(*desc), GFP_KERNEL); + if (!desc) + return -ENOMEM; + + INIT_LIST_HEAD(&desc->list); + desc->ctrl = ctrl; + desc->device = device; + + mutex_lock(&tstamping_devices_lock); + list_add_tail(&mii_timestamping_devices, &desc->list); + mutex_unlock(&tstamping_devices_lock); + + return 0; +} +EXPORT_SYMBOL(register_mii_tstamp_controller); + +/** + * unregister_mii_tstamp_controller() - unregisters an MII time stamping device. + * + * @device: A device previously passed to register_mii_tstamp_controller(). + */ +void unregister_mii_tstamp_controller(struct device *device) +{ + struct mii_timestamping_desc *desc; + struct list_head *this, *next; + + mutex_lock(&tstamping_devices_lock); + list_for_each_safe(this, next, &mii_timestamping_devices) { + desc = list_entry(this, struct mii_timestamping_desc, list); + if (desc->device == device) { + list_del_init(&desc->list); + kfree(desc); + break; + } + } + mutex_unlock(&tstamping_devices_lock); +} +EXPORT_SYMBOL(unregister_mii_tstamp_controller); + +/** + * register_mii_timestamper - Enables a given port of an MII time stamper. + * + * @node: The device tree node of the MII time stamp controller. + * @port: The index of the port to be enabled. + * + * Returns a valid interface on success or ERR_PTR otherwise. + */ +struct mii_timestamper *register_mii_timestamper(struct device_node *node, + unsigned int port) +{ + struct mii_timestamper *mii_ts = NULL; + struct mii_timestamping_desc *desc; + struct list_head *this; + + mutex_lock(&tstamping_devices_lock); + list_for_each(this, &mii_timestamping_devices) { + desc = list_entry(this, struct mii_timestamping_desc, list); + if (desc->device->of_node == node) { + mii_ts = desc->ctrl->probe_channel(desc->device, port); + if (!IS_ERR(mii_ts)) { + mii_ts->device = desc->device; + get_device(desc->device); + } + break; + } + } + mutex_unlock(&tstamping_devices_lock); + + return mii_ts ? mii_ts : ERR_PTR(-EPROBE_DEFER); +} +EXPORT_SYMBOL(register_mii_timestamper); + +/** + * unregister_mii_timestamper - Disables a given MII time stamper. + * + * @mii_ts: An interface obtained via register_mii_timestamper(). + * + */ +void unregister_mii_timestamper(struct mii_timestamper *mii_ts) +{ + struct mii_timestamping_desc *desc; + struct list_head *this; + + mutex_lock(&tstamping_devices_lock); + list_for_each(this, &mii_timestamping_devices) { + desc = list_entry(this, struct mii_timestamping_desc, list); + if (desc->device == mii_ts->device) { + desc->ctrl->release_channel(desc->device, mii_ts); + put_device(desc->device); + break; + } + } + mutex_unlock(&tstamping_devices_lock); +} +EXPORT_SYMBOL(unregister_mii_timestamper); diff --git a/drivers/net/phy/mscc.c b/drivers/net/phy/mscc.c index d5f8f351d9ef..937ac7da2789 100644 --- a/drivers/net/phy/mscc.c +++ b/drivers/net/phy/mscc.c @@ -18,6 +18,17 @@ #include <linux/netdevice.h> #include <dt-bindings/net/mscc-phy-vsc8531.h> +#include <linux/scatterlist.h> +#include <crypto/skcipher.h> + +#if IS_ENABLED(CONFIG_MACSEC) +#include <net/macsec.h> +#endif + +#include "mscc_macsec.h" +#include "mscc_mac.h" +#include "mscc_fc_buffer.h" + enum rgmii_rx_clock_delay { RGMII_RX_CLK_DELAY_0_2_NS = 0, RGMII_RX_CLK_DELAY_0_8_NS = 1, @@ -69,7 +80,7 @@ enum rgmii_rx_clock_delay { #define MSCC_PHY_EXT_PHY_CNTL_2 24 #define MII_VSC85XX_INT_MASK 25 -#define MII_VSC85XX_INT_MASK_MASK 0xa000 +#define MII_VSC85XX_INT_MASK_MASK 0xa020 #define MII_VSC85XX_INT_MASK_WOL 0x0040 #define MII_VSC85XX_INT_STATUS 26 @@ -121,6 +132,26 @@ enum rgmii_rx_clock_delay { #define PHY_S6G_PLL_FSM_CTRL_DATA_POS 8 #define PHY_S6G_PLL_FSM_ENA_POS 7 +#define MSCC_EXT_PAGE_MACSEC_17 17 +#define MSCC_EXT_PAGE_MACSEC_18 18 + +#define MSCC_EXT_PAGE_MACSEC_19 19 +#define MSCC_PHY_MACSEC_19_REG_ADDR(x) (x) +#define MSCC_PHY_MACSEC_19_TARGET(x) ((x) << 12) +#define MSCC_PHY_MACSEC_19_READ BIT(14) +#define MSCC_PHY_MACSEC_19_CMD BIT(15) + +#define MSCC_EXT_PAGE_MACSEC_20 20 +#define MSCC_PHY_MACSEC_20_TARGET(x) (x) +enum macsec_bank { + FC_BUFFER = 0x04, + HOST_MAC = 0x05, + LINE_MAC = 0x06, + IP_1588 = 0x0e, + MACSEC_INGR = 0x38, + MACSEC_EGR = 0x3c, +}; + #define MSCC_EXT_PAGE_ACCESS 31 #define MSCC_PHY_PAGE_STANDARD 0x0000 /* Standard registers */ #define MSCC_PHY_PAGE_EXTENDED 0x0001 /* Extended registers */ @@ -128,6 +159,7 @@ enum rgmii_rx_clock_delay { #define MSCC_PHY_PAGE_EXTENDED_3 0x0003 /* Extended reg - page 3 */ #define MSCC_PHY_PAGE_EXTENDED_4 0x0004 /* Extended reg - page 4 */ #define MSCC_PHY_PAGE_CSR_CNTL MSCC_PHY_PAGE_EXTENDED_4 +#define MSCC_PHY_PAGE_MACSEC MSCC_PHY_PAGE_EXTENDED_4 /* Extended reg - GPIO; this is a bank of registers that are shared for all PHYs * in the same package. */ @@ -175,6 +207,9 @@ enum rgmii_rx_clock_delay { #define SECURE_ON_ENABLE 0x8000 #define SECURE_ON_PASSWD_LEN_4 0x4000 +#define MSCC_PHY_EXTENDED_INT 28 +#define MSCC_PHY_EXTENDED_INT_MS_EGR BIT(9) + /* Extended Page 3 Registers */ #define MSCC_PHY_SERDES_TX_VALID_CNT 21 #define MSCC_PHY_SERDES_TX_CRC_ERR_CNT 22 @@ -411,6 +446,44 @@ static const struct vsc85xx_hw_stat vsc8584_hw_stats[] = { }, }; +#if IS_ENABLED(CONFIG_MACSEC) +struct macsec_flow { + struct list_head list; + enum mscc_macsec_destination_ports port; + enum macsec_bank bank; + u32 index; + int assoc_num; + bool has_transformation; + + /* Highest takes precedence [0..15] */ + u8 priority; + + u8 key[MACSEC_KEYID_LEN]; + + union { + struct macsec_rx_sa *rx_sa; + struct macsec_tx_sa *tx_sa; + }; + + /* Matching */ + struct { + u8 sci:1; + u8 tagged:1; + u8 untagged:1; + u8 etype:1; + } match; + + u16 etype; + + /* Action */ + struct { + u8 bypass:1; + u8 drop:1; + } action; + +}; +#endif + struct vsc8531_private { int rate_magic; u16 supp_led_modes; @@ -424,6 +497,19 @@ struct vsc8531_private { * package. */ unsigned int base_addr; + +#if IS_ENABLED(CONFIG_MACSEC) + /* MACsec fields: + * - One SecY per device (enforced at the s/w implementation level) + * - macsec_flows: list of h/w flows + * - ingr_flows: bitmap of ingress flows + * - egr_flows: bitmap of egress flows + */ + struct macsec_secy *secy; + struct list_head macsec_flows; + unsigned long ingr_flows; + unsigned long egr_flows; +#endif }; #ifdef CONFIG_OF_MDIO @@ -1584,6 +1670,978 @@ out: return ret; } +#if IS_ENABLED(CONFIG_MACSEC) +static u32 vsc8584_macsec_phy_read(struct phy_device *phydev, + enum macsec_bank bank, u32 reg) +{ + u32 val, val_l = 0, val_h = 0; + unsigned long deadline; + int rc; + + rc = phy_select_page(phydev, MSCC_PHY_PAGE_MACSEC); + if (rc < 0) + goto failed; + + __phy_write(phydev, MSCC_EXT_PAGE_MACSEC_20, + MSCC_PHY_MACSEC_20_TARGET(bank >> 2)); + + if (bank >> 2 == 0x1) + /* non-MACsec access */ + bank &= 0x3; + else + bank = 0; + + __phy_write(phydev, MSCC_EXT_PAGE_MACSEC_19, + MSCC_PHY_MACSEC_19_CMD | MSCC_PHY_MACSEC_19_READ | + MSCC_PHY_MACSEC_19_REG_ADDR(reg) | + MSCC_PHY_MACSEC_19_TARGET(bank)); + + deadline = jiffies + msecs_to_jiffies(PROC_CMD_NCOMPLETED_TIMEOUT_MS); + do { + val = __phy_read(phydev, MSCC_EXT_PAGE_MACSEC_19); + } while (time_before(jiffies, deadline) && !(val & MSCC_PHY_MACSEC_19_CMD)); + + val_l = __phy_read(phydev, MSCC_EXT_PAGE_MACSEC_17); + val_h = __phy_read(phydev, MSCC_EXT_PAGE_MACSEC_18); + +failed: + phy_restore_page(phydev, rc, rc); + + return (val_h << 16) | val_l; +} + +static void vsc8584_macsec_phy_write(struct phy_device *phydev, + enum macsec_bank bank, u32 reg, u32 val) +{ + unsigned long deadline; + int rc; + + rc = phy_select_page(phydev, MSCC_PHY_PAGE_MACSEC); + if (rc < 0) + goto failed; + + __phy_write(phydev, MSCC_EXT_PAGE_MACSEC_20, + MSCC_PHY_MACSEC_20_TARGET(bank >> 2)); + + if ((bank >> 2 == 0x1) || (bank >> 2 == 0x3)) + bank &= 0x3; + else + /* MACsec access */ + bank = 0; + + __phy_write(phydev, MSCC_EXT_PAGE_MACSEC_17, (u16)val); + __phy_write(phydev, MSCC_EXT_PAGE_MACSEC_18, (u16)(val >> 16)); + + __phy_write(phydev, MSCC_EXT_PAGE_MACSEC_19, + MSCC_PHY_MACSEC_19_CMD | MSCC_PHY_MACSEC_19_REG_ADDR(reg) | + MSCC_PHY_MACSEC_19_TARGET(bank)); + + deadline = jiffies + msecs_to_jiffies(PROC_CMD_NCOMPLETED_TIMEOUT_MS); + do { + val = __phy_read(phydev, MSCC_EXT_PAGE_MACSEC_19); + } while (time_before(jiffies, deadline) && !(val & MSCC_PHY_MACSEC_19_CMD)); + +failed: + phy_restore_page(phydev, rc, rc); +} + +static void vsc8584_macsec_classification(struct phy_device *phydev, + enum macsec_bank bank) +{ + /* enable VLAN tag parsing */ + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_CP_TAG, + MSCC_MS_SAM_CP_TAG_PARSE_STAG | + MSCC_MS_SAM_CP_TAG_PARSE_QTAG | + MSCC_MS_SAM_CP_TAG_PARSE_QINQ); +} + +static void vsc8584_macsec_flow_default_action(struct phy_device *phydev, + enum macsec_bank bank, + bool block) +{ + u32 port = (bank == MACSEC_INGR) ? + MSCC_MS_PORT_UNCONTROLLED : MSCC_MS_PORT_COMMON; + u32 action = MSCC_MS_FLOW_BYPASS; + + if (block) + action = MSCC_MS_FLOW_DROP; + + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_NM_FLOW_NCP, + /* MACsec untagged */ + MSCC_MS_SAM_NM_FLOW_NCP_UNTAGGED_FLOW_TYPE(action) | + MSCC_MS_SAM_NM_FLOW_NCP_UNTAGGED_DROP_ACTION(MSCC_MS_ACTION_DROP) | + MSCC_MS_SAM_NM_FLOW_NCP_UNTAGGED_DEST_PORT(port) | + /* MACsec tagged */ + MSCC_MS_SAM_NM_FLOW_NCP_TAGGED_FLOW_TYPE(action) | + MSCC_MS_SAM_NM_FLOW_NCP_TAGGED_DROP_ACTION(MSCC_MS_ACTION_DROP) | + MSCC_MS_SAM_NM_FLOW_NCP_TAGGED_DEST_PORT(port) | + /* Bad tag */ + MSCC_MS_SAM_NM_FLOW_NCP_BADTAG_FLOW_TYPE(action) | + MSCC_MS_SAM_NM_FLOW_NCP_BADTAG_DROP_ACTION(MSCC_MS_ACTION_DROP) | + MSCC_MS_SAM_NM_FLOW_NCP_BADTAG_DEST_PORT(port) | + /* Kay tag */ + MSCC_MS_SAM_NM_FLOW_NCP_KAY_FLOW_TYPE(action) | + MSCC_MS_SAM_NM_FLOW_NCP_KAY_DROP_ACTION(MSCC_MS_ACTION_DROP) | + MSCC_MS_SAM_NM_FLOW_NCP_KAY_DEST_PORT(port)); + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_NM_FLOW_CP, + /* MACsec untagged */ + MSCC_MS_SAM_NM_FLOW_NCP_UNTAGGED_FLOW_TYPE(action) | + MSCC_MS_SAM_NM_FLOW_CP_UNTAGGED_DROP_ACTION(MSCC_MS_ACTION_DROP) | + MSCC_MS_SAM_NM_FLOW_CP_UNTAGGED_DEST_PORT(port) | + /* MACsec tagged */ + MSCC_MS_SAM_NM_FLOW_NCP_TAGGED_FLOW_TYPE(action) | + MSCC_MS_SAM_NM_FLOW_CP_TAGGED_DROP_ACTION(MSCC_MS_ACTION_DROP) | + MSCC_MS_SAM_NM_FLOW_CP_TAGGED_DEST_PORT(port) | + /* Bad tag */ + MSCC_MS_SAM_NM_FLOW_NCP_BADTAG_FLOW_TYPE(action) | + MSCC_MS_SAM_NM_FLOW_CP_BADTAG_DROP_ACTION(MSCC_MS_ACTION_DROP) | + MSCC_MS_SAM_NM_FLOW_CP_BADTAG_DEST_PORT(port) | + /* Kay tag */ + MSCC_MS_SAM_NM_FLOW_NCP_KAY_FLOW_TYPE(action) | + MSCC_MS_SAM_NM_FLOW_CP_KAY_DROP_ACTION(MSCC_MS_ACTION_DROP) | + MSCC_MS_SAM_NM_FLOW_CP_KAY_DEST_PORT(port)); +} + +static void vsc8584_macsec_integrity_checks(struct phy_device *phydev, + enum macsec_bank bank) +{ + u32 val; + + if (bank != MACSEC_INGR) + return; + + /* Set default rules to pass unmatched frames */ + val = vsc8584_macsec_phy_read(phydev, bank, + MSCC_MS_PARAMS2_IG_CC_CONTROL); + val |= MSCC_MS_PARAMS2_IG_CC_CONTROL_NON_MATCH_CTRL_ACT | + MSCC_MS_PARAMS2_IG_CC_CONTROL_NON_MATCH_ACT; + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_PARAMS2_IG_CC_CONTROL, + val); + + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_PARAMS2_IG_CP_TAG, + MSCC_MS_PARAMS2_IG_CP_TAG_PARSE_STAG | + MSCC_MS_PARAMS2_IG_CP_TAG_PARSE_QTAG | + MSCC_MS_PARAMS2_IG_CP_TAG_PARSE_QINQ); +} + +static void vsc8584_macsec_block_init(struct phy_device *phydev, + enum macsec_bank bank) +{ + u32 val; + int i; + + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_ENA_CFG, + MSCC_MS_ENA_CFG_SW_RST | + MSCC_MS_ENA_CFG_MACSEC_BYPASS_ENA); + + /* Set the MACsec block out of s/w reset and enable clocks */ + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_ENA_CFG, + MSCC_MS_ENA_CFG_CLK_ENA); + + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_STATUS_CONTEXT_CTRL, + bank == MACSEC_INGR ? 0xe5880214 : 0xe5880218); + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_MISC_CONTROL, + MSCC_MS_MISC_CONTROL_MC_LATENCY_FIX(bank == MACSEC_INGR ? 57 : 40) | + MSCC_MS_MISC_CONTROL_XFORM_REC_SIZE(bank == MACSEC_INGR ? 1 : 2)); + + /* Clear the counters */ + val = vsc8584_macsec_phy_read(phydev, bank, MSCC_MS_COUNT_CONTROL); + val |= MSCC_MS_COUNT_CONTROL_AUTO_CNTR_RESET; + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_COUNT_CONTROL, val); + + /* Enable octet increment mode */ + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_PP_CTRL, + MSCC_MS_PP_CTRL_MACSEC_OCTET_INCR_MODE); + + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_BLOCK_CTX_UPDATE, 0x3); + + val = vsc8584_macsec_phy_read(phydev, bank, MSCC_MS_COUNT_CONTROL); + val |= MSCC_MS_COUNT_CONTROL_RESET_ALL; + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_COUNT_CONTROL, val); + + /* Set the MTU */ + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_NON_VLAN_MTU_CHECK, + MSCC_MS_NON_VLAN_MTU_CHECK_NV_MTU_COMPARE(32761) | + MSCC_MS_NON_VLAN_MTU_CHECK_NV_MTU_COMP_DROP); + + for (i = 0; i < 8; i++) + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_VLAN_MTU_CHECK(i), + MSCC_MS_VLAN_MTU_CHECK_MTU_COMPARE(32761) | + MSCC_MS_VLAN_MTU_CHECK_MTU_COMP_DROP); + + if (bank == MACSEC_EGR) { + val = vsc8584_macsec_phy_read(phydev, bank, MSCC_MS_INTR_CTRL_STATUS); + val &= ~MSCC_MS_INTR_CTRL_STATUS_INTR_ENABLE_M; + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_INTR_CTRL_STATUS, val); + + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_FC_CFG, + MSCC_MS_FC_CFG_FCBUF_ENA | + MSCC_MS_FC_CFG_LOW_THRESH(0x1) | + MSCC_MS_FC_CFG_HIGH_THRESH(0x4) | + MSCC_MS_FC_CFG_LOW_BYTES_VAL(0x4) | + MSCC_MS_FC_CFG_HIGH_BYTES_VAL(0x6)); + } + + vsc8584_macsec_classification(phydev, bank); + vsc8584_macsec_flow_default_action(phydev, bank, false); + vsc8584_macsec_integrity_checks(phydev, bank); + + /* Enable the MACsec block */ + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_ENA_CFG, + MSCC_MS_ENA_CFG_CLK_ENA | + MSCC_MS_ENA_CFG_MACSEC_ENA | + MSCC_MS_ENA_CFG_MACSEC_SPEED_MODE(0x5)); +} + +static void vsc8584_macsec_mac_init(struct phy_device *phydev, + enum macsec_bank bank) +{ + u32 val; + int i; + + /* Clear host & line stats */ + for (i = 0; i < 36; i++) + vsc8584_macsec_phy_write(phydev, bank, 0x1c + i, 0); + + val = vsc8584_macsec_phy_read(phydev, bank, + MSCC_MAC_PAUSE_CFG_TX_FRAME_CTRL); + val &= ~MSCC_MAC_PAUSE_CFG_TX_FRAME_CTRL_PAUSE_MODE_M; + val |= MSCC_MAC_PAUSE_CFG_TX_FRAME_CTRL_PAUSE_MODE(2) | + MSCC_MAC_PAUSE_CFG_TX_FRAME_CTRL_PAUSE_VALUE(0xffff); + vsc8584_macsec_phy_write(phydev, bank, + MSCC_MAC_PAUSE_CFG_TX_FRAME_CTRL, val); + + val = vsc8584_macsec_phy_read(phydev, bank, + MSCC_MAC_PAUSE_CFG_TX_FRAME_CTRL_2); + val |= 0xffff; + vsc8584_macsec_phy_write(phydev, bank, + MSCC_MAC_PAUSE_CFG_TX_FRAME_CTRL_2, val); + + val = vsc8584_macsec_phy_read(phydev, bank, + MSCC_MAC_PAUSE_CFG_RX_FRAME_CTRL); + if (bank == HOST_MAC) + val |= MSCC_MAC_PAUSE_CFG_RX_FRAME_CTRL_PAUSE_TIMER_ENA | + MSCC_MAC_PAUSE_CFG_RX_FRAME_CTRL_PAUSE_FRAME_DROP_ENA; + else + val |= MSCC_MAC_PAUSE_CFG_RX_FRAME_CTRL_PAUSE_REACT_ENA | + MSCC_MAC_PAUSE_CFG_RX_FRAME_CTRL_PAUSE_FRAME_DROP_ENA | + MSCC_MAC_PAUSE_CFG_RX_FRAME_CTRL_PAUSE_MODE | + MSCC_MAC_PAUSE_CFG_RX_FRAME_CTRL_EARLY_PAUSE_DETECT_ENA; + vsc8584_macsec_phy_write(phydev, bank, + MSCC_MAC_PAUSE_CFG_RX_FRAME_CTRL, val); + + vsc8584_macsec_phy_write(phydev, bank, MSCC_MAC_CFG_PKTINF_CFG, + MSCC_MAC_CFG_PKTINF_CFG_STRIP_FCS_ENA | + MSCC_MAC_CFG_PKTINF_CFG_INSERT_FCS_ENA | + MSCC_MAC_CFG_PKTINF_CFG_LPI_RELAY_ENA | + MSCC_MAC_CFG_PKTINF_CFG_STRIP_PREAMBLE_ENA | + MSCC_MAC_CFG_PKTINF_CFG_INSERT_PREAMBLE_ENA | + (bank == HOST_MAC ? + MSCC_MAC_CFG_PKTINF_CFG_ENABLE_TX_PADDING : 0)); + + val = vsc8584_macsec_phy_read(phydev, bank, MSCC_MAC_CFG_MODE_CFG); + val &= ~MSCC_MAC_CFG_MODE_CFG_DISABLE_DIC; + vsc8584_macsec_phy_write(phydev, bank, MSCC_MAC_CFG_MODE_CFG, val); + + val = vsc8584_macsec_phy_read(phydev, bank, MSCC_MAC_CFG_MAXLEN_CFG); + val &= ~MSCC_MAC_CFG_MAXLEN_CFG_MAX_LEN_M; + val |= MSCC_MAC_CFG_MAXLEN_CFG_MAX_LEN(10240); + vsc8584_macsec_phy_write(phydev, bank, MSCC_MAC_CFG_MAXLEN_CFG, val); + + vsc8584_macsec_phy_write(phydev, bank, MSCC_MAC_CFG_ADV_CHK_CFG, + MSCC_MAC_CFG_ADV_CHK_CFG_SFD_CHK_ENA | + MSCC_MAC_CFG_ADV_CHK_CFG_PRM_CHK_ENA | + MSCC_MAC_CFG_ADV_CHK_CFG_OOR_ERR_ENA | + MSCC_MAC_CFG_ADV_CHK_CFG_INR_ERR_ENA); + + val = vsc8584_macsec_phy_read(phydev, bank, MSCC_MAC_CFG_LFS_CFG); + val &= ~MSCC_MAC_CFG_LFS_CFG_LFS_MODE_ENA; + vsc8584_macsec_phy_write(phydev, bank, MSCC_MAC_CFG_LFS_CFG, val); + + vsc8584_macsec_phy_write(phydev, bank, MSCC_MAC_CFG_ENA_CFG, + MSCC_MAC_CFG_ENA_CFG_RX_CLK_ENA | + MSCC_MAC_CFG_ENA_CFG_TX_CLK_ENA | + MSCC_MAC_CFG_ENA_CFG_RX_ENA | + MSCC_MAC_CFG_ENA_CFG_TX_ENA); +} + +/* Must be called with mdio_lock taken */ +static int vsc8584_macsec_init(struct phy_device *phydev) +{ + u32 val; + + vsc8584_macsec_block_init(phydev, MACSEC_INGR); + vsc8584_macsec_block_init(phydev, MACSEC_EGR); + vsc8584_macsec_mac_init(phydev, HOST_MAC); + vsc8584_macsec_mac_init(phydev, LINE_MAC); + + vsc8584_macsec_phy_write(phydev, FC_BUFFER, + MSCC_FCBUF_FC_READ_THRESH_CFG, + MSCC_FCBUF_FC_READ_THRESH_CFG_TX_THRESH(4) | + MSCC_FCBUF_FC_READ_THRESH_CFG_RX_THRESH(5)); + + val = vsc8584_macsec_phy_read(phydev, FC_BUFFER, MSCC_FCBUF_MODE_CFG); + val |= MSCC_FCBUF_MODE_CFG_PAUSE_GEN_ENA | + MSCC_FCBUF_MODE_CFG_RX_PPM_RATE_ADAPT_ENA | + MSCC_FCBUF_MODE_CFG_TX_PPM_RATE_ADAPT_ENA; + vsc8584_macsec_phy_write(phydev, FC_BUFFER, MSCC_FCBUF_MODE_CFG, val); + + vsc8584_macsec_phy_write(phydev, FC_BUFFER, MSCC_FCBUF_PPM_RATE_ADAPT_THRESH_CFG, + MSCC_FCBUF_PPM_RATE_ADAPT_THRESH_CFG_TX_THRESH(8) | + MSCC_FCBUF_PPM_RATE_ADAPT_THRESH_CFG_TX_OFFSET(9)); + + val = vsc8584_macsec_phy_read(phydev, FC_BUFFER, + MSCC_FCBUF_TX_DATA_QUEUE_CFG); + val &= ~(MSCC_FCBUF_TX_DATA_QUEUE_CFG_START_M | + MSCC_FCBUF_TX_DATA_QUEUE_CFG_END_M); + val |= MSCC_FCBUF_TX_DATA_QUEUE_CFG_START(0) | + MSCC_FCBUF_TX_DATA_QUEUE_CFG_END(5119); + vsc8584_macsec_phy_write(phydev, FC_BUFFER, + MSCC_FCBUF_TX_DATA_QUEUE_CFG, val); + + val = vsc8584_macsec_phy_read(phydev, FC_BUFFER, MSCC_FCBUF_ENA_CFG); + val |= MSCC_FCBUF_ENA_CFG_TX_ENA | MSCC_FCBUF_ENA_CFG_RX_ENA; + vsc8584_macsec_phy_write(phydev, FC_BUFFER, MSCC_FCBUF_ENA_CFG, val); + + val = vsc8584_macsec_phy_read(phydev, IP_1588, + MSCC_PROC_0_IP_1588_TOP_CFG_STAT_MODE_CTL); + val &= ~MSCC_PROC_0_IP_1588_TOP_CFG_STAT_MODE_CTL_PROTOCOL_MODE_M; + val |= MSCC_PROC_0_IP_1588_TOP_CFG_STAT_MODE_CTL_PROTOCOL_MODE(4); + vsc8584_macsec_phy_write(phydev, IP_1588, + MSCC_PROC_0_IP_1588_TOP_CFG_STAT_MODE_CTL, val); + + return 0; +} + +static void vsc8584_macsec_flow(struct phy_device *phydev, + struct macsec_flow *flow) +{ + struct vsc8531_private *priv = phydev->priv; + enum macsec_bank bank = flow->bank; + u32 val, match = 0, mask = 0, action = 0, idx = flow->index; + + if (flow->match.tagged) + match |= MSCC_MS_SAM_MISC_MATCH_TAGGED; + if (flow->match.untagged) + match |= MSCC_MS_SAM_MISC_MATCH_UNTAGGED; + + if (bank == MACSEC_INGR && flow->assoc_num >= 0) { + match |= MSCC_MS_SAM_MISC_MATCH_AN(flow->assoc_num); + mask |= MSCC_MS_SAM_MASK_AN_MASK(0x3); + } + + if (bank == MACSEC_INGR && flow->match.sci && flow->rx_sa->sc->sci) { + match |= MSCC_MS_SAM_MISC_MATCH_TCI(BIT(3)); + mask |= MSCC_MS_SAM_MASK_TCI_MASK(BIT(3)) | + MSCC_MS_SAM_MASK_SCI_MASK; + + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_MATCH_SCI_LO(idx), + lower_32_bits(flow->rx_sa->sc->sci)); + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_MATCH_SCI_HI(idx), + upper_32_bits(flow->rx_sa->sc->sci)); + } + + if (flow->match.etype) { + mask |= MSCC_MS_SAM_MASK_MAC_ETYPE_MASK; + + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_MAC_SA_MATCH_HI(idx), + MSCC_MS_SAM_MAC_SA_MATCH_HI_ETYPE(htons(flow->etype))); + } + + match |= MSCC_MS_SAM_MISC_MATCH_PRIORITY(flow->priority); + + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_MISC_MATCH(idx), match); + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_MASK(idx), mask); + + /* Action for matching packets */ + if (flow->action.drop) + action = MSCC_MS_FLOW_DROP; + else if (flow->action.bypass || flow->port == MSCC_MS_PORT_UNCONTROLLED) + action = MSCC_MS_FLOW_BYPASS; + else + action = (bank == MACSEC_INGR) ? + MSCC_MS_FLOW_INGRESS : MSCC_MS_FLOW_EGRESS; + + val = MSCC_MS_SAM_FLOW_CTRL_FLOW_TYPE(action) | + MSCC_MS_SAM_FLOW_CTRL_DROP_ACTION(MSCC_MS_ACTION_DROP) | + MSCC_MS_SAM_FLOW_CTRL_DEST_PORT(flow->port); + + if (action == MSCC_MS_FLOW_BYPASS) + goto write_ctrl; + + if (bank == MACSEC_INGR) { + if (priv->secy->replay_protect) + val |= MSCC_MS_SAM_FLOW_CTRL_REPLAY_PROTECT; + if (priv->secy->validate_frames == MACSEC_VALIDATE_STRICT) + val |= MSCC_MS_SAM_FLOW_CTRL_VALIDATE_FRAMES(MSCC_MS_VALIDATE_STRICT); + else if (priv->secy->validate_frames == MACSEC_VALIDATE_CHECK) + val |= MSCC_MS_SAM_FLOW_CTRL_VALIDATE_FRAMES(MSCC_MS_VALIDATE_CHECK); + } else if (bank == MACSEC_EGR) { + if (priv->secy->protect_frames) + val |= MSCC_MS_SAM_FLOW_CTRL_PROTECT_FRAME; + if (priv->secy->tx_sc.encrypt) + val |= MSCC_MS_SAM_FLOW_CTRL_CONF_PROTECT; + if (priv->secy->tx_sc.send_sci) + val |= MSCC_MS_SAM_FLOW_CTRL_INCLUDE_SCI; + } + +write_ctrl: + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_FLOW_CTRL(idx), val); +} + +static struct macsec_flow *vsc8584_macsec_find_flow(struct macsec_context *ctx, + enum macsec_bank bank) +{ + struct vsc8531_private *priv = ctx->phydev->priv; + struct macsec_flow *pos, *tmp; + + list_for_each_entry_safe(pos, tmp, &priv->macsec_flows, list) + if (pos->assoc_num == ctx->sa.assoc_num && pos->bank == bank) + return pos; + + return ERR_PTR(-ENOENT); +} + +static void vsc8584_macsec_flow_enable(struct phy_device *phydev, + struct macsec_flow *flow) +{ + enum macsec_bank bank = flow->bank; + u32 val, idx = flow->index; + + if ((flow->bank == MACSEC_INGR && flow->rx_sa && !flow->rx_sa->active) || + (flow->bank == MACSEC_EGR && flow->tx_sa && !flow->tx_sa->active)) + return; + + /* Enable */ + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_ENTRY_SET1, BIT(idx)); + + /* Set in-use */ + val = vsc8584_macsec_phy_read(phydev, bank, MSCC_MS_SAM_FLOW_CTRL(idx)); + val |= MSCC_MS_SAM_FLOW_CTRL_SA_IN_USE; + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_FLOW_CTRL(idx), val); +} + +static void vsc8584_macsec_flow_disable(struct phy_device *phydev, + struct macsec_flow *flow) +{ + enum macsec_bank bank = flow->bank; + u32 val, idx = flow->index; + + /* Disable */ + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_ENTRY_CLEAR1, BIT(idx)); + + /* Clear in-use */ + val = vsc8584_macsec_phy_read(phydev, bank, MSCC_MS_SAM_FLOW_CTRL(idx)); + val &= ~MSCC_MS_SAM_FLOW_CTRL_SA_IN_USE; + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_FLOW_CTRL(idx), val); +} + +static u32 vsc8584_macsec_flow_context_id(struct macsec_flow *flow) +{ + if (flow->bank == MACSEC_INGR) + return flow->index + MSCC_MS_MAX_FLOWS; + + return flow->index; +} + +/* Derive the AES key to get a key for the hash autentication */ +static int vsc8584_macsec_derive_key(const u8 key[MACSEC_KEYID_LEN], + u16 key_len, u8 hkey[16]) +{ + struct crypto_skcipher *tfm = crypto_alloc_skcipher("ecb(aes)", 0, 0); + struct skcipher_request *req = NULL; + struct scatterlist src, dst; + DECLARE_CRYPTO_WAIT(wait); + u32 input[4] = {0}; + int ret; + + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + req = skcipher_request_alloc(tfm, GFP_KERNEL); + if (!req) { + ret = -ENOMEM; + goto out; + } + + skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | + CRYPTO_TFM_REQ_MAY_SLEEP, crypto_req_done, + &wait); + ret = crypto_skcipher_setkey(tfm, key, key_len); + if (ret < 0) + goto out; + + sg_init_one(&src, input, 16); + sg_init_one(&dst, hkey, 16); + skcipher_request_set_crypt(req, &src, &dst, 16, NULL); + + ret = crypto_wait_req(crypto_skcipher_encrypt(req), &wait); + +out: + skcipher_request_free(req); + crypto_free_skcipher(tfm); + return ret; +} + +static int vsc8584_macsec_transformation(struct phy_device *phydev, + struct macsec_flow *flow) +{ + struct vsc8531_private *priv = phydev->priv; + enum macsec_bank bank = flow->bank; + int i, ret, index = flow->index; + u32 rec = 0, control = 0; + u8 hkey[16]; + sci_t sci; + + ret = vsc8584_macsec_derive_key(flow->key, priv->secy->key_len, hkey); + if (ret) + return ret; + + switch (priv->secy->key_len) { + case 16: + control |= CONTROL_CRYPTO_ALG(CTRYPTO_ALG_AES_CTR_128); + break; + case 32: + control |= CONTROL_CRYPTO_ALG(CTRYPTO_ALG_AES_CTR_256); + break; + default: + return -EINVAL; + } + + control |= (bank == MACSEC_EGR) ? + (CONTROL_TYPE_EGRESS | CONTROL_AN(priv->secy->tx_sc.encoding_sa)) : + (CONTROL_TYPE_INGRESS | CONTROL_SEQ_MASK); + + control |= CONTROL_UPDATE_SEQ | CONTROL_ENCRYPT_AUTH | CONTROL_KEY_IN_CTX | + CONTROL_IV0 | CONTROL_IV1 | CONTROL_IV_IN_SEQ | + CONTROL_DIGEST_TYPE(0x2) | CONTROL_SEQ_TYPE(0x1) | + CONTROL_AUTH_ALG(AUTH_ALG_AES_GHAS) | CONTROL_CONTEXT_ID; + + /* Set the control word */ + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_XFORM_REC(index, rec++), + control); + + /* Set the context ID. Must be unique. */ + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_XFORM_REC(index, rec++), + vsc8584_macsec_flow_context_id(flow)); + + /* Set the encryption/decryption key */ + for (i = 0; i < priv->secy->key_len / sizeof(u32); i++) + vsc8584_macsec_phy_write(phydev, bank, + MSCC_MS_XFORM_REC(index, rec++), + ((u32 *)flow->key)[i]); + + /* Set the authentication key */ + for (i = 0; i < 4; i++) + vsc8584_macsec_phy_write(phydev, bank, + MSCC_MS_XFORM_REC(index, rec++), + ((u32 *)hkey)[i]); + + /* Initial sequence number */ + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_XFORM_REC(index, rec++), + bank == MACSEC_INGR ? + flow->rx_sa->next_pn : flow->tx_sa->next_pn); + + if (bank == MACSEC_INGR) + /* Set the mask (replay window size) */ + vsc8584_macsec_phy_write(phydev, bank, + MSCC_MS_XFORM_REC(index, rec++), + priv->secy->replay_window); + + /* Set the input vectors */ + sci = bank == MACSEC_INGR ? flow->rx_sa->sc->sci : priv->secy->sci; + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_XFORM_REC(index, rec++), + lower_32_bits(sci)); + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_XFORM_REC(index, rec++), + upper_32_bits(sci)); + + while (rec < 20) + vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_XFORM_REC(index, rec++), + 0); + + flow->has_transformation = true; + return 0; +} + +static struct macsec_flow *vsc8584_macsec_alloc_flow(struct vsc8531_private *priv, + enum macsec_bank bank) +{ + unsigned long *bitmap = bank == MACSEC_INGR ? + &priv->ingr_flows : &priv->egr_flows; + struct macsec_flow *flow; + int index; + + index = find_first_zero_bit(bitmap, MSCC_MS_MAX_FLOWS); + + if (index == MSCC_MS_MAX_FLOWS) + return ERR_PTR(-ENOMEM); + + flow = kzalloc(sizeof(*flow), GFP_KERNEL); + if (!flow) + return ERR_PTR(-ENOMEM); + + set_bit(index, bitmap); + flow->index = index; + flow->bank = bank; + flow->priority = 8; + flow->assoc_num = -1; + + list_add_tail(&flow->list, &priv->macsec_flows); + return flow; +} + +static void vsc8584_macsec_free_flow(struct vsc8531_private *priv, + struct macsec_flow *flow) +{ + unsigned long *bitmap = flow->bank == MACSEC_INGR ? + &priv->ingr_flows : &priv->egr_flows; + + list_del(&flow->list); + clear_bit(flow->index, bitmap); + kfree(flow); +} + +static int vsc8584_macsec_add_flow(struct phy_device *phydev, + struct macsec_flow *flow, bool update) +{ + int ret; + + flow->port = MSCC_MS_PORT_CONTROLLED; + vsc8584_macsec_flow(phydev, flow); + + if (update) + return 0; + + ret = vsc8584_macsec_transformation(phydev, flow); + if (ret) { + vsc8584_macsec_free_flow(phydev->priv, flow); + return ret; + } + + return 0; +} + +static int vsc8584_macsec_default_flows(struct phy_device *phydev) +{ + struct macsec_flow *flow; + + /* Add a rule to let the MKA traffic go through, ingress */ + flow = vsc8584_macsec_alloc_flow(phydev->priv, MACSEC_INGR); + if (IS_ERR(flow)) + return PTR_ERR(flow); + + flow->priority = 15; + flow->port = MSCC_MS_PORT_UNCONTROLLED; + flow->match.tagged = 1; + flow->match.untagged = 1; + flow->match.etype = 1; + flow->etype = ETH_P_PAE; + flow->action.bypass = 1; + + vsc8584_macsec_flow(phydev, flow); + vsc8584_macsec_flow_enable(phydev, flow); + + /* Add a rule to let the MKA traffic go through, egress */ + flow = vsc8584_macsec_alloc_flow(phydev->priv, MACSEC_EGR); + if (IS_ERR(flow)) + return PTR_ERR(flow); + + flow->priority = 15; + flow->port = MSCC_MS_PORT_COMMON; + flow->match.untagged = 1; + flow->match.etype = 1; + flow->etype = ETH_P_PAE; + flow->action.bypass = 1; + + vsc8584_macsec_flow(phydev, flow); + vsc8584_macsec_flow_enable(phydev, flow); + + return 0; +} + +static void vsc8584_macsec_del_flow(struct phy_device *phydev, + struct macsec_flow *flow) +{ + vsc8584_macsec_flow_disable(phydev, flow); + vsc8584_macsec_free_flow(phydev->priv, flow); +} + +static int __vsc8584_macsec_add_rxsa(struct macsec_context *ctx, + struct macsec_flow *flow, bool update) +{ + struct phy_device *phydev = ctx->phydev; + struct vsc8531_private *priv = phydev->priv; + + if (!flow) { + flow = vsc8584_macsec_alloc_flow(priv, MACSEC_INGR); + if (IS_ERR(flow)) + return PTR_ERR(flow); + + memcpy(flow->key, ctx->sa.key, priv->secy->key_len); + } + + flow->assoc_num = ctx->sa.assoc_num; + flow->rx_sa = ctx->sa.rx_sa; + + /* Always match tagged packets on ingress */ + flow->match.tagged = 1; + flow->match.sci = 1; + + if (priv->secy->validate_frames != MACSEC_VALIDATE_DISABLED) + flow->match.untagged = 1; + + return vsc8584_macsec_add_flow(phydev, flow, update); +} + +static int __vsc8584_macsec_add_txsa(struct macsec_context *ctx, + struct macsec_flow *flow, bool update) +{ + struct phy_device *phydev = ctx->phydev; + struct vsc8531_private *priv = phydev->priv; + + if (!flow) { + flow = vsc8584_macsec_alloc_flow(priv, MACSEC_EGR); + if (IS_ERR(flow)) + return PTR_ERR(flow); + + memcpy(flow->key, ctx->sa.key, priv->secy->key_len); + } + + flow->assoc_num = ctx->sa.assoc_num; + flow->tx_sa = ctx->sa.tx_sa; + + /* Always match untagged packets on egress */ + flow->match.untagged = 1; + + return vsc8584_macsec_add_flow(phydev, flow, update); +} + +static int vsc8584_macsec_dev_open(struct macsec_context *ctx) +{ + struct vsc8531_private *priv = ctx->phydev->priv; + struct macsec_flow *flow, *tmp; + + /* No operation to perform before the commit step */ + if (ctx->prepare) + return 0; + + list_for_each_entry_safe(flow, tmp, &priv->macsec_flows, list) + vsc8584_macsec_flow_enable(ctx->phydev, flow); + + return 0; +} + +static int vsc8584_macsec_dev_stop(struct macsec_context *ctx) +{ + struct vsc8531_private *priv = ctx->phydev->priv; + struct macsec_flow *flow, *tmp; + + /* No operation to perform before the commit step */ + if (ctx->prepare) + return 0; + + list_for_each_entry_safe(flow, tmp, &priv->macsec_flows, list) + vsc8584_macsec_flow_disable(ctx->phydev, flow); + + return 0; +} + +static int vsc8584_macsec_add_secy(struct macsec_context *ctx) +{ + struct vsc8531_private *priv = ctx->phydev->priv; + struct macsec_secy *secy = ctx->secy; + + if (ctx->prepare) { + if (priv->secy) + return -EEXIST; + + return 0; + } + + priv->secy = secy; + + vsc8584_macsec_flow_default_action(ctx->phydev, MACSEC_EGR, + secy->validate_frames != MACSEC_VALIDATE_DISABLED); + vsc8584_macsec_flow_default_action(ctx->phydev, MACSEC_INGR, + secy->validate_frames != MACSEC_VALIDATE_DISABLED); + + return vsc8584_macsec_default_flows(ctx->phydev); +} + +static int vsc8584_macsec_del_secy(struct macsec_context *ctx) +{ + struct vsc8531_private *priv = ctx->phydev->priv; + struct macsec_flow *flow, *tmp; + + /* No operation to perform before the commit step */ + if (ctx->prepare) + return 0; + + list_for_each_entry_safe(flow, tmp, &priv->macsec_flows, list) + vsc8584_macsec_del_flow(ctx->phydev, flow); + + vsc8584_macsec_flow_default_action(ctx->phydev, MACSEC_EGR, false); + vsc8584_macsec_flow_default_action(ctx->phydev, MACSEC_INGR, false); + + priv->secy = NULL; + return 0; +} + +static int vsc8584_macsec_upd_secy(struct macsec_context *ctx) +{ + /* No operation to perform before the commit step */ + if (ctx->prepare) + return 0; + + vsc8584_macsec_del_secy(ctx); + return vsc8584_macsec_add_secy(ctx); +} + +static int vsc8584_macsec_add_rxsc(struct macsec_context *ctx) +{ + /* Nothing to do */ + return 0; +} + +static int vsc8584_macsec_upd_rxsc(struct macsec_context *ctx) +{ + return -EOPNOTSUPP; +} + +static int vsc8584_macsec_del_rxsc(struct macsec_context *ctx) +{ + struct vsc8531_private *priv = ctx->phydev->priv; + struct macsec_flow *flow, *tmp; + + /* No operation to perform before the commit step */ + if (ctx->prepare) + return 0; + + list_for_each_entry_safe(flow, tmp, &priv->macsec_flows, list) { + if (flow->bank == MACSEC_INGR && flow->rx_sa && + flow->rx_sa->sc->sci == ctx->rx_sc->sci) + vsc8584_macsec_del_flow(ctx->phydev, flow); + } + + return 0; +} + +static int vsc8584_macsec_add_rxsa(struct macsec_context *ctx) +{ + struct macsec_flow *flow = NULL; + + if (ctx->prepare) + return __vsc8584_macsec_add_rxsa(ctx, flow, false); + + flow = vsc8584_macsec_find_flow(ctx, MACSEC_INGR); + if (IS_ERR(flow)) + return PTR_ERR(flow); + + vsc8584_macsec_flow_enable(ctx->phydev, flow); + return 0; +} + +static int vsc8584_macsec_upd_rxsa(struct macsec_context *ctx) +{ + struct macsec_flow *flow; + + flow = vsc8584_macsec_find_flow(ctx, MACSEC_INGR); + if (IS_ERR(flow)) + return PTR_ERR(flow); + + if (ctx->prepare) { + /* Make sure the flow is disabled before updating it */ + vsc8584_macsec_flow_disable(ctx->phydev, flow); + + return __vsc8584_macsec_add_rxsa(ctx, flow, true); + } + + vsc8584_macsec_flow_enable(ctx->phydev, flow); + return 0; +} + +static int vsc8584_macsec_del_rxsa(struct macsec_context *ctx) +{ + struct macsec_flow *flow; + + flow = vsc8584_macsec_find_flow(ctx, MACSEC_INGR); + + if (IS_ERR(flow)) + return PTR_ERR(flow); + if (ctx->prepare) + return 0; + + vsc8584_macsec_del_flow(ctx->phydev, flow); + return 0; +} + +static int vsc8584_macsec_add_txsa(struct macsec_context *ctx) +{ + struct macsec_flow *flow = NULL; + + if (ctx->prepare) + return __vsc8584_macsec_add_txsa(ctx, flow, false); + + flow = vsc8584_macsec_find_flow(ctx, MACSEC_EGR); + if (IS_ERR(flow)) + return PTR_ERR(flow); + + vsc8584_macsec_flow_enable(ctx->phydev, flow); + return 0; +} + +static int vsc8584_macsec_upd_txsa(struct macsec_context *ctx) +{ + struct macsec_flow *flow; + + flow = vsc8584_macsec_find_flow(ctx, MACSEC_EGR); + if (IS_ERR(flow)) + return PTR_ERR(flow); + + if (ctx->prepare) { + /* Make sure the flow is disabled before updating it */ + vsc8584_macsec_flow_disable(ctx->phydev, flow); + + return __vsc8584_macsec_add_txsa(ctx, flow, true); + } + + vsc8584_macsec_flow_enable(ctx->phydev, flow); + return 0; +} + +static int vsc8584_macsec_del_txsa(struct macsec_context *ctx) +{ + struct macsec_flow *flow; + + flow = vsc8584_macsec_find_flow(ctx, MACSEC_EGR); + + if (IS_ERR(flow)) + return PTR_ERR(flow); + if (ctx->prepare) + return 0; + + vsc8584_macsec_del_flow(ctx->phydev, flow); + return 0; +} + +static struct macsec_ops vsc8584_macsec_ops = { + .mdo_dev_open = vsc8584_macsec_dev_open, + .mdo_dev_stop = vsc8584_macsec_dev_stop, + .mdo_add_secy = vsc8584_macsec_add_secy, + .mdo_upd_secy = vsc8584_macsec_upd_secy, + .mdo_del_secy = vsc8584_macsec_del_secy, + .mdo_add_rxsc = vsc8584_macsec_add_rxsc, + .mdo_upd_rxsc = vsc8584_macsec_upd_rxsc, + .mdo_del_rxsc = vsc8584_macsec_del_rxsc, + .mdo_add_rxsa = vsc8584_macsec_add_rxsa, + .mdo_upd_rxsa = vsc8584_macsec_upd_rxsa, + .mdo_del_rxsa = vsc8584_macsec_del_rxsa, + .mdo_add_txsa = vsc8584_macsec_add_txsa, + .mdo_upd_txsa = vsc8584_macsec_upd_txsa, + .mdo_del_txsa = vsc8584_macsec_del_txsa, +}; +#endif /* CONFIG_MACSEC */ + /* Check if one PHY has already done the init of the parts common to all PHYs * in the Quad PHY package. */ @@ -1733,6 +2791,24 @@ static int vsc8584_config_init(struct phy_device *phydev) mutex_unlock(&phydev->mdio.bus->mdio_lock); +#if IS_ENABLED(CONFIG_MACSEC) + /* MACsec */ + switch (phydev->phy_id & phydev->drv->phy_id_mask) { + case PHY_ID_VSC856X: + case PHY_ID_VSC8575: + case PHY_ID_VSC8582: + case PHY_ID_VSC8584: + INIT_LIST_HEAD(&vsc8531->macsec_flows); + vsc8531->secy = NULL; + + phydev->macsec_ops = &vsc8584_macsec_ops; + + ret = vsc8584_macsec_init(phydev); + if (ret) + goto err; + } +#endif + phy_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD); val = phy_read(phydev, MSCC_PHY_EXT_PHY_CNTL_1); @@ -1758,6 +2834,43 @@ err: return ret; } +static int vsc8584_handle_interrupt(struct phy_device *phydev) +{ +#if IS_ENABLED(CONFIG_MACSEC) + struct vsc8531_private *priv = phydev->priv; + struct macsec_flow *flow, *tmp; + u32 cause, rec; + + /* Check MACsec PN rollover */ + cause = vsc8584_macsec_phy_read(phydev, MACSEC_EGR, + MSCC_MS_INTR_CTRL_STATUS); + cause &= MSCC_MS_INTR_CTRL_STATUS_INTR_CLR_STATUS_M; + if (!(cause & MACSEC_INTR_CTRL_STATUS_ROLLOVER)) + goto skip_rollover; + + rec = 6 + priv->secy->key_len / sizeof(u32); + list_for_each_entry_safe(flow, tmp, &priv->macsec_flows, list) { + u32 val; + + if (flow->bank != MACSEC_EGR || !flow->has_transformation) + continue; + + val = vsc8584_macsec_phy_read(phydev, MACSEC_EGR, + MSCC_MS_XFORM_REC(flow->index, rec)); + if (val == 0xffffffff) { + vsc8584_macsec_flow_disable(phydev, flow); + macsec_pn_wrapped(priv->secy, flow->tx_sa); + break; + } + } + +skip_rollover: +#endif + + phy_mac_interrupt(phydev); + return 0; +} + static int vsc85xx_config_init(struct phy_device *phydev) { int rc, i, phy_id; @@ -2201,6 +3314,20 @@ static int vsc85xx_config_intr(struct phy_device *phydev) int rc; if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { +#if IS_ENABLED(CONFIG_MACSEC) + phy_write(phydev, MSCC_EXT_PAGE_ACCESS, + MSCC_PHY_PAGE_EXTENDED_2); + phy_write(phydev, MSCC_PHY_EXTENDED_INT, + MSCC_PHY_EXTENDED_INT_MS_EGR); + phy_write(phydev, MSCC_EXT_PAGE_ACCESS, + MSCC_PHY_PAGE_STANDARD); + + vsc8584_macsec_phy_write(phydev, MACSEC_EGR, + MSCC_MS_AIC_CTRL, 0xf); + vsc8584_macsec_phy_write(phydev, MACSEC_EGR, + MSCC_MS_INTR_CTRL_STATUS, + MSCC_MS_INTR_CTRL_STATUS_INTR_ENABLE(MACSEC_INTR_CTRL_STATUS_ROLLOVER)); +#endif rc = phy_write(phydev, MII_VSC85XX_INT_MASK, MII_VSC85XX_INT_MASK_MASK); } else { @@ -2404,7 +3531,6 @@ static struct phy_driver vsc85xx_driver[] = { .soft_reset = &genphy_soft_reset, .config_init = &vsc85xx_config_init, .config_aneg = &vsc85xx_config_aneg, - .aneg_done = &genphy_aneg_done, .read_status = &vsc85xx_read_status, .ack_interrupt = &vsc85xx_ack_interrupt, .config_intr = &vsc85xx_config_intr, @@ -2429,7 +3555,6 @@ static struct phy_driver vsc85xx_driver[] = { .soft_reset = &genphy_soft_reset, .config_init = &vsc85xx_config_init, .config_aneg = &vsc85xx_config_aneg, - .aneg_done = &genphy_aneg_done, .read_status = &vsc85xx_read_status, .ack_interrupt = &vsc85xx_ack_interrupt, .config_intr = &vsc85xx_config_intr, @@ -2454,7 +3579,6 @@ static struct phy_driver vsc85xx_driver[] = { .soft_reset = &genphy_soft_reset, .config_init = &vsc85xx_config_init, .config_aneg = &vsc85xx_config_aneg, - .aneg_done = &genphy_aneg_done, .read_status = &vsc85xx_read_status, .ack_interrupt = &vsc85xx_ack_interrupt, .config_intr = &vsc85xx_config_intr, @@ -2479,7 +3603,6 @@ static struct phy_driver vsc85xx_driver[] = { .soft_reset = &genphy_soft_reset, .config_init = &vsc85xx_config_init, .config_aneg = &vsc85xx_config_aneg, - .aneg_done = &genphy_aneg_done, .read_status = &vsc85xx_read_status, .ack_interrupt = &vsc85xx_ack_interrupt, .config_intr = &vsc85xx_config_intr, @@ -2504,7 +3627,6 @@ static struct phy_driver vsc85xx_driver[] = { .soft_reset = &genphy_soft_reset, .config_init = &vsc8584_config_init, .config_aneg = &vsc85xx_config_aneg, - .aneg_done = &genphy_aneg_done, .read_status = &vsc85xx_read_status, .ack_interrupt = &vsc85xx_ack_interrupt, .config_intr = &vsc85xx_config_intr, @@ -2530,7 +3652,6 @@ static struct phy_driver vsc85xx_driver[] = { .soft_reset = &genphy_soft_reset, .config_init = &vsc8584_config_init, .config_aneg = &vsc85xx_config_aneg, - .aneg_done = &genphy_aneg_done, .read_status = &vsc85xx_read_status, .ack_interrupt = &vsc85xx_ack_interrupt, .config_intr = &vsc85xx_config_intr, @@ -2556,6 +3677,7 @@ static struct phy_driver vsc85xx_driver[] = { .config_aneg = &vsc85xx_config_aneg, .aneg_done = &genphy_aneg_done, .read_status = &vsc85xx_read_status, + .handle_interrupt = &vsc8584_handle_interrupt, .ack_interrupt = &vsc85xx_ack_interrupt, .config_intr = &vsc85xx_config_intr, .did_interrupt = &vsc8584_did_interrupt, @@ -2608,6 +3730,7 @@ static struct phy_driver vsc85xx_driver[] = { .config_aneg = &vsc85xx_config_aneg, .aneg_done = &genphy_aneg_done, .read_status = &vsc85xx_read_status, + .handle_interrupt = &vsc8584_handle_interrupt, .ack_interrupt = &vsc85xx_ack_interrupt, .config_intr = &vsc85xx_config_intr, .did_interrupt = &vsc8584_did_interrupt, @@ -2632,6 +3755,7 @@ static struct phy_driver vsc85xx_driver[] = { .config_aneg = &vsc85xx_config_aneg, .aneg_done = &genphy_aneg_done, .read_status = &vsc85xx_read_status, + .handle_interrupt = &vsc8584_handle_interrupt, .ack_interrupt = &vsc85xx_ack_interrupt, .config_intr = &vsc85xx_config_intr, .did_interrupt = &vsc8584_did_interrupt, @@ -2656,6 +3780,7 @@ static struct phy_driver vsc85xx_driver[] = { .config_aneg = &vsc85xx_config_aneg, .aneg_done = &genphy_aneg_done, .read_status = &vsc85xx_read_status, + .handle_interrupt = &vsc8584_handle_interrupt, .ack_interrupt = &vsc85xx_ack_interrupt, .config_intr = &vsc85xx_config_intr, .did_interrupt = &vsc8584_did_interrupt, diff --git a/drivers/net/phy/mscc_fc_buffer.h b/drivers/net/phy/mscc_fc_buffer.h new file mode 100644 index 000000000000..7e9c0e877895 --- /dev/null +++ b/drivers/net/phy/mscc_fc_buffer.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ +/* + * Microsemi Ocelot Switch driver + * + * Copyright (C) 2019 Microsemi Corporation + */ + +#ifndef _MSCC_OCELOT_FC_BUFFER_H_ +#define _MSCC_OCELOT_FC_BUFFER_H_ + +#define MSCC_FCBUF_ENA_CFG 0x00 +#define MSCC_FCBUF_MODE_CFG 0x01 +#define MSCC_FCBUF_PPM_RATE_ADAPT_THRESH_CFG 0x02 +#define MSCC_FCBUF_TX_CTRL_QUEUE_CFG 0x03 +#define MSCC_FCBUF_TX_DATA_QUEUE_CFG 0x04 +#define MSCC_FCBUF_RX_DATA_QUEUE_CFG 0x05 +#define MSCC_FCBUF_TX_BUFF_XON_XOFF_THRESH_CFG 0x06 +#define MSCC_FCBUF_FC_READ_THRESH_CFG 0x07 +#define MSCC_FCBUF_TX_FRM_GAP_COMP 0x08 + +#define MSCC_FCBUF_ENA_CFG_TX_ENA BIT(0) +#define MSCC_FCBUF_ENA_CFG_RX_ENA BIT(4) + +#define MSCC_FCBUF_MODE_CFG_DROP_BEHAVIOUR BIT(4) +#define MSCC_FCBUF_MODE_CFG_PAUSE_REACT_ENA BIT(8) +#define MSCC_FCBUF_MODE_CFG_RX_PPM_RATE_ADAPT_ENA BIT(12) +#define MSCC_FCBUF_MODE_CFG_TX_PPM_RATE_ADAPT_ENA BIT(16) +#define MSCC_FCBUF_MODE_CFG_TX_CTRL_QUEUE_ENA BIT(20) +#define MSCC_FCBUF_MODE_CFG_PAUSE_GEN_ENA BIT(24) +#define MSCC_FCBUF_MODE_CFG_INCLUDE_PAUSE_RCVD_IN_PAUSE_GEN BIT(28) + +#define MSCC_FCBUF_PPM_RATE_ADAPT_THRESH_CFG_TX_THRESH(x) (x) +#define MSCC_FCBUF_PPM_RATE_ADAPT_THRESH_CFG_TX_THRESH_M GENMASK(15, 0) +#define MSCC_FCBUF_PPM_RATE_ADAPT_THRESH_CFG_TX_OFFSET(x) ((x) << 16) +#define MSCC_FCBUF_PPM_RATE_ADAPT_THRESH_CFG_TX_OFFSET_M GENMASK(19, 16) +#define MSCC_FCBUF_PPM_RATE_ADAPT_THRESH_CFG_RX_THRESH(x) ((x) << 20) +#define MSCC_FCBUF_PPM_RATE_ADAPT_THRESH_CFG_RX_THRESH_M GENMASK(31, 20) + +#define MSCC_FCBUF_TX_CTRL_QUEUE_CFG_START(x) (x) +#define MSCC_FCBUF_TX_CTRL_QUEUE_CFG_START_M GENMASK(15, 0) +#define MSCC_FCBUF_TX_CTRL_QUEUE_CFG_END(x) ((x) << 16) +#define MSCC_FCBUF_TX_CTRL_QUEUE_CFG_END_M GENMASK(31, 16) + +#define MSCC_FCBUF_TX_DATA_QUEUE_CFG_START(x) (x) +#define MSCC_FCBUF_TX_DATA_QUEUE_CFG_START_M GENMASK(15, 0) +#define MSCC_FCBUF_TX_DATA_QUEUE_CFG_END(x) ((x) << 16) +#define MSCC_FCBUF_TX_DATA_QUEUE_CFG_END_M GENMASK(31, 16) + +#define MSCC_FCBUF_RX_DATA_QUEUE_CFG_START(x) (x) +#define MSCC_FCBUF_RX_DATA_QUEUE_CFG_START_M GENMASK(15, 0) +#define MSCC_FCBUF_RX_DATA_QUEUE_CFG_END(x) ((x) << 16) +#define MSCC_FCBUF_RX_DATA_QUEUE_CFG_END_M GENMASK(31, 16) + +#define MSCC_FCBUF_TX_BUFF_XON_XOFF_THRESH_CFG_XOFF_THRESH(x) (x) +#define MSCC_FCBUF_TX_BUFF_XON_XOFF_THRESH_CFG_XOFF_THRESH_M GENMASK(15, 0) +#define MSCC_FCBUF_TX_BUFF_XON_XOFF_THRESH_CFG_XON_THRESH(x) ((x) << 16) +#define MSCC_FCBUF_TX_BUFF_XON_XOFF_THRESH_CFG_XON_THRESH_M GENMASK(31, 16) + +#define MSCC_FCBUF_FC_READ_THRESH_CFG_TX_THRESH(x) (x) +#define MSCC_FCBUF_FC_READ_THRESH_CFG_TX_THRESH_M GENMASK(15, 0) +#define MSCC_FCBUF_FC_READ_THRESH_CFG_RX_THRESH(x) ((x) << 16) +#define MSCC_FCBUF_FC_READ_THRESH_CFG_RX_THRESH_M GENMASK(31, 16) + +#endif diff --git a/drivers/net/phy/mscc_mac.h b/drivers/net/phy/mscc_mac.h new file mode 100644 index 000000000000..9420ee5175a6 --- /dev/null +++ b/drivers/net/phy/mscc_mac.h @@ -0,0 +1,159 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ +/* + * Microsemi Ocelot Switch driver + * + * Copyright (c) 2017 Microsemi Corporation + */ + +#ifndef _MSCC_OCELOT_LINE_MAC_H_ +#define _MSCC_OCELOT_LINE_MAC_H_ + +#define MSCC_MAC_CFG_ENA_CFG 0x00 +#define MSCC_MAC_CFG_MODE_CFG 0x01 +#define MSCC_MAC_CFG_MAXLEN_CFG 0x02 +#define MSCC_MAC_CFG_NUM_TAGS_CFG 0x03 +#define MSCC_MAC_CFG_TAGS_CFG 0x04 +#define MSCC_MAC_CFG_ADV_CHK_CFG 0x07 +#define MSCC_MAC_CFG_LFS_CFG 0x08 +#define MSCC_MAC_CFG_LB_CFG 0x09 +#define MSCC_MAC_CFG_PKTINF_CFG 0x0a +#define MSCC_MAC_PAUSE_CFG_TX_FRAME_CTRL 0x0b +#define MSCC_MAC_PAUSE_CFG_TX_FRAME_CTRL_2 0x0c +#define MSCC_MAC_PAUSE_CFG_RX_FRAME_CTRL 0x0d +#define MSCC_MAC_PAUSE_CFG_STATE 0x0e +#define MSCC_MAC_PAUSE_CFG_MAC_ADDRESS_LSB 0x0f +#define MSCC_MAC_PAUSE_CFG_MAC_ADDRESS_MSB 0x10 +#define MSCC_MAC_STATUS_RX_LANE_STICKY_0 0x11 +#define MSCC_MAC_STATUS_RX_LANE_STICKY_1 0x12 +#define MSCC_MAC_STATUS_TX_MONITOR_STICKY 0x13 +#define MSCC_MAC_STATUS_TX_MONITOR_STICKY_MASK 0x14 +#define MSCC_MAC_STATUS_STICKY 0x15 +#define MSCC_MAC_STATUS_STICKY_MASK 0x16 +#define MSCC_MAC_STATS_32BIT_RX_HIH_CKSM_ERR_CNT 0x17 +#define MSCC_MAC_STATS_32BIT_RX_XGMII_PROT_ERR_CNT 0x18 +#define MSCC_MAC_STATS_32BIT_RX_SYMBOL_ERR_CNT 0x19 +#define MSCC_MAC_STATS_32BIT_RX_PAUSE_CNT 0x1a +#define MSCC_MAC_STATS_32BIT_RX_UNSUP_OPCODE_CNT 0x1b +#define MSCC_MAC_STATS_32BIT_RX_UC_CNT 0x1c +#define MSCC_MAC_STATS_32BIT_RX_MC_CNT 0x1d +#define MSCC_MAC_STATS_32BIT_RX_BC_CNT 0x1e +#define MSCC_MAC_STATS_32BIT_RX_CRC_ERR_CNT 0x1f +#define MSCC_MAC_STATS_32BIT_RX_UNDERSIZE_CNT 0x20 +#define MSCC_MAC_STATS_32BIT_RX_FRAGMENTS_CNT 0x21 +#define MSCC_MAC_STATS_32BIT_RX_IN_RANGE_LEN_ERR_CNT 0x22 +#define MSCC_MAC_STATS_32BIT_RX_OUT_OF_RANGE_LEN_ERR_CNT 0x23 +#define MSCC_MAC_STATS_32BIT_RX_OVERSIZE_CNT 0x24 +#define MSCC_MAC_STATS_32BIT_RX_JABBERS_CNT 0x25 +#define MSCC_MAC_STATS_32BIT_RX_SIZE64_CNT 0x26 +#define MSCC_MAC_STATS_32BIT_RX_SIZE65TO127_CNT 0x27 +#define MSCC_MAC_STATS_32BIT_RX_SIZE128TO255_CNT 0x28 +#define MSCC_MAC_STATS_32BIT_RX_SIZE256TO511_CNT 0x29 +#define MSCC_MAC_STATS_32BIT_RX_SIZE512TO1023_CNT 0x2a +#define MSCC_MAC_STATS_32BIT_RX_SIZE1024TO1518_CNT 0x2b +#define MSCC_MAC_STATS_32BIT_RX_SIZE1519TOMAX_CNT 0x2c +#define MSCC_MAC_STATS_32BIT_RX_IPG_SHRINK_CNT 0x2d +#define MSCC_MAC_STATS_32BIT_TX_PAUSE_CNT 0x2e +#define MSCC_MAC_STATS_32BIT_TX_UC_CNT 0x2f +#define MSCC_MAC_STATS_32BIT_TX_MC_CNT 0x30 +#define MSCC_MAC_STATS_32BIT_TX_BC_CNT 0x31 +#define MSCC_MAC_STATS_32BIT_TX_SIZE64_CNT 0x32 +#define MSCC_MAC_STATS_32BIT_TX_SIZE65TO127_CNT 0x33 +#define MSCC_MAC_STATS_32BIT_TX_SIZE128TO255_CNT 0x34 +#define MSCC_MAC_STATS_32BIT_TX_SIZE256TO511_CNT 0x35 +#define MSCC_MAC_STATS_32BIT_TX_SIZE512TO1023_CNT 0x36 +#define MSCC_MAC_STATS_32BIT_TX_SIZE1024TO1518_CNT 0x37 +#define MSCC_MAC_STATS_32BIT_TX_SIZE1519TOMAX_CNT 0x38 +#define MSCC_MAC_STATS_40BIT_RX_BAD_BYTES_CNT 0x39 +#define MSCC_MAC_STATS_40BIT_RX_BAD_BYTES_MSB_CNT 0x3a +#define MSCC_MAC_STATS_40BIT_RX_OK_BYTES_CNT 0x3b +#define MSCC_MAC_STATS_40BIT_RX_OK_BYTES_MSB_CNT 0x3c +#define MSCC_MAC_STATS_40BIT_RX_IN_BYTES_CNT 0x3d +#define MSCC_MAC_STATS_40BIT_RX_IN_BYTES_MSB_CNT 0x3e +#define MSCC_MAC_STATS_40BIT_TX_OK_BYTES_CNT 0x3f +#define MSCC_MAC_STATS_40BIT_TX_OK_BYTES_MSB_CNT 0x40 +#define MSCC_MAC_STATS_40BIT_TX_OUT_BYTES_CNT 0x41 +#define MSCC_MAC_STATS_40BIT_TX_OUT_BYTES_MSB_CNT 0x42 + +#define MSCC_MAC_CFG_ENA_CFG_RX_CLK_ENA BIT(0) +#define MSCC_MAC_CFG_ENA_CFG_TX_CLK_ENA BIT(4) +#define MSCC_MAC_CFG_ENA_CFG_RX_SW_RST BIT(8) +#define MSCC_MAC_CFG_ENA_CFG_TX_SW_RST BIT(12) +#define MSCC_MAC_CFG_ENA_CFG_RX_ENA BIT(16) +#define MSCC_MAC_CFG_ENA_CFG_TX_ENA BIT(20) + +#define MSCC_MAC_CFG_MODE_CFG_FORCE_CW_UPDATE_INTERVAL(x) ((x) << 20) +#define MSCC_MAC_CFG_MODE_CFG_FORCE_CW_UPDATE_INTERVAL_M GENMASK(29, 20) +#define MSCC_MAC_CFG_MODE_CFG_FORCE_CW_UPDATE BIT(16) +#define MSCC_MAC_CFG_MODE_CFG_TUNNEL_PAUSE_FRAMES BIT(14) +#define MSCC_MAC_CFG_MODE_CFG_MAC_PREAMBLE_CFG(x) ((x) << 10) +#define MSCC_MAC_CFG_MODE_CFG_MAC_PREAMBLE_CFG_M GENMASK(12, 10) +#define MSCC_MAC_CFG_MODE_CFG_MAC_IPG_CFG BIT(6) +#define MSCC_MAC_CFG_MODE_CFG_XGMII_GEN_MODE_ENA BIT(4) +#define MSCC_MAC_CFG_MODE_CFG_HIH_CRC_CHECK BIT(2) +#define MSCC_MAC_CFG_MODE_CFG_UNDERSIZED_FRAME_DROP_DIS BIT(1) +#define MSCC_MAC_CFG_MODE_CFG_DISABLE_DIC BIT(0) + +#define MSCC_MAC_CFG_MAXLEN_CFG_MAX_LEN_TAG_CHK BIT(16) +#define MSCC_MAC_CFG_MAXLEN_CFG_MAX_LEN(x) (x) +#define MSCC_MAC_CFG_MAXLEN_CFG_MAX_LEN_M GENMASK(15, 0) + +#define MSCC_MAC_CFG_TAGS_CFG_RSZ 0x4 +#define MSCC_MAC_CFG_TAGS_CFG_TAG_ID(x) ((x) << 16) +#define MSCC_MAC_CFG_TAGS_CFG_TAG_ID_M GENMASK(31, 16) +#define MSCC_MAC_CFG_TAGS_CFG_TAG_ENA BIT(4) + +#define MSCC_MAC_CFG_ADV_CHK_CFG_EXT_EOP_CHK_ENA BIT(24) +#define MSCC_MAC_CFG_ADV_CHK_CFG_EXT_SOP_CHK_ENA BIT(20) +#define MSCC_MAC_CFG_ADV_CHK_CFG_SFD_CHK_ENA BIT(16) +#define MSCC_MAC_CFG_ADV_CHK_CFG_PRM_SHK_CHK_DIS BIT(12) +#define MSCC_MAC_CFG_ADV_CHK_CFG_PRM_CHK_ENA BIT(8) +#define MSCC_MAC_CFG_ADV_CHK_CFG_OOR_ERR_ENA BIT(4) +#define MSCC_MAC_CFG_ADV_CHK_CFG_INR_ERR_ENA BIT(0) + +#define MSCC_MAC_CFG_LFS_CFG_LFS_INH_TX BIT(8) +#define MSCC_MAC_CFG_LFS_CFG_LFS_DIS_TX BIT(4) +#define MSCC_MAC_CFG_LFS_CFG_LFS_UNIDIR_ENA BIT(3) +#define MSCC_MAC_CFG_LFS_CFG_USE_LEADING_EDGE_DETECT BIT(2) +#define MSCC_MAC_CFG_LFS_CFG_SPURIOUS_Q_DIS BIT(1) +#define MSCC_MAC_CFG_LFS_CFG_LFS_MODE_ENA BIT(0) + +#define MSCC_MAC_CFG_LB_CFG_XGMII_HOST_LB_ENA BIT(4) +#define MSCC_MAC_CFG_LB_CFG_XGMII_PHY_LB_ENA BIT(0) + +#define MSCC_MAC_CFG_PKTINF_CFG_STRIP_FCS_ENA BIT(0) +#define MSCC_MAC_CFG_PKTINF_CFG_INSERT_FCS_ENA BIT(4) +#define MSCC_MAC_CFG_PKTINF_CFG_STRIP_PREAMBLE_ENA BIT(8) +#define MSCC_MAC_CFG_PKTINF_CFG_INSERT_PREAMBLE_ENA BIT(12) +#define MSCC_MAC_CFG_PKTINF_CFG_LPI_RELAY_ENA BIT(16) +#define MSCC_MAC_CFG_PKTINF_CFG_LF_RELAY_ENA BIT(20) +#define MSCC_MAC_CFG_PKTINF_CFG_RF_RELAY_ENA BIT(24) +#define MSCC_MAC_CFG_PKTINF_CFG_ENABLE_TX_PADDING BIT(25) +#define MSCC_MAC_CFG_PKTINF_CFG_ENABLE_RX_PADDING BIT(26) +#define MSCC_MAC_CFG_PKTINF_CFG_ENABLE_4BYTE_PREAMBLE BIT(27) +#define MSCC_MAC_CFG_PKTINF_CFG_MACSEC_BYPASS_NUM_PTP_STALL_CLKS(x) ((x) << 28) +#define MSCC_MAC_CFG_PKTINF_CFG_MACSEC_BYPASS_NUM_PTP_STALL_CLKS_M GENMASK(30, 28) + +#define MSCC_MAC_PAUSE_CFG_TX_FRAME_CTRL_PAUSE_VALUE(x) ((x) << 16) +#define MSCC_MAC_PAUSE_CFG_TX_FRAME_CTRL_PAUSE_VALUE_M GENMASK(31, 16) +#define MSCC_MAC_PAUSE_CFG_TX_FRAME_CTRL_WAIT_FOR_LPI_LOW BIT(12) +#define MSCC_MAC_PAUSE_CFG_TX_FRAME_CTRL_USE_PAUSE_STALL_ENA BIT(8) +#define MSCC_MAC_PAUSE_CFG_TX_FRAME_CTRL_PAUSE_REPL_MODE BIT(4) +#define MSCC_MAC_PAUSE_CFG_TX_FRAME_CTRL_PAUSE_FRC_FRAME BIT(2) +#define MSCC_MAC_PAUSE_CFG_TX_FRAME_CTRL_PAUSE_MODE(x) (x) +#define MSCC_MAC_PAUSE_CFG_TX_FRAME_CTRL_PAUSE_MODE_M GENMASK(1, 0) + +#define MSCC_MAC_PAUSE_CFG_RX_FRAME_CTRL_EARLY_PAUSE_DETECT_ENA BIT(16) +#define MSCC_MAC_PAUSE_CFG_RX_FRAME_CTRL_PRE_CRC_MODE BIT(20) +#define MSCC_MAC_PAUSE_CFG_RX_FRAME_CTRL_PAUSE_TIMER_ENA BIT(12) +#define MSCC_MAC_PAUSE_CFG_RX_FRAME_CTRL_PAUSE_REACT_ENA BIT(8) +#define MSCC_MAC_PAUSE_CFG_RX_FRAME_CTRL_PAUSE_FRAME_DROP_ENA BIT(4) +#define MSCC_MAC_PAUSE_CFG_RX_FRAME_CTRL_PAUSE_MODE BIT(0) + +#define MSCC_MAC_PAUSE_CFG_STATE_PAUSE_STATE BIT(0) +#define MSCC_MAC_PAUSE_CFG_STATE_MAC_TX_PAUSE_GEN BIT(4) + +#define MSCC_PROC_0_IP_1588_TOP_CFG_STAT_MODE_CTL 0x2 +#define MSCC_PROC_0_IP_1588_TOP_CFG_STAT_MODE_CTL_PROTOCOL_MODE(x) (x) +#define MSCC_PROC_0_IP_1588_TOP_CFG_STAT_MODE_CTL_PROTOCOL_MODE_M GENMASK(2, 0) + +#endif /* _MSCC_OCELOT_LINE_MAC_H_ */ diff --git a/drivers/net/phy/mscc_macsec.h b/drivers/net/phy/mscc_macsec.h new file mode 100644 index 000000000000..d9ab6aba7482 --- /dev/null +++ b/drivers/net/phy/mscc_macsec.h @@ -0,0 +1,266 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ +/* + * Microsemi Ocelot Switch driver + * + * Copyright (c) 2018 Microsemi Corporation + */ + +#ifndef _MSCC_OCELOT_MACSEC_H_ +#define _MSCC_OCELOT_MACSEC_H_ + +#define MSCC_MS_MAX_FLOWS 16 + +#define CONTROL_TYPE_EGRESS 0x6 +#define CONTROL_TYPE_INGRESS 0xf +#define CONTROL_IV0 BIT(5) +#define CONTROL_IV1 BIT(6) +#define CONTROL_IV2 BIT(7) +#define CONTROL_UPDATE_SEQ BIT(13) +#define CONTROL_IV_IN_SEQ BIT(14) +#define CONTROL_ENCRYPT_AUTH BIT(15) +#define CONTROL_KEY_IN_CTX BIT(16) +#define CONTROL_CRYPTO_ALG(x) ((x) << 17) +#define CTRYPTO_ALG_AES_CTR_128 0x5 +#define CTRYPTO_ALG_AES_CTR_192 0x6 +#define CTRYPTO_ALG_AES_CTR_256 0x7 +#define CONTROL_DIGEST_TYPE(x) ((x) << 21) +#define CONTROL_AUTH_ALG(x) ((x) << 23) +#define AUTH_ALG_AES_GHAS 0x4 +#define CONTROL_AN(x) ((x) << 26) +#define CONTROL_SEQ_TYPE(x) ((x) << 28) +#define CONTROL_SEQ_MASK BIT(30) +#define CONTROL_CONTEXT_ID BIT(31) + +enum mscc_macsec_destination_ports { + MSCC_MS_PORT_COMMON = 0, + MSCC_MS_PORT_RSVD = 1, + MSCC_MS_PORT_CONTROLLED = 2, + MSCC_MS_PORT_UNCONTROLLED = 3, +}; + +enum mscc_macsec_drop_actions { + MSCC_MS_ACTION_BYPASS_CRC = 0, + MSCC_MS_ACTION_BYPASS_BAD = 1, + MSCC_MS_ACTION_DROP = 2, + MSCC_MS_ACTION_BYPASS = 3, +}; + +enum mscc_macsec_flow_types { + MSCC_MS_FLOW_BYPASS = 0, + MSCC_MS_FLOW_DROP = 1, + MSCC_MS_FLOW_INGRESS = 2, + MSCC_MS_FLOW_EGRESS = 3, +}; + +enum mscc_macsec_validate_levels { + MSCC_MS_VALIDATE_DISABLED = 0, + MSCC_MS_VALIDATE_CHECK = 1, + MSCC_MS_VALIDATE_STRICT = 2, +}; + +#define MSCC_MS_XFORM_REC(x, y) (((x) << 5) + (y)) +#define MSCC_MS_ENA_CFG 0x800 +#define MSCC_MS_FC_CFG 0x804 +#define MSCC_MS_SAM_MAC_SA_MATCH_LO(x) (0x1000 + ((x) << 4)) +#define MSCC_MS_SAM_MAC_SA_MATCH_HI(x) (0x1001 + ((x) << 4)) +#define MSCC_MS_SAM_MISC_MATCH(x) (0x1004 + ((x) << 4)) +#define MSCC_MS_SAM_MATCH_SCI_LO(x) (0x1005 + ((x) << 4)) +#define MSCC_MS_SAM_MATCH_SCI_HI(x) (0x1006 + ((x) << 4)) +#define MSCC_MS_SAM_MASK(x) (0x1007 + ((x) << 4)) +#define MSCC_MS_SAM_ENTRY_SET1 0x1808 +#define MSCC_MS_SAM_ENTRY_CLEAR1 0x180c +#define MSCC_MS_SAM_FLOW_CTRL(x) (0x1c00 + (x)) +#define MSCC_MS_SAM_CP_TAG 0x1e40 +#define MSCC_MS_SAM_NM_FLOW_NCP 0x1e51 +#define MSCC_MS_SAM_NM_FLOW_CP 0x1e52 +#define MSCC_MS_MISC_CONTROL 0x1e5f +#define MSCC_MS_COUNT_CONTROL 0x3204 +#define MSCC_MS_PARAMS2_IG_CC_CONTROL 0x3a10 +#define MSCC_MS_PARAMS2_IG_CP_TAG 0x3a14 +#define MSCC_MS_VLAN_MTU_CHECK(x) (0x3c40 + (x)) +#define MSCC_MS_NON_VLAN_MTU_CHECK 0x3c48 +#define MSCC_MS_PP_CTRL 0x3c4b +#define MSCC_MS_STATUS_CONTEXT_CTRL 0x3d02 +#define MSCC_MS_INTR_CTRL_STATUS 0x3d04 +#define MSCC_MS_BLOCK_CTX_UPDATE 0x3d0c +#define MSCC_MS_AIC_CTRL 0x3e02 + +/* MACSEC_ENA_CFG */ +#define MSCC_MS_ENA_CFG_CLK_ENA BIT(0) +#define MSCC_MS_ENA_CFG_SW_RST BIT(1) +#define MSCC_MS_ENA_CFG_MACSEC_BYPASS_ENA BIT(8) +#define MSCC_MS_ENA_CFG_MACSEC_ENA BIT(9) +#define MSCC_MS_ENA_CFG_MACSEC_SPEED_MODE(x) ((x) << 10) +#define MSCC_MS_ENA_CFG_MACSEC_SPEED_MODE_M GENMASK(12, 10) + +/* MACSEC_FC_CFG */ +#define MSCC_MS_FC_CFG_FCBUF_ENA BIT(0) +#define MSCC_MS_FC_CFG_USE_PKT_EXPANSION_INDICATION BIT(1) +#define MSCC_MS_FC_CFG_LOW_THRESH(x) ((x) << 4) +#define MSCC_MS_FC_CFG_LOW_THRESH_M GENMASK(7, 4) +#define MSCC_MS_FC_CFG_HIGH_THRESH(x) ((x) << 8) +#define MSCC_MS_FC_CFG_HIGH_THRESH_M GENMASK(11, 8) +#define MSCC_MS_FC_CFG_LOW_BYTES_VAL(x) ((x) << 12) +#define MSCC_MS_FC_CFG_LOW_BYTES_VAL_M GENMASK(14, 12) +#define MSCC_MS_FC_CFG_HIGH_BYTES_VAL(x) ((x) << 16) +#define MSCC_MS_FC_CFG_HIGH_BYTES_VAL_M GENMASK(18, 16) + +/* MSCC_MS_SAM_MAC_SA_MATCH_HI */ +#define MSCC_MS_SAM_MAC_SA_MATCH_HI_ETYPE(x) ((x) << 16) +#define MSCC_MS_SAM_MAC_SA_MATCH_HI_ETYPE_M GENMASK(31, 16) + +/* MACSEC_SAM_MISC_MATCH */ +#define MSCC_MS_SAM_MISC_MATCH_VLAN_VALID BIT(0) +#define MSCC_MS_SAM_MISC_MATCH_QINQ_FOUND BIT(1) +#define MSCC_MS_SAM_MISC_MATCH_STAG_VALID BIT(2) +#define MSCC_MS_SAM_MISC_MATCH_QTAG_VALID BIT(3) +#define MSCC_MS_SAM_MISC_MATCH_VLAN_UP(x) ((x) << 4) +#define MSCC_MS_SAM_MISC_MATCH_VLAN_UP_M GENMASK(6, 4) +#define MSCC_MS_SAM_MISC_MATCH_CONTROL_PACKET BIT(7) +#define MSCC_MS_SAM_MISC_MATCH_UNTAGGED BIT(8) +#define MSCC_MS_SAM_MISC_MATCH_TAGGED BIT(9) +#define MSCC_MS_SAM_MISC_MATCH_BAD_TAG BIT(10) +#define MSCC_MS_SAM_MISC_MATCH_KAY_TAG BIT(11) +#define MSCC_MS_SAM_MISC_MATCH_SOURCE_PORT(x) ((x) << 12) +#define MSCC_MS_SAM_MISC_MATCH_SOURCE_PORT_M GENMASK(13, 12) +#define MSCC_MS_SAM_MISC_MATCH_PRIORITY(x) ((x) << 16) +#define MSCC_MS_SAM_MISC_MATCH_PRIORITY_M GENMASK(19, 16) +#define MSCC_MS_SAM_MISC_MATCH_AN(x) ((x) << 24) +#define MSCC_MS_SAM_MISC_MATCH_TCI(x) ((x) << 26) + +/* MACSEC_SAM_MASK */ +#define MSCC_MS_SAM_MASK_MAC_SA_MASK(x) (x) +#define MSCC_MS_SAM_MASK_MAC_SA_MASK_M GENMASK(5, 0) +#define MSCC_MS_SAM_MASK_MAC_DA_MASK(x) ((x) << 6) +#define MSCC_MS_SAM_MASK_MAC_DA_MASK_M GENMASK(11, 6) +#define MSCC_MS_SAM_MASK_MAC_ETYPE_MASK BIT(12) +#define MSCC_MS_SAM_MASK_VLAN_VLD_MASK BIT(13) +#define MSCC_MS_SAM_MASK_QINQ_FOUND_MASK BIT(14) +#define MSCC_MS_SAM_MASK_STAG_VLD_MASK BIT(15) +#define MSCC_MS_SAM_MASK_QTAG_VLD_MASK BIT(16) +#define MSCC_MS_SAM_MASK_VLAN_UP_MASK BIT(17) +#define MSCC_MS_SAM_MASK_VLAN_ID_MASK BIT(18) +#define MSCC_MS_SAM_MASK_SOURCE_PORT_MASK BIT(19) +#define MSCC_MS_SAM_MASK_CTL_PACKET_MASK BIT(20) +#define MSCC_MS_SAM_MASK_VLAN_UP_INNER_MASK BIT(21) +#define MSCC_MS_SAM_MASK_VLAN_ID_INNER_MASK BIT(22) +#define MSCC_MS_SAM_MASK_SCI_MASK BIT(23) +#define MSCC_MS_SAM_MASK_AN_MASK(x) ((x) << 24) +#define MSCC_MS_SAM_MASK_TCI_MASK(x) ((x) << 26) + +/* MACSEC_SAM_FLOW_CTRL_EGR */ +#define MSCC_MS_SAM_FLOW_CTRL_FLOW_TYPE(x) (x) +#define MSCC_MS_SAM_FLOW_CTRL_FLOW_TYPE_M GENMASK(1, 0) +#define MSCC_MS_SAM_FLOW_CTRL_DEST_PORT(x) ((x) << 2) +#define MSCC_MS_SAM_FLOW_CTRL_DEST_PORT_M GENMASK(3, 2) +#define MSCC_MS_SAM_FLOW_CTRL_RESV_4 BIT(4) +#define MSCC_MS_SAM_FLOW_CTRL_FLOW_CRYPT_AUTH BIT(5) +#define MSCC_MS_SAM_FLOW_CTRL_DROP_ACTION(x) ((x) << 6) +#define MSCC_MS_SAM_FLOW_CTRL_DROP_ACTION_M GENMASK(7, 6) +#define MSCC_MS_SAM_FLOW_CTRL_RESV_15_TO_8(x) ((x) << 8) +#define MSCC_MS_SAM_FLOW_CTRL_RESV_15_TO_8_M GENMASK(15, 8) +#define MSCC_MS_SAM_FLOW_CTRL_PROTECT_FRAME BIT(16) +#define MSCC_MS_SAM_FLOW_CTRL_REPLAY_PROTECT BIT(16) +#define MSCC_MS_SAM_FLOW_CTRL_SA_IN_USE BIT(17) +#define MSCC_MS_SAM_FLOW_CTRL_INCLUDE_SCI BIT(18) +#define MSCC_MS_SAM_FLOW_CTRL_USE_ES BIT(19) +#define MSCC_MS_SAM_FLOW_CTRL_USE_SCB BIT(20) +#define MSCC_MS_SAM_FLOW_CTRL_VALIDATE_FRAMES(x) ((x) << 19) +#define MSCC_MS_SAM_FLOW_CTRL_TAG_BYPASS_SIZE(x) ((x) << 21) +#define MSCC_MS_SAM_FLOW_CTRL_TAG_BYPASS_SIZE_M GENMASK(22, 21) +#define MSCC_MS_SAM_FLOW_CTRL_RESV_23 BIT(23) +#define MSCC_MS_SAM_FLOW_CTRL_CONFIDENTIALITY_OFFSET(x) ((x) << 24) +#define MSCC_MS_SAM_FLOW_CTRL_CONFIDENTIALITY_OFFSET_M GENMASK(30, 24) +#define MSCC_MS_SAM_FLOW_CTRL_CONF_PROTECT BIT(31) + +/* MACSEC_SAM_CP_TAG */ +#define MSCC_MS_SAM_CP_TAG_MAP_TBL(x) (x) +#define MSCC_MS_SAM_CP_TAG_MAP_TBL_M GENMASK(23, 0) +#define MSCC_MS_SAM_CP_TAG_DEF_UP(x) ((x) << 24) +#define MSCC_MS_SAM_CP_TAG_DEF_UP_M GENMASK(26, 24) +#define MSCC_MS_SAM_CP_TAG_STAG_UP_EN BIT(27) +#define MSCC_MS_SAM_CP_TAG_QTAG_UP_EN BIT(28) +#define MSCC_MS_SAM_CP_TAG_PARSE_QINQ BIT(29) +#define MSCC_MS_SAM_CP_TAG_PARSE_STAG BIT(30) +#define MSCC_MS_SAM_CP_TAG_PARSE_QTAG BIT(31) + +/* MACSEC_SAM_NM_FLOW_NCP */ +#define MSCC_MS_SAM_NM_FLOW_NCP_UNTAGGED_FLOW_TYPE(x) (x) +#define MSCC_MS_SAM_NM_FLOW_NCP_UNTAGGED_DEST_PORT(x) ((x) << 2) +#define MSCC_MS_SAM_NM_FLOW_NCP_UNTAGGED_DROP_ACTION(x) ((x) << 6) +#define MSCC_MS_SAM_NM_FLOW_NCP_TAGGED_FLOW_TYPE(x) ((x) << 8) +#define MSCC_MS_SAM_NM_FLOW_NCP_TAGGED_DEST_PORT(x) ((x) << 10) +#define MSCC_MS_SAM_NM_FLOW_NCP_TAGGED_DROP_ACTION(x) ((x) << 14) +#define MSCC_MS_SAM_NM_FLOW_NCP_BADTAG_FLOW_TYPE(x) ((x) << 16) +#define MSCC_MS_SAM_NM_FLOW_NCP_BADTAG_DEST_PORT(x) ((x) << 18) +#define MSCC_MS_SAM_NM_FLOW_NCP_BADTAG_DROP_ACTION(x) ((x) << 22) +#define MSCC_MS_SAM_NM_FLOW_NCP_KAY_FLOW_TYPE(x) ((x) << 24) +#define MSCC_MS_SAM_NM_FLOW_NCP_KAY_DEST_PORT(x) ((x) << 26) +#define MSCC_MS_SAM_NM_FLOW_NCP_KAY_DROP_ACTION(x) ((x) << 30) + +/* MACSEC_SAM_NM_FLOW_CP */ +#define MSCC_MS_SAM_NM_FLOW_CP_UNTAGGED_FLOW_TYPE(x) (x) +#define MSCC_MS_SAM_NM_FLOW_CP_UNTAGGED_DEST_PORT(x) ((x) << 2) +#define MSCC_MS_SAM_NM_FLOW_CP_UNTAGGED_DROP_ACTION(x) ((x) << 6) +#define MSCC_MS_SAM_NM_FLOW_CP_TAGGED_FLOW_TYPE(x) ((x) << 8) +#define MSCC_MS_SAM_NM_FLOW_CP_TAGGED_DEST_PORT(x) ((x) << 10) +#define MSCC_MS_SAM_NM_FLOW_CP_TAGGED_DROP_ACTION(x) ((x) << 14) +#define MSCC_MS_SAM_NM_FLOW_CP_BADTAG_FLOW_TYPE(x) ((x) << 16) +#define MSCC_MS_SAM_NM_FLOW_CP_BADTAG_DEST_PORT(x) ((x) << 18) +#define MSCC_MS_SAM_NM_FLOW_CP_BADTAG_DROP_ACTION(x) ((x) << 22) +#define MSCC_MS_SAM_NM_FLOW_CP_KAY_FLOW_TYPE(x) ((x) << 24) +#define MSCC_MS_SAM_NM_FLOW_CP_KAY_DEST_PORT(x) ((x) << 26) +#define MSCC_MS_SAM_NM_FLOW_CP_KAY_DROP_ACTION(x) ((x) << 30) + +/* MACSEC_MISC_CONTROL */ +#define MSCC_MS_MISC_CONTROL_MC_LATENCY_FIX(x) (x) +#define MSCC_MS_MISC_CONTROL_MC_LATENCY_FIX_M GENMASK(5, 0) +#define MSCC_MS_MISC_CONTROL_STATIC_BYPASS BIT(8) +#define MSCC_MS_MISC_CONTROL_NM_MACSEC_EN BIT(9) +#define MSCC_MS_MISC_CONTROL_VALIDATE_FRAMES(x) ((x) << 10) +#define MSCC_MS_MISC_CONTROL_VALIDATE_FRAMES_M GENMASK(11, 10) +#define MSCC_MS_MISC_CONTROL_XFORM_REC_SIZE(x) ((x) << 24) +#define MSCC_MS_MISC_CONTROL_XFORM_REC_SIZE_M GENMASK(25, 24) + +/* MACSEC_COUNT_CONTROL */ +#define MSCC_MS_COUNT_CONTROL_RESET_ALL BIT(0) +#define MSCC_MS_COUNT_CONTROL_DEBUG_ACCESS BIT(1) +#define MSCC_MS_COUNT_CONTROL_SATURATE_CNTRS BIT(2) +#define MSCC_MS_COUNT_CONTROL_AUTO_CNTR_RESET BIT(3) + +/* MACSEC_PARAMS2_IG_CC_CONTROL */ +#define MSCC_MS_PARAMS2_IG_CC_CONTROL_NON_MATCH_CTRL_ACT BIT(14) +#define MSCC_MS_PARAMS2_IG_CC_CONTROL_NON_MATCH_ACT BIT(15) + +/* MACSEC_PARAMS2_IG_CP_TAG */ +#define MSCC_MS_PARAMS2_IG_CP_TAG_MAP_TBL(x) (x) +#define MSCC_MS_PARAMS2_IG_CP_TAG_MAP_TBL_M GENMASK(23, 0) +#define MSCC_MS_PARAMS2_IG_CP_TAG_DEF_UP(x) ((x) << 24) +#define MSCC_MS_PARAMS2_IG_CP_TAG_DEF_UP_M GENMASK(26, 24) +#define MSCC_MS_PARAMS2_IG_CP_TAG_STAG_UP_EN BIT(27) +#define MSCC_MS_PARAMS2_IG_CP_TAG_QTAG_UP_EN BIT(28) +#define MSCC_MS_PARAMS2_IG_CP_TAG_PARSE_QINQ BIT(29) +#define MSCC_MS_PARAMS2_IG_CP_TAG_PARSE_STAG BIT(30) +#define MSCC_MS_PARAMS2_IG_CP_TAG_PARSE_QTAG BIT(31) + +/* MACSEC_VLAN_MTU_CHECK */ +#define MSCC_MS_VLAN_MTU_CHECK_MTU_COMPARE(x) (x) +#define MSCC_MS_VLAN_MTU_CHECK_MTU_COMPARE_M GENMASK(14, 0) +#define MSCC_MS_VLAN_MTU_CHECK_MTU_COMP_DROP BIT(15) + +/* MACSEC_NON_VLAN_MTU_CHECK */ +#define MSCC_MS_NON_VLAN_MTU_CHECK_NV_MTU_COMPARE(x) (x) +#define MSCC_MS_NON_VLAN_MTU_CHECK_NV_MTU_COMPARE_M GENMASK(14, 0) +#define MSCC_MS_NON_VLAN_MTU_CHECK_NV_MTU_COMP_DROP BIT(15) + +/* MACSEC_PP_CTRL */ +#define MSCC_MS_PP_CTRL_MACSEC_OCTET_INCR_MODE BIT(0) + +/* MACSEC_INTR_CTRL_STATUS */ +#define MSCC_MS_INTR_CTRL_STATUS_INTR_CLR_STATUS(x) (x) +#define MSCC_MS_INTR_CTRL_STATUS_INTR_CLR_STATUS_M GENMASK(15, 0) +#define MSCC_MS_INTR_CTRL_STATUS_INTR_ENABLE(x) ((x) << 16) +#define MSCC_MS_INTR_CTRL_STATUS_INTR_ENABLE_M GENMASK(31, 16) +#define MACSEC_INTR_CTRL_STATUS_ROLLOVER BIT(5) + +#endif diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c index 769a076514b0..a4d2d59fceca 100644 --- a/drivers/net/phy/phy-core.c +++ b/drivers/net/phy/phy-core.c @@ -387,7 +387,7 @@ int __phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum) if (regnum > (u16)~0 || devad > 32) return -EINVAL; - if (phydev->drv->read_mmd) { + if (phydev->drv && phydev->drv->read_mmd) { val = phydev->drv->read_mmd(phydev, devad, regnum); } else if (phydev->is_c45) { u32 addr = MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff); @@ -444,7 +444,7 @@ int __phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val) if (regnum > (u16)~0 || devad > 32) return -EINVAL; - if (phydev->drv->write_mmd) { + if (phydev->drv && phydev->drv->write_mmd) { ret = phydev->drv->write_mmd(phydev, devad, regnum, val); } else if (phydev->is_c45) { u32 addr = MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff); diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 80be4d691e5b..d76e038cf2cb 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -422,8 +422,8 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd) return 0; case SIOCSHWTSTAMP: - if (phydev->drv && phydev->drv->hwtstamp) - return phydev->drv->hwtstamp(phydev, ifr); + if (phydev->mii_ts && phydev->mii_ts->hwtstamp) + return phydev->mii_ts->hwtstamp(phydev->mii_ts, ifr); /* fall through */ default: @@ -432,6 +432,31 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd) } EXPORT_SYMBOL(phy_mii_ioctl); +/** + * phy_do_ioctl - generic ndo_do_ioctl implementation + * @dev: the net_device struct + * @ifr: &struct ifreq for socket ioctl's + * @cmd: ioctl cmd to execute + */ +int phy_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + if (!dev->phydev) + return -ENODEV; + + return phy_mii_ioctl(dev->phydev, ifr, cmd); +} +EXPORT_SYMBOL(phy_do_ioctl); + +/* same as phy_do_ioctl, but ensures that net_device is running */ +int phy_do_ioctl_running(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + if (!netif_running(dev)) + return -ENODEV; + + return phy_do_ioctl(dev, ifr, cmd); +} +EXPORT_SYMBOL(phy_do_ioctl_running); + void phy_queue_state_machine(struct phy_device *phydev, unsigned long jiffies) { mod_delayed_work(system_power_efficient_wq, &phydev->state_queue, diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index b13c52873ef5..6a5056e0ae77 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -881,6 +881,9 @@ EXPORT_SYMBOL(phy_device_register); */ void phy_device_remove(struct phy_device *phydev) { + if (phydev->mii_ts) + unregister_mii_timestamper(phydev->mii_ts); + device_del(&phydev->mdio.dev); /* Assert the reset signal */ @@ -919,6 +922,8 @@ static void phy_link_change(struct phy_device *phydev, bool up, bool do_carrier) netif_carrier_off(netdev); } phydev->adjust_link(netdev); + if (phydev->mii_ts && phydev->mii_ts->link_state) + phydev->mii_ts->link_state(phydev->mii_ts, phydev); } /** @@ -1102,9 +1107,8 @@ void phy_attached_info(struct phy_device *phydev) EXPORT_SYMBOL(phy_attached_info); #define ATTACHED_FMT "attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%s)" -void phy_attached_print(struct phy_device *phydev, const char *fmt, ...) +char *phy_attached_info_irq(struct phy_device *phydev) { - const char *drv_name = phydev->drv ? phydev->drv->name : "unbound"; char *irq_str; char irq_num[8]; @@ -1121,6 +1125,14 @@ void phy_attached_print(struct phy_device *phydev, const char *fmt, ...) break; } + return kasprintf(GFP_KERNEL, "%s", irq_str); +} +EXPORT_SYMBOL(phy_attached_info_irq); + +void phy_attached_print(struct phy_device *phydev, const char *fmt, ...) +{ + const char *drv_name = phydev->drv ? phydev->drv->name : "unbound"; + char *irq_str = phy_attached_info_irq(phydev); if (!fmt) { phydev_info(phydev, ATTACHED_FMT "\n", @@ -1137,6 +1149,7 @@ void phy_attached_print(struct phy_device *phydev, const char *fmt, ...) vprintk(fmt, ap); va_end(ap); } + kfree(irq_str); } EXPORT_SYMBOL(phy_attached_print); @@ -1771,6 +1784,36 @@ int genphy_restart_aneg(struct phy_device *phydev) EXPORT_SYMBOL(genphy_restart_aneg); /** + * genphy_check_and_restart_aneg - Enable and restart auto-negotiation + * @phydev: target phy_device struct + * @restart: whether aneg restart is requested + * + * Check, and restart auto-negotiation if needed. + */ +int genphy_check_and_restart_aneg(struct phy_device *phydev, bool restart) +{ + int ret = 0; + + if (!restart) { + /* Advertisement hasn't changed, but maybe aneg was never on to + * begin with? Or maybe phy was isolated? + */ + ret = phy_read(phydev, MII_BMCR); + if (ret < 0) + return ret; + + if (!(ret & BMCR_ANENABLE) || (ret & BMCR_ISOLATE)) + restart = true; + } + + if (restart) + ret = genphy_restart_aneg(phydev); + + return ret; +} +EXPORT_SYMBOL(genphy_check_and_restart_aneg); + +/** * __genphy_config_aneg - restart auto-negotiation or write BMCR * @phydev: target phy_device struct * @changed: whether autoneg is requested @@ -1795,23 +1838,7 @@ int __genphy_config_aneg(struct phy_device *phydev, bool changed) else if (err) changed = true; - if (!changed) { - /* Advertisement hasn't changed, but maybe aneg was never on to - * begin with? Or maybe phy was isolated? - */ - int ctl = phy_read(phydev, MII_BMCR); - - if (ctl < 0) - return ctl; - - if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE)) - changed = true; /* do restart aneg */ - } - - /* Only restart aneg if we are advertising something different - * than we were before. - */ - return changed ? genphy_restart_aneg(phydev) : 0; + return genphy_check_and_restart_aneg(phydev, changed); } EXPORT_SYMBOL(__genphy_config_aneg); @@ -1979,6 +2006,36 @@ int genphy_read_lpa(struct phy_device *phydev) EXPORT_SYMBOL(genphy_read_lpa); /** + * genphy_read_status_fixed - read the link parameters for !aneg mode + * @phydev: target phy_device struct + * + * Read the current duplex and speed state for a PHY operating with + * autonegotiation disabled. + */ +int genphy_read_status_fixed(struct phy_device *phydev) +{ + int bmcr = phy_read(phydev, MII_BMCR); + + if (bmcr < 0) + return bmcr; + + if (bmcr & BMCR_FULLDPLX) + phydev->duplex = DUPLEX_FULL; + else + phydev->duplex = DUPLEX_HALF; + + if (bmcr & BMCR_SPEED1000) + phydev->speed = SPEED_1000; + else if (bmcr & BMCR_SPEED100) + phydev->speed = SPEED_100; + else + phydev->speed = SPEED_10; + + return 0; +} +EXPORT_SYMBOL(genphy_read_status_fixed); + +/** * genphy_read_status - check the link status and update current link state * @phydev: target phy_device struct * @@ -2012,22 +2069,9 @@ int genphy_read_status(struct phy_device *phydev) if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) { phy_resolve_aneg_linkmode(phydev); } else if (phydev->autoneg == AUTONEG_DISABLE) { - int bmcr = phy_read(phydev, MII_BMCR); - - if (bmcr < 0) - return bmcr; - - if (bmcr & BMCR_FULLDPLX) - phydev->duplex = DUPLEX_FULL; - else - phydev->duplex = DUPLEX_HALF; - - if (bmcr & BMCR_SPEED1000) - phydev->speed = SPEED_1000; - else if (bmcr & BMCR_SPEED100) - phydev->speed = SPEED_100; - else - phydev->speed = SPEED_10; + err = genphy_read_status_fixed(phydev); + if (err < 0) + return err; } return 0; @@ -2575,7 +2619,6 @@ static struct phy_driver genphy_driver = { .name = "Generic PHY", .soft_reset = genphy_no_soft_reset, .get_features = genphy_read_abilities, - .aneg_done = genphy_aneg_done, .suspend = genphy_suspend, .resume = genphy_resume, .set_loopback = genphy_loopback, diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index ee7a718662c6..70b9a143db84 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -48,7 +48,8 @@ struct phylink { unsigned long phylink_disable_state; /* bitmask of disables */ struct phy_device *phydev; phy_interface_t link_interface; /* PHY_INTERFACE_xxx */ - u8 link_an_mode; /* MLO_AN_xxx */ + u8 cfg_link_an_mode; /* MLO_AN_xxx */ + u8 cur_link_an_mode; u8 link_port; /* The current non-phy ethtool port */ __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); @@ -71,6 +72,9 @@ struct phylink { bool mac_link_dropped; struct sfp_bus *sfp_bus; + bool sfp_may_have_phy; + __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support); + u8 sfp_port; }; #define phylink_printk(level, pl, fmt, ...) \ @@ -182,8 +186,8 @@ static int phylink_parse_fixedlink(struct phylink *pl, pl->link_config.pause |= MLO_PAUSE_ASYM; if (ret == 0) { - desc = fwnode_get_named_gpiod(fixed_node, "link-gpios", - 0, GPIOD_IN, "?"); + desc = fwnode_gpiod_get_index(fixed_node, "link", 0, + GPIOD_IN, "?"); if (!IS_ERR(desc)) pl->link_gpio = desc; @@ -256,12 +260,12 @@ static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode) dn = fwnode_get_named_child_node(fwnode, "fixed-link"); if (dn || fwnode_property_present(fwnode, "fixed-link")) - pl->link_an_mode = MLO_AN_FIXED; + pl->cfg_link_an_mode = MLO_AN_FIXED; fwnode_handle_put(dn); if (fwnode_property_read_string(fwnode, "managed", &managed) == 0 && strcmp(managed, "in-band-status") == 0) { - if (pl->link_an_mode == MLO_AN_FIXED) { + if (pl->cfg_link_an_mode == MLO_AN_FIXED) { phylink_err(pl, "can't use both fixed-link and in-band-status\n"); return -EINVAL; @@ -273,10 +277,11 @@ static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode) phylink_set(pl->supported, Asym_Pause); phylink_set(pl->supported, Pause); pl->link_config.an_enabled = true; - pl->link_an_mode = MLO_AN_INBAND; + pl->cfg_link_an_mode = MLO_AN_INBAND; switch (pl->link_config.interface) { case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_QSGMII: phylink_set(pl->supported, 10baseT_Half); phylink_set(pl->supported, 10baseT_Full); phylink_set(pl->supported, 100baseT_Half); @@ -293,7 +298,9 @@ static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode) phylink_set(pl->supported, 2500baseX_Full); break; + case PHY_INTERFACE_MODE_USXGMII: case PHY_INTERFACE_MODE_10GKR: + case PHY_INTERFACE_MODE_10GBASER: phylink_set(pl->supported, 10baseT_Half); phylink_set(pl->supported, 10baseT_Full); phylink_set(pl->supported, 100baseT_Half); @@ -301,6 +308,10 @@ static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode) phylink_set(pl->supported, 1000baseT_Half); phylink_set(pl->supported, 1000baseT_Full); phylink_set(pl->supported, 1000baseX_Full); + phylink_set(pl->supported, 2500baseT_Full); + phylink_set(pl->supported, 2500baseX_Full); + phylink_set(pl->supported, 5000baseT_Full); + phylink_set(pl->supported, 10000baseT_Full); phylink_set(pl->supported, 10000baseKR_Full); phylink_set(pl->supported, 10000baseCR_Full); phylink_set(pl->supported, 10000baseSR_Full); @@ -333,14 +344,14 @@ static void phylink_mac_config(struct phylink *pl, { phylink_dbg(pl, "%s: mode=%s/%s/%s/%s adv=%*pb pause=%02x link=%u an=%u\n", - __func__, phylink_an_mode_str(pl->link_an_mode), + __func__, phylink_an_mode_str(pl->cur_link_an_mode), phy_modes(state->interface), phy_speed_to_str(state->speed), phy_duplex_to_str(state->duplex), __ETHTOOL_LINK_MODE_MASK_NBITS, state->advertising, state->pause, state->link, state->an_enabled); - pl->ops->mac_config(pl->config, pl->link_an_mode, state); + pl->ops->mac_config(pl->config, pl->cur_link_an_mode, state); } static void phylink_mac_config_up(struct phylink *pl, @@ -441,7 +452,7 @@ static void phylink_mac_link_up(struct phylink *pl, struct net_device *ndev = pl->netdev; pl->cur_interface = link_state.interface; - pl->ops->mac_link_up(pl->config, pl->link_an_mode, + pl->ops->mac_link_up(pl->config, pl->cur_link_an_mode, pl->cur_interface, pl->phydev); if (ndev) @@ -460,7 +471,7 @@ static void phylink_mac_link_down(struct phylink *pl) if (ndev) netif_carrier_off(ndev); - pl->ops->mac_link_down(pl->config, pl->link_an_mode, + pl->ops->mac_link_down(pl->config, pl->cur_link_an_mode, pl->cur_interface); phylink_info(pl, "Link is Down\n"); } @@ -479,7 +490,7 @@ static void phylink_resolve(struct work_struct *w) } else if (pl->mac_link_dropped) { link_state.link = false; } else { - switch (pl->link_an_mode) { + switch (pl->cur_link_an_mode) { case MLO_AN_PHY: link_state = pl->phy_state; phylink_resolve_flow(pl, &link_state); @@ -650,7 +661,7 @@ struct phylink *phylink_create(struct phylink_config *config, return ERR_PTR(ret); } - if (pl->link_an_mode == MLO_AN_FIXED) { + if (pl->cfg_link_an_mode == MLO_AN_FIXED) { ret = phylink_parse_fixedlink(pl, fwnode); if (ret < 0) { kfree(pl); @@ -658,6 +669,8 @@ struct phylink *phylink_create(struct phylink_config *config, } } + pl->cur_link_an_mode = pl->cfg_link_an_mode; + ret = phylink_register_sfp(pl, fwnode); if (ret < 0) { kfree(pl); @@ -713,10 +726,12 @@ static void phylink_phy_change(struct phy_device *phydev, bool up, phy_duplex_to_str(phydev->duplex)); } -static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy) +static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy, + phy_interface_t interface) { struct phylink_link_state config; __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); + char *irq_str; int ret; /* @@ -731,7 +746,19 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy) memset(&config, 0, sizeof(config)); linkmode_copy(supported, phy->supported); linkmode_copy(config.advertising, phy->advertising); - config.interface = pl->link_config.interface; + + /* Clause 45 PHYs switch their Serdes lane between several different + * modes, normally 10GBASE-R, SGMII. Some use 2500BASE-X for 2.5G + * speeds. We really need to know which interface modes the PHY and + * MAC supports to properly work out which linkmodes can be supported. + */ + if (phy->is_c45 && + interface != PHY_INTERFACE_MODE_RXAUI && + interface != PHY_INTERFACE_MODE_XAUI && + interface != PHY_INTERFACE_MODE_USXGMII) + config.interface = PHY_INTERFACE_MODE_NA; + else + config.interface = interface; ret = phylink_validate(pl, supported, &config); if (ret) @@ -740,13 +767,16 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy) phy->phylink = pl; phy->phy_link_change = phylink_phy_change; + irq_str = phy_attached_info_irq(phy); phylink_info(pl, - "PHY [%s] driver [%s]\n", dev_name(&phy->mdio.dev), - phy->drv->name); + "PHY [%s] driver [%s] (irq=%s)\n", + dev_name(&phy->mdio.dev), phy->drv->name, irq_str); + kfree(irq_str); mutex_lock(&phy->lock); mutex_lock(&pl->state_mutex); pl->phydev = phy; + pl->phy_state.interface = interface; linkmode_copy(pl->supported, supported); linkmode_copy(pl->link_config.advertising, config.advertising); @@ -766,28 +796,18 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy) return 0; } -static int __phylink_connect_phy(struct phylink *pl, struct phy_device *phy, - phy_interface_t interface) +static int phylink_attach_phy(struct phylink *pl, struct phy_device *phy, + phy_interface_t interface) { - int ret; - - if (WARN_ON(pl->link_an_mode == MLO_AN_FIXED || - (pl->link_an_mode == MLO_AN_INBAND && + if (WARN_ON(pl->cfg_link_an_mode == MLO_AN_FIXED || + (pl->cfg_link_an_mode == MLO_AN_INBAND && phy_interface_mode_is_8023z(interface)))) return -EINVAL; if (pl->phydev) return -EBUSY; - ret = phy_attach_direct(pl->netdev, phy, 0, interface); - if (ret) - return ret; - - ret = phylink_bringup_phy(pl, phy); - if (ret) - phy_detach(phy); - - return ret; + return phy_attach_direct(pl->netdev, phy, 0, interface); } /** @@ -807,13 +827,23 @@ static int __phylink_connect_phy(struct phylink *pl, struct phy_device *phy, */ int phylink_connect_phy(struct phylink *pl, struct phy_device *phy) { + int ret; + /* Use PHY device/driver interface */ if (pl->link_interface == PHY_INTERFACE_MODE_NA) { pl->link_interface = phy->interface; pl->link_config.interface = pl->link_interface; } - return __phylink_connect_phy(pl, phy, pl->link_interface); + ret = phylink_attach_phy(pl, phy, pl->link_interface); + if (ret < 0) + return ret; + + ret = phylink_bringup_phy(pl, phy, pl->link_config.interface); + if (ret) + phy_detach(phy); + + return ret; } EXPORT_SYMBOL_GPL(phylink_connect_phy); @@ -837,8 +867,8 @@ int phylink_of_phy_connect(struct phylink *pl, struct device_node *dn, int ret; /* Fixed links and 802.3z are handled without needing a PHY */ - if (pl->link_an_mode == MLO_AN_FIXED || - (pl->link_an_mode == MLO_AN_INBAND && + if (pl->cfg_link_an_mode == MLO_AN_FIXED || + (pl->cfg_link_an_mode == MLO_AN_INBAND && phy_interface_mode_is_8023z(pl->link_interface))) return 0; @@ -849,20 +879,23 @@ int phylink_of_phy_connect(struct phylink *pl, struct device_node *dn, phy_node = of_parse_phandle(dn, "phy-device", 0); if (!phy_node) { - if (pl->link_an_mode == MLO_AN_PHY) + if (pl->cfg_link_an_mode == MLO_AN_PHY) return -ENODEV; return 0; } - phy_dev = of_phy_attach(pl->netdev, phy_node, flags, - pl->link_interface); + phy_dev = of_phy_find_device(phy_node); /* We're done with the phy_node handle */ of_node_put(phy_node); - if (!phy_dev) return -ENODEV; - ret = phylink_bringup_phy(pl, phy_dev); + ret = phy_attach_direct(pl->netdev, phy_dev, flags, + pl->link_interface); + if (ret) + return ret; + + ret = phylink_bringup_phy(pl, phy_dev, pl->link_config.interface); if (ret) phy_detach(phy_dev); @@ -912,7 +945,7 @@ int phylink_fixed_state_cb(struct phylink *pl, /* It does not make sense to let the link be overriden unless we use * MLO_AN_FIXED */ - if (pl->link_an_mode != MLO_AN_FIXED) + if (pl->cfg_link_an_mode != MLO_AN_FIXED) return -EINVAL; mutex_lock(&pl->state_mutex); @@ -962,7 +995,7 @@ void phylink_start(struct phylink *pl) ASSERT_RTNL(); phylink_info(pl, "configuring for %s/%s link mode\n", - phylink_an_mode_str(pl->link_an_mode), + phylink_an_mode_str(pl->cur_link_an_mode), phy_modes(pl->link_config.interface)); /* Always set the carrier off */ @@ -985,7 +1018,7 @@ void phylink_start(struct phylink *pl) clear_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); phylink_run_resolve(pl); - if (pl->link_an_mode == MLO_AN_FIXED && pl->link_gpio) { + if (pl->cfg_link_an_mode == MLO_AN_FIXED && pl->link_gpio) { int irq = gpiod_to_irq(pl->link_gpio); if (irq > 0) { @@ -1000,7 +1033,8 @@ void phylink_start(struct phylink *pl) if (irq <= 0) mod_timer(&pl->link_poll, jiffies + HZ); } - if (pl->link_an_mode == MLO_AN_FIXED && pl->get_fixed_state) + if ((pl->cfg_link_an_mode == MLO_AN_FIXED && pl->get_fixed_state) || + pl->config->pcs_poll) mod_timer(&pl->link_poll, jiffies + HZ); if (pl->phydev) phy_start(pl->phydev); @@ -1127,7 +1161,7 @@ int phylink_ethtool_ksettings_get(struct phylink *pl, linkmode_copy(kset->link_modes.supported, pl->supported); - switch (pl->link_an_mode) { + switch (pl->cur_link_an_mode) { case MLO_AN_FIXED: /* We are using fixed settings. Report these as the * current link settings - and note that these also @@ -1199,7 +1233,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, /* If we have a fixed link (as specified by firmware), refuse * to change link parameters. */ - if (pl->link_an_mode == MLO_AN_FIXED && + if (pl->cur_link_an_mode == MLO_AN_FIXED && (s->speed != pl->link_config.speed || s->duplex != pl->link_config.duplex)) return -EINVAL; @@ -1211,7 +1245,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, __clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising); } else { /* If we have a fixed link, refuse to enable autonegotiation */ - if (pl->link_an_mode == MLO_AN_FIXED) + if (pl->cur_link_an_mode == MLO_AN_FIXED) return -EINVAL; config.speed = SPEED_UNKNOWN; @@ -1221,44 +1255,66 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, __set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising); } - if (phylink_validate(pl, support, &config)) - return -EINVAL; - - /* If autonegotiation is enabled, we must have an advertisement */ - if (config.an_enabled && phylink_is_empty_linkmode(config.advertising)) - return -EINVAL; - - our_kset = *kset; - linkmode_copy(our_kset.link_modes.advertising, config.advertising); - our_kset.base.speed = config.speed; - our_kset.base.duplex = config.duplex; - - /* If we have a PHY, configure the phy */ if (pl->phydev) { + /* If we have a PHY, we process the kset change via phylib. + * phylib will call our link state function if the PHY + * parameters have changed, which will trigger a resolve + * and update the MAC configuration. + */ + our_kset = *kset; + linkmode_copy(our_kset.link_modes.advertising, + config.advertising); + our_kset.base.speed = config.speed; + our_kset.base.duplex = config.duplex; + ret = phy_ethtool_ksettings_set(pl->phydev, &our_kset); if (ret) return ret; - } - mutex_lock(&pl->state_mutex); - /* Configure the MAC to match the new settings */ - linkmode_copy(pl->link_config.advertising, our_kset.link_modes.advertising); - pl->link_config.interface = config.interface; - pl->link_config.speed = our_kset.base.speed; - pl->link_config.duplex = our_kset.base.duplex; - pl->link_config.an_enabled = our_kset.base.autoneg != AUTONEG_DISABLE; + mutex_lock(&pl->state_mutex); + /* Save the new configuration */ + linkmode_copy(pl->link_config.advertising, + our_kset.link_modes.advertising); + pl->link_config.interface = config.interface; + pl->link_config.speed = our_kset.base.speed; + pl->link_config.duplex = our_kset.base.duplex; + pl->link_config.an_enabled = our_kset.base.autoneg != + AUTONEG_DISABLE; + mutex_unlock(&pl->state_mutex); + } else { + /* For a fixed link, this isn't able to change any parameters, + * which just leaves inband mode. + */ + if (phylink_validate(pl, support, &config)) + return -EINVAL; - /* If we have a PHY, phylib will call our link state function if the - * mode has changed, which will trigger a resolve and update the MAC - * configuration. For a fixed link, this isn't able to change any - * parameters, which just leaves inband mode. - */ - if (pl->link_an_mode == MLO_AN_INBAND && - !test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) { - phylink_mac_config(pl, &pl->link_config); - phylink_mac_an_restart(pl); + /* If autonegotiation is enabled, we must have an advertisement */ + if (config.an_enabled && + phylink_is_empty_linkmode(config.advertising)) + return -EINVAL; + + mutex_lock(&pl->state_mutex); + linkmode_copy(pl->link_config.advertising, config.advertising); + pl->link_config.interface = config.interface; + pl->link_config.speed = config.speed; + pl->link_config.duplex = config.duplex; + pl->link_config.an_enabled = kset->base.autoneg != + AUTONEG_DISABLE; + + if (pl->cur_link_an_mode == MLO_AN_INBAND && + !test_bit(PHYLINK_DISABLE_STOPPED, + &pl->phylink_disable_state)) { + /* If in 802.3z mode, this updates the advertisement. + * + * If we are in SGMII mode without a PHY, there is no + * advertisement; the only thing we have is the pause + * modes which can only come from a PHY. + */ + phylink_mac_config(pl, &pl->link_config); + phylink_mac_an_restart(pl); + } + mutex_unlock(&pl->state_mutex); } - mutex_unlock(&pl->state_mutex); return 0; } @@ -1343,7 +1399,7 @@ int phylink_ethtool_set_pauseparam(struct phylink *pl, pause->tx_pause); } else if (!test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) { - switch (pl->link_an_mode) { + switch (pl->cur_link_an_mode) { case MLO_AN_FIXED: /* Should we allow fixed links to change against the config? */ phylink_resolve_flow(pl, config); @@ -1550,7 +1606,7 @@ static int phylink_mii_read(struct phylink *pl, unsigned int phy_id, struct phylink_link_state state; int val = 0xffff; - switch (pl->link_an_mode) { + switch (pl->cur_link_an_mode) { case MLO_AN_FIXED: if (phy_id == 0) { phylink_get_fixed_state(pl, &state); @@ -1575,7 +1631,7 @@ static int phylink_mii_read(struct phylink *pl, unsigned int phy_id, static int phylink_mii_write(struct phylink *pl, unsigned int phy_id, unsigned int reg, unsigned int val) { - switch (pl->link_an_mode) { + switch (pl->cur_link_an_mode) { case MLO_AN_FIXED: break; @@ -1681,25 +1737,21 @@ static void phylink_sfp_detach(void *upstream, struct sfp_bus *bus) pl->netdev->sfp_bus = NULL; } -static int phylink_sfp_module_insert(void *upstream, - const struct sfp_eeprom_id *id) +static int phylink_sfp_config(struct phylink *pl, u8 mode, + const unsigned long *supported, + const unsigned long *advertising) { - struct phylink *pl = upstream; - __ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, }; __ETHTOOL_DECLARE_LINK_MODE_MASK(support1); + __ETHTOOL_DECLARE_LINK_MODE_MASK(support); struct phylink_link_state config; phy_interface_t iface; - int ret = 0; bool changed; - u8 port; - - ASSERT_RTNL(); + int ret; - sfp_parse_support(pl->sfp_bus, id, support); - port = sfp_parse_port(pl->sfp_bus, id, support); + linkmode_copy(support, supported); memset(&config, 0, sizeof(config)); - linkmode_copy(config.advertising, support); + linkmode_copy(config.advertising, advertising); config.interface = PHY_INTERFACE_MODE_NA; config.speed = SPEED_UNKNOWN; config.duplex = DUPLEX_UNKNOWN; @@ -1714,9 +1766,7 @@ static int phylink_sfp_module_insert(void *upstream, return ret; } - linkmode_copy(support1, support); - - iface = sfp_select_interface(pl->sfp_bus, id, config.advertising); + iface = sfp_select_interface(pl->sfp_bus, config.advertising); if (iface == PHY_INTERFACE_MODE_NA) { phylink_err(pl, "selection of interface failed, advertisement %*pb\n", @@ -1725,18 +1775,18 @@ static int phylink_sfp_module_insert(void *upstream, } config.interface = iface; + linkmode_copy(support1, support); ret = phylink_validate(pl, support1, &config); if (ret) { phylink_err(pl, "validation of %s/%s with support %*pb failed: %d\n", - phylink_an_mode_str(MLO_AN_INBAND), + phylink_an_mode_str(mode), phy_modes(config.interface), __ETHTOOL_LINK_MODE_MASK_NBITS, support, ret); return ret; } phylink_dbg(pl, "requesting link mode %s/%s with support %*pb\n", - phylink_an_mode_str(MLO_AN_INBAND), - phy_modes(config.interface), + phylink_an_mode_str(mode), phy_modes(config.interface), __ETHTOOL_LINK_MODE_MASK_NBITS, support); if (phy_interface_mode_is_8023z(iface) && pl->phydev) @@ -1748,19 +1798,19 @@ static int phylink_sfp_module_insert(void *upstream, linkmode_copy(pl->link_config.advertising, config.advertising); } - if (pl->link_an_mode != MLO_AN_INBAND || + if (pl->cur_link_an_mode != mode || pl->link_config.interface != config.interface) { pl->link_config.interface = config.interface; - pl->link_an_mode = MLO_AN_INBAND; + pl->cur_link_an_mode = mode; changed = true; phylink_info(pl, "switched to %s/%s link mode\n", - phylink_an_mode_str(MLO_AN_INBAND), + phylink_an_mode_str(mode), phy_modes(config.interface)); } - pl->link_port = port; + pl->link_port = pl->sfp_port; if (changed && !test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) @@ -1769,6 +1819,55 @@ static int phylink_sfp_module_insert(void *upstream, return ret; } +static int phylink_sfp_module_insert(void *upstream, + const struct sfp_eeprom_id *id) +{ + struct phylink *pl = upstream; + unsigned long *support = pl->sfp_support; + + ASSERT_RTNL(); + + linkmode_zero(support); + sfp_parse_support(pl->sfp_bus, id, support); + pl->sfp_port = sfp_parse_port(pl->sfp_bus, id, support); + + /* If this module may have a PHY connecting later, defer until later */ + pl->sfp_may_have_phy = sfp_may_have_phy(pl->sfp_bus, id); + if (pl->sfp_may_have_phy) + return 0; + + return phylink_sfp_config(pl, MLO_AN_INBAND, support, support); +} + +static int phylink_sfp_module_start(void *upstream) +{ + struct phylink *pl = upstream; + + /* If this SFP module has a PHY, start the PHY now. */ + if (pl->phydev) { + phy_start(pl->phydev); + return 0; + } + + /* If the module may have a PHY but we didn't detect one we + * need to configure the MAC here. + */ + if (!pl->sfp_may_have_phy) + return 0; + + return phylink_sfp_config(pl, MLO_AN_INBAND, + pl->sfp_support, pl->sfp_support); +} + +static void phylink_sfp_module_stop(void *upstream) +{ + struct phylink *pl = upstream; + + /* If this SFP module has a PHY, stop it. */ + if (pl->phydev) + phy_stop(pl->phydev); +} + static void phylink_sfp_link_down(void *upstream) { struct phylink *pl = upstream; @@ -1788,11 +1887,51 @@ static void phylink_sfp_link_up(void *upstream) phylink_run_resolve(pl); } +/* The Broadcom BCM84881 in the Methode DM7052 is unable to provide a SGMII + * or 802.3z control word, so inband will not work. + */ +static bool phylink_phy_no_inband(struct phy_device *phy) +{ + return phy->is_c45 && + (phy->c45_ids.device_ids[1] & 0xfffffff0) == 0xae025150; +} + static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy) { struct phylink *pl = upstream; + phy_interface_t interface; + u8 mode; + int ret; - return __phylink_connect_phy(upstream, phy, pl->link_config.interface); + /* + * This is the new way of dealing with flow control for PHYs, + * as described by Timur Tabi in commit 529ed1275263 ("net: phy: + * phy drivers should not set SUPPORTED_[Asym_]Pause") except + * using our validate call to the MAC, we rely upon the MAC + * clearing the bits from both supported and advertising fields. + */ + phy_support_asym_pause(phy); + + if (phylink_phy_no_inband(phy)) + mode = MLO_AN_PHY; + else + mode = MLO_AN_INBAND; + + /* Do the initial configuration */ + ret = phylink_sfp_config(pl, mode, phy->supported, phy->advertising); + if (ret < 0) + return ret; + + interface = pl->link_config.interface; + ret = phylink_attach_phy(pl, phy, interface); + if (ret < 0) + return ret; + + ret = phylink_bringup_phy(pl, phy, interface); + if (ret) + phy_detach(phy); + + return ret; } static void phylink_sfp_disconnect_phy(void *upstream) @@ -1804,6 +1943,8 @@ static const struct sfp_upstream_ops sfp_phylink_ops = { .attach = phylink_sfp_attach, .detach = phylink_sfp_detach, .module_insert = phylink_sfp_module_insert, + .module_start = phylink_sfp_module_start, + .module_stop = phylink_sfp_module_stop, .link_up = phylink_sfp_link_up, .link_down = phylink_sfp_link_down, .connect_phy = phylink_sfp_connect_phy, diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c index 476db5345e1a..f5fa2fff3ddc 100644 --- a/drivers/net/phy/realtek.c +++ b/drivers/net/phy/realtek.c @@ -29,6 +29,8 @@ #define RTL8211F_INSR 0x1d #define RTL8211F_TX_DELAY BIT(8) +#define RTL8211F_RX_DELAY BIT(3) + #define RTL8211E_TX_DELAY BIT(1) #define RTL8211E_RX_DELAY BIT(2) #define RTL8211E_MODE_MII_GMII BIT(3) @@ -171,25 +173,66 @@ static int rtl8211c_config_init(struct phy_device *phydev) static int rtl8211f_config_init(struct phy_device *phydev) { - u16 val; + struct device *dev = &phydev->mdio.dev; + u16 val_txdly, val_rxdly; + int ret; - /* enable TX-delay for rgmii-{id,txid}, and disable it for rgmii and - * rgmii-rxid. The RX-delay can be enabled by the external RXDLY pin. - */ switch (phydev->interface) { case PHY_INTERFACE_MODE_RGMII: + val_txdly = 0; + val_rxdly = 0; + break; + case PHY_INTERFACE_MODE_RGMII_RXID: - val = 0; + val_txdly = 0; + val_rxdly = RTL8211F_RX_DELAY; break; - case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_TXID: - val = RTL8211F_TX_DELAY; + val_txdly = RTL8211F_TX_DELAY; + val_rxdly = 0; + break; + + case PHY_INTERFACE_MODE_RGMII_ID: + val_txdly = RTL8211F_TX_DELAY; + val_rxdly = RTL8211F_RX_DELAY; break; + default: /* the rest of the modes imply leaving delay as is. */ return 0; } - return phy_modify_paged(phydev, 0xd08, 0x11, RTL8211F_TX_DELAY, val); + ret = phy_modify_paged_changed(phydev, 0xd08, 0x11, RTL8211F_TX_DELAY, + val_txdly); + if (ret < 0) { + dev_err(dev, "Failed to update the TX delay register\n"); + return ret; + } else if (ret) { + dev_dbg(dev, + "%s 2ns TX delay (and changing the value from pin-strapping RXD1 or the bootloader)\n", + val_txdly ? "Enabling" : "Disabling"); + } else { + dev_dbg(dev, + "2ns TX delay was already %s (by pin-strapping RXD1 or bootloader configuration)\n", + val_txdly ? "enabled" : "disabled"); + } + + ret = phy_modify_paged_changed(phydev, 0xd08, 0x15, RTL8211F_RX_DELAY, + val_rxdly); + if (ret < 0) { + dev_err(dev, "Failed to update the RX delay register\n"); + return ret; + } else if (ret) { + dev_dbg(dev, + "%s 2ns RX delay (and changing the value from pin-strapping RXD0 or the bootloader)\n", + val_rxdly ? "Enabling" : "Disabling"); + } else { + dev_dbg(dev, + "2ns RX delay was already %s (by pin-strapping RXD0 or bootloader configuration)\n", + val_rxdly ? "enabled" : "disabled"); + } + + return 0; } static int rtl8211e_config_init(struct phy_device *phydev) diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c index 5a72093ab6e7..d949ea7b4f8c 100644 --- a/drivers/net/phy/sfp-bus.c +++ b/drivers/net/phy/sfp-bus.c @@ -103,6 +103,7 @@ static const struct sfp_quirk *sfp_lookup_quirk(const struct sfp_eeprom_id *id) return NULL; } + /** * sfp_parse_port() - Parse the EEPROM base ID, setting the port type * @bus: a pointer to the &struct sfp_bus structure for the sfp module @@ -124,35 +125,35 @@ int sfp_parse_port(struct sfp_bus *bus, const struct sfp_eeprom_id *id, /* port is the physical connector, set this from the connector field. */ switch (id->base.connector) { - case SFP_CONNECTOR_SC: - case SFP_CONNECTOR_FIBERJACK: - case SFP_CONNECTOR_LC: - case SFP_CONNECTOR_MT_RJ: - case SFP_CONNECTOR_MU: - case SFP_CONNECTOR_OPTICAL_PIGTAIL: + case SFF8024_CONNECTOR_SC: + case SFF8024_CONNECTOR_FIBERJACK: + case SFF8024_CONNECTOR_LC: + case SFF8024_CONNECTOR_MT_RJ: + case SFF8024_CONNECTOR_MU: + case SFF8024_CONNECTOR_OPTICAL_PIGTAIL: + case SFF8024_CONNECTOR_MPO_1X12: + case SFF8024_CONNECTOR_MPO_2X16: port = PORT_FIBRE; break; - case SFP_CONNECTOR_RJ45: + case SFF8024_CONNECTOR_RJ45: port = PORT_TP; break; - case SFP_CONNECTOR_COPPER_PIGTAIL: + case SFF8024_CONNECTOR_COPPER_PIGTAIL: port = PORT_DA; break; - case SFP_CONNECTOR_UNSPEC: + case SFF8024_CONNECTOR_UNSPEC: if (id->base.e1000_base_t) { port = PORT_TP; break; } /* fallthrough */ - case SFP_CONNECTOR_SG: /* guess */ - case SFP_CONNECTOR_MPO_1X12: - case SFP_CONNECTOR_MPO_2X16: - case SFP_CONNECTOR_HSSDC_II: - case SFP_CONNECTOR_NOSEPARATE: - case SFP_CONNECTOR_MXC_2X16: + case SFF8024_CONNECTOR_SG: /* guess */ + case SFF8024_CONNECTOR_HSSDC_II: + case SFF8024_CONNECTOR_NOSEPARATE: + case SFF8024_CONNECTOR_MXC_2X16: port = PORT_OTHER; break; default: @@ -179,6 +180,33 @@ int sfp_parse_port(struct sfp_bus *bus, const struct sfp_eeprom_id *id, EXPORT_SYMBOL_GPL(sfp_parse_port); /** + * sfp_may_have_phy() - indicate whether the module may have a PHY + * @bus: a pointer to the &struct sfp_bus structure for the sfp module + * @id: a pointer to the module's &struct sfp_eeprom_id + * + * Parse the EEPROM identification given in @id, and return whether + * this module may have a PHY. + */ +bool sfp_may_have_phy(struct sfp_bus *bus, const struct sfp_eeprom_id *id) +{ + if (id->base.e1000_base_t) + return true; + + if (id->base.phys_id != SFF8024_ID_DWDM_SFP) { + switch (id->base.extended_cc) { + case SFF8024_ECC_10GBASE_T_SFI: + case SFF8024_ECC_10GBASE_T_SR: + case SFF8024_ECC_5GBASE_T: + case SFF8024_ECC_2_5GBASE_T: + return true; + } + } + + return false; +} +EXPORT_SYMBOL_GPL(sfp_may_have_phy); + +/** * sfp_parse_support() - Parse the eeprom id for supported link modes * @bus: a pointer to the &struct sfp_bus structure for the sfp module * @id: a pointer to the module's &struct sfp_eeprom_id @@ -261,22 +289,33 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, } switch (id->base.extended_cc) { - case 0x00: /* Unspecified */ + case SFF8024_ECC_UNSPEC: break; - case 0x02: /* 100Gbase-SR4 or 25Gbase-SR */ + case SFF8024_ECC_100GBASE_SR4_25GBASE_SR: phylink_set(modes, 100000baseSR4_Full); phylink_set(modes, 25000baseSR_Full); break; - case 0x03: /* 100Gbase-LR4 or 25Gbase-LR */ - case 0x04: /* 100Gbase-ER4 or 25Gbase-ER */ + case SFF8024_ECC_100GBASE_LR4_25GBASE_LR: + case SFF8024_ECC_100GBASE_ER4_25GBASE_ER: phylink_set(modes, 100000baseLR4_ER4_Full); break; - case 0x0b: /* 100Gbase-CR4 or 25Gbase-CR CA-L */ - case 0x0c: /* 25Gbase-CR CA-S */ - case 0x0d: /* 25Gbase-CR CA-N */ + case SFF8024_ECC_100GBASE_CR4: phylink_set(modes, 100000baseCR4_Full); + /* fallthrough */ + case SFF8024_ECC_25GBASE_CR_S: + case SFF8024_ECC_25GBASE_CR_N: phylink_set(modes, 25000baseCR_Full); break; + case SFF8024_ECC_10GBASE_T_SFI: + case SFF8024_ECC_10GBASE_T_SR: + phylink_set(modes, 10000baseT_Full); + break; + case SFF8024_ECC_5GBASE_T: + phylink_set(modes, 5000baseT_Full); + break; + case SFF8024_ECC_2_5GBASE_T: + phylink_set(modes, 2500baseT_Full); + break; default: dev_warn(bus->sfp_dev, "Unknown/unsupported extended compliance code: 0x%02x\n", @@ -301,7 +340,7 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, */ if (bitmap_empty(modes, __ETHTOOL_LINK_MODE_MASK_NBITS)) { /* If the encoding and bit rate allows 1000baseX */ - if (id->base.encoding == SFP_ENCODING_8B10B && br_nom && + if (id->base.encoding == SFF8024_ENCODING_8B10B && br_nom && br_min <= 1300 && br_max >= 1200) phylink_set(modes, 1000baseX_Full); } @@ -320,31 +359,27 @@ EXPORT_SYMBOL_GPL(sfp_parse_support); /** * sfp_select_interface() - Select appropriate phy_interface_t mode * @bus: a pointer to the &struct sfp_bus structure for the sfp module - * @id: a pointer to the module's &struct sfp_eeprom_id * @link_modes: ethtool link modes mask * - * Derive the phy_interface_t mode for the information found in the - * module's identifying EEPROM and the link modes mask. There is no - * standard or defined way to derive this information, so we decide - * based upon the link mode mask. + * Derive the phy_interface_t mode for the SFP module from the link + * modes mask. */ phy_interface_t sfp_select_interface(struct sfp_bus *bus, - const struct sfp_eeprom_id *id, unsigned long *link_modes) { if (phylink_test(link_modes, 10000baseCR_Full) || phylink_test(link_modes, 10000baseSR_Full) || phylink_test(link_modes, 10000baseLR_Full) || phylink_test(link_modes, 10000baseLRM_Full) || - phylink_test(link_modes, 10000baseER_Full)) - return PHY_INTERFACE_MODE_10GKR; + phylink_test(link_modes, 10000baseER_Full) || + phylink_test(link_modes, 10000baseT_Full)) + return PHY_INTERFACE_MODE_10GBASER; if (phylink_test(link_modes, 2500baseX_Full)) return PHY_INTERFACE_MODE_2500BASEX; - if (id->base.e1000_base_t || - id->base.e100_base_lx || - id->base.e100_base_fx) + if (phylink_test(link_modes, 1000baseT_Half) || + phylink_test(link_modes, 1000baseT_Full)) return PHY_INTERFACE_MODE_SGMII; if (phylink_test(link_modes, 1000baseX_Full)) @@ -705,6 +740,27 @@ void sfp_module_remove(struct sfp_bus *bus) } EXPORT_SYMBOL_GPL(sfp_module_remove); +int sfp_module_start(struct sfp_bus *bus) +{ + const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus); + int ret = 0; + + if (ops && ops->module_start) + ret = ops->module_start(bus->upstream); + + return ret; +} +EXPORT_SYMBOL_GPL(sfp_module_start); + +void sfp_module_stop(struct sfp_bus *bus) +{ + const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus); + + if (ops && ops->module_stop) + ops->module_stop(bus->upstream); +} +EXPORT_SYMBOL_GPL(sfp_module_stop); + static void sfp_socket_clear(struct sfp_bus *bus) { bus->sfp_dev = NULL; diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index c0b9a8e4e65a..73c2969f11a4 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -59,8 +59,10 @@ enum { SFP_DEV_UP, SFP_S_DOWN = 0, + SFP_S_FAIL, SFP_S_WAIT, SFP_S_INIT, + SFP_S_INIT_PHY, SFP_S_INIT_TX_FAULT, SFP_S_WAIT_LOS, SFP_S_LINK_UP, @@ -122,8 +124,10 @@ static const char *event_to_str(unsigned short event) static const char * const sm_state_strings[] = { [SFP_S_DOWN] = "down", + [SFP_S_FAIL] = "fail", [SFP_S_WAIT] = "wait", [SFP_S_INIT] = "init", + [SFP_S_INIT_PHY] = "init_phy", [SFP_S_INIT_TX_FAULT] = "init_tx_fault", [SFP_S_WAIT_LOS] = "wait_los", [SFP_S_LINK_UP] = "link_up", @@ -155,10 +159,34 @@ static const enum gpiod_flags gpio_flags[] = { GPIOD_ASIS, }; -#define T_WAIT msecs_to_jiffies(50) -#define T_INIT_JIFFIES msecs_to_jiffies(300) -#define T_RESET_US 10 -#define T_FAULT_RECOVER msecs_to_jiffies(1000) +/* t_start_up (SFF-8431) or t_init (SFF-8472) is the time required for a + * non-cooled module to initialise its laser safety circuitry. We wait + * an initial T_WAIT period before we check the tx fault to give any PHY + * on board (for a copper SFP) time to initialise. + */ +#define T_WAIT msecs_to_jiffies(50) +#define T_START_UP msecs_to_jiffies(300) +#define T_START_UP_BAD_GPON msecs_to_jiffies(60000) + +/* t_reset is the time required to assert the TX_DISABLE signal to reset + * an indicated TX_FAULT. + */ +#define T_RESET_US 10 +#define T_FAULT_RECOVER msecs_to_jiffies(1000) + +/* N_FAULT_INIT is the number of recovery attempts at module initialisation + * time. If the TX_FAULT signal is not deasserted after this number of + * attempts at clearing it, we decide that the module is faulty. + * N_FAULT is the same but after the module has initialised. + */ +#define N_FAULT_INIT 5 +#define N_FAULT 5 + +/* T_PHY_RETRY is the time interval between attempts to probe the PHY. + * R_PHY_RETRY is the number of attempts. + */ +#define T_PHY_RETRY msecs_to_jiffies(50) +#define R_PHY_RETRY 12 /* SFP module presence detection is poor: the three MOD DEF signals are * the same length on the PCB, which means it's possible for MOD DEF 0 to @@ -214,10 +242,12 @@ struct sfp { unsigned char sm_mod_tries; unsigned char sm_dev_state; unsigned short sm_state; - unsigned int sm_retries; + unsigned char sm_fault_retries; + unsigned char sm_phy_retries; struct sfp_eeprom_id id; unsigned int module_power_mW; + unsigned int module_t_start_up; #if IS_ENABLED(CONFIG_HWMON) struct sfp_diag diag; @@ -231,7 +261,7 @@ struct sfp { static bool sff_module_supported(const struct sfp_eeprom_id *id) { - return id->base.phys_id == SFP_PHYS_ID_SFF && + return id->base.phys_id == SFF8024_ID_SFF_8472 && id->base.phys_ext_id == SFP_PHYS_EXT_ID_SFP; } @@ -242,7 +272,7 @@ static const struct sff_data sff_data = { static bool sfp_module_supported(const struct sfp_eeprom_id *id) { - return id->base.phys_id == SFP_PHYS_ID_SFP && + return id->base.phys_id == SFF8024_ID_SFP && id->base.phys_ext_id == SFP_PHYS_EXT_ID_SFP; } @@ -412,13 +442,20 @@ static unsigned int sfp_soft_get_state(struct sfp *sfp) { unsigned int state = 0; u8 status; + int ret; - if (sfp_read(sfp, true, SFP_STATUS, &status, sizeof(status)) == - sizeof(status)) { + ret = sfp_read(sfp, true, SFP_STATUS, &status, sizeof(status)); + if (ret == sizeof(status)) { if (status & SFP_STATUS_RX_LOS) state |= SFP_F_LOS; if (status & SFP_STATUS_TX_FAULT) state |= SFP_F_TX_FAULT; + } else { + dev_err_ratelimited(sfp->dev, + "failed to read SFP soft status: %d\n", + ret); + /* Preserve the current state */ + state = sfp->state; } return state & sfp->state_soft_mask; @@ -1383,26 +1420,30 @@ static void sfp_sm_mod_next(struct sfp *sfp, unsigned int state, static void sfp_sm_phy_detach(struct sfp *sfp) { - phy_stop(sfp->mod_phy); sfp_remove_phy(sfp->sfp_bus); phy_device_remove(sfp->mod_phy); phy_device_free(sfp->mod_phy); sfp->mod_phy = NULL; } -static void sfp_sm_probe_phy(struct sfp *sfp) +static int sfp_sm_probe_phy(struct sfp *sfp, bool is_c45) { struct phy_device *phy; int err; - phy = mdiobus_scan(sfp->i2c_mii, SFP_PHY_ADDR); - if (phy == ERR_PTR(-ENODEV)) { - dev_info(sfp->dev, "no PHY detected\n"); - return; - } + phy = get_phy_device(sfp->i2c_mii, SFP_PHY_ADDR, is_c45); + if (phy == ERR_PTR(-ENODEV)) + return PTR_ERR(phy); if (IS_ERR(phy)) { dev_err(sfp->dev, "mdiobus scan returned %ld\n", PTR_ERR(phy)); - return; + return PTR_ERR(phy); + } + + err = phy_device_register(phy); + if (err) { + phy_device_free(phy); + dev_err(sfp->dev, "phy_device_register failed: %d\n", err); + return err; } err = sfp_add_phy(sfp->sfp_bus, phy); @@ -1410,11 +1451,12 @@ static void sfp_sm_probe_phy(struct sfp *sfp) phy_device_remove(phy); phy_device_free(phy); dev_err(sfp->dev, "sfp_add_phy failed: %d\n", err); - return; + return err; } sfp->mod_phy = phy; - phy_start(phy); + + return 0; } static void sfp_sm_link_up(struct sfp *sfp) @@ -1464,7 +1506,7 @@ static bool sfp_los_event_inactive(struct sfp *sfp, unsigned int event) static void sfp_sm_fault(struct sfp *sfp, unsigned int next_state, bool warn) { - if (sfp->sm_retries && !--sfp->sm_retries) { + if (sfp->sm_fault_retries && !--sfp->sm_fault_retries) { dev_err(sfp->dev, "module persistently indicates fault, disabling\n"); sfp_sm_next(sfp, SFP_S_TX_DISABLE, 0); @@ -1476,21 +1518,35 @@ static void sfp_sm_fault(struct sfp *sfp, unsigned int next_state, bool warn) } } -static void sfp_sm_probe_for_phy(struct sfp *sfp) +/* Probe a SFP for a PHY device if the module supports copper - the PHY + * normally sits at I2C bus address 0x56, and may either be a clause 22 + * or clause 45 PHY. + * + * Clause 22 copper SFP modules normally operate in Cisco SGMII mode with + * negotiation enabled, but some may be in 1000base-X - which is for the + * PHY driver to determine. + * + * Clause 45 copper SFP+ modules (10G) appear to switch their interface + * mode according to the negotiated line speed. + */ +static int sfp_sm_probe_for_phy(struct sfp *sfp) { - /* Setting the serdes link mode is guesswork: there's no - * field in the EEPROM which indicates what mode should - * be used. - * - * If it's a gigabit-only fiber module, it probably does - * not have a PHY, so switch to 802.3z negotiation mode. - * Otherwise, switch to SGMII mode (which is required to - * support non-gigabit speeds) and probe for a PHY. - */ - if (sfp->id.base.e1000_base_t || - sfp->id.base.e100_base_lx || - sfp->id.base.e100_base_fx) - sfp_sm_probe_phy(sfp); + int err = 0; + + switch (sfp->id.base.extended_cc) { + case SFF8024_ECC_10GBASE_T_SFI: + case SFF8024_ECC_10GBASE_T_SR: + case SFF8024_ECC_5GBASE_T: + case SFF8024_ECC_2_5GBASE_T: + err = sfp_sm_probe_phy(sfp, true); + break; + + default: + if (sfp->id.base.e1000_base_t) + err = sfp_sm_probe_phy(sfp, false); + break; + } + return err; } static int sfp_module_parse_power(struct sfp *sfp) @@ -1550,6 +1606,13 @@ static int sfp_sm_mod_hpower(struct sfp *sfp, bool enable) return -EAGAIN; } + /* DM7052 reports as a high power module, responds to reads (with + * all bytes 0xff) at 0x51 but does not accept writes. In any case, + * if the bit is already set, we're already in high power mode. + */ + if (!!(val & BIT(0)) == enable) + return 0; + if (enable) val |= BIT(0); else @@ -1655,6 +1718,12 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) if (ret < 0) return ret; + if (!memcmp(id.base.vendor_name, "ALCATELLUCENT ", 16) && + !memcmp(id.base.vendor_pn, "3FE46541AA ", 16)) + sfp->module_t_start_up = T_START_UP_BAD_GPON; + else + sfp->module_t_start_up = T_START_UP; + return 0; } @@ -1812,6 +1881,7 @@ static void sfp_sm_module(struct sfp *sfp, unsigned int event) static void sfp_sm_main(struct sfp *sfp, unsigned int event) { unsigned long timeout; + int ret; /* Some events are global */ if (sfp->sm_state != SFP_S_DOWN && @@ -1820,6 +1890,8 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event) if (sfp->sm_state == SFP_S_LINK_UP && sfp->sm_dev_state == SFP_DEV_UP) sfp_sm_link_down(sfp); + if (sfp->sm_state > SFP_S_INIT) + sfp_module_stop(sfp->sfp_bus); if (sfp->mod_phy) sfp_sm_phy_detach(sfp); sfp_module_tx_disable(sfp); @@ -1841,7 +1913,7 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event) sfp_module_tx_enable(sfp); /* Initialise the fault clearance retries */ - sfp->sm_retries = 5; + sfp->sm_fault_retries = N_FAULT_INIT; /* We need to check the TX_FAULT state, which is not defined * while TX_DISABLE is asserted. The earliest we want to do @@ -1855,11 +1927,12 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event) break; if (sfp->state & SFP_F_TX_FAULT) { - /* Wait t_init before indicating that the link is up, - * provided the current state indicates no TX_FAULT. If - * TX_FAULT clears before this time, that's fine too. + /* Wait up to t_init (SFF-8472) or t_start_up (SFF-8431) + * from the TX_DISABLE deassertion for the module to + * initialise, which is indicated by TX_FAULT + * deasserting. */ - timeout = T_INIT_JIFFIES; + timeout = sfp->module_t_start_up; if (timeout > T_WAIT) timeout -= T_WAIT; else @@ -1876,27 +1949,51 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event) case SFP_S_INIT: if (event == SFP_E_TIMEOUT && sfp->state & SFP_F_TX_FAULT) { - /* TX_FAULT is still asserted after t_init, so assume - * there is a fault. + /* TX_FAULT is still asserted after t_init or + * or t_start_up, so assume there is a fault. */ sfp_sm_fault(sfp, SFP_S_INIT_TX_FAULT, - sfp->sm_retries == 5); + sfp->sm_fault_retries == N_FAULT_INIT); } else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR) { - init_done: /* TX_FAULT deasserted or we timed out with TX_FAULT - * clear. Probe for the PHY and check the LOS state. - */ - sfp_sm_probe_for_phy(sfp); - sfp_sm_link_check_los(sfp); + init_done: + sfp->sm_phy_retries = R_PHY_RETRY; + goto phy_probe; + } + break; - /* Reset the fault retry count */ - sfp->sm_retries = 5; + case SFP_S_INIT_PHY: + if (event != SFP_E_TIMEOUT) + break; + phy_probe: + /* TX_FAULT deasserted or we timed out with TX_FAULT + * clear. Probe for the PHY and check the LOS state. + */ + ret = sfp_sm_probe_for_phy(sfp); + if (ret == -ENODEV) { + if (--sfp->sm_phy_retries) { + sfp_sm_next(sfp, SFP_S_INIT_PHY, T_PHY_RETRY); + break; + } else { + dev_info(sfp->dev, "no PHY detected\n"); + } + } else if (ret) { + sfp_sm_next(sfp, SFP_S_FAIL, 0); + break; } + if (sfp_module_start(sfp->sfp_bus)) { + sfp_sm_next(sfp, SFP_S_FAIL, 0); + break; + } + sfp_sm_link_check_los(sfp); + + /* Reset the fault retry count */ + sfp->sm_fault_retries = N_FAULT; break; case SFP_S_INIT_TX_FAULT: if (event == SFP_E_TIMEOUT) { sfp_module_tx_fault_reset(sfp); - sfp_sm_next(sfp, SFP_S_INIT, T_INIT_JIFFIES); + sfp_sm_next(sfp, SFP_S_INIT, sfp->module_t_start_up); } break; @@ -1920,7 +2017,7 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event) case SFP_S_TX_FAULT: if (event == SFP_E_TIMEOUT) { sfp_module_tx_fault_reset(sfp); - sfp_sm_next(sfp, SFP_S_REINIT, T_INIT_JIFFIES); + sfp_sm_next(sfp, SFP_S_REINIT, sfp->module_t_start_up); } break; diff --git a/drivers/net/phy/sfp.h b/drivers/net/phy/sfp.h index 64f54b0bbd8c..b83f70526270 100644 --- a/drivers/net/phy/sfp.h +++ b/drivers/net/phy/sfp.h @@ -22,6 +22,8 @@ void sfp_link_up(struct sfp_bus *bus); void sfp_link_down(struct sfp_bus *bus); int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id); void sfp_module_remove(struct sfp_bus *bus); +int sfp_module_start(struct sfp_bus *bus); +void sfp_module_stop(struct sfp_bus *bus); int sfp_link_configure(struct sfp_bus *bus, const struct sfp_eeprom_id *id); struct sfp_bus *sfp_register_socket(struct device *dev, struct sfp *sfp, const struct sfp_socket_ops *ops); diff --git a/drivers/net/phy/uPD60620.c b/drivers/net/phy/uPD60620.c index a32b3fd8a370..38834347a427 100644 --- a/drivers/net/phy/uPD60620.c +++ b/drivers/net/phy/uPD60620.c @@ -68,12 +68,7 @@ static int upd60620_read_status(struct phy_device *phydev) mii_lpa_to_linkmode_lpa_t(phydev->lp_advertising, phy_state); - if (phydev->duplex == DUPLEX_FULL) { - if (phy_state & LPA_PAUSE_CAP) - phydev->pause = 1; - if (phy_state & LPA_PAUSE_ASYM) - phydev->asym_pause = 1; - } + phy_resolve_aneg_pause(phydev); } } return 0; |