diff options
Diffstat (limited to 'drivers/net/phy')
-rw-r--r-- | drivers/net/phy/Kconfig | 3 | ||||
-rw-r--r-- | drivers/net/phy/Makefile | 8 | ||||
-rw-r--r-- | drivers/net/phy/at803x.c | 77 | ||||
-rw-r--r-- | drivers/net/phy/marvell-88x2222.c | 2 | ||||
-rw-r--r-- | drivers/net/phy/marvell.c | 2 | ||||
-rw-r--r-- | drivers/net/phy/marvell10g.c | 2 | ||||
-rw-r--r-- | drivers/net/phy/mdio_bus.c | 3 | ||||
-rw-r--r-- | drivers/net/phy/nxp-c45-tja11xx-macsec.c | 1729 | ||||
-rw-r--r-- | drivers/net/phy/nxp-c45-tja11xx.c | 77 | ||||
-rw-r--r-- | drivers/net/phy/nxp-c45-tja11xx.h | 62 | ||||
-rw-r--r-- | drivers/net/phy/phy_device.c | 61 | ||||
-rw-r--r-- | drivers/net/phy/phy_link_topology.c | 66 | ||||
-rw-r--r-- | drivers/net/phy/phylink.c | 3 | ||||
-rw-r--r-- | drivers/net/phy/sfp-bus.c | 15 | ||||
-rw-r--r-- | drivers/net/phy/sfp.c | 17 |
15 files changed, 2051 insertions, 76 deletions
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 2e4667bf9ff5..9e2672800f0b 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -317,9 +317,10 @@ config NXP_CBTX_PHY config NXP_C45_TJA11XX_PHY tristate "NXP C45 TJA11XX PHYs" depends on PTP_1588_CLOCK_OPTIONAL + depends on MACSEC || !MACSEC help Enable support for NXP C45 TJA11XX PHYs. - Currently supports the TJA1103 and TJA1120 PHYs. + Currently supports the TJA1103, TJA1104 and TJA1120 PHYs. config NXP_TJA11XX_PHY tristate "NXP TJA11xx PHYs support" diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index e35ea69d9cb4..f218954fd7a8 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -2,7 +2,7 @@ # Makefile for Linux PHY drivers libphy-y := phy.o phy-c45.o phy-core.o phy_device.o \ - linkmode.o + linkmode.o phy_link_topology.o mdio-bus-y += mdio_bus.o mdio_device.o ifdef CONFIG_MDIO_DEVICE @@ -84,7 +84,11 @@ obj-$(CONFIG_MICROSEMI_PHY) += mscc/ obj-$(CONFIG_MOTORCOMM_PHY) += motorcomm.o obj-$(CONFIG_NATIONAL_PHY) += national.o obj-$(CONFIG_NCN26000_PHY) += ncn26000.o -obj-$(CONFIG_NXP_C45_TJA11XX_PHY) += nxp-c45-tja11xx.o +nxp-c45-tja-objs += nxp-c45-tja11xx.o +ifdef CONFIG_MACSEC +nxp-c45-tja-objs += nxp-c45-tja11xx-macsec.o +endif +obj-$(CONFIG_NXP_C45_TJA11XX_PHY) += nxp-c45-tja.o obj-$(CONFIG_NXP_CBTX_PHY) += nxp-cbtx.o obj-$(CONFIG_NXP_TJA11XX_PHY) += nxp-tja11xx.o obj-$(CONFIG_QSEMI_PHY) += qsemi.o diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index a7d28848ee93..aaf6c654aaed 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -462,7 +462,7 @@ static int at803x_set_wol(struct phy_device *phydev, if (!ndev) return -ENODEV; - mac = (const u8 *) ndev->dev_addr; + mac = (const u8 *)ndev->dev_addr; if (!is_valid_ether_addr(mac)) return -EINVAL; @@ -916,9 +916,9 @@ static void at803x_link_change_notify(struct phy_device *phydev) at803x_context_save(phydev, &context); phy_device_reset(phydev, 1); - msleep(1); + usleep_range(1000, 2000); phy_device_reset(phydev, 0); - msleep(1); + usleep_range(1000, 2000); at803x_context_restore(phydev, &context); @@ -1452,6 +1452,8 @@ static const struct sfp_upstream_ops at8031_sfp_ops = { .attach = phy_sfp_attach, .detach = phy_sfp_detach, .module_insert = at8031_sfp_insert, + .connect_phy = phy_sfp_connect_phy, + .disconnect_phy = phy_sfp_disconnect_phy, }; static int at8031_parse_dt(struct phy_device *phydev) @@ -1733,7 +1735,7 @@ static int qca83xx_resume(struct phy_device *phydev) if (ret) return ret; - msleep(1); + usleep_range(1000, 2000); return 0; } @@ -1781,27 +1783,27 @@ static int qca808x_phy_fast_retrain_config(struct phy_device *phydev) return ret; phy_write_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_TOP_OPTION1, - QCA808X_TOP_OPTION1_DATA); + QCA808X_TOP_OPTION1_DATA); phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB, - QCA808X_MSE_THRESHOLD_20DB_VALUE); + QCA808X_MSE_THRESHOLD_20DB_VALUE); phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB, - QCA808X_MSE_THRESHOLD_17DB_VALUE); + QCA808X_MSE_THRESHOLD_17DB_VALUE); phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB, - QCA808X_MSE_THRESHOLD_27DB_VALUE); + QCA808X_MSE_THRESHOLD_27DB_VALUE); phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB, - QCA808X_MSE_THRESHOLD_28DB_VALUE); + QCA808X_MSE_THRESHOLD_28DB_VALUE); phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_1, - QCA808X_MMD3_DEBUG_1_VALUE); + QCA808X_MMD3_DEBUG_1_VALUE); phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_4, - QCA808X_MMD3_DEBUG_4_VALUE); + QCA808X_MMD3_DEBUG_4_VALUE); phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_5, - QCA808X_MMD3_DEBUG_5_VALUE); + QCA808X_MMD3_DEBUG_5_VALUE); phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_3, - QCA808X_MMD3_DEBUG_3_VALUE); + QCA808X_MMD3_DEBUG_3_VALUE); phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_6, - QCA808X_MMD3_DEBUG_6_VALUE); + QCA808X_MMD3_DEBUG_6_VALUE); phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_2, - QCA808X_MMD3_DEBUG_2_VALUE); + QCA808X_MMD3_DEBUG_2_VALUE); return 0; } @@ -1838,13 +1840,14 @@ static int qca808x_config_init(struct phy_device *phydev) /* Active adc&vga on 802.3az for the link 1000M and 100M */ ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7, - QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN); + QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN); if (ret) return ret; /* Adjust the threshold on 802.3az for the link 1000M */ ret = phy_write_mmd(phydev, MDIO_MMD_PCS, - QCA808X_PHY_MMD3_AZ_TRAINING_CTRL, QCA808X_MMD3_AZ_TRAINING_VAL); + QCA808X_PHY_MMD3_AZ_TRAINING_CTRL, + QCA808X_MMD3_AZ_TRAINING_VAL); if (ret) return ret; @@ -1870,7 +1873,8 @@ static int qca808x_config_init(struct phy_device *phydev) /* Configure adc threshold as 100mv for the link 10M */ return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD, - QCA808X_ADC_THRESHOLD_MASK, QCA808X_ADC_THRESHOLD_100MV); + QCA808X_ADC_THRESHOLD_MASK, + QCA808X_ADC_THRESHOLD_100MV); } static int qca808x_read_status(struct phy_device *phydev) @@ -1883,7 +1887,7 @@ static int qca808x_read_status(struct phy_device *phydev) return ret; linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->lp_advertising, - ret & MDIO_AN_10GBT_STAT_LP2_5G); + ret & MDIO_AN_10GBT_STAT_LP2_5G); ret = genphy_read_status(phydev); if (ret) @@ -1913,7 +1917,7 @@ static int qca808x_read_status(struct phy_device *phydev) */ if (qca808x_has_fast_retrain_or_slave_seed(phydev)) { if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR || - qca808x_is_prefer_master(phydev)) { + qca808x_is_prefer_master(phydev)) { qca808x_phy_ms_seed_enable(phydev, false); } else { qca808x_phy_ms_seed_enable(phydev, true); @@ -2070,18 +2074,22 @@ static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finish ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_D, qca808x_cable_test_result_trans(pair_d)); - if (qca808x_cdt_fault_length_valid(pair_a)) - ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A, - qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A)); - if (qca808x_cdt_fault_length_valid(pair_b)) - ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B, - qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B)); - if (qca808x_cdt_fault_length_valid(pair_c)) - ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C, - qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C)); - if (qca808x_cdt_fault_length_valid(pair_d)) - ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D, - qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D)); + if (qca808x_cdt_fault_length_valid(pair_a)) { + val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A); + ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A, val); + } + if (qca808x_cdt_fault_length_valid(pair_b)) { + val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B); + ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B, val); + } + if (qca808x_cdt_fault_length_valid(pair_c)) { + val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C); + ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C, val); + } + if (qca808x_cdt_fault_length_valid(pair_d)) { + val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D); + ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D, val); + } *finished = true; @@ -2148,8 +2156,9 @@ static void qca808x_link_change_notify(struct phy_device *phydev) * the interface device address is always phy address added by 1. */ mdiobus_c45_modify_changed(phydev->mdio.bus, phydev->mdio.addr + 1, - MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL, - QCA8081_PHY_FIFO_RSTN, phydev->link ? QCA8081_PHY_FIFO_RSTN : 0); + MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL, + QCA8081_PHY_FIFO_RSTN, + phydev->link ? QCA8081_PHY_FIFO_RSTN : 0); } static struct phy_driver at803x_driver[] = { diff --git a/drivers/net/phy/marvell-88x2222.c b/drivers/net/phy/marvell-88x2222.c index e3aa30dad2e6..3f77bbc7e04f 100644 --- a/drivers/net/phy/marvell-88x2222.c +++ b/drivers/net/phy/marvell-88x2222.c @@ -555,6 +555,8 @@ static const struct sfp_upstream_ops sfp_phy_ops = { .link_down = mv2222_sfp_link_down, .attach = phy_sfp_attach, .detach = phy_sfp_detach, + .connect_phy = phy_sfp_connect_phy, + .disconnect_phy = phy_sfp_disconnect_phy, }; static int mv2222_probe(struct phy_device *phydev) diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index eba652a4c1d8..674e29bce2cc 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -3254,6 +3254,8 @@ static const struct sfp_upstream_ops m88e1510_sfp_ops = { .module_remove = m88e1510_sfp_remove, .attach = phy_sfp_attach, .detach = phy_sfp_detach, + .connect_phy = phy_sfp_connect_phy, + .disconnect_phy = phy_sfp_disconnect_phy, }; static int m88e1510_probe(struct phy_device *phydev) diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c index ad43e280930c..6642eb642d4b 100644 --- a/drivers/net/phy/marvell10g.c +++ b/drivers/net/phy/marvell10g.c @@ -503,6 +503,8 @@ static int mv3310_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) static const struct sfp_upstream_ops mv3310_sfp_ops = { .attach = phy_sfp_attach, .detach = phy_sfp_detach, + .connect_phy = phy_sfp_connect_phy, + .disconnect_phy = phy_sfp_disconnect_phy, .module_insert = mv3310_sfp_insert, }; diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 6cf73c15635b..4a30757c4ff8 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -787,6 +787,9 @@ void mdiobus_unregister(struct mii_bus *bus) gpiod_set_value_cansleep(bus->reset_gpiod, 1); device_del(&bus->dev); + + if (bus->__unregister_callback) + bus->__unregister_callback(bus); } EXPORT_SYMBOL(mdiobus_unregister); diff --git a/drivers/net/phy/nxp-c45-tja11xx-macsec.c b/drivers/net/phy/nxp-c45-tja11xx-macsec.c new file mode 100644 index 000000000000..550ef08970f4 --- /dev/null +++ b/drivers/net/phy/nxp-c45-tja11xx-macsec.c @@ -0,0 +1,1729 @@ +// SPDX-License-Identifier: GPL-2.0 +/* NXP C45 PTP PHY driver interface + * Copyright 2023 NXP + * Author: Radu Pirea <radu-nicolae.pirea@oss.nxp.com> + */ + +#include <linux/delay.h> +#include <linux/ethtool_netlink.h> +#include <linux/kernel.h> +#include <linux/mii.h> +#include <linux/module.h> +#include <linux/phy.h> +#include <linux/processor.h> +#include <net/dst_metadata.h> +#include <net/macsec.h> + +#include "nxp-c45-tja11xx.h" + +#define MACSEC_REG_SIZE 32 +#define TX_SC_MAX 4 + +#define TX_SC_BIT(secy_id) BIT(MACSEC_REG_SIZE - (secy_id) - 1) + +#define VEND1_MACSEC_BASE 0x9000 + +#define MACSEC_CFG 0x0000 +#define MACSEC_CFG_BYPASS BIT(1) +#define MACSEC_CFG_S0I BIT(0) + +#define MACSEC_TPNET 0x0044 +#define PN_WRAP_THRESHOLD 0xffffffff + +#define MACSEC_RXSCA 0x0080 +#define MACSEC_RXSCKA 0x0084 + +#define MACSEC_TXSCA 0x00C0 +#define MACSEC_TXSCKA 0x00C4 + +#define MACSEC_RXSC_SCI_1H 0x0100 + +#define MACSEC_RXSC_CFG 0x0128 +#define MACSEC_RXSC_CFG_XPN BIT(25) +#define MACSEC_RXSC_CFG_AES_256 BIT(24) +#define MACSEC_RXSC_CFG_SCI_EN BIT(11) +#define MACSEC_RXSC_CFG_RP BIT(10) +#define MACSEC_RXSC_CFG_VF_MASK GENMASK(9, 8) +#define MACSEC_RXSC_CFG_VF_OFF 8 + +#define MACSEC_RPW 0x012C + +#define MACSEC_RXSA_A_CS 0x0180 +#define MACSEC_RXSA_A_NPN 0x0184 +#define MACSEC_RXSA_A_XNPN 0x0188 +#define MACSEC_RXSA_A_LNPN 0x018C +#define MACSEC_RXSA_A_LXNPN 0x0190 + +#define MACSEC_RXSA_B_CS 0x01C0 +#define MACSEC_RXSA_B_NPN 0x01C4 +#define MACSEC_RXSA_B_XNPN 0x01C8 +#define MACSEC_RXSA_B_LNPN 0x01CC +#define MACSEC_RXSA_B_LXNPN 0x01D0 + +#define MACSEC_RXSA_CS_AN_OFF 1 +#define MACSEC_RXSA_CS_EN BIT(0) + +#define MACSEC_TXSC_SCI_1H 0x0200 +#define MACSEC_TXSC_CFG 0x0228 +#define MACSEC_TXSC_CFG_XPN BIT(25) +#define MACSEC_TXSC_CFG_AES_256 BIT(24) +#define MACSEC_TXSC_CFG_AN_MASK GENMASK(19, 18) +#define MACSEC_TXSC_CFG_AN_OFF 18 +#define MACSEC_TXSC_CFG_ASA BIT(17) +#define MACSEC_TXSC_CFG_SCE BIT(16) +#define MACSEC_TXSC_CFG_ENCRYPT BIT(4) +#define MACSEC_TXSC_CFG_PROTECT BIT(3) +#define MACSEC_TXSC_CFG_SEND_SCI BIT(2) +#define MACSEC_TXSC_CFG_END_STATION BIT(1) +#define MACSEC_TXSC_CFG_SCB BIT(0) + +#define MACSEC_TXSA_A_CS 0x0280 +#define MACSEC_TXSA_A_NPN 0x0284 +#define MACSEC_TXSA_A_XNPN 0x0288 + +#define MACSEC_TXSA_B_CS 0x02C0 +#define MACSEC_TXSA_B_NPN 0x02C4 +#define MACSEC_TXSA_B_XNPN 0x02C8 + +#define MACSEC_SA_CS_A BIT(31) + +#define MACSEC_EVR 0x0400 +#define MACSEC_EVER 0x0404 + +#define MACSEC_RXSA_A_KA 0x0700 +#define MACSEC_RXSA_A_SSCI 0x0720 +#define MACSEC_RXSA_A_SALT 0x0724 + +#define MACSEC_RXSA_B_KA 0x0740 +#define MACSEC_RXSA_B_SSCI 0x0760 +#define MACSEC_RXSA_B_SALT 0x0764 + +#define MACSEC_TXSA_A_KA 0x0780 +#define MACSEC_TXSA_A_SSCI 0x07A0 +#define MACSEC_TXSA_A_SALT 0x07A4 + +#define MACSEC_TXSA_B_KA 0x07C0 +#define MACSEC_TXSA_B_SSCI 0x07E0 +#define MACSEC_TXSA_B_SALT 0x07E4 + +#define MACSEC_UPFR0D2 0x0A08 +#define MACSEC_UPFR0M1 0x0A10 +#define MACSEC_OVP BIT(12) + +#define MACSEC_UPFR0M2 0x0A14 +#define ETYPE_MASK 0xffff + +#define MACSEC_UPFR0R 0x0A18 +#define MACSEC_UPFR_EN BIT(0) + +#define ADPTR_CNTRL 0x0F00 +#define ADPTR_CNTRL_CONFIG_EN BIT(14) +#define ADPTR_CNTRL_ADPTR_EN BIT(12) +#define ADPTR_TX_TAG_CNTRL 0x0F0C +#define ADPTR_TX_TAG_CNTRL_ENA BIT(31) + +#define TX_SC_FLT_BASE 0x800 +#define TX_SC_FLT_SIZE 0x10 +#define TX_FLT_BASE(flt_id) (TX_SC_FLT_BASE + \ + TX_SC_FLT_SIZE * (flt_id)) + +#define TX_SC_FLT_OFF_MAC_DA_SA 0x04 +#define TX_SC_FLT_OFF_MAC_SA 0x08 +#define TX_SC_FLT_OFF_MAC_CFG 0x0C +#define TX_SC_FLT_BY_SA BIT(14) +#define TX_SC_FLT_EN BIT(8) + +#define TX_SC_FLT_MAC_DA_SA(base) ((base) + TX_SC_FLT_OFF_MAC_DA_SA) +#define TX_SC_FLT_MAC_SA(base) ((base) + TX_SC_FLT_OFF_MAC_SA) +#define TX_SC_FLT_MAC_CFG(base) ((base) + TX_SC_FLT_OFF_MAC_CFG) + +#define ADAPTER_EN BIT(6) +#define MACSEC_EN BIT(5) + +#define MACSEC_INOV1HS 0x0140 +#define MACSEC_INOV2HS 0x0144 +#define MACSEC_INOD1HS 0x0148 +#define MACSEC_INOD2HS 0x014C +#define MACSEC_RXSCIPUS 0x0150 +#define MACSEC_RXSCIPDS 0x0154 +#define MACSEC_RXSCIPLS 0x0158 +#define MACSEC_RXAN0INUSS 0x0160 +#define MACSEC_RXAN0IPUSS 0x0170 +#define MACSEC_RXSA_A_IPOS 0x0194 +#define MACSEC_RXSA_A_IPIS 0x01B0 +#define MACSEC_RXSA_A_IPNVS 0x01B4 +#define MACSEC_RXSA_B_IPOS 0x01D4 +#define MACSEC_RXSA_B_IPIS 0x01F0 +#define MACSEC_RXSA_B_IPNVS 0x01F4 +#define MACSEC_OPUS 0x021C +#define MACSEC_OPTLS 0x022C +#define MACSEC_OOP1HS 0x0240 +#define MACSEC_OOP2HS 0x0244 +#define MACSEC_OOE1HS 0x0248 +#define MACSEC_OOE2HS 0x024C +#define MACSEC_TXSA_A_OPPS 0x028C +#define MACSEC_TXSA_A_OPES 0x0290 +#define MACSEC_TXSA_B_OPPS 0x02CC +#define MACSEC_TXSA_B_OPES 0x02D0 +#define MACSEC_INPWTS 0x0630 +#define MACSEC_INPBTS 0x0638 +#define MACSEC_IPSNFS 0x063C + +#define TJA11XX_TLV_TX_NEEDED_HEADROOM (32) +#define TJA11XX_TLV_NEEDED_TAILROOM (0) + +#define ETH_P_TJA11XX_TLV (0x4e58) + +enum nxp_c45_sa_type { + TX_SA, + RX_SA, +}; + +struct nxp_c45_sa { + void *sa; + const struct nxp_c45_sa_regs *regs; + enum nxp_c45_sa_type type; + bool is_key_a; + u8 an; + struct list_head list; +}; + +struct nxp_c45_secy { + struct macsec_secy *secy; + struct macsec_rx_sc *rx_sc; + struct list_head sa_list; + int secy_id; + bool rx_sc0_impl; + struct list_head list; +}; + +struct nxp_c45_macsec { + struct list_head secy_list; + DECLARE_BITMAP(secy_bitmap, TX_SC_MAX); + DECLARE_BITMAP(tx_sc_bitmap, TX_SC_MAX); +}; + +struct nxp_c45_sa_regs { + u16 cs; + u16 npn; + u16 xnpn; + u16 lnpn; + u16 lxnpn; + u16 ka; + u16 ssci; + u16 salt; + u16 ipis; + u16 ipnvs; + u16 ipos; + u16 opps; + u16 opes; +}; + +static const struct nxp_c45_sa_regs rx_sa_a_regs = { + .cs = MACSEC_RXSA_A_CS, + .npn = MACSEC_RXSA_A_NPN, + .xnpn = MACSEC_RXSA_A_XNPN, + .lnpn = MACSEC_RXSA_A_LNPN, + .lxnpn = MACSEC_RXSA_A_LXNPN, + .ka = MACSEC_RXSA_A_KA, + .ssci = MACSEC_RXSA_A_SSCI, + .salt = MACSEC_RXSA_A_SALT, + .ipis = MACSEC_RXSA_A_IPIS, + .ipnvs = MACSEC_RXSA_A_IPNVS, + .ipos = MACSEC_RXSA_A_IPOS, +}; + +static const struct nxp_c45_sa_regs rx_sa_b_regs = { + .cs = MACSEC_RXSA_B_CS, + .npn = MACSEC_RXSA_B_NPN, + .xnpn = MACSEC_RXSA_B_XNPN, + .lnpn = MACSEC_RXSA_B_LNPN, + .lxnpn = MACSEC_RXSA_B_LXNPN, + .ka = MACSEC_RXSA_B_KA, + .ssci = MACSEC_RXSA_B_SSCI, + .salt = MACSEC_RXSA_B_SALT, + .ipis = MACSEC_RXSA_B_IPIS, + .ipnvs = MACSEC_RXSA_B_IPNVS, + .ipos = MACSEC_RXSA_B_IPOS, +}; + +static const struct nxp_c45_sa_regs tx_sa_a_regs = { + .cs = MACSEC_TXSA_A_CS, + .npn = MACSEC_TXSA_A_NPN, + .xnpn = MACSEC_TXSA_A_XNPN, + .ka = MACSEC_TXSA_A_KA, + .ssci = MACSEC_TXSA_A_SSCI, + .salt = MACSEC_TXSA_A_SALT, + .opps = MACSEC_TXSA_A_OPPS, + .opes = MACSEC_TXSA_A_OPES, +}; + +static const struct nxp_c45_sa_regs tx_sa_b_regs = { + .cs = MACSEC_TXSA_B_CS, + .npn = MACSEC_TXSA_B_NPN, + .xnpn = MACSEC_TXSA_B_XNPN, + .ka = MACSEC_TXSA_B_KA, + .ssci = MACSEC_TXSA_B_SSCI, + .salt = MACSEC_TXSA_B_SALT, + .opps = MACSEC_TXSA_B_OPPS, + .opes = MACSEC_TXSA_B_OPES, +}; + +static const +struct nxp_c45_sa_regs *nxp_c45_sa_regs_get(enum nxp_c45_sa_type sa_type, + bool key_a) +{ + if (sa_type == RX_SA) + if (key_a) + return &rx_sa_a_regs; + else + return &rx_sa_b_regs; + else if (sa_type == TX_SA) + if (key_a) + return &tx_sa_a_regs; + else + return &tx_sa_b_regs; + else + return NULL; +} + +static int nxp_c45_macsec_write(struct phy_device *phydev, u16 addr, u32 value) +{ + u32 lvalue = value; + u16 laddr; + int ret; + + WARN_ON_ONCE(addr % 4); + + phydev_dbg(phydev, "write addr 0x%x value 0x%x\n", addr, value); + + laddr = VEND1_MACSEC_BASE + addr / 2; + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, laddr, lvalue); + if (ret) + return ret; + + laddr += 1; + lvalue >>= 16; + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, laddr, lvalue); + + return ret; +} + +static int nxp_c45_macsec_read(struct phy_device *phydev, u16 addr, u32 *value) +{ + u32 lvalue; + u16 laddr; + int ret; + + WARN_ON_ONCE(addr % 4); + + laddr = VEND1_MACSEC_BASE + addr / 2; + ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, laddr); + if (ret < 0) + return ret; + + laddr += 1; + lvalue = (u32)ret & 0xffff; + ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, laddr); + if (ret < 0) + return ret; + + lvalue |= (u32)ret << 16; + *value = lvalue; + + phydev_dbg(phydev, "read addr 0x%x value 0x%x\n", addr, *value); + + return 0; +} + +static void nxp_c45_macsec_read32_64(struct phy_device *phydev, u16 addr, + u64 *value) +{ + u32 lvalue; + + nxp_c45_macsec_read(phydev, addr, &lvalue); + *value = lvalue; +} + +static void nxp_c45_macsec_read64(struct phy_device *phydev, u16 addr, + u64 *value) +{ + u32 lvalue; + + nxp_c45_macsec_read(phydev, addr, &lvalue); + *value = (u64)lvalue << 32; + nxp_c45_macsec_read(phydev, addr + 4, &lvalue); + *value |= lvalue; +} + +static void nxp_c45_secy_irq_en(struct phy_device *phydev, + struct nxp_c45_secy *phy_secy, bool en) +{ + u32 reg; + + nxp_c45_macsec_read(phydev, MACSEC_EVER, ®); + if (en) + reg |= TX_SC_BIT(phy_secy->secy_id); + else + reg &= ~TX_SC_BIT(phy_secy->secy_id); + nxp_c45_macsec_write(phydev, MACSEC_EVER, reg); +} + +static struct nxp_c45_secy *nxp_c45_find_secy(struct list_head *secy_list, + sci_t sci) +{ + struct nxp_c45_secy *pos, *tmp; + + list_for_each_entry_safe(pos, tmp, secy_list, list) + if (pos->secy->sci == sci) + return pos; + + return ERR_PTR(-EINVAL); +} + +static struct +nxp_c45_secy *nxp_c45_find_secy_by_id(struct list_head *secy_list, + int id) +{ + struct nxp_c45_secy *pos, *tmp; + + list_for_each_entry_safe(pos, tmp, secy_list, list) + if (pos->secy_id == id) + return pos; + + return ERR_PTR(-EINVAL); +} + +static void nxp_c45_secy_free(struct nxp_c45_secy *phy_secy) +{ + list_del(&phy_secy->list); + kfree(phy_secy); +} + +static struct nxp_c45_sa *nxp_c45_find_sa(struct list_head *sa_list, + enum nxp_c45_sa_type sa_type, u8 an) +{ + struct nxp_c45_sa *pos, *tmp; + + list_for_each_entry_safe(pos, tmp, sa_list, list) + if (pos->an == an && pos->type == sa_type) + return pos; + + return ERR_PTR(-EINVAL); +} + +static struct nxp_c45_sa *nxp_c45_sa_alloc(struct list_head *sa_list, void *sa, + enum nxp_c45_sa_type sa_type, u8 an) +{ + struct nxp_c45_sa *first = NULL, *pos, *tmp; + int occurrences = 0; + + list_for_each_entry_safe(pos, tmp, sa_list, list) { + if (pos->type != sa_type) + continue; + + if (pos->an == an) + return ERR_PTR(-EINVAL); + + first = pos; + occurrences++; + if (occurrences >= 2) + return ERR_PTR(-ENOSPC); + } + + tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); + if (!tmp) + return ERR_PTR(-ENOMEM); + + if (first) + tmp->is_key_a = !first->is_key_a; + else + tmp->is_key_a = true; + + tmp->sa = sa; + tmp->type = sa_type; + tmp->an = an; + tmp->regs = nxp_c45_sa_regs_get(tmp->type, tmp->is_key_a); + list_add_tail(&tmp->list, sa_list); + + return tmp; +} + +static void nxp_c45_sa_free(struct nxp_c45_sa *sa) +{ + list_del(&sa->list); + kfree(sa); +} + +static void nxp_c45_sa_list_free(struct list_head *sa_list) +{ + struct nxp_c45_sa *pos, *tmp; + + list_for_each_entry_safe(pos, tmp, sa_list, list) + nxp_c45_sa_free(pos); +} + +static void nxp_c45_sa_set_pn(struct phy_device *phydev, + struct nxp_c45_sa *sa, u64 pn, + u32 replay_window) +{ + const struct nxp_c45_sa_regs *sa_regs = sa->regs; + pn_t npn = {.full64 = pn}; + pn_t lnpn; + + nxp_c45_macsec_write(phydev, sa_regs->npn, npn.lower); + nxp_c45_macsec_write(phydev, sa_regs->xnpn, npn.upper); + if (sa->type != RX_SA) + return; + + if (pn > replay_window) + lnpn.full64 = pn - replay_window; + else + lnpn.full64 = 1; + + nxp_c45_macsec_write(phydev, sa_regs->lnpn, lnpn.lower); + nxp_c45_macsec_write(phydev, sa_regs->lxnpn, lnpn.upper); +} + +static void nxp_c45_sa_set_key(struct macsec_context *ctx, + const struct nxp_c45_sa_regs *sa_regs, + u8 *salt, ssci_t ssci) +{ + struct phy_device *phydev = ctx->phydev; + u32 key_size = ctx->secy->key_len / 4; + u32 salt_size = MACSEC_SALT_LEN / 4; + u32 *key_u32 = (u32 *)ctx->sa.key; + u32 *salt_u32 = (u32 *)salt; + u32 reg, value; + int i; + + for (i = 0; i < key_size; i++) { + reg = sa_regs->ka + i * 4; + value = (__force u32)cpu_to_be32(key_u32[i]); + nxp_c45_macsec_write(phydev, reg, value); + } + + if (ctx->secy->xpn) { + for (i = 0; i < salt_size; i++) { + reg = sa_regs->salt + (2 - i) * 4; + value = (__force u32)cpu_to_be32(salt_u32[i]); + nxp_c45_macsec_write(phydev, reg, value); + } + + value = (__force u32)cpu_to_be32((__force u32)ssci); + nxp_c45_macsec_write(phydev, sa_regs->ssci, value); + } + + nxp_c45_macsec_write(phydev, sa_regs->cs, MACSEC_SA_CS_A); +} + +static void nxp_c45_rx_sa_clear_stats(struct phy_device *phydev, + struct nxp_c45_sa *sa) +{ + nxp_c45_macsec_write(phydev, sa->regs->ipis, 0); + nxp_c45_macsec_write(phydev, sa->regs->ipnvs, 0); + nxp_c45_macsec_write(phydev, sa->regs->ipos, 0); + + nxp_c45_macsec_write(phydev, MACSEC_RXAN0INUSS + sa->an * 4, 0); + nxp_c45_macsec_write(phydev, MACSEC_RXAN0IPUSS + sa->an * 4, 0); +} + +static void nxp_c45_rx_sa_read_stats(struct phy_device *phydev, + struct nxp_c45_sa *sa, + struct macsec_rx_sa_stats *stats) +{ + nxp_c45_macsec_read(phydev, sa->regs->ipis, &stats->InPktsInvalid); + nxp_c45_macsec_read(phydev, sa->regs->ipnvs, &stats->InPktsNotValid); + nxp_c45_macsec_read(phydev, sa->regs->ipos, &stats->InPktsOK); +} + +static void nxp_c45_tx_sa_clear_stats(struct phy_device *phydev, + struct nxp_c45_sa *sa) +{ + nxp_c45_macsec_write(phydev, sa->regs->opps, 0); + nxp_c45_macsec_write(phydev, sa->regs->opes, 0); +} + +static void nxp_c45_tx_sa_read_stats(struct phy_device *phydev, + struct nxp_c45_sa *sa, + struct macsec_tx_sa_stats *stats) +{ + nxp_c45_macsec_read(phydev, sa->regs->opps, &stats->OutPktsProtected); + nxp_c45_macsec_read(phydev, sa->regs->opes, &stats->OutPktsEncrypted); +} + +static void nxp_c45_rx_sa_update(struct phy_device *phydev, + struct nxp_c45_sa *sa, bool en) +{ + const struct nxp_c45_sa_regs *sa_regs = sa->regs; + u32 cfg; + + cfg = sa->an << MACSEC_RXSA_CS_AN_OFF; + cfg |= en ? MACSEC_RXSA_CS_EN : 0; + nxp_c45_macsec_write(phydev, sa_regs->cs, cfg); +} + +static void nxp_c45_tx_sa_update(struct phy_device *phydev, + struct nxp_c45_sa *sa, bool en) +{ + u32 cfg = 0; + + nxp_c45_macsec_read(phydev, MACSEC_TXSC_CFG, &cfg); + + cfg &= ~MACSEC_TXSC_CFG_AN_MASK; + cfg |= sa->an << MACSEC_TXSC_CFG_AN_OFF; + + if (sa->is_key_a) + cfg &= ~MACSEC_TXSC_CFG_ASA; + else + cfg |= MACSEC_TXSC_CFG_ASA; + + if (en) + cfg |= MACSEC_TXSC_CFG_SCE; + else + cfg &= ~MACSEC_TXSC_CFG_SCE; + + nxp_c45_macsec_write(phydev, MACSEC_TXSC_CFG, cfg); +} + +static void nxp_c45_set_sci(struct phy_device *phydev, u16 sci_base_addr, + sci_t sci) +{ + u64 lsci = sci_to_cpu(sci); + + nxp_c45_macsec_write(phydev, sci_base_addr, lsci >> 32); + nxp_c45_macsec_write(phydev, sci_base_addr + 4, lsci); +} + +static bool nxp_c45_port_is_1(sci_t sci) +{ + u16 port = sci_to_cpu(sci); + + return port == 1; +} + +static void nxp_c45_select_secy(struct phy_device *phydev, u8 id) +{ + nxp_c45_macsec_write(phydev, MACSEC_RXSCA, id); + nxp_c45_macsec_write(phydev, MACSEC_RXSCKA, id); + nxp_c45_macsec_write(phydev, MACSEC_TXSCA, id); + nxp_c45_macsec_write(phydev, MACSEC_TXSCKA, id); +} + +static bool nxp_c45_secy_valid(struct nxp_c45_secy *phy_secy, + bool can_rx_sc0_impl) +{ + bool end_station = phy_secy->secy->tx_sc.end_station; + bool scb = phy_secy->secy->tx_sc.scb; + + phy_secy->rx_sc0_impl = false; + + if (end_station) { + if (!nxp_c45_port_is_1(phy_secy->secy->sci)) + return false; + if (!phy_secy->rx_sc) + return true; + return nxp_c45_port_is_1(phy_secy->rx_sc->sci); + } + + if (scb) + return false; + + if (!can_rx_sc0_impl) + return false; + + if (phy_secy->secy_id != 0) + return false; + + phy_secy->rx_sc0_impl = true; + + return true; +} + +static bool nxp_c45_rx_sc0_impl(struct nxp_c45_secy *phy_secy) +{ + bool end_station = phy_secy->secy->tx_sc.end_station; + bool send_sci = phy_secy->secy->tx_sc.send_sci; + bool scb = phy_secy->secy->tx_sc.scb; + + return !end_station && !send_sci && !scb; +} + +static bool nxp_c45_mac_addr_free(struct macsec_context *ctx) +{ + struct nxp_c45_phy *priv = ctx->phydev->priv; + struct nxp_c45_secy *pos, *tmp; + + list_for_each_entry_safe(pos, tmp, &priv->macsec->secy_list, list) { + if (pos->secy == ctx->secy) + continue; + + if (memcmp(pos->secy->netdev->dev_addr, + ctx->secy->netdev->dev_addr, ETH_ALEN) == 0) + return false; + } + + return true; +} + +static void nxp_c45_tx_sc_en_flt(struct phy_device *phydev, int secy_id, + bool en) +{ + u32 tx_flt_base = TX_FLT_BASE(secy_id); + u32 reg = 0; + + nxp_c45_macsec_read(phydev, TX_SC_FLT_MAC_CFG(tx_flt_base), ®); + if (en) + reg |= TX_SC_FLT_EN; + else + reg &= ~TX_SC_FLT_EN; + nxp_c45_macsec_write(phydev, TX_SC_FLT_MAC_CFG(tx_flt_base), reg); +} + +static void nxp_c45_tx_sc_set_flt(struct phy_device *phydev, + struct nxp_c45_secy *phy_secy) +{ + const u8 *dev_addr = phy_secy->secy->netdev->dev_addr; + u32 tx_flt_base = TX_FLT_BASE(phy_secy->secy_id); + u32 reg; + + reg = dev_addr[0] << 8 | dev_addr[1]; + nxp_c45_macsec_write(phydev, TX_SC_FLT_MAC_DA_SA(tx_flt_base), reg); + reg = dev_addr[5] | dev_addr[4] << 8 | dev_addr[3] << 16 | + dev_addr[2] << 24; + + nxp_c45_macsec_write(phydev, TX_SC_FLT_MAC_SA(tx_flt_base), reg); + nxp_c45_macsec_read(phydev, TX_SC_FLT_MAC_CFG(tx_flt_base), ®); + reg &= TX_SC_FLT_EN; + reg |= TX_SC_FLT_BY_SA | phy_secy->secy_id; + nxp_c45_macsec_write(phydev, TX_SC_FLT_MAC_CFG(tx_flt_base), reg); +} + +static void nxp_c45_tx_sc_update(struct phy_device *phydev, + struct nxp_c45_secy *phy_secy) +{ + u32 cfg = 0; + + nxp_c45_macsec_read(phydev, MACSEC_TXSC_CFG, &cfg); + + phydev_dbg(phydev, "XPN %s\n", phy_secy->secy->xpn ? "on" : "off"); + if (phy_secy->secy->xpn) + cfg |= MACSEC_TXSC_CFG_XPN; + else + cfg &= ~MACSEC_TXSC_CFG_XPN; + + phydev_dbg(phydev, "key len %u\n", phy_secy->secy->key_len); + if (phy_secy->secy->key_len == 32) + cfg |= MACSEC_TXSC_CFG_AES_256; + else + cfg &= ~MACSEC_TXSC_CFG_AES_256; + + phydev_dbg(phydev, "encryption %s\n", + phy_secy->secy->tx_sc.encrypt ? "on" : "off"); + if (phy_secy->secy->tx_sc.encrypt) + cfg |= MACSEC_TXSC_CFG_ENCRYPT; + else + cfg &= ~MACSEC_TXSC_CFG_ENCRYPT; + + phydev_dbg(phydev, "protect frames %s\n", + phy_secy->secy->protect_frames ? "on" : "off"); + if (phy_secy->secy->protect_frames) + cfg |= MACSEC_TXSC_CFG_PROTECT; + else + cfg &= ~MACSEC_TXSC_CFG_PROTECT; + + phydev_dbg(phydev, "send sci %s\n", + phy_secy->secy->tx_sc.send_sci ? "on" : "off"); + if (phy_secy->secy->tx_sc.send_sci) + cfg |= MACSEC_TXSC_CFG_SEND_SCI; + else + cfg &= ~MACSEC_TXSC_CFG_SEND_SCI; + + phydev_dbg(phydev, "end station %s\n", + phy_secy->secy->tx_sc.end_station ? "on" : "off"); + if (phy_secy->secy->tx_sc.end_station) + cfg |= MACSEC_TXSC_CFG_END_STATION; + else + cfg &= ~MACSEC_TXSC_CFG_END_STATION; + + phydev_dbg(phydev, "scb %s\n", + phy_secy->secy->tx_sc.scb ? "on" : "off"); + if (phy_secy->secy->tx_sc.scb) + cfg |= MACSEC_TXSC_CFG_SCB; + else + cfg &= ~MACSEC_TXSC_CFG_SCB; + + nxp_c45_macsec_write(phydev, MACSEC_TXSC_CFG, cfg); +} + +static void nxp_c45_tx_sc_clear_stats(struct phy_device *phydev, + struct nxp_c45_secy *phy_secy) +{ + struct nxp_c45_sa *pos, *tmp; + + list_for_each_entry_safe(pos, tmp, &phy_secy->sa_list, list) + if (pos->type == TX_SA) + nxp_c45_tx_sa_clear_stats(phydev, pos); + + nxp_c45_macsec_write(phydev, MACSEC_OPUS, 0); + nxp_c45_macsec_write(phydev, MACSEC_OPTLS, 0); + nxp_c45_macsec_write(phydev, MACSEC_OOP1HS, 0); + nxp_c45_macsec_write(phydev, MACSEC_OOP2HS, 0); + nxp_c45_macsec_write(phydev, MACSEC_OOE1HS, 0); + nxp_c45_macsec_write(phydev, MACSEC_OOE2HS, 0); +} + +static void nxp_c45_set_rx_sc0_impl(struct phy_device *phydev, + bool enable) +{ + u32 reg = 0; + + nxp_c45_macsec_read(phydev, MACSEC_CFG, ®); + if (enable) + reg |= MACSEC_CFG_S0I; + else + reg &= ~MACSEC_CFG_S0I; + nxp_c45_macsec_write(phydev, MACSEC_CFG, reg); +} + +static bool nxp_c45_is_rx_sc0_impl(struct list_head *secy_list) +{ + struct nxp_c45_secy *pos, *tmp; + + list_for_each_entry_safe(pos, tmp, secy_list, list) + if (pos->rx_sc0_impl) + return pos->rx_sc0_impl; + + return false; +} + +static void nxp_c45_rx_sc_en(struct phy_device *phydev, + struct macsec_rx_sc *rx_sc, bool en) +{ + u32 reg = 0; + + nxp_c45_macsec_read(phydev, MACSEC_RXSC_CFG, ®); + if (rx_sc->active && en) + reg |= MACSEC_RXSC_CFG_SCI_EN; + else + reg &= ~MACSEC_RXSC_CFG_SCI_EN; + nxp_c45_macsec_write(phydev, MACSEC_RXSC_CFG, reg); +} + +static void nxp_c45_rx_sc_update(struct phy_device *phydev, + struct nxp_c45_secy *phy_secy) +{ + struct macsec_rx_sc *rx_sc = phy_secy->rx_sc; + struct nxp_c45_phy *priv = phydev->priv; + u32 cfg = 0; + + nxp_c45_macsec_read(phydev, MACSEC_RXSC_CFG, &cfg); + cfg &= ~MACSEC_RXSC_CFG_VF_MASK; + cfg = phy_secy->secy->validate_frames << MACSEC_RXSC_CFG_VF_OFF; + + phydev_dbg(phydev, "validate frames %u\n", + phy_secy->secy->validate_frames); + phydev_dbg(phydev, "replay_protect %s window %u\n", + phy_secy->secy->replay_protect ? "on" : "off", + phy_secy->secy->replay_window); + if (phy_secy->secy->replay_protect) { + cfg |= MACSEC_RXSC_CFG_RP; + nxp_c45_macsec_write(phydev, MACSEC_RPW, + phy_secy->secy->replay_window); + } else { + cfg &= ~MACSEC_RXSC_CFG_RP; + } + + phydev_dbg(phydev, "rx_sc->active %s\n", + rx_sc->active ? "on" : "off"); + if (rx_sc->active && + test_bit(phy_secy->secy_id, priv->macsec->secy_bitmap)) + cfg |= MACSEC_RXSC_CFG_SCI_EN; + else + cfg &= ~MACSEC_RXSC_CFG_SCI_EN; + + phydev_dbg(phydev, "key len %u\n", phy_secy->secy->key_len); + if (phy_secy->secy->key_len == 32) + cfg |= MACSEC_RXSC_CFG_AES_256; + else + cfg &= ~MACSEC_RXSC_CFG_AES_256; + + phydev_dbg(phydev, "XPN %s\n", phy_secy->secy->xpn ? "on" : "off"); + if (phy_secy->secy->xpn) + cfg |= MACSEC_RXSC_CFG_XPN; + else + cfg &= ~MACSEC_RXSC_CFG_XPN; + + nxp_c45_macsec_write(phydev, MACSEC_RXSC_CFG, cfg); +} + +static void nxp_c45_rx_sc_clear_stats(struct phy_device *phydev, + struct nxp_c45_secy *phy_secy) +{ + struct nxp_c45_sa *pos, *tmp; + int i; + + list_for_each_entry_safe(pos, tmp, &phy_secy->sa_list, list) + if (pos->type == RX_SA) + nxp_c45_rx_sa_clear_stats(phydev, pos); + + nxp_c45_macsec_write(phydev, MACSEC_INOD1HS, 0); + nxp_c45_macsec_write(phydev, MACSEC_INOD2HS, 0); + + nxp_c45_macsec_write(phydev, MACSEC_INOV1HS, 0); + nxp_c45_macsec_write(phydev, MACSEC_INOV2HS, 0); + + nxp_c45_macsec_write(phydev, MACSEC_RXSCIPDS, 0); + nxp_c45_macsec_write(phydev, MACSEC_RXSCIPLS, 0); + nxp_c45_macsec_write(phydev, MACSEC_RXSCIPUS, 0); + + for (i = 0; i < MACSEC_NUM_AN; i++) { + nxp_c45_macsec_write(phydev, MACSEC_RXAN0INUSS + i * 4, 0); + nxp_c45_macsec_write(phydev, MACSEC_RXAN0IPUSS + i * 4, 0); + } +} + +static void nxp_c45_rx_sc_del(struct phy_device *phydev, + struct nxp_c45_secy *phy_secy) +{ + struct nxp_c45_sa *pos, *tmp; + + nxp_c45_macsec_write(phydev, MACSEC_RXSC_CFG, 0); + nxp_c45_macsec_write(phydev, MACSEC_RPW, 0); + nxp_c45_set_sci(phydev, MACSEC_RXSC_SCI_1H, 0); + + nxp_c45_rx_sc_clear_stats(phydev, phy_secy); + + list_for_each_entry_safe(pos, tmp, &phy_secy->sa_list, list) { + if (pos->type == RX_SA) { + nxp_c45_rx_sa_update(phydev, pos, false); + nxp_c45_sa_free(pos); + } + } +} + +static void nxp_c45_clear_global_stats(struct phy_device *phydev) +{ + nxp_c45_macsec_write(phydev, MACSEC_INPBTS, 0); + nxp_c45_macsec_write(phydev, MACSEC_INPWTS, 0); + nxp_c45_macsec_write(phydev, MACSEC_IPSNFS, 0); +} + +static void nxp_c45_macsec_en(struct phy_device *phydev, bool en) +{ + u32 reg; + + nxp_c45_macsec_read(phydev, MACSEC_CFG, ®); + if (en) + reg |= MACSEC_CFG_BYPASS; + else + reg &= ~MACSEC_CFG_BYPASS; + nxp_c45_macsec_write(phydev, MACSEC_CFG, reg); +} + +static int nxp_c45_mdo_dev_open(struct macsec_context *ctx) +{ + struct phy_device *phydev = ctx->phydev; + struct nxp_c45_phy *priv = phydev->priv; + struct nxp_c45_secy *phy_secy; + int any_bit_set; + + phy_secy = nxp_c45_find_secy(&priv->macsec->secy_list, ctx->secy->sci); + if (IS_ERR(phy_secy)) + return PTR_ERR(phy_secy); + + nxp_c45_select_secy(phydev, phy_secy->secy_id); + + nxp_c45_tx_sc_en_flt(phydev, phy_secy->secy_id, true); + nxp_c45_set_rx_sc0_impl(phydev, phy_secy->rx_sc0_impl); + if (phy_secy->rx_sc) + nxp_c45_rx_sc_en(phydev, phy_secy->rx_sc, true); + + any_bit_set = find_first_bit(priv->macsec->secy_bitmap, TX_SC_MAX); + if (any_bit_set == TX_SC_MAX) + nxp_c45_macsec_en(phydev, true); + + set_bit(phy_secy->secy_id, priv->macsec->secy_bitmap); + + return 0; +} + +static int nxp_c45_mdo_dev_stop(struct macsec_context *ctx) +{ + struct phy_device *phydev = ctx->phydev; + struct nxp_c45_phy *priv = phydev->priv; + struct nxp_c45_secy *phy_secy; + int any_bit_set; + + phy_secy = nxp_c45_find_secy(&priv->macsec->secy_list, ctx->secy->sci); + if (IS_ERR(phy_secy)) + return PTR_ERR(phy_secy); + + nxp_c45_select_secy(phydev, phy_secy->secy_id); + + nxp_c45_tx_sc_en_flt(phydev, phy_secy->secy_id, false); + if (phy_secy->rx_sc) + nxp_c45_rx_sc_en(phydev, phy_secy->rx_sc, false); + nxp_c45_set_rx_sc0_impl(phydev, false); + + clear_bit(phy_secy->secy_id, priv->macsec->secy_bitmap); + any_bit_set = find_first_bit(priv->macsec->secy_bitmap, TX_SC_MAX); + if (any_bit_set == TX_SC_MAX) + nxp_c45_macsec_en(phydev, false); + + return 0; +} + +static int nxp_c45_mdo_add_secy(struct macsec_context *ctx) +{ + struct phy_device *phydev = ctx->phydev; + struct nxp_c45_phy *priv = phydev->priv; + struct nxp_c45_secy *phy_secy; + bool can_rx_sc0_impl; + int idx; + + phydev_dbg(phydev, "add SecY SCI %016llx\n", + sci_to_cpu(ctx->secy->sci)); + + if (!nxp_c45_mac_addr_free(ctx)) + return -EBUSY; + + if (nxp_c45_is_rx_sc0_impl(&priv->macsec->secy_list)) + return -EBUSY; + + idx = find_first_zero_bit(priv->macsec->tx_sc_bitmap, TX_SC_MAX); + if (idx == TX_SC_MAX) + return -ENOSPC; + + phy_secy = kzalloc(sizeof(*phy_secy), GFP_KERNEL); + if (!phy_secy) + return -ENOMEM; + + INIT_LIST_HEAD(&phy_secy->sa_list); + phy_secy->secy = ctx->secy; + phy_secy->secy_id = idx; + + /* If the point to point mode should be enabled, we should have no + * SecY added yet. + */ + can_rx_sc0_impl = list_count_nodes(&priv->macsec->secy_list) == 0; + if (!nxp_c45_secy_valid(phy_secy, can_rx_sc0_impl)) { + kfree(phy_secy); + return -EINVAL; + } + + phy_secy->rx_sc0_impl = nxp_c45_rx_sc0_impl(phy_secy); + + nxp_c45_select_secy(phydev, phy_secy->secy_id); + nxp_c45_set_sci(phydev, MACSEC_TXSC_SCI_1H, ctx->secy->sci); + nxp_c45_tx_sc_set_flt(phydev, phy_secy); + nxp_c45_tx_sc_update(phydev, phy_secy); + if (phy_interrupt_is_valid(phydev)) + nxp_c45_secy_irq_en(phydev, phy_secy, true); + + set_bit(idx, priv->macsec->tx_sc_bitmap); + list_add_tail(&phy_secy->list, &priv->macsec->secy_list); + + return 0; +} + +static void nxp_c45_tx_sa_next(struct nxp_c45_secy *phy_secy, + struct nxp_c45_sa *next_sa, u8 encoding_sa) +{ + struct nxp_c45_sa *sa; + + sa = nxp_c45_find_sa(&phy_secy->sa_list, TX_SA, encoding_sa); + if (!IS_ERR(sa)) { + memcpy(next_sa, sa, sizeof(*sa)); + } else { + next_sa->is_key_a = true; + next_sa->an = encoding_sa; + } +} + +static int nxp_c45_mdo_upd_secy(struct macsec_context *ctx) +{ + u8 encoding_sa = ctx->secy->tx_sc.encoding_sa; + struct phy_device *phydev = ctx->phydev; + struct nxp_c45_phy *priv = phydev->priv; + struct nxp_c45_secy *phy_secy; + struct nxp_c45_sa next_sa; + bool can_rx_sc0_impl; + + phydev_dbg(phydev, "update SecY SCI %016llx\n", + sci_to_cpu(ctx->secy->sci)); + + phy_secy = nxp_c45_find_secy(&priv->macsec->secy_list, ctx->secy->sci); + if (IS_ERR(phy_secy)) + return PTR_ERR(phy_secy); + + if (!nxp_c45_mac_addr_free(ctx)) + return -EBUSY; + + /* If the point to point mode should be enabled, we should have only + * one SecY added, respectively the updated one. + */ + can_rx_sc0_impl = list_count_nodes(&priv->macsec->secy_list) == 1; + if (!nxp_c45_secy_valid(phy_secy, can_rx_sc0_impl)) + return -EINVAL; + phy_secy->rx_sc0_impl = nxp_c45_rx_sc0_impl(phy_secy); + + nxp_c45_select_secy(phydev, phy_secy->secy_id); + nxp_c45_tx_sc_set_flt(phydev, phy_secy); + nxp_c45_tx_sc_update(phydev, phy_secy); + nxp_c45_tx_sa_next(phy_secy, &next_sa, encoding_sa); + nxp_c45_tx_sa_update(phydev, &next_sa, ctx->secy->operational); + + nxp_c45_set_rx_sc0_impl(phydev, phy_secy->rx_sc0_impl); + if (phy_secy->rx_sc) + nxp_c45_rx_sc_update(phydev, phy_secy); + + return 0; +} + +static int nxp_c45_mdo_del_secy(struct macsec_context *ctx) +{ + u8 encoding_sa = ctx->secy->tx_sc.encoding_sa; + struct phy_device *phydev = ctx->phydev; + struct nxp_c45_phy *priv = phydev->priv; + struct nxp_c45_secy *phy_secy; + struct nxp_c45_sa next_sa; + + phydev_dbg(phydev, "delete SecY SCI %016llx\n", + sci_to_cpu(ctx->secy->sci)); + + phy_secy = nxp_c45_find_secy(&priv->macsec->secy_list, ctx->secy->sci); + if (IS_ERR(phy_secy)) + return PTR_ERR(phy_secy); + nxp_c45_select_secy(phydev, phy_secy->secy_id); + + nxp_c45_mdo_dev_stop(ctx); + nxp_c45_tx_sa_next(phy_secy, &next_sa, encoding_sa); + nxp_c45_tx_sa_update(phydev, &next_sa, false); + nxp_c45_tx_sc_clear_stats(phydev, phy_secy); + if (phy_secy->rx_sc) + nxp_c45_rx_sc_del(phydev, phy_secy); + + nxp_c45_sa_list_free(&phy_secy->sa_list); + if (phy_interrupt_is_valid(phydev)) + nxp_c45_secy_irq_en(phydev, phy_secy, false); + + clear_bit(phy_secy->secy_id, priv->macsec->tx_sc_bitmap); + nxp_c45_secy_free(phy_secy); + + if (list_empty(&priv->macsec->secy_list)) + nxp_c45_clear_global_stats(phydev); + + return 0; +} + +static int nxp_c45_mdo_add_rxsc(struct macsec_context *ctx) +{ + struct phy_device *phydev = ctx->phydev; + struct nxp_c45_phy *priv = phydev->priv; + struct nxp_c45_secy *phy_secy; + + phydev_dbg(phydev, "add RX SC SCI %016llx %s\n", + sci_to_cpu(ctx->rx_sc->sci), + ctx->rx_sc->active ? "enabled" : "disabled"); + + phy_secy = nxp_c45_find_secy(&priv->macsec->secy_list, ctx->secy->sci); + if (IS_ERR(phy_secy)) + return PTR_ERR(phy_secy); + + if (phy_secy->rx_sc) + return -ENOSPC; + + if (phy_secy->secy->tx_sc.end_station && + !nxp_c45_port_is_1(ctx->rx_sc->sci)) + return -EINVAL; + + phy_secy->rx_sc = ctx->rx_sc; + + nxp_c45_select_secy(phydev, phy_secy->secy_id); + nxp_c45_set_sci(phydev, MACSEC_RXSC_SCI_1H, ctx->rx_sc->sci); + nxp_c45_rx_sc_update(phydev, phy_secy); + + return 0; +} + +static int nxp_c45_mdo_upd_rxsc(struct macsec_context *ctx) +{ + struct phy_device *phydev = ctx->phydev; + struct nxp_c45_phy *priv = phydev->priv; + struct nxp_c45_secy *phy_secy; + + phydev_dbg(phydev, "update RX SC SCI %016llx %s\n", + sci_to_cpu(ctx->rx_sc->sci), + ctx->rx_sc->active ? "enabled" : "disabled"); + + phy_secy = nxp_c45_find_secy(&priv->macsec->secy_list, ctx->secy->sci); + if (IS_ERR(phy_secy)) + return PTR_ERR(phy_secy); + + nxp_c45_select_secy(phydev, phy_secy->secy_id); + nxp_c45_rx_sc_update(phydev, phy_secy); + + return 0; +} + +static int nxp_c45_mdo_del_rxsc(struct macsec_context *ctx) +{ + struct phy_device *phydev = ctx->phydev; + struct nxp_c45_phy *priv = phydev->priv; + struct nxp_c45_secy *phy_secy; + + phydev_dbg(phydev, "delete RX SC SCI %016llx %s\n", + sci_to_cpu(ctx->rx_sc->sci), + ctx->rx_sc->active ? "enabled" : "disabled"); + + phy_secy = nxp_c45_find_secy(&priv->macsec->secy_list, ctx->secy->sci); + if (IS_ERR(phy_secy)) + return PTR_ERR(phy_secy); + + nxp_c45_select_secy(phydev, phy_secy->secy_id); + nxp_c45_rx_sc_del(phydev, phy_secy); + phy_secy->rx_sc = NULL; + + return 0; +} + +static int nxp_c45_mdo_add_rxsa(struct macsec_context *ctx) +{ + struct macsec_rx_sa *rx_sa = ctx->sa.rx_sa; + struct phy_device *phydev = ctx->phydev; + struct nxp_c45_phy *priv = phydev->priv; + struct nxp_c45_secy *phy_secy; + u8 an = ctx->sa.assoc_num; + struct nxp_c45_sa *sa; + + phydev_dbg(phydev, "add RX SA %u %s to RX SC SCI %016llx\n", + an, rx_sa->active ? "enabled" : "disabled", + sci_to_cpu(rx_sa->sc->sci)); + + phy_secy = nxp_c45_find_secy(&priv->macsec->secy_list, ctx->secy->sci); + if (IS_ERR(phy_secy)) + return PTR_ERR(phy_secy); + + sa = nxp_c45_sa_alloc(&phy_secy->sa_list, rx_sa, RX_SA, an); + if (IS_ERR(sa)) + return PTR_ERR(sa); + + nxp_c45_select_secy(phydev, phy_secy->secy_id); + nxp_c45_sa_set_pn(phydev, sa, rx_sa->next_pn, + ctx->secy->replay_window); + nxp_c45_sa_set_key(ctx, sa->regs, rx_sa->key.salt.bytes, rx_sa->ssci); + nxp_c45_rx_sa_update(phydev, sa, rx_sa->active); + + return 0; +} + +static int nxp_c45_mdo_upd_rxsa(struct macsec_context *ctx) +{ + struct macsec_rx_sa *rx_sa = ctx->sa.rx_sa; + struct phy_device *phydev = ctx->phydev; + struct nxp_c45_phy *priv = phydev->priv; + struct nxp_c45_secy *phy_secy; + u8 an = ctx->sa.assoc_num; + struct nxp_c45_sa *sa; + + phydev_dbg(phydev, "update RX SA %u %s to RX SC SCI %016llx\n", + an, rx_sa->active ? "enabled" : "disabled", + sci_to_cpu(rx_sa->sc->sci)); + + phy_secy = nxp_c45_find_secy(&priv->macsec->secy_list, ctx->secy->sci); + if (IS_ERR(phy_secy)) + return PTR_ERR(phy_secy); + + sa = nxp_c45_find_sa(&phy_secy->sa_list, RX_SA, an); + if (IS_ERR(sa)) + return PTR_ERR(sa); + + nxp_c45_select_secy(phydev, phy_secy->secy_id); + if (ctx->sa.update_pn) + nxp_c45_sa_set_pn(phydev, sa, rx_sa->next_pn, + ctx->secy->replay_window); + nxp_c45_rx_sa_update(phydev, sa, rx_sa->active); + + return 0; +} + +static int nxp_c45_mdo_del_rxsa(struct macsec_context *ctx) +{ + struct macsec_rx_sa *rx_sa = ctx->sa.rx_sa; + struct phy_device *phydev = ctx->phydev; + struct nxp_c45_phy *priv = phydev->priv; + struct nxp_c45_secy *phy_secy; + u8 an = ctx->sa.assoc_num; + struct nxp_c45_sa *sa; + + phydev_dbg(phydev, "delete RX SA %u %s to RX SC SCI %016llx\n", + an, rx_sa->active ? "enabled" : "disabled", + sci_to_cpu(rx_sa->sc->sci)); + + phy_secy = nxp_c45_find_secy(&priv->macsec->secy_list, ctx->secy->sci); + if (IS_ERR(phy_secy)) + return PTR_ERR(phy_secy); + + sa = nxp_c45_find_sa(&phy_secy->sa_list, RX_SA, an); + if (IS_ERR(sa)) + return PTR_ERR(sa); + + nxp_c45_select_secy(phydev, phy_secy->secy_id); + nxp_c45_rx_sa_update(phydev, sa, false); + nxp_c45_rx_sa_clear_stats(phydev, sa); + + nxp_c45_sa_free(sa); + + return 0; +} + +static int nxp_c45_mdo_add_txsa(struct macsec_context *ctx) +{ + struct macsec_tx_sa *tx_sa = ctx->sa.tx_sa; + struct phy_device *phydev = ctx->phydev; + struct nxp_c45_phy *priv = phydev->priv; + struct nxp_c45_secy *phy_secy; + u8 an = ctx->sa.assoc_num; + struct nxp_c45_sa *sa; + + phydev_dbg(phydev, "add TX SA %u %s to TX SC %016llx\n", + an, ctx->sa.tx_sa->active ? "enabled" : "disabled", + sci_to_cpu(ctx->secy->sci)); + + phy_secy = nxp_c45_find_secy(&priv->macsec->secy_list, ctx->secy->sci); + if (IS_ERR(phy_secy)) + return PTR_ERR(phy_secy); + + sa = nxp_c45_sa_alloc(&phy_secy->sa_list, tx_sa, TX_SA, an); + if (IS_ERR(sa)) + return PTR_ERR(sa); + + nxp_c45_select_secy(phydev, phy_secy->secy_id); + nxp_c45_sa_set_pn(phydev, sa, tx_sa->next_pn, 0); + nxp_c45_sa_set_key(ctx, sa->regs, tx_sa->key.salt.bytes, tx_sa->ssci); + if (ctx->secy->tx_sc.encoding_sa == sa->an) + nxp_c45_tx_sa_update(phydev, sa, tx_sa->active); + + return 0; +} + +static int nxp_c45_mdo_upd_txsa(struct macsec_context *ctx) +{ + struct macsec_tx_sa *tx_sa = ctx->sa.tx_sa; + struct phy_device *phydev = ctx->phydev; + struct nxp_c45_phy *priv = phydev->priv; + struct nxp_c45_secy *phy_secy; + u8 an = ctx->sa.assoc_num; + struct nxp_c45_sa *sa; + + phydev_dbg(phydev, "update TX SA %u %s to TX SC %016llx\n", + an, ctx->sa.tx_sa->active ? "enabled" : "disabled", + sci_to_cpu(ctx->secy->sci)); + + phy_secy = nxp_c45_find_secy(&priv->macsec->secy_list, ctx->secy->sci); + if (IS_ERR(phy_secy)) + return PTR_ERR(phy_secy); + + sa = nxp_c45_find_sa(&phy_secy->sa_list, TX_SA, an); + if (IS_ERR(sa)) + return PTR_ERR(sa); + + nxp_c45_select_secy(phydev, phy_secy->secy_id); + if (ctx->sa.update_pn) + nxp_c45_sa_set_pn(phydev, sa, tx_sa->next_pn, 0); + if (ctx->secy->tx_sc.encoding_sa == sa->an) + nxp_c45_tx_sa_update(phydev, sa, tx_sa->active); + + return 0; +} + +static int nxp_c45_mdo_del_txsa(struct macsec_context *ctx) +{ + struct phy_device *phydev = ctx->phydev; + struct nxp_c45_phy *priv = phydev->priv; + struct nxp_c45_secy *phy_secy; + u8 an = ctx->sa.assoc_num; + struct nxp_c45_sa *sa; + + phydev_dbg(phydev, "delete TX SA %u %s to TX SC %016llx\n", + an, ctx->sa.tx_sa->active ? "enabled" : "disabled", + sci_to_cpu(ctx->secy->sci)); + + phy_secy = nxp_c45_find_secy(&priv->macsec->secy_list, ctx->secy->sci); + if (IS_ERR(phy_secy)) + return PTR_ERR(phy_secy); + + sa = nxp_c45_find_sa(&phy_secy->sa_list, TX_SA, an); + if (IS_ERR(sa)) + return PTR_ERR(sa); + + nxp_c45_select_secy(phydev, phy_secy->secy_id); + if (ctx->secy->tx_sc.encoding_sa == sa->an) + nxp_c45_tx_sa_update(phydev, sa, false); + nxp_c45_tx_sa_clear_stats(phydev, sa); + + nxp_c45_sa_free(sa); + + return 0; +} + +static int nxp_c45_mdo_get_dev_stats(struct macsec_context *ctx) +{ + struct phy_device *phydev = ctx->phydev; + struct nxp_c45_phy *priv = phydev->priv; + struct macsec_dev_stats *dev_stats; + struct nxp_c45_secy *phy_secy; + + phy_secy = nxp_c45_find_secy(&priv->macsec->secy_list, ctx->secy->sci); + if (IS_ERR(phy_secy)) + return PTR_ERR(phy_secy); + + dev_stats = ctx->stats.dev_stats; + nxp_c45_select_secy(phydev, phy_secy->secy_id); + + nxp_c45_macsec_read32_64(phydev, MACSEC_OPUS, + &dev_stats->OutPktsUntagged); + nxp_c45_macsec_read32_64(phydev, MACSEC_OPTLS, + &dev_stats->OutPktsTooLong); + nxp_c45_macsec_read32_64(phydev, MACSEC_INPBTS, + &dev_stats->InPktsBadTag); + + if (phy_secy->secy->validate_frames == MACSEC_VALIDATE_STRICT) + nxp_c45_macsec_read32_64(phydev, MACSEC_INPWTS, + &dev_stats->InPktsNoTag); + else + nxp_c45_macsec_read32_64(phydev, MACSEC_INPWTS, + &dev_stats->InPktsUntagged); + + if (phy_secy->secy->validate_frames == MACSEC_VALIDATE_STRICT) + nxp_c45_macsec_read32_64(phydev, MACSEC_IPSNFS, + &dev_stats->InPktsNoSCI); + else + nxp_c45_macsec_read32_64(phydev, MACSEC_IPSNFS, + &dev_stats->InPktsUnknownSCI); + + /* Always 0. */ + dev_stats->InPktsOverrun = 0; + + return 0; +} + +static int nxp_c45_mdo_get_tx_sc_stats(struct macsec_context *ctx) +{ + struct phy_device *phydev = ctx->phydev; + struct nxp_c45_phy *priv = phydev->priv; + struct macsec_tx_sa_stats tx_sa_stats; + struct macsec_tx_sc_stats *stats; + struct nxp_c45_secy *phy_secy; + struct nxp_c45_sa *pos, *tmp; + + phy_secy = nxp_c45_find_secy(&priv->macsec->secy_list, ctx->secy->sci); + if (IS_ERR(phy_secy)) + return PTR_ERR(phy_secy); + + stats = ctx->stats.tx_sc_stats; + nxp_c45_select_secy(phydev, phy_secy->secy_id); + + nxp_c45_macsec_read64(phydev, MACSEC_OOE1HS, + &stats->OutOctetsEncrypted); + nxp_c45_macsec_read64(phydev, MACSEC_OOP1HS, + &stats->OutOctetsProtected); + list_for_each_entry_safe(pos, tmp, &phy_secy->sa_list, list) { + if (pos->type != TX_SA) + continue; + + memset(&tx_sa_stats, 0, sizeof(tx_sa_stats)); + nxp_c45_tx_sa_read_stats(phydev, pos, &tx_sa_stats); + + stats->OutPktsEncrypted += tx_sa_stats.OutPktsEncrypted; + stats->OutPktsProtected += tx_sa_stats.OutPktsProtected; + } + + return 0; +} + +static int nxp_c45_mdo_get_tx_sa_stats(struct macsec_context *ctx) +{ + struct phy_device *phydev = ctx->phydev; + struct nxp_c45_phy *priv = phydev->priv; + struct macsec_tx_sa_stats *stats; + struct nxp_c45_secy *phy_secy; + u8 an = ctx->sa.assoc_num; + struct nxp_c45_sa *sa; + + phy_secy = nxp_c45_find_secy(&priv->macsec->secy_list, ctx->secy->sci); + if (IS_ERR(phy_secy)) + return PTR_ERR(phy_secy); + + sa = nxp_c45_find_sa(&phy_secy->sa_list, TX_SA, an); + if (IS_ERR(sa)) + return PTR_ERR(sa); + + stats = ctx->stats.tx_sa_stats; + nxp_c45_select_secy(phydev, phy_secy->secy_id); + nxp_c45_tx_sa_read_stats(phydev, sa, stats); + + return 0; +} + +static int nxp_c45_mdo_get_rx_sc_stats(struct macsec_context *ctx) +{ + struct phy_device *phydev = ctx->phydev; + struct nxp_c45_phy *priv = phydev->priv; + struct macsec_rx_sa_stats rx_sa_stats; + struct macsec_rx_sc_stats *stats; + struct nxp_c45_secy *phy_secy; + struct nxp_c45_sa *pos, *tmp; + u32 reg = 0; + int i; + + phy_secy = nxp_c45_find_secy(&priv->macsec->secy_list, ctx->secy->sci); + if (IS_ERR(phy_secy)) + return PTR_ERR(phy_secy); + + if (phy_secy->rx_sc != ctx->rx_sc) + return -EINVAL; + + stats = ctx->stats.rx_sc_stats; + nxp_c45_select_secy(phydev, phy_secy->secy_id); + + list_for_each_entry_safe(pos, tmp, &phy_secy->sa_list, list) { + if (pos->type != RX_SA) + continue; + + memset(&rx_sa_stats, 0, sizeof(rx_sa_stats)); + nxp_c45_rx_sa_read_stats(phydev, pos, &rx_sa_stats); + + stats->InPktsInvalid += rx_sa_stats.InPktsInvalid; + stats->InPktsNotValid += rx_sa_stats.InPktsNotValid; + stats->InPktsOK += rx_sa_stats.InPktsOK; + } + + for (i = 0; i < MACSEC_NUM_AN; i++) { + nxp_c45_macsec_read(phydev, MACSEC_RXAN0INUSS + i * 4, ®); + stats->InPktsNotUsingSA += reg; + nxp_c45_macsec_read(phydev, MACSEC_RXAN0IPUSS + i * 4, ®); + stats->InPktsUnusedSA += reg; + } + + nxp_c45_macsec_read64(phydev, MACSEC_INOD1HS, + &stats->InOctetsDecrypted); + nxp_c45_macsec_read64(phydev, MACSEC_INOV1HS, + &stats->InOctetsValidated); + + nxp_c45_macsec_read32_64(phydev, MACSEC_RXSCIPDS, + &stats->InPktsDelayed); + nxp_c45_macsec_read32_64(phydev, MACSEC_RXSCIPLS, + &stats->InPktsLate); + nxp_c45_macsec_read32_64(phydev, MACSEC_RXSCIPUS, + &stats->InPktsUnchecked); + + return 0; +} + +static int nxp_c45_mdo_get_rx_sa_stats(struct macsec_context *ctx) +{ + struct phy_device *phydev = ctx->phydev; + struct nxp_c45_phy *priv = phydev->priv; + struct macsec_rx_sa_stats *stats; + struct nxp_c45_secy *phy_secy; + u8 an = ctx->sa.assoc_num; + struct nxp_c45_sa *sa; + + phy_secy = nxp_c45_find_secy(&priv->macsec->secy_list, ctx->secy->sci); + if (IS_ERR(phy_secy)) + return PTR_ERR(phy_secy); + + sa = nxp_c45_find_sa(&phy_secy->sa_list, RX_SA, an); + if (IS_ERR(sa)) + return PTR_ERR(sa); + + stats = ctx->stats.rx_sa_stats; + nxp_c45_select_secy(phydev, phy_secy->secy_id); + + nxp_c45_rx_sa_read_stats(phydev, sa, stats); + nxp_c45_macsec_read(phydev, MACSEC_RXAN0INUSS + an * 4, + &stats->InPktsNotUsingSA); + nxp_c45_macsec_read(phydev, MACSEC_RXAN0IPUSS + an * 4, + &stats->InPktsUnusedSA); + + return 0; +} + +struct tja11xx_tlv_header { + struct ethhdr eth; + u8 subtype; + u8 len; + u8 payload[28]; +}; + +static int nxp_c45_mdo_insert_tx_tag(struct phy_device *phydev, + struct sk_buff *skb) +{ + struct tja11xx_tlv_header *tlv; + struct ethhdr *eth; + + eth = eth_hdr(skb); + tlv = skb_push(skb, TJA11XX_TLV_TX_NEEDED_HEADROOM); + memmove(tlv, eth, sizeof(*eth)); + skb_reset_mac_header(skb); + tlv->eth.h_proto = htons(ETH_P_TJA11XX_TLV); + tlv->subtype = 1; + tlv->len = sizeof(tlv->payload); + memset(tlv->payload, 0, sizeof(tlv->payload)); + + return 0; +} + +static const struct macsec_ops nxp_c45_macsec_ops = { + .mdo_dev_open = nxp_c45_mdo_dev_open, + .mdo_dev_stop = nxp_c45_mdo_dev_stop, + .mdo_add_secy = nxp_c45_mdo_add_secy, + .mdo_upd_secy = nxp_c45_mdo_upd_secy, + .mdo_del_secy = nxp_c45_mdo_del_secy, + .mdo_add_rxsc = nxp_c45_mdo_add_rxsc, + .mdo_upd_rxsc = nxp_c45_mdo_upd_rxsc, + .mdo_del_rxsc = nxp_c45_mdo_del_rxsc, + .mdo_add_rxsa = nxp_c45_mdo_add_rxsa, + .mdo_upd_rxsa = nxp_c45_mdo_upd_rxsa, + .mdo_del_rxsa = nxp_c45_mdo_del_rxsa, + .mdo_add_txsa = nxp_c45_mdo_add_txsa, + .mdo_upd_txsa = nxp_c45_mdo_upd_txsa, + .mdo_del_txsa = nxp_c45_mdo_del_txsa, + .mdo_get_dev_stats = nxp_c45_mdo_get_dev_stats, + .mdo_get_tx_sc_stats = nxp_c45_mdo_get_tx_sc_stats, + .mdo_get_tx_sa_stats = nxp_c45_mdo_get_tx_sa_stats, + .mdo_get_rx_sc_stats = nxp_c45_mdo_get_rx_sc_stats, + .mdo_get_rx_sa_stats = nxp_c45_mdo_get_rx_sa_stats, + .mdo_insert_tx_tag = nxp_c45_mdo_insert_tx_tag, + .needed_headroom = TJA11XX_TLV_TX_NEEDED_HEADROOM, + .needed_tailroom = TJA11XX_TLV_NEEDED_TAILROOM, +}; + +int nxp_c45_macsec_config_init(struct phy_device *phydev) +{ + struct nxp_c45_phy *priv = phydev->priv; + int ret; + + if (!priv->macsec) + return 0; + + ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_PORT_FUNC_ENABLES, + MACSEC_EN | ADAPTER_EN); + if (ret) + return ret; + + ret = nxp_c45_macsec_write(phydev, ADPTR_CNTRL, ADPTR_CNTRL_CONFIG_EN | + ADPTR_CNTRL_ADPTR_EN); + if (ret) + return ret; + + ret = nxp_c45_macsec_write(phydev, ADPTR_TX_TAG_CNTRL, + ADPTR_TX_TAG_CNTRL_ENA); + if (ret) + return ret; + + ret = nxp_c45_macsec_write(phydev, ADPTR_CNTRL, ADPTR_CNTRL_ADPTR_EN); + if (ret) + return ret; + + ret = nxp_c45_macsec_write(phydev, MACSEC_TPNET, PN_WRAP_THRESHOLD); + if (ret) + return ret; + + /* Set MKA filter. */ + ret = nxp_c45_macsec_write(phydev, MACSEC_UPFR0D2, ETH_P_PAE); + if (ret) + return ret; + + ret = nxp_c45_macsec_write(phydev, MACSEC_UPFR0M1, MACSEC_OVP); + if (ret) + return ret; + + ret = nxp_c45_macsec_write(phydev, MACSEC_UPFR0M2, ETYPE_MASK); + if (ret) + return ret; + + ret = nxp_c45_macsec_write(phydev, MACSEC_UPFR0R, MACSEC_UPFR_EN); + + return ret; +} + +int nxp_c45_macsec_probe(struct phy_device *phydev) +{ + struct nxp_c45_phy *priv = phydev->priv; + struct device *dev = &phydev->mdio.dev; + + priv->macsec = devm_kzalloc(dev, sizeof(*priv->macsec), GFP_KERNEL); + if (!priv->macsec) + return -ENOMEM; + + INIT_LIST_HEAD(&priv->macsec->secy_list); + phydev->macsec_ops = &nxp_c45_macsec_ops; + + return 0; +} + +void nxp_c45_macsec_remove(struct phy_device *phydev) +{ + struct nxp_c45_phy *priv = phydev->priv; + struct nxp_c45_secy *secy_p, *secy_t; + struct nxp_c45_sa *sa_p, *sa_t; + struct list_head *secy_list; + + if (!priv->macsec) + return; + + secy_list = &priv->macsec->secy_list; + nxp_c45_macsec_en(phydev, false); + + list_for_each_entry_safe(secy_p, secy_t, secy_list, list) { + list_for_each_entry_safe(sa_p, sa_t, &secy_p->sa_list, list) + nxp_c45_sa_free(sa_p); + nxp_c45_secy_free(secy_p); + } +} + +void nxp_c45_handle_macsec_interrupt(struct phy_device *phydev, + irqreturn_t *ret) +{ + struct nxp_c45_phy *priv = phydev->priv; + struct nxp_c45_secy *secy; + struct nxp_c45_sa *sa; + u8 encoding_sa; + int secy_id; + u32 reg = 0; + + if (!priv->macsec) + return; + + do { + nxp_c45_macsec_read(phydev, MACSEC_EVR, ®); + if (!reg) + return; + + secy_id = MACSEC_REG_SIZE - ffs(reg); + secy = nxp_c45_find_secy_by_id(&priv->macsec->secy_list, + secy_id); + if (IS_ERR(secy)) { + WARN_ON(1); + goto macsec_ack_irq; + } + + encoding_sa = secy->secy->tx_sc.encoding_sa; + phydev_dbg(phydev, "pn_wrapped: TX SC %d, encoding_sa %u\n", + secy->secy_id, encoding_sa); + + sa = nxp_c45_find_sa(&secy->sa_list, TX_SA, encoding_sa); + if (!IS_ERR(sa)) + macsec_pn_wrapped(secy->secy, sa->sa); + else + WARN_ON(1); + +macsec_ack_irq: + nxp_c45_macsec_write(phydev, MACSEC_EVR, + TX_SC_BIT(secy_id)); + *ret = IRQ_HANDLED; + } while (reg); +} diff --git a/drivers/net/phy/nxp-c45-tja11xx.c b/drivers/net/phy/nxp-c45-tja11xx.c index 780ad353cf55..3cf614b4cd52 100644 --- a/drivers/net/phy/nxp-c45-tja11xx.c +++ b/drivers/net/phy/nxp-c45-tja11xx.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* NXP C45 PHY driver - * Copyright (C) 2021 NXP + * Copyright 2021-2023 NXP * Author: Radu Pirea <radu-nicolae.pirea@oss.nxp.com> */ @@ -14,9 +14,10 @@ #include <linux/processor.h> #include <linux/property.h> #include <linux/ptp_classify.h> -#include <linux/ptp_clock_kernel.h> #include <linux/net_tstamp.h> +#include "nxp-c45-tja11xx.h" + #define PHY_ID_TJA_1103 0x001BB010 #define PHY_ID_TJA_1120 0x001BB031 @@ -75,9 +76,11 @@ #define PORT_CONTROL_EN BIT(14) #define VEND1_PORT_ABILITIES 0x8046 +#define MACSEC_ABILITY BIT(5) #define PTP_ABILITY BIT(3) #define VEND1_PORT_FUNC_IRQ_EN 0x807A +#define MACSEC_IRQS BIT(5) #define PTP_IRQS BIT(3) #define VEND1_PTP_IRQ_ACK 0x9008 @@ -148,7 +151,6 @@ #define TS_SEC_MASK GENMASK(1, 0) -#define VEND1_PORT_FUNC_ENABLES 0x8048 #define PTP_ENABLE BIT(3) #define PHY_TEST_ENABLE BIT(0) @@ -281,25 +283,6 @@ struct nxp_c45_phy_data { irqreturn_t *irq_status); }; -struct nxp_c45_phy { - const struct nxp_c45_phy_data *phy_data; - struct phy_device *phydev; - struct mii_timestamper mii_ts; - struct ptp_clock *ptp_clock; - struct ptp_clock_info caps; - struct sk_buff_head tx_queue; - struct sk_buff_head rx_queue; - /* used to access the PTP registers atomic */ - struct mutex ptp_lock; - int hwts_tx; - int hwts_rx; - u32 tx_delay; - u32 rx_delay; - struct timespec64 extts_ts; - int extts_index; - bool extts; -}; - static const struct nxp_c45_phy_data *nxp_c45_get_data(struct phy_device *phydev) { @@ -1215,12 +1198,25 @@ static int nxp_c45_start_op(struct phy_device *phydev) static int nxp_c45_config_intr(struct phy_device *phydev) { - if (phydev->interrupts == PHY_INTERRUPT_ENABLED) + int ret; + + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { + ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, + VEND1_PORT_FUNC_IRQ_EN, MACSEC_IRQS); + if (ret) + return ret; + return phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_PHY_IRQ_EN, PHY_IRQ_LINK_EVENT); - else - return phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, - VEND1_PHY_IRQ_EN, PHY_IRQ_LINK_EVENT); + } + + ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, + VEND1_PORT_FUNC_IRQ_EN, MACSEC_IRQS); + if (ret) + return ret; + + return phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, + VEND1_PHY_IRQ_EN, PHY_IRQ_LINK_EVENT); } static int tja1103_config_intr(struct phy_device *phydev) @@ -1286,6 +1282,7 @@ static irqreturn_t nxp_c45_handle_interrupt(struct phy_device *phydev) } data->nmi_handler(phydev, &ret); + nxp_c45_handle_macsec_interrupt(phydev, &ret); return ret; } @@ -1611,6 +1608,9 @@ static int nxp_c45_config_init(struct phy_device *phydev) nxp_c45_counters_enable(phydev); nxp_c45_ptp_init(phydev); + ret = nxp_c45_macsec_config_init(phydev); + if (ret) + return ret; return nxp_c45_start_op(phydev); } @@ -1626,7 +1626,9 @@ static int nxp_c45_get_features(struct phy_device *phydev) static int nxp_c45_probe(struct phy_device *phydev) { struct nxp_c45_phy *priv; - int ptp_ability; + bool macsec_ability; + int phy_abilities; + bool ptp_ability; int ret = 0; priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); @@ -1642,9 +1644,9 @@ static int nxp_c45_probe(struct phy_device *phydev) mutex_init(&priv->ptp_lock); - ptp_ability = phy_read_mmd(phydev, MDIO_MMD_VEND1, - VEND1_PORT_ABILITIES); - ptp_ability = !!(ptp_ability & PTP_ABILITY); + phy_abilities = phy_read_mmd(phydev, MDIO_MMD_VEND1, + VEND1_PORT_ABILITIES); + ptp_ability = !!(phy_abilities & PTP_ABILITY); if (!ptp_ability) { phydev_dbg(phydev, "the phy does not support PTP"); goto no_ptp_support; @@ -1663,6 +1665,20 @@ static int nxp_c45_probe(struct phy_device *phydev) } no_ptp_support: + macsec_ability = !!(phy_abilities & MACSEC_ABILITY); + if (!macsec_ability) { + phydev_info(phydev, "the phy does not support MACsec\n"); + goto no_macsec_support; + } + + if (IS_ENABLED(CONFIG_MACSEC)) { + ret = nxp_c45_macsec_probe(phydev); + phydev_dbg(phydev, "MACsec support enabled."); + } else { + phydev_dbg(phydev, "MACsec support not enabled even if the phy supports it"); + } + +no_macsec_support: return ret; } @@ -1676,6 +1692,7 @@ static void nxp_c45_remove(struct phy_device *phydev) skb_queue_purge(&priv->tx_queue); skb_queue_purge(&priv->rx_queue); + nxp_c45_macsec_remove(phydev); } static void tja1103_counters_enable(struct phy_device *phydev) diff --git a/drivers/net/phy/nxp-c45-tja11xx.h b/drivers/net/phy/nxp-c45-tja11xx.h new file mode 100644 index 000000000000..f364fca68f0b --- /dev/null +++ b/drivers/net/phy/nxp-c45-tja11xx.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* NXP C45 PHY driver header file + * Copyright 2023 NXP + * Author: Radu Pirea <radu-nicolae.pirea@oss.nxp.com> + */ + +#include <linux/ptp_clock_kernel.h> + +#define VEND1_PORT_FUNC_ENABLES 0x8048 + +struct nxp_c45_macsec; + +struct nxp_c45_phy { + const struct nxp_c45_phy_data *phy_data; + struct phy_device *phydev; + struct mii_timestamper mii_ts; + struct ptp_clock *ptp_clock; + struct ptp_clock_info caps; + struct sk_buff_head tx_queue; + struct sk_buff_head rx_queue; + /* used to access the PTP registers atomic */ + struct mutex ptp_lock; + int hwts_tx; + int hwts_rx; + u32 tx_delay; + u32 rx_delay; + struct timespec64 extts_ts; + int extts_index; + bool extts; + struct nxp_c45_macsec *macsec; +}; + +#if IS_ENABLED(CONFIG_MACSEC) +int nxp_c45_macsec_config_init(struct phy_device *phydev); +int nxp_c45_macsec_probe(struct phy_device *phydev); +void nxp_c45_macsec_remove(struct phy_device *phydev); +void nxp_c45_handle_macsec_interrupt(struct phy_device *phydev, + irqreturn_t *ret); +#else +static inline +int nxp_c45_macsec_config_init(struct phy_device *phydev) +{ + return 0; +} + +static inline +int nxp_c45_macsec_probe(struct phy_device *phydev) +{ + return 0; +} + +static inline +void nxp_c45_macsec_remove(struct phy_device *phydev) +{ +} + +static inline +void nxp_c45_handle_macsec_interrupt(struct phy_device *phydev, + irqreturn_t *ret) +{ +} +#endif diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 0c52a9eff188..1e595762afea 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -29,6 +29,7 @@ #include <linux/phy.h> #include <linux/phylib_stubs.h> #include <linux/phy_led_triggers.h> +#include <linux/phy_link_topology.h> #include <linux/pse-pd/pse.h> #include <linux/property.h> #include <linux/rtnetlink.h> @@ -265,6 +266,14 @@ static void phy_mdio_device_remove(struct mdio_device *mdiodev) static struct phy_driver genphy_driver; +static struct phy_link_topology *phy_get_link_topology(struct phy_device *phydev) +{ + if (phydev->attached_dev) + return &phydev->attached_dev->link_topo; + + return NULL; +} + static LIST_HEAD(phy_fixup_list); static DEFINE_MUTEX(phy_fixup_lock); @@ -1355,6 +1364,46 @@ phy_standalone_show(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR_RO(phy_standalone); /** + * phy_sfp_connect_phy - Connect the SFP module's PHY to the upstream PHY + * @upstream: pointer to the upstream phy device + * @phy: pointer to the SFP module's phy device + * + * This helper allows keeping track of PHY devices on the link. It adds the + * SFP module's phy to the phy namespace of the upstream phy + */ +int phy_sfp_connect_phy(void *upstream, struct phy_device *phy) +{ + struct phy_device *phydev = upstream; + struct phy_link_topology *topo = phy_get_link_topology(phydev); + + if (topo) + return phy_link_topo_add_phy(topo, phy, PHY_UPSTREAM_PHY, phydev); + + return 0; +} +EXPORT_SYMBOL(phy_sfp_connect_phy); + +/** + * phy_sfp_disconnect_phy - Disconnect the SFP module's PHY from the upstream PHY + * @upstream: pointer to the upstream phy device + * @phy: pointer to the SFP module's phy device + * + * This helper allows keeping track of PHY devices on the link. It removes the + * SFP module's phy to the phy namespace of the upstream phy. As the module phy + * will be destroyed, re-inserting the same module will add a new phy with a + * new index. + */ +void phy_sfp_disconnect_phy(void *upstream, struct phy_device *phy) +{ + struct phy_device *phydev = upstream; + struct phy_link_topology *topo = phy_get_link_topology(phydev); + + if (topo) + phy_link_topo_del_phy(topo, phy); +} +EXPORT_SYMBOL(phy_sfp_disconnect_phy); + +/** * phy_sfp_attach - attach the SFP bus to the PHY upstream network device * @upstream: pointer to the phy device * @bus: sfp bus representing cage being attached @@ -1491,6 +1540,11 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, if (phydev->sfp_bus_attached) dev->sfp_bus = phydev->sfp_bus; + + err = phy_link_topo_add_phy(&dev->link_topo, phydev, + PHY_UPSTREAM_MAC, dev); + if (err) + goto error; } /* Some Ethernet drivers try to connect to a PHY device before @@ -1550,7 +1604,8 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, goto error; phy_resume(phydev); - phy_led_triggers_register(phydev); + if (!phydev->is_on_sfp_module) + phy_led_triggers_register(phydev); /** * If the external phy used by current mac interface is managed by @@ -1819,10 +1874,12 @@ void phy_detach(struct phy_device *phydev) if (dev) { phydev->attached_dev->phydev = NULL; phydev->attached_dev = NULL; + phy_link_topo_del_phy(&dev->link_topo, phydev); } phydev->phylink = NULL; - phy_led_triggers_unregister(phydev); + if (!phydev->is_on_sfp_module) + phy_led_triggers_unregister(phydev); if (phydev->mdio.dev.driver) module_put(phydev->mdio.dev.driver->owner); diff --git a/drivers/net/phy/phy_link_topology.c b/drivers/net/phy/phy_link_topology.c new file mode 100644 index 000000000000..34e7e08fbfc3 --- /dev/null +++ b/drivers/net/phy/phy_link_topology.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Infrastructure to handle all PHY devices connected to a given netdev, + * either directly or indirectly attached. + * + * Copyright (c) 2023 Maxime Chevallier<maxime.chevallier@bootlin.com> + */ + +#include <linux/phy_link_topology.h> +#include <linux/netdevice.h> +#include <linux/phy.h> +#include <linux/rtnetlink.h> +#include <linux/xarray.h> + +int phy_link_topo_add_phy(struct phy_link_topology *topo, + struct phy_device *phy, + enum phy_upstream upt, void *upstream) +{ + struct phy_device_node *pdn; + int ret; + + pdn = kzalloc(sizeof(*pdn), GFP_KERNEL); + if (!pdn) + return -ENOMEM; + + pdn->phy = phy; + switch (upt) { + case PHY_UPSTREAM_MAC: + pdn->upstream.netdev = (struct net_device *)upstream; + if (phy_on_sfp(phy)) + pdn->parent_sfp_bus = pdn->upstream.netdev->sfp_bus; + break; + case PHY_UPSTREAM_PHY: + pdn->upstream.phydev = (struct phy_device *)upstream; + if (phy_on_sfp(phy)) + pdn->parent_sfp_bus = pdn->upstream.phydev->sfp_bus; + break; + default: + ret = -EINVAL; + goto err; + } + pdn->upstream_type = upt; + + ret = xa_alloc_cyclic(&topo->phys, &phy->phyindex, pdn, xa_limit_32b, + &topo->next_phy_index, GFP_KERNEL); + if (ret) + goto err; + + return 0; + +err: + kfree(pdn); + return ret; +} +EXPORT_SYMBOL_GPL(phy_link_topo_add_phy); + +void phy_link_topo_del_phy(struct phy_link_topology *topo, + struct phy_device *phy) +{ + struct phy_device_node *pdn = xa_erase(&topo->phys, phy->phyindex); + + phy->phyindex = 0; + + kfree(pdn); +} +EXPORT_SYMBOL_GPL(phy_link_topo_del_phy); diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 298dfd6982a5..3d25a4a6212b 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -3319,7 +3319,8 @@ static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy) return ret; } -static void phylink_sfp_disconnect_phy(void *upstream) +static void phylink_sfp_disconnect_phy(void *upstream, + struct phy_device *phydev) { phylink_disconnect_phy(upstream); } diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c index 6fa679b36290..fb1c102714b5 100644 --- a/drivers/net/phy/sfp-bus.c +++ b/drivers/net/phy/sfp-bus.c @@ -486,7 +486,7 @@ static void sfp_unregister_bus(struct sfp_bus *bus) bus->socket_ops->stop(bus->sfp); bus->socket_ops->detach(bus->sfp); if (bus->phydev && ops && ops->disconnect_phy) - ops->disconnect_phy(bus->upstream); + ops->disconnect_phy(bus->upstream, bus->phydev); } bus->registered = false; } @@ -742,7 +742,7 @@ void sfp_remove_phy(struct sfp_bus *bus) const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus); if (ops && ops->disconnect_phy) - ops->disconnect_phy(bus->upstream); + ops->disconnect_phy(bus->upstream, bus->phydev); bus->phydev = NULL; } EXPORT_SYMBOL_GPL(sfp_remove_phy); @@ -859,3 +859,14 @@ void sfp_unregister_socket(struct sfp_bus *bus) sfp_bus_put(bus); } EXPORT_SYMBOL_GPL(sfp_unregister_socket); + +const char *sfp_get_name(struct sfp_bus *bus) +{ + ASSERT_RTNL(); + + if (bus->sfp_dev) + return dev_name(bus->sfp_dev); + + return NULL; +} +EXPORT_SYMBOL_GPL(sfp_get_name); diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index 3780a96d2caa..f75c9eb3958e 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -274,6 +274,7 @@ struct sfp { struct sfp_eeprom_id id; unsigned int module_power_mW; unsigned int module_t_start_up; + unsigned int module_t_wait; unsigned int phy_t_retry; unsigned int rate_kbd; @@ -388,6 +389,12 @@ static void sfp_fixup_fs_10gt(struct sfp *sfp) { sfp_fixup_10gbaset_30m(sfp); sfp_fixup_rollball(sfp); + + /* The RollBall fixup is not enough for FS modules, the AQR chip inside + * them does not return 0xffff for PHY ID registers in all MMDs for the + * while initializing. They need a 4 second wait before accessing PHY. + */ + sfp->module_t_wait = msecs_to_jiffies(4000); } static void sfp_fixup_halny_gsfp(struct sfp *sfp) @@ -2329,6 +2336,7 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) mask |= SFP_F_RS1; sfp->module_t_start_up = T_START_UP; + sfp->module_t_wait = T_WAIT; sfp->phy_t_retry = T_PHY_RETRY; sfp->state_ignore_mask = 0; @@ -2566,9 +2574,10 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event) /* We need to check the TX_FAULT state, which is not defined * while TX_DISABLE is asserted. The earliest we want to do - * anything (such as probe for a PHY) is 50ms. + * anything (such as probe for a PHY) is 50ms (or more on + * specific modules). */ - sfp_sm_next(sfp, SFP_S_WAIT, T_WAIT); + sfp_sm_next(sfp, SFP_S_WAIT, sfp->module_t_wait); break; case SFP_S_WAIT: @@ -2582,8 +2591,8 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event) * deasserting. */ timeout = sfp->module_t_start_up; - if (timeout > T_WAIT) - timeout -= T_WAIT; + if (timeout > sfp->module_t_wait) + timeout -= sfp->module_t_wait; else timeout = 1; |