diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 18:50:36 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 18:50:36 +0000 |
commit | 50ba0232fd5312410f1b65247e774244f89a628e (patch) | |
tree | fd8f2fc78e9e548af0ff9590276602ee6125be00 /drivers/net/phy | |
parent | Releasing progress-linux version 6.7.12-1~progress7.99u1. (diff) | |
download | linux-50ba0232fd5312410f1b65247e774244f89a628e.tar.xz linux-50ba0232fd5312410f1b65247e774244f89a628e.zip |
Merging upstream version 6.8.9.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/net/phy')
40 files changed, 4310 insertions, 1014 deletions
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 421d2b6291..9e2672800f 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -60,6 +60,14 @@ config FIXED_PHY Currently tested with mpc866ads and mpc8349e-mitx. +config RUST_PHYLIB_ABSTRACTIONS + bool "Rust PHYLIB abstractions support" + depends on RUST + depends on PHYLIB=y + help + Adds support needed for PHY drivers written in Rust. It provides + a wrapper around the C phylib core. + config SFP tristate "SFP cage support" depends on I2C && PHYLINK @@ -96,10 +104,7 @@ config ADIN1100_PHY Currently supports the: - ADIN1100 - Robust,Industrial, Low Power 10BASE-T1L Ethernet PHY -config AQUANTIA_PHY - tristate "Aquantia PHYs" - help - Currently supports the Aquantia AQ1202, AQ2104, AQR105, AQR405 +source "drivers/net/phy/aquantia/Kconfig" config AX88796B_PHY tristate "Asix PHYs" @@ -107,6 +112,14 @@ config AX88796B_PHY Currently supports the Asix Electronics PHY found in the X-Surf 100 AX88796B package. +config AX88796B_RUST_PHY + bool "Rust reference driver for Asix PHYs" + depends on RUST_PHYLIB_ABSTRACTIONS && AX88796B_PHY + help + Uses the Rust reference driver for Asix PHYs (ax88796b_rust.ko). + The features are equivalent. It supports the Asix Electronics PHY + found in the X-Surf 100 AX88796B package. + config BROADCOM_PHY tristate "Broadcom 54XX PHYs" select BCM_NET_PHYLIB @@ -304,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" @@ -397,6 +411,19 @@ config DP83TD510_PHY Support for the DP83TD510 Ethernet 10Base-T1L PHY. This PHY supports a 10M single pair Ethernet connection for up to 1000 meter cable. +config DP83TG720_PHY + tristate "Texas Instruments DP83TG720 Ethernet 1000Base-T1 PHY" + help + The DP83TG720S-Q1 is an automotive Ethernet physical layer + transceiver compliant with IEEE 802.3bp and Open Alliance + standards. It supports key functions necessary for + transmitting and receiving data over both unshielded and + shielded single twisted-pair cables. This device offers + flexible xMII interface options, including support for both + RGMII and SGMII MAC interfaces. It's suitable for applications + requiring high-speed data transmission in automotive + networking environments. + config VITESSE_PHY tristate "Vitesse PHYs" help diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index c945ed9bd1..6097afd443 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -35,13 +35,13 @@ obj-y += $(sfp-obj-y) $(sfp-obj-m) obj-$(CONFIG_ADIN_PHY) += adin.o obj-$(CONFIG_ADIN1100_PHY) += adin1100.o obj-$(CONFIG_AMD_PHY) += amd.o -aquantia-objs += aquantia_main.o -ifdef CONFIG_HWMON -aquantia-objs += aquantia_hwmon.o -endif -obj-$(CONFIG_AQUANTIA_PHY) += aquantia.o +obj-$(CONFIG_AQUANTIA_PHY) += aquantia/ obj-$(CONFIG_AT803X_PHY) += at803x.o -obj-$(CONFIG_AX88796B_PHY) += ax88796b.o +ifdef CONFIG_AX88796B_RUST_PHY + obj-$(CONFIG_AX88796B_PHY) += ax88796b_rust.o +else + obj-$(CONFIG_AX88796B_PHY) += ax88796b.o +endif obj-$(CONFIG_BCM54140_PHY) += bcm54140.o obj-$(CONFIG_BCM63XX_PHY) += bcm63xx.o obj-$(CONFIG_BCM7XXX_PHY) += bcm7xxx.o @@ -61,6 +61,7 @@ obj-$(CONFIG_DP83867_PHY) += dp83867.o obj-$(CONFIG_DP83869_PHY) += dp83869.o obj-$(CONFIG_DP83TC811_PHY) += dp83tc811.o obj-$(CONFIG_DP83TD510_PHY) += dp83td510.o +obj-$(CONFIG_DP83TG720_PHY) += dp83tg720.o obj-$(CONFIG_FIXED_PHY) += fixed_phy.o obj-$(CONFIG_ICPLUS_PHY) += icplus.o obj-$(CONFIG_INTEL_XWAY_PHY) += intel-xway.o @@ -83,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/adin.c b/drivers/net/phy/adin.c index 134637584a..2e1a46e121 100644 --- a/drivers/net/phy/adin.c +++ b/drivers/net/phy/adin.c @@ -68,6 +68,24 @@ #define ADIN1300_EEE_CAP_REG 0x8000 #define ADIN1300_EEE_ADV_REG 0x8001 #define ADIN1300_EEE_LPABLE_REG 0x8002 + +#define ADIN1300_FLD_EN_REG 0x8E27 +#define ADIN1300_FLD_PCS_ERR_100_EN BIT(7) +#define ADIN1300_FLD_PCS_ERR_1000_EN BIT(6) +#define ADIN1300_FLD_SLCR_OUT_STUCK_100_EN BIT(5) +#define ADIN1300_FLD_SLCR_OUT_STUCK_1000_EN BIT(4) +#define ADIN1300_FLD_SLCR_IN_ZDET_100_EN BIT(3) +#define ADIN1300_FLD_SLCR_IN_ZDET_1000_EN BIT(2) +#define ADIN1300_FLD_SLCR_IN_INVLD_100_EN BIT(1) +#define ADIN1300_FLD_SLCR_IN_INVLD_1000_EN BIT(0) +/* These bits are the ones which are enabled by default. */ +#define ADIN1300_FLD_EN_ON \ + (ADIN1300_FLD_SLCR_OUT_STUCK_100_EN | \ + ADIN1300_FLD_SLCR_OUT_STUCK_1000_EN | \ + ADIN1300_FLD_SLCR_IN_ZDET_100_EN | \ + ADIN1300_FLD_SLCR_IN_ZDET_1000_EN | \ + ADIN1300_FLD_SLCR_IN_INVLD_1000_EN) + #define ADIN1300_CLOCK_STOP_REG 0x9400 #define ADIN1300_LPI_WAKE_ERR_CNT_REG 0xa000 @@ -416,6 +434,37 @@ static int adin_set_edpd(struct phy_device *phydev, u16 tx_interval) val); } +static int adin_get_fast_down(struct phy_device *phydev, u8 *msecs) +{ + int reg; + + reg = phy_read_mmd(phydev, MDIO_MMD_VEND1, ADIN1300_FLD_EN_REG); + if (reg < 0) + return reg; + + if (reg & ADIN1300_FLD_EN_ON) + *msecs = ETHTOOL_PHY_FAST_LINK_DOWN_ON; + else + *msecs = ETHTOOL_PHY_FAST_LINK_DOWN_OFF; + + return 0; +} + +static int adin_set_fast_down(struct phy_device *phydev, const u8 *msecs) +{ + if (*msecs == ETHTOOL_PHY_FAST_LINK_DOWN_ON) + return phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, + ADIN1300_FLD_EN_REG, + ADIN1300_FLD_EN_ON); + + if (*msecs == ETHTOOL_PHY_FAST_LINK_DOWN_OFF) + return phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, + ADIN1300_FLD_EN_REG, + ADIN1300_FLD_EN_ON); + + return -EINVAL; +} + static int adin_get_tunable(struct phy_device *phydev, struct ethtool_tunable *tuna, void *data) { @@ -424,6 +473,8 @@ static int adin_get_tunable(struct phy_device *phydev, return adin_get_downshift(phydev, data); case ETHTOOL_PHY_EDPD: return adin_get_edpd(phydev, data); + case ETHTOOL_PHY_FAST_LINK_DOWN: + return adin_get_fast_down(phydev, data); default: return -EOPNOTSUPP; } @@ -437,6 +488,8 @@ static int adin_set_tunable(struct phy_device *phydev, return adin_set_downshift(phydev, *(const u8 *)data); case ETHTOOL_PHY_EDPD: return adin_set_edpd(phydev, *(const u16 *)data); + case ETHTOOL_PHY_FAST_LINK_DOWN: + return adin_set_fast_down(phydev, data); default: return -EOPNOTSUPP; } diff --git a/drivers/net/phy/aquantia.h b/drivers/net/phy/aquantia.h deleted file mode 100644 index c684b65c64..0000000000 --- a/drivers/net/phy/aquantia.h +++ /dev/null @@ -1,16 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* HWMON driver for Aquantia PHY - * - * Author: Nikita Yushchenko <nikita.yoush@cogentembedded.com> - * Author: Andrew Lunn <andrew@lunn.ch> - * Author: Heiner Kallweit <hkallweit1@gmail.com> - */ - -#include <linux/device.h> -#include <linux/phy.h> - -#if IS_REACHABLE(CONFIG_HWMON) -int aqr_hwmon_probe(struct phy_device *phydev); -#else -static inline int aqr_hwmon_probe(struct phy_device *phydev) { return 0; } -#endif diff --git a/drivers/net/phy/aquantia/Kconfig b/drivers/net/phy/aquantia/Kconfig new file mode 100644 index 0000000000..1a65678583 --- /dev/null +++ b/drivers/net/phy/aquantia/Kconfig @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only +config AQUANTIA_PHY + tristate "Aquantia PHYs" + select CRC_ITU_T + help + Currently supports the Aquantia AQ1202, AQ2104, AQR105, AQR405 diff --git a/drivers/net/phy/aquantia/Makefile b/drivers/net/phy/aquantia/Makefile new file mode 100644 index 0000000000..aa77fb63c8 --- /dev/null +++ b/drivers/net/phy/aquantia/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +aquantia-objs += aquantia_main.o aquantia_firmware.o +ifdef CONFIG_HWMON +aquantia-objs += aquantia_hwmon.o +endif +obj-$(CONFIG_AQUANTIA_PHY) += aquantia.o diff --git a/drivers/net/phy/aquantia/aquantia.h b/drivers/net/phy/aquantia/aquantia.h new file mode 100644 index 0000000000..1c19ae74ad --- /dev/null +++ b/drivers/net/phy/aquantia/aquantia.h @@ -0,0 +1,122 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* HWMON driver for Aquantia PHY + * + * Author: Nikita Yushchenko <nikita.yoush@cogentembedded.com> + * Author: Andrew Lunn <andrew@lunn.ch> + * Author: Heiner Kallweit <hkallweit1@gmail.com> + */ + +#include <linux/device.h> +#include <linux/phy.h> + +/* Vendor specific 1, MDIO_MMD_VEND1 */ +#define VEND1_GLOBAL_SC 0x0 +#define VEND1_GLOBAL_SC_SOFT_RESET BIT(15) +#define VEND1_GLOBAL_SC_LOW_POWER BIT(11) + +#define VEND1_GLOBAL_FW_ID 0x0020 +#define VEND1_GLOBAL_FW_ID_MAJOR GENMASK(15, 8) +#define VEND1_GLOBAL_FW_ID_MINOR GENMASK(7, 0) + +#define VEND1_GLOBAL_MAILBOX_INTERFACE1 0x0200 +#define VEND1_GLOBAL_MAILBOX_INTERFACE1_EXECUTE BIT(15) +#define VEND1_GLOBAL_MAILBOX_INTERFACE1_WRITE BIT(14) +#define VEND1_GLOBAL_MAILBOX_INTERFACE1_CRC_RESET BIT(12) +#define VEND1_GLOBAL_MAILBOX_INTERFACE1_BUSY BIT(8) + +#define VEND1_GLOBAL_MAILBOX_INTERFACE2 0x0201 +#define VEND1_GLOBAL_MAILBOX_INTERFACE3 0x0202 +#define VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR_MASK GENMASK(15, 0) +#define VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR(x) FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR_MASK, (u16)((x) >> 16)) +#define VEND1_GLOBAL_MAILBOX_INTERFACE4 0x0203 +#define VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR_MASK GENMASK(15, 2) +#define VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR(x) FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR_MASK, (u16)(x)) + +#define VEND1_GLOBAL_MAILBOX_INTERFACE5 0x0204 +#define VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA_MASK GENMASK(15, 0) +#define VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA(x) FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA_MASK, (u16)((x) >> 16)) +#define VEND1_GLOBAL_MAILBOX_INTERFACE6 0x0205 +#define VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA_MASK GENMASK(15, 0) +#define VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA(x) FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA_MASK, (u16)(x)) + +/* The following registers all have similar layouts; first the registers... */ +#define VEND1_GLOBAL_CFG_10M 0x0310 +#define VEND1_GLOBAL_CFG_100M 0x031b +#define VEND1_GLOBAL_CFG_1G 0x031c +#define VEND1_GLOBAL_CFG_2_5G 0x031d +#define VEND1_GLOBAL_CFG_5G 0x031e +#define VEND1_GLOBAL_CFG_10G 0x031f +/* ...and now the fields */ +#define VEND1_GLOBAL_CFG_SERDES_MODE GENMASK(2, 0) +#define VEND1_GLOBAL_CFG_SERDES_MODE_XFI 0 +#define VEND1_GLOBAL_CFG_SERDES_MODE_SGMII 3 +#define VEND1_GLOBAL_CFG_SERDES_MODE_OCSGMII 4 +#define VEND1_GLOBAL_CFG_SERDES_MODE_XFI5G 6 +#define VEND1_GLOBAL_CFG_RATE_ADAPT GENMASK(8, 7) +#define VEND1_GLOBAL_CFG_RATE_ADAPT_NONE 0 +#define VEND1_GLOBAL_CFG_RATE_ADAPT_USX 1 +#define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE 2 + +/* Vendor specific 1, MDIO_MMD_VEND2 */ +#define VEND1_GLOBAL_CONTROL2 0xc001 +#define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_RST BIT(15) +#define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD BIT(6) +#define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL BIT(0) + +#define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL 0xc421 +#define VEND1_THERMAL_PROV_LOW_TEMP_FAIL 0xc422 +#define VEND1_THERMAL_PROV_HIGH_TEMP_WARN 0xc423 +#define VEND1_THERMAL_PROV_LOW_TEMP_WARN 0xc424 +#define VEND1_THERMAL_STAT1 0xc820 +#define VEND1_THERMAL_STAT2 0xc821 +#define VEND1_THERMAL_STAT2_VALID BIT(0) +#define VEND1_GENERAL_STAT1 0xc830 +#define VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL BIT(14) +#define VEND1_GENERAL_STAT1_LOW_TEMP_FAIL BIT(13) +#define VEND1_GENERAL_STAT1_HIGH_TEMP_WARN BIT(12) +#define VEND1_GENERAL_STAT1_LOW_TEMP_WARN BIT(11) + +#define VEND1_GLOBAL_GEN_STAT2 0xc831 +#define VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG BIT(15) + +#define VEND1_GLOBAL_RSVD_STAT1 0xc885 +#define VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID GENMASK(7, 4) +#define VEND1_GLOBAL_RSVD_STAT1_PROV_ID GENMASK(3, 0) + +#define VEND1_GLOBAL_RSVD_STAT9 0xc88d +#define VEND1_GLOBAL_RSVD_STAT9_MODE GENMASK(7, 0) +#define VEND1_GLOBAL_RSVD_STAT9_1000BT2 0x23 + +#define VEND1_GLOBAL_INT_STD_STATUS 0xfc00 +#define VEND1_GLOBAL_INT_VEND_STATUS 0xfc01 + +#define VEND1_GLOBAL_INT_STD_MASK 0xff00 +#define VEND1_GLOBAL_INT_STD_MASK_PMA1 BIT(15) +#define VEND1_GLOBAL_INT_STD_MASK_PMA2 BIT(14) +#define VEND1_GLOBAL_INT_STD_MASK_PCS1 BIT(13) +#define VEND1_GLOBAL_INT_STD_MASK_PCS2 BIT(12) +#define VEND1_GLOBAL_INT_STD_MASK_PCS3 BIT(11) +#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS1 BIT(10) +#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS2 BIT(9) +#define VEND1_GLOBAL_INT_STD_MASK_AN1 BIT(8) +#define VEND1_GLOBAL_INT_STD_MASK_AN2 BIT(7) +#define VEND1_GLOBAL_INT_STD_MASK_GBE BIT(6) +#define VEND1_GLOBAL_INT_STD_MASK_ALL BIT(0) + +#define VEND1_GLOBAL_INT_VEND_MASK 0xff01 +#define VEND1_GLOBAL_INT_VEND_MASK_PMA BIT(15) +#define VEND1_GLOBAL_INT_VEND_MASK_PCS BIT(14) +#define VEND1_GLOBAL_INT_VEND_MASK_PHY_XS BIT(13) +#define VEND1_GLOBAL_INT_VEND_MASK_AN BIT(12) +#define VEND1_GLOBAL_INT_VEND_MASK_GBE BIT(11) +#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL1 BIT(2) +#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2 BIT(1) +#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 BIT(0) + +#if IS_REACHABLE(CONFIG_HWMON) +int aqr_hwmon_probe(struct phy_device *phydev); +#else +static inline int aqr_hwmon_probe(struct phy_device *phydev) { return 0; } +#endif + +int aqr_firmware_load(struct phy_device *phydev); diff --git a/drivers/net/phy/aquantia/aquantia_firmware.c b/drivers/net/phy/aquantia/aquantia_firmware.c new file mode 100644 index 0000000000..0c9640ef15 --- /dev/null +++ b/drivers/net/phy/aquantia/aquantia_firmware.c @@ -0,0 +1,374 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/bitfield.h> +#include <linux/of.h> +#include <linux/firmware.h> +#include <linux/crc-itu-t.h> +#include <linux/nvmem-consumer.h> + +#include <asm/unaligned.h> + +#include "aquantia.h" + +#define UP_RESET_SLEEP 100 + +/* addresses of memory segments in the phy */ +#define DRAM_BASE_ADDR 0x3FFE0000 +#define IRAM_BASE_ADDR 0x40000000 + +/* firmware image format constants */ +#define VERSION_STRING_SIZE 0x40 +#define VERSION_STRING_OFFSET 0x0200 +/* primary offset is written at an offset from the start of the fw blob */ +#define PRIMARY_OFFSET_OFFSET 0x8 +/* primary offset needs to be then added to a base offset */ +#define PRIMARY_OFFSET_SHIFT 12 +#define PRIMARY_OFFSET(x) ((x) << PRIMARY_OFFSET_SHIFT) +#define HEADER_OFFSET 0x300 + +struct aqr_fw_header { + u32 padding; + u8 iram_offset[3]; + u8 iram_size[3]; + u8 dram_offset[3]; + u8 dram_size[3]; +} __packed; + +enum aqr_fw_src { + AQR_FW_SRC_NVMEM = 0, + AQR_FW_SRC_FS, +}; + +static const char * const aqr_fw_src_string[] = { + [AQR_FW_SRC_NVMEM] = "NVMEM", + [AQR_FW_SRC_FS] = "FS", +}; + +/* AQR firmware doesn't have fixed offsets for iram and dram section + * but instead provide an header with the offset to use on reading + * and parsing the firmware. + * + * AQR firmware can't be trusted and each offset is validated to be + * not negative and be in the size of the firmware itself. + */ +static bool aqr_fw_validate_get(size_t size, size_t offset, size_t get_size) +{ + return offset + get_size <= size; +} + +static int aqr_fw_get_be16(const u8 *data, size_t offset, size_t size, u16 *value) +{ + if (!aqr_fw_validate_get(size, offset, sizeof(u16))) + return -EINVAL; + + *value = get_unaligned_be16(data + offset); + + return 0; +} + +static int aqr_fw_get_le16(const u8 *data, size_t offset, size_t size, u16 *value) +{ + if (!aqr_fw_validate_get(size, offset, sizeof(u16))) + return -EINVAL; + + *value = get_unaligned_le16(data + offset); + + return 0; +} + +static int aqr_fw_get_le24(const u8 *data, size_t offset, size_t size, u32 *value) +{ + if (!aqr_fw_validate_get(size, offset, sizeof(u8) * 3)) + return -EINVAL; + + *value = get_unaligned_le24(data + offset); + + return 0; +} + +/* load data into the phy's memory */ +static int aqr_fw_load_memory(struct phy_device *phydev, u32 addr, + const u8 *data, size_t len) +{ + u16 crc = 0, up_crc; + size_t pos; + + phy_write_mmd(phydev, MDIO_MMD_VEND1, + VEND1_GLOBAL_MAILBOX_INTERFACE1, + VEND1_GLOBAL_MAILBOX_INTERFACE1_CRC_RESET); + phy_write_mmd(phydev, MDIO_MMD_VEND1, + VEND1_GLOBAL_MAILBOX_INTERFACE3, + VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR(addr)); + phy_write_mmd(phydev, MDIO_MMD_VEND1, + VEND1_GLOBAL_MAILBOX_INTERFACE4, + VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR(addr)); + + /* We assume and enforce the size to be word aligned. + * If a firmware that is not word aligned is found, please report upstream. + */ + for (pos = 0; pos < len; pos += sizeof(u32)) { + u8 crc_data[4]; + u32 word; + + /* FW data is always stored in little-endian */ + word = get_unaligned_le32((const u32 *)(data + pos)); + + phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE5, + VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA(word)); + phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE6, + VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA(word)); + + phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE1, + VEND1_GLOBAL_MAILBOX_INTERFACE1_EXECUTE | + VEND1_GLOBAL_MAILBOX_INTERFACE1_WRITE); + + /* Word is swapped internally and MAILBOX CRC is calculated + * using big-endian order. Mimic what the PHY does to have a + * matching CRC... + */ + crc_data[0] = word >> 24; + crc_data[1] = word >> 16; + crc_data[2] = word >> 8; + crc_data[3] = word; + + /* ...calculate CRC as we load data... */ + crc = crc_itu_t(crc, crc_data, sizeof(crc_data)); + } + /* ...gets CRC from MAILBOX after we have loaded the entire section... */ + up_crc = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE2); + /* ...and make sure it does match our calculated CRC */ + if (crc != up_crc) { + phydev_err(phydev, "CRC mismatch: calculated 0x%04x PHY 0x%04x\n", + crc, up_crc); + return -EINVAL; + } + + return 0; +} + +static int aqr_fw_boot(struct phy_device *phydev, const u8 *data, size_t size, + enum aqr_fw_src fw_src) +{ + u16 calculated_crc, read_crc, read_primary_offset; + u32 iram_offset = 0, iram_size = 0; + u32 dram_offset = 0, dram_size = 0; + char version[VERSION_STRING_SIZE]; + u32 primary_offset = 0; + int ret; + + /* extract saved CRC at the end of the fw + * CRC is saved in big-endian as PHY is BE + */ + ret = aqr_fw_get_be16(data, size - sizeof(u16), size, &read_crc); + if (ret) { + phydev_err(phydev, "bad firmware CRC in firmware\n"); + return ret; + } + calculated_crc = crc_itu_t(0, data, size - sizeof(u16)); + if (read_crc != calculated_crc) { + phydev_err(phydev, "bad firmware CRC: file 0x%04x calculated 0x%04x\n", + read_crc, calculated_crc); + return -EINVAL; + } + + /* Get the primary offset to extract DRAM and IRAM sections. */ + ret = aqr_fw_get_le16(data, PRIMARY_OFFSET_OFFSET, size, &read_primary_offset); + if (ret) { + phydev_err(phydev, "bad primary offset in firmware\n"); + return ret; + } + primary_offset = PRIMARY_OFFSET(read_primary_offset); + + /* Find the DRAM and IRAM sections within the firmware file. + * Make sure the fw_header is correctly in the firmware. + */ + if (!aqr_fw_validate_get(size, primary_offset + HEADER_OFFSET, + sizeof(struct aqr_fw_header))) { + phydev_err(phydev, "bad fw_header in firmware\n"); + return -EINVAL; + } + + /* offset are in LE and values needs to be converted to cpu endian */ + ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET + + offsetof(struct aqr_fw_header, iram_offset), + size, &iram_offset); + if (ret) { + phydev_err(phydev, "bad iram offset in firmware\n"); + return ret; + } + ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET + + offsetof(struct aqr_fw_header, iram_size), + size, &iram_size); + if (ret) { + phydev_err(phydev, "invalid iram size in firmware\n"); + return ret; + } + ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET + + offsetof(struct aqr_fw_header, dram_offset), + size, &dram_offset); + if (ret) { + phydev_err(phydev, "bad dram offset in firmware\n"); + return ret; + } + ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET + + offsetof(struct aqr_fw_header, dram_size), + size, &dram_size); + if (ret) { + phydev_err(phydev, "invalid dram size in firmware\n"); + return ret; + } + + /* Increment the offset with the primary offset. + * Validate iram/dram offset and size. + */ + iram_offset += primary_offset; + if (iram_size % sizeof(u32)) { + phydev_err(phydev, "iram size if not aligned to word size. Please report this upstream!\n"); + return -EINVAL; + } + if (!aqr_fw_validate_get(size, iram_offset, iram_size)) { + phydev_err(phydev, "invalid iram offset for iram size\n"); + return -EINVAL; + } + + dram_offset += primary_offset; + if (dram_size % sizeof(u32)) { + phydev_err(phydev, "dram size if not aligned to word size. Please report this upstream!\n"); + return -EINVAL; + } + if (!aqr_fw_validate_get(size, dram_offset, dram_size)) { + phydev_err(phydev, "invalid iram offset for iram size\n"); + return -EINVAL; + } + + phydev_dbg(phydev, "primary %d IRAM offset=%d size=%d DRAM offset=%d size=%d\n", + primary_offset, iram_offset, iram_size, dram_offset, dram_size); + + if (!aqr_fw_validate_get(size, dram_offset + VERSION_STRING_OFFSET, + VERSION_STRING_SIZE)) { + phydev_err(phydev, "invalid version in firmware\n"); + return -EINVAL; + } + strscpy(version, (char *)data + dram_offset + VERSION_STRING_OFFSET, + VERSION_STRING_SIZE); + if (version[0] == '\0') { + phydev_err(phydev, "invalid version in firmware\n"); + return -EINVAL; + } + phydev_info(phydev, "loading firmware version '%s' from '%s'\n", version, + aqr_fw_src_string[fw_src]); + + /* stall the microcprocessor */ + phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2, + VEND1_GLOBAL_CONTROL2_UP_RUN_STALL | VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD); + + phydev_dbg(phydev, "loading DRAM 0x%08x from offset=%d size=%d\n", + DRAM_BASE_ADDR, dram_offset, dram_size); + ret = aqr_fw_load_memory(phydev, DRAM_BASE_ADDR, data + dram_offset, + dram_size); + if (ret) + return ret; + + phydev_dbg(phydev, "loading IRAM 0x%08x from offset=%d size=%d\n", + IRAM_BASE_ADDR, iram_offset, iram_size); + ret = aqr_fw_load_memory(phydev, IRAM_BASE_ADDR, data + iram_offset, + iram_size); + if (ret) + return ret; + + /* make sure soft reset and low power mode are clear */ + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_SC, + VEND1_GLOBAL_SC_SOFT_RESET | VEND1_GLOBAL_SC_LOW_POWER); + + /* Release the microprocessor. UP_RESET must be held for 100 usec. */ + phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2, + VEND1_GLOBAL_CONTROL2_UP_RUN_STALL | + VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD | + VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_RST); + usleep_range(UP_RESET_SLEEP, UP_RESET_SLEEP * 2); + + phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2, + VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD); + + return 0; +} + +static int aqr_firmware_load_nvmem(struct phy_device *phydev) +{ + struct nvmem_cell *cell; + size_t size; + u8 *buf; + int ret; + + cell = nvmem_cell_get(&phydev->mdio.dev, "firmware"); + if (IS_ERR(cell)) + return PTR_ERR(cell); + + buf = nvmem_cell_read(cell, &size); + if (IS_ERR(buf)) { + ret = PTR_ERR(buf); + goto exit; + } + + ret = aqr_fw_boot(phydev, buf, size, AQR_FW_SRC_NVMEM); + if (ret) + phydev_err(phydev, "firmware loading failed: %d\n", ret); + + kfree(buf); +exit: + nvmem_cell_put(cell); + + return ret; +} + +static int aqr_firmware_load_fs(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + const struct firmware *fw; + const char *fw_name; + int ret; + + ret = of_property_read_string(dev->of_node, "firmware-name", + &fw_name); + if (ret) + return ret; + + ret = request_firmware(&fw, fw_name, dev); + if (ret) { + phydev_err(phydev, "failed to find FW file %s (%d)\n", + fw_name, ret); + return ret; + } + + ret = aqr_fw_boot(phydev, fw->data, fw->size, AQR_FW_SRC_FS); + if (ret) + phydev_err(phydev, "firmware loading failed: %d\n", ret); + + release_firmware(fw); + + return ret; +} + +int aqr_firmware_load(struct phy_device *phydev) +{ + int ret; + + /* Check if the firmware is not already loaded by pooling + * the current version returned by the PHY. If 0 is returned, + * no firmware is loaded. + */ + ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_FW_ID); + if (ret > 0) + goto exit; + + ret = aqr_firmware_load_nvmem(phydev); + if (!ret) + goto exit; + + ret = aqr_firmware_load_fs(phydev); + if (ret) + return ret; + +exit: + return 0; +} diff --git a/drivers/net/phy/aquantia_hwmon.c b/drivers/net/phy/aquantia/aquantia_hwmon.c index 0da451e46f..7b3c49c3bf 100644 --- a/drivers/net/phy/aquantia_hwmon.c +++ b/drivers/net/phy/aquantia/aquantia_hwmon.c @@ -13,20 +13,6 @@ #include "aquantia.h" -/* Vendor specific 1, MDIO_MMD_VEND2 */ -#define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL 0xc421 -#define VEND1_THERMAL_PROV_LOW_TEMP_FAIL 0xc422 -#define VEND1_THERMAL_PROV_HIGH_TEMP_WARN 0xc423 -#define VEND1_THERMAL_PROV_LOW_TEMP_WARN 0xc424 -#define VEND1_THERMAL_STAT1 0xc820 -#define VEND1_THERMAL_STAT2 0xc821 -#define VEND1_THERMAL_STAT2_VALID BIT(0) -#define VEND1_GENERAL_STAT1 0xc830 -#define VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL BIT(14) -#define VEND1_GENERAL_STAT1_LOW_TEMP_FAIL BIT(13) -#define VEND1_GENERAL_STAT1_HIGH_TEMP_WARN BIT(12) -#define VEND1_GENERAL_STAT1_LOW_TEMP_WARN BIT(11) - #if IS_REACHABLE(CONFIG_HWMON) static umode_t aqr_hwmon_is_visible(const void *data, diff --git a/drivers/net/phy/aquantia_main.c b/drivers/net/phy/aquantia/aquantia_main.c index 334a6904ca..97a2fafa15 100644 --- a/drivers/net/phy/aquantia_main.c +++ b/drivers/net/phy/aquantia/aquantia_main.c @@ -91,61 +91,6 @@ #define MDIO_C22EXT_STAT_SGMII_TX_FRAME_ALIGN_ERR 0xd31a #define MDIO_C22EXT_STAT_SGMII_TX_RUNT_FRAMES 0xd31b -/* Vendor specific 1, MDIO_MMD_VEND1 */ -#define VEND1_GLOBAL_FW_ID 0x0020 -#define VEND1_GLOBAL_FW_ID_MAJOR GENMASK(15, 8) -#define VEND1_GLOBAL_FW_ID_MINOR GENMASK(7, 0) - -#define VEND1_GLOBAL_GEN_STAT2 0xc831 -#define VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG BIT(15) - -/* The following registers all have similar layouts; first the registers... */ -#define VEND1_GLOBAL_CFG_10M 0x0310 -#define VEND1_GLOBAL_CFG_100M 0x031b -#define VEND1_GLOBAL_CFG_1G 0x031c -#define VEND1_GLOBAL_CFG_2_5G 0x031d -#define VEND1_GLOBAL_CFG_5G 0x031e -#define VEND1_GLOBAL_CFG_10G 0x031f -/* ...and now the fields */ -#define VEND1_GLOBAL_CFG_RATE_ADAPT GENMASK(8, 7) -#define VEND1_GLOBAL_CFG_RATE_ADAPT_NONE 0 -#define VEND1_GLOBAL_CFG_RATE_ADAPT_USX 1 -#define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE 2 - -#define VEND1_GLOBAL_RSVD_STAT1 0xc885 -#define VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID GENMASK(7, 4) -#define VEND1_GLOBAL_RSVD_STAT1_PROV_ID GENMASK(3, 0) - -#define VEND1_GLOBAL_RSVD_STAT9 0xc88d -#define VEND1_GLOBAL_RSVD_STAT9_MODE GENMASK(7, 0) -#define VEND1_GLOBAL_RSVD_STAT9_1000BT2 0x23 - -#define VEND1_GLOBAL_INT_STD_STATUS 0xfc00 -#define VEND1_GLOBAL_INT_VEND_STATUS 0xfc01 - -#define VEND1_GLOBAL_INT_STD_MASK 0xff00 -#define VEND1_GLOBAL_INT_STD_MASK_PMA1 BIT(15) -#define VEND1_GLOBAL_INT_STD_MASK_PMA2 BIT(14) -#define VEND1_GLOBAL_INT_STD_MASK_PCS1 BIT(13) -#define VEND1_GLOBAL_INT_STD_MASK_PCS2 BIT(12) -#define VEND1_GLOBAL_INT_STD_MASK_PCS3 BIT(11) -#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS1 BIT(10) -#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS2 BIT(9) -#define VEND1_GLOBAL_INT_STD_MASK_AN1 BIT(8) -#define VEND1_GLOBAL_INT_STD_MASK_AN2 BIT(7) -#define VEND1_GLOBAL_INT_STD_MASK_GBE BIT(6) -#define VEND1_GLOBAL_INT_STD_MASK_ALL BIT(0) - -#define VEND1_GLOBAL_INT_VEND_MASK 0xff01 -#define VEND1_GLOBAL_INT_VEND_MASK_PMA BIT(15) -#define VEND1_GLOBAL_INT_VEND_MASK_PCS BIT(14) -#define VEND1_GLOBAL_INT_VEND_MASK_PHY_XS BIT(13) -#define VEND1_GLOBAL_INT_VEND_MASK_AN BIT(12) -#define VEND1_GLOBAL_INT_VEND_MASK_GBE BIT(11) -#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL1 BIT(2) -#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2 BIT(1) -#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 BIT(0) - /* Sleep and timeout for checking if the Processor-Intensive * MDIO operation is finished */ @@ -711,13 +656,93 @@ static int aqr107_resume(struct phy_device *phydev) return aqr107_wait_processor_intensive_op(phydev); } +static const u16 aqr_global_cfg_regs[] = { + VEND1_GLOBAL_CFG_10M, + VEND1_GLOBAL_CFG_100M, + VEND1_GLOBAL_CFG_1G, + VEND1_GLOBAL_CFG_2_5G, + VEND1_GLOBAL_CFG_5G, + VEND1_GLOBAL_CFG_10G +}; + +static int aqr107_fill_interface_modes(struct phy_device *phydev) +{ + unsigned long *possible = phydev->possible_interfaces; + unsigned int serdes_mode, rate_adapt; + phy_interface_t interface; + int i, val; + + /* Walk the media-speed configuration registers to determine which + * host-side serdes modes may be used by the PHY depending on the + * negotiated media speed. + */ + for (i = 0; i < ARRAY_SIZE(aqr_global_cfg_regs); i++) { + val = phy_read_mmd(phydev, MDIO_MMD_VEND1, + aqr_global_cfg_regs[i]); + if (val < 0) + return val; + + serdes_mode = FIELD_GET(VEND1_GLOBAL_CFG_SERDES_MODE, val); + rate_adapt = FIELD_GET(VEND1_GLOBAL_CFG_RATE_ADAPT, val); + + switch (serdes_mode) { + case VEND1_GLOBAL_CFG_SERDES_MODE_XFI: + if (rate_adapt == VEND1_GLOBAL_CFG_RATE_ADAPT_USX) + interface = PHY_INTERFACE_MODE_USXGMII; + else + interface = PHY_INTERFACE_MODE_10GBASER; + break; + + case VEND1_GLOBAL_CFG_SERDES_MODE_XFI5G: + interface = PHY_INTERFACE_MODE_5GBASER; + break; + + case VEND1_GLOBAL_CFG_SERDES_MODE_OCSGMII: + interface = PHY_INTERFACE_MODE_2500BASEX; + break; + + case VEND1_GLOBAL_CFG_SERDES_MODE_SGMII: + interface = PHY_INTERFACE_MODE_SGMII; + break; + + default: + phydev_warn(phydev, "unrecognised serdes mode %u\n", + serdes_mode); + interface = PHY_INTERFACE_MODE_NA; + break; + } + + if (interface != PHY_INTERFACE_MODE_NA) + __set_bit(interface, possible); + } + + return 0; +} + +static int aqr113c_config_init(struct phy_device *phydev) +{ + int ret; + + ret = aqr107_config_init(phydev); + if (ret < 0) + return ret; + + return aqr107_fill_interface_modes(phydev); +} + static int aqr107_probe(struct phy_device *phydev) { + int ret; + phydev->priv = devm_kzalloc(&phydev->mdio.dev, sizeof(struct aqr107_priv), GFP_KERNEL); if (!phydev->priv) return -ENOMEM; + ret = aqr_firmware_load(phydev); + if (ret) + return ret; + return aqr_hwmon_probe(phydev); } @@ -843,7 +868,7 @@ static struct phy_driver aqr_driver[] = { .name = "Aquantia AQR113C", .probe = aqr107_probe, .get_rate_matching = aqr107_get_rate_matching, - .config_init = aqr107_config_init, + .config_init = aqr113c_config_init, .config_aneg = aqr_config_aneg, .config_intr = aqr_config_intr, .handle_interrupt = aqr_handle_interrupt, diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index ef203b0807..fdb89a505d 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -254,6 +254,7 @@ #define QCA808X_CDT_ENABLE_TEST BIT(15) #define QCA808X_CDT_INTER_CHECK_DIS BIT(13) +#define QCA808X_CDT_STATUS BIT(11) #define QCA808X_CDT_LENGTH_UNIT BIT(10) #define QCA808X_MMD3_CDT_STATUS 0x8064 @@ -261,16 +262,44 @@ #define QCA808X_MMD3_CDT_DIAG_PAIR_B 0x8066 #define QCA808X_MMD3_CDT_DIAG_PAIR_C 0x8067 #define QCA808X_MMD3_CDT_DIAG_PAIR_D 0x8068 -#define QCA808X_CDT_DIAG_LENGTH GENMASK(7, 0) +#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT GENMASK(15, 8) +#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT GENMASK(7, 0) #define QCA808X_CDT_CODE_PAIR_A GENMASK(15, 12) #define QCA808X_CDT_CODE_PAIR_B GENMASK(11, 8) #define QCA808X_CDT_CODE_PAIR_C GENMASK(7, 4) #define QCA808X_CDT_CODE_PAIR_D GENMASK(3, 0) -#define QCA808X_CDT_STATUS_STAT_FAIL 0 -#define QCA808X_CDT_STATUS_STAT_NORMAL 1 -#define QCA808X_CDT_STATUS_STAT_OPEN 2 -#define QCA808X_CDT_STATUS_STAT_SHORT 3 + +#define QCA808X_CDT_STATUS_STAT_TYPE GENMASK(1, 0) +#define QCA808X_CDT_STATUS_STAT_FAIL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0) +#define QCA808X_CDT_STATUS_STAT_NORMAL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1) +#define QCA808X_CDT_STATUS_STAT_SAME_OPEN FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2) +#define QCA808X_CDT_STATUS_STAT_SAME_SHORT FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3) + +#define QCA808X_CDT_STATUS_STAT_MDI GENMASK(3, 2) +#define QCA808X_CDT_STATUS_STAT_MDI1 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1) +#define QCA808X_CDT_STATUS_STAT_MDI2 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2) +#define QCA808X_CDT_STATUS_STAT_MDI3 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3) + +/* NORMAL are MDI with type set to 0 */ +#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI1 +#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ + QCA808X_CDT_STATUS_STAT_MDI1) +#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ + QCA808X_CDT_STATUS_STAT_MDI1) +#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI2 +#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ + QCA808X_CDT_STATUS_STAT_MDI2) +#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ + QCA808X_CDT_STATUS_STAT_MDI2) +#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI3 +#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ + QCA808X_CDT_STATUS_STAT_MDI3) +#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ + QCA808X_CDT_STATUS_STAT_MDI3) + +/* Added for reference of existence but should be handled by wait_for_completion already */ +#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3)) /* QCA808X 1G chip type */ #define QCA808X_PHY_MMD7_CHIP_TYPE 0x901d @@ -295,12 +324,17 @@ struct at803x_hw_stat { enum stat_access_type access_type; }; -static struct at803x_hw_stat at803x_hw_stats[] = { +static struct at803x_hw_stat qca83xx_hw_stats[] = { { "phy_idle_errors", 0xa, GENMASK(7, 0), PHY}, { "phy_receive_errors", 0x15, GENMASK(15, 0), PHY}, { "eee_wake_errors", 0x16, GENMASK(15, 0), MMD}, }; +struct at803x_ss_mask { + u16 speed_mask; + u8 speed_shift; +}; + struct at803x_priv { int flags; u16 clk_25m_reg; @@ -311,7 +345,7 @@ struct at803x_priv { bool is_1000basex; struct regulator_dev *vddio_rdev; struct regulator_dev *vddh_rdev; - u64 stats[ARRAY_SIZE(at803x_hw_stats)]; + u64 stats[ARRAY_SIZE(qca83xx_hw_stats)]; }; struct at803x_context { @@ -457,7 +491,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; @@ -466,27 +500,11 @@ static int at803x_set_wol(struct phy_device *phydev, phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i], mac[(i * 2) + 1] | (mac[(i * 2)] << 8)); - /* Enable WOL function for 1588 */ - if (phydev->drv->phy_id == ATH8031_PHY_ID) { - ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, - AT803X_PHY_MMD3_WOL_CTRL, - 0, AT803X_WOL_EN); - if (ret) - return ret; - } /* Enable WOL interrupt */ ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL); if (ret) return ret; } else { - /* Disable WoL function for 1588 */ - if (phydev->drv->phy_id == ATH8031_PHY_ID) { - ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, - AT803X_PHY_MMD3_WOL_CTRL, - AT803X_WOL_EN, 0); - if (ret) - return ret; - } /* Disable WOL interrupt */ ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0); if (ret) @@ -529,24 +547,24 @@ static void at803x_get_wol(struct phy_device *phydev, wol->wolopts |= WAKE_MAGIC; } -static int at803x_get_sset_count(struct phy_device *phydev) +static int qca83xx_get_sset_count(struct phy_device *phydev) { - return ARRAY_SIZE(at803x_hw_stats); + return ARRAY_SIZE(qca83xx_hw_stats); } -static void at803x_get_strings(struct phy_device *phydev, u8 *data) +static void qca83xx_get_strings(struct phy_device *phydev, u8 *data) { int i; - for (i = 0; i < ARRAY_SIZE(at803x_hw_stats); i++) { + for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) { strscpy(data + i * ETH_GSTRING_LEN, - at803x_hw_stats[i].string, ETH_GSTRING_LEN); + qca83xx_hw_stats[i].string, ETH_GSTRING_LEN); } } -static u64 at803x_get_stat(struct phy_device *phydev, int i) +static u64 qca83xx_get_stat(struct phy_device *phydev, int i) { - struct at803x_hw_stat stat = at803x_hw_stats[i]; + struct at803x_hw_stat stat = qca83xx_hw_stats[i]; struct at803x_priv *priv = phydev->priv; int val; u64 ret; @@ -567,13 +585,13 @@ static u64 at803x_get_stat(struct phy_device *phydev, int i) return ret; } -static void at803x_get_stats(struct phy_device *phydev, - struct ethtool_stats *stats, u64 *data) +static void qca83xx_get_stats(struct phy_device *phydev, + struct ethtool_stats *stats, u64 *data) { int i; - for (i = 0; i < ARRAY_SIZE(at803x_hw_stats); i++) - data[i] = at803x_get_stat(phydev, i); + for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) + data[i] = qca83xx_get_stat(phydev, i); } static int at803x_suspend(struct phy_device *phydev) @@ -599,139 +617,6 @@ static int at803x_resume(struct phy_device *phydev) return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0); } -static int at803x_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev, - unsigned int selector) -{ - struct phy_device *phydev = rdev_get_drvdata(rdev); - - if (selector) - return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, - 0, AT803X_DEBUG_RGMII_1V8); - else - return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, - AT803X_DEBUG_RGMII_1V8, 0); -} - -static int at803x_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev) -{ - struct phy_device *phydev = rdev_get_drvdata(rdev); - int val; - - val = at803x_debug_reg_read(phydev, AT803X_DEBUG_REG_1F); - if (val < 0) - return val; - - return (val & AT803X_DEBUG_RGMII_1V8) ? 1 : 0; -} - -static const struct regulator_ops vddio_regulator_ops = { - .list_voltage = regulator_list_voltage_table, - .set_voltage_sel = at803x_rgmii_reg_set_voltage_sel, - .get_voltage_sel = at803x_rgmii_reg_get_voltage_sel, -}; - -static const unsigned int vddio_voltage_table[] = { - 1500000, - 1800000, -}; - -static const struct regulator_desc vddio_desc = { - .name = "vddio", - .of_match = of_match_ptr("vddio-regulator"), - .n_voltages = ARRAY_SIZE(vddio_voltage_table), - .volt_table = vddio_voltage_table, - .ops = &vddio_regulator_ops, - .type = REGULATOR_VOLTAGE, - .owner = THIS_MODULE, -}; - -static const struct regulator_ops vddh_regulator_ops = { -}; - -static const struct regulator_desc vddh_desc = { - .name = "vddh", - .of_match = of_match_ptr("vddh-regulator"), - .n_voltages = 1, - .fixed_uV = 2500000, - .ops = &vddh_regulator_ops, - .type = REGULATOR_VOLTAGE, - .owner = THIS_MODULE, -}; - -static int at8031_register_regulators(struct phy_device *phydev) -{ - struct at803x_priv *priv = phydev->priv; - struct device *dev = &phydev->mdio.dev; - struct regulator_config config = { }; - - config.dev = dev; - config.driver_data = phydev; - - priv->vddio_rdev = devm_regulator_register(dev, &vddio_desc, &config); - if (IS_ERR(priv->vddio_rdev)) { - phydev_err(phydev, "failed to register VDDIO regulator\n"); - return PTR_ERR(priv->vddio_rdev); - } - - priv->vddh_rdev = devm_regulator_register(dev, &vddh_desc, &config); - if (IS_ERR(priv->vddh_rdev)) { - phydev_err(phydev, "failed to register VDDH regulator\n"); - return PTR_ERR(priv->vddh_rdev); - } - - return 0; -} - -static int at803x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) -{ - struct phy_device *phydev = upstream; - __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support); - __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support); - DECLARE_PHY_INTERFACE_MASK(interfaces); - phy_interface_t iface; - - linkmode_zero(phy_support); - phylink_set(phy_support, 1000baseX_Full); - phylink_set(phy_support, 1000baseT_Full); - phylink_set(phy_support, Autoneg); - phylink_set(phy_support, Pause); - phylink_set(phy_support, Asym_Pause); - - linkmode_zero(sfp_support); - sfp_parse_support(phydev->sfp_bus, id, sfp_support, interfaces); - /* Some modules support 10G modes as well as others we support. - * Mask out non-supported modes so the correct interface is picked. - */ - linkmode_and(sfp_support, phy_support, sfp_support); - - if (linkmode_empty(sfp_support)) { - dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n"); - return -EINVAL; - } - - iface = sfp_select_interface(phydev->sfp_bus, sfp_support); - - /* Only 1000Base-X is supported by AR8031/8033 as the downstream SerDes - * interface for use with SFP modules. - * However, some copper modules detected as having a preferred SGMII - * interface do default to and function in 1000Base-X mode, so just - * print a warning and allow such modules, as they may have some chance - * of working. - */ - if (iface == PHY_INTERFACE_MODE_SGMII) - dev_warn(&phydev->mdio.dev, "module may not function if 1000Base-X not supported\n"); - else if (iface != PHY_INTERFACE_MODE_1000BASEX) - return -EINVAL; - - return 0; -} - -static const struct sfp_upstream_ops at803x_sfp_ops = { - .attach = phy_sfp_attach, - .detach = phy_sfp_detach, - .module_insert = at803x_sfp_insert, -}; - static int at803x_parse_dt(struct phy_device *phydev) { struct device_node *node = phydev->mdio.dev.of_node; @@ -787,23 +672,6 @@ static int at803x_parse_dt(struct phy_device *phydev) priv->clk_25m_reg |= FIELD_PREP(AT803X_CLK_OUT_MASK, sel); priv->clk_25m_mask |= AT803X_CLK_OUT_MASK; - - /* Fixup for the AR8030/AR8035. This chip has another mask and - * doesn't support the DSP reference. Eg. the lowest bit of the - * mask. The upper two bits select the same frequencies. Mask - * the lowest bit here. - * - * Warning: - * There was no datasheet for the AR8030 available so this is - * just a guess. But the AR8035 is listed as pin compatible - * to the AR8030 so there might be a good chance it works on - * the AR8030 too. - */ - if (phydev->drv->phy_id == ATH8030_PHY_ID || - phydev->drv->phy_id == ATH8035_PHY_ID) { - priv->clk_25m_reg &= AT8035_CLK_OUT_MASK; - priv->clk_25m_mask &= AT8035_CLK_OUT_MASK; - } } ret = of_property_read_u32(node, "qca,clk-out-strength", &strength); @@ -825,30 +693,6 @@ static int at803x_parse_dt(struct phy_device *phydev) } } - /* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping - * options. - */ - if (phydev->drv->phy_id == ATH8031_PHY_ID) { - if (of_property_read_bool(node, "qca,keep-pll-enabled")) - priv->flags |= AT803X_KEEP_PLL_ENABLED; - - ret = at8031_register_regulators(phydev); - if (ret < 0) - return ret; - - ret = devm_regulator_get_enable_optional(&phydev->mdio.dev, - "vddio"); - if (ret) { - phydev_err(phydev, "failed to get VDDIO regulator\n"); - return ret; - } - - /* Only AR8031/8033 support 1000Base-X for SFP modules */ - ret = phy_sfp_probe(phydev, &at803x_sfp_ops); - if (ret < 0) - return ret; - } - return 0; } @@ -868,35 +712,6 @@ static int at803x_probe(struct phy_device *phydev) if (ret) return ret; - if (phydev->drv->phy_id == ATH8031_PHY_ID) { - int ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG); - int mode_cfg; - - if (ccr < 0) - return ccr; - mode_cfg = ccr & AT803X_MODE_CFG_MASK; - - switch (mode_cfg) { - case AT803X_MODE_CFG_BX1000_RGMII_50OHM: - case AT803X_MODE_CFG_BX1000_RGMII_75OHM: - priv->is_1000basex = true; - fallthrough; - case AT803X_MODE_CFG_FX100_RGMII_50OHM: - case AT803X_MODE_CFG_FX100_RGMII_75OHM: - priv->is_fiber = true; - break; - } - - /* Disable WoL in 1588 register which is enabled - * by default - */ - ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, - AT803X_PHY_MMD3_WOL_CTRL, - AT803X_WOL_EN, 0); - if (ret) - return ret; - } - return 0; } @@ -1004,27 +819,8 @@ static int at803x_hibernation_mode_config(struct phy_device *phydev) static int at803x_config_init(struct phy_device *phydev) { - struct at803x_priv *priv = phydev->priv; int ret; - if (phydev->drv->phy_id == ATH8031_PHY_ID) { - /* Some bootloaders leave the fiber page selected. - * Switch to the appropriate page (fiber or copper), as otherwise we - * read the PHY capabilities from the wrong page. - */ - phy_lock_mdio_bus(phydev); - ret = at803x_write_page(phydev, - priv->is_fiber ? AT803X_PAGE_FIBER : - AT803X_PAGE_COPPER); - phy_unlock_mdio_bus(phydev); - if (ret) - return ret; - - ret = at8031_pll_config(phydev); - if (ret < 0) - return ret; - } - /* The RX and TX delay default is: * after HW reset: RX delay enabled and TX delay disabled * after SW reset: RX delay enabled, while TX delay retains the @@ -1078,7 +874,6 @@ static int at803x_ack_interrupt(struct phy_device *phydev) static int at803x_config_intr(struct phy_device *phydev) { - struct at803x_priv *priv = phydev->priv; int err; int value; @@ -1095,10 +890,6 @@ static int at803x_config_intr(struct phy_device *phydev) value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED; value |= AT803X_INTR_ENABLE_LINK_FAIL; value |= AT803X_INTR_ENABLE_LINK_SUCCESS; - if (priv->is_fiber) { - value |= AT803X_INTR_ENABLE_LINK_FAIL_BX; - value |= AT803X_INTR_ENABLE_LINK_SUCCESS_BX; - } err = phy_write(phydev, AT803X_INTR_ENABLE, value); } else { @@ -1154,9 +945,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); @@ -1164,7 +955,8 @@ static void at803x_link_change_notify(struct phy_device *phydev) } } -static int at803x_read_specific_status(struct phy_device *phydev) +static int at803x_read_specific_status(struct phy_device *phydev, + struct at803x_ss_mask ss_mask) { int ss; @@ -1183,11 +975,8 @@ static int at803x_read_specific_status(struct phy_device *phydev) if (sfc < 0) return sfc; - /* qca8081 takes the different bits for speed value from at803x */ - if (phydev->drv->phy_id == QCA8081_PHY_ID) - speed = FIELD_GET(QCA808X_SS_SPEED_MASK, ss); - else - speed = FIELD_GET(AT803X_SS_SPEED_MASK, ss); + speed = ss & ss_mask.speed_mask; + speed >>= ss_mask.speed_shift; switch (speed) { case AT803X_SS_SPEED_10: @@ -1231,12 +1020,9 @@ static int at803x_read_specific_status(struct phy_device *phydev) static int at803x_read_status(struct phy_device *phydev) { - struct at803x_priv *priv = phydev->priv; + struct at803x_ss_mask ss_mask = { 0 }; int err, old_link = phydev->link; - if (priv->is_1000basex) - return genphy_c37_read_status(phydev); - /* Update the link, but return if there was an error */ err = genphy_update_link(phydev); if (err) @@ -1255,7 +1041,9 @@ static int at803x_read_status(struct phy_device *phydev) if (err < 0) return err; - err = at803x_read_specific_status(phydev); + ss_mask.speed_mask = AT803X_SS_SPEED_MASK; + ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK); + err = at803x_read_specific_status(phydev, ss_mask); if (err < 0) return err; @@ -1288,9 +1076,8 @@ static int at803x_config_mdix(struct phy_device *phydev, u8 ctrl) FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val)); } -static int at803x_config_aneg(struct phy_device *phydev) +static int at803x_prepare_config_aneg(struct phy_device *phydev) { - struct at803x_priv *priv = phydev->priv; int ret; ret = at803x_config_mdix(phydev, phydev->mdix_ctrl); @@ -1307,33 +1094,22 @@ static int at803x_config_aneg(struct phy_device *phydev) return ret; } - if (priv->is_1000basex) - return genphy_c37_config_aneg(phydev); - - /* Do not restart auto-negotiation by setting ret to 0 defautly, - * when calling __genphy_config_aneg later. - */ - ret = 0; - - if (phydev->drv->phy_id == QCA8081_PHY_ID) { - int phy_ctrl = 0; + return 0; +} - /* The reg MII_BMCR also needs to be configured for force mode, the - * genphy_config_aneg is also needed. - */ - if (phydev->autoneg == AUTONEG_DISABLE) - genphy_c45_pma_setup_forced(phydev); +static int at803x_config_aneg(struct phy_device *phydev) +{ + struct at803x_priv *priv = phydev->priv; + int ret; - if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising)) - phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G; + ret = at803x_prepare_config_aneg(phydev); + if (ret) + return ret; - ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, - MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl); - if (ret < 0) - return ret; - } + if (priv->is_1000basex) + return genphy_c37_config_aneg(phydev); - return __genphy_config_aneg(phydev, ret); + return genphy_config_aneg(phydev); } static int at803x_get_downshift(struct phy_device *phydev, u8 *d) @@ -1441,10 +1217,8 @@ static bool at803x_cdt_fault_length_valid(u16 status) return false; } -static int at803x_cdt_fault_length(u16 status) +static int at803x_cdt_fault_length(int dt) { - int dt; - /* According to the datasheet the distance to the fault is * DELTA_TIME * 0.824 meters. * @@ -1460,36 +1234,19 @@ static int at803x_cdt_fault_length(u16 status) * With a VF of 0.69 we get the factor 0.824 mentioned in the * datasheet. */ - dt = FIELD_GET(AT803X_CDT_STATUS_DELTA_TIME_MASK, status); - return (dt * 824) / 10; } -static int at803x_cdt_start(struct phy_device *phydev, int pair) +static int at803x_cdt_start(struct phy_device *phydev, + u32 cdt_start) { - u16 cdt; - - /* qca8081 takes the different bit 15 to enable CDT test */ - if (phydev->drv->phy_id == QCA8081_PHY_ID) - cdt = QCA808X_CDT_ENABLE_TEST | - QCA808X_CDT_LENGTH_UNIT | - QCA808X_CDT_INTER_CHECK_DIS; - else - cdt = FIELD_PREP(AT803X_CDT_MDI_PAIR_MASK, pair) | - AT803X_CDT_ENABLE_TEST; - - return phy_write(phydev, AT803X_CDT, cdt); + return phy_write(phydev, AT803X_CDT, cdt_start); } -static int at803x_cdt_wait_for_completion(struct phy_device *phydev) +static int at803x_cdt_wait_for_completion(struct phy_device *phydev, + u32 cdt_en) { int val, ret; - u16 cdt_en; - - if (phydev->drv->phy_id == QCA8081_PHY_ID) - cdt_en = QCA808X_CDT_ENABLE_TEST; - else - cdt_en = AT803X_CDT_ENABLE_TEST; /* One test run takes about 25ms */ ret = phy_read_poll_timeout(phydev, AT803X_CDT, val, @@ -1509,11 +1266,13 @@ static int at803x_cable_test_one_pair(struct phy_device *phydev, int pair) }; int ret, val; - ret = at803x_cdt_start(phydev, pair); + val = FIELD_PREP(AT803X_CDT_MDI_PAIR_MASK, pair) | + AT803X_CDT_ENABLE_TEST; + ret = at803x_cdt_start(phydev, val); if (ret) return ret; - ret = at803x_cdt_wait_for_completion(phydev); + ret = at803x_cdt_wait_for_completion(phydev, AT803X_CDT_ENABLE_TEST); if (ret) return ret; @@ -1527,27 +1286,21 @@ static int at803x_cable_test_one_pair(struct phy_device *phydev, int pair) ethnl_cable_test_result(phydev, ethtool_pair[pair], at803x_cable_test_result_trans(val)); - if (at803x_cdt_fault_length_valid(val)) + if (at803x_cdt_fault_length_valid(val)) { + val = FIELD_GET(AT803X_CDT_STATUS_DELTA_TIME_MASK, val); ethnl_cable_test_fault_length(phydev, ethtool_pair[pair], at803x_cdt_fault_length(val)); + } return 1; } static int at803x_cable_test_get_status(struct phy_device *phydev, - bool *finished) + bool *finished, unsigned long pair_mask) { - unsigned long pair_mask; int retries = 20; int pair, ret; - if (phydev->phy_id == ATH9331_PHY_ID || - phydev->phy_id == ATH8032_PHY_ID || - phydev->phy_id == QCA9561_PHY_ID) - pair_mask = 0x3; - else - pair_mask = 0xf; - *finished = false; /* According to the datasheet the CDT can be performed when @@ -1574,7 +1327,7 @@ static int at803x_cable_test_get_status(struct phy_device *phydev, return 0; } -static int at803x_cable_test_start(struct phy_device *phydev) +static void at803x_cable_test_autoneg(struct phy_device *phydev) { /* Enable auto-negotiation, but advertise no capabilities, no link * will be established. A restart of the auto-negotiation is not @@ -1582,15 +1335,360 @@ static int at803x_cable_test_start(struct phy_device *phydev) */ phy_write(phydev, MII_BMCR, BMCR_ANENABLE); phy_write(phydev, MII_ADVERTISE, ADVERTISE_CSMA); - if (phydev->phy_id != ATH9331_PHY_ID && - phydev->phy_id != ATH8032_PHY_ID && - phydev->phy_id != QCA9561_PHY_ID) - phy_write(phydev, MII_CTRL1000, 0); +} + +static int at803x_cable_test_start(struct phy_device *phydev) +{ + at803x_cable_test_autoneg(phydev); + /* we do all the (time consuming) work later */ + return 0; +} + +static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector) +{ + struct phy_device *phydev = rdev_get_drvdata(rdev); + + if (selector) + return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, + 0, AT803X_DEBUG_RGMII_1V8); + else + return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, + AT803X_DEBUG_RGMII_1V8, 0); +} + +static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev) +{ + struct phy_device *phydev = rdev_get_drvdata(rdev); + int val; + + val = at803x_debug_reg_read(phydev, AT803X_DEBUG_REG_1F); + if (val < 0) + return val; + + return (val & AT803X_DEBUG_RGMII_1V8) ? 1 : 0; +} + +static const struct regulator_ops vddio_regulator_ops = { + .list_voltage = regulator_list_voltage_table, + .set_voltage_sel = at8031_rgmii_reg_set_voltage_sel, + .get_voltage_sel = at8031_rgmii_reg_get_voltage_sel, +}; + +static const unsigned int vddio_voltage_table[] = { + 1500000, + 1800000, +}; + +static const struct regulator_desc vddio_desc = { + .name = "vddio", + .of_match = of_match_ptr("vddio-regulator"), + .n_voltages = ARRAY_SIZE(vddio_voltage_table), + .volt_table = vddio_voltage_table, + .ops = &vddio_regulator_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, +}; + +static const struct regulator_ops vddh_regulator_ops = { +}; + +static const struct regulator_desc vddh_desc = { + .name = "vddh", + .of_match = of_match_ptr("vddh-regulator"), + .n_voltages = 1, + .fixed_uV = 2500000, + .ops = &vddh_regulator_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, +}; + +static int at8031_register_regulators(struct phy_device *phydev) +{ + struct at803x_priv *priv = phydev->priv; + struct device *dev = &phydev->mdio.dev; + struct regulator_config config = { }; + + config.dev = dev; + config.driver_data = phydev; + + priv->vddio_rdev = devm_regulator_register(dev, &vddio_desc, &config); + if (IS_ERR(priv->vddio_rdev)) { + phydev_err(phydev, "failed to register VDDIO regulator\n"); + return PTR_ERR(priv->vddio_rdev); + } + + priv->vddh_rdev = devm_regulator_register(dev, &vddh_desc, &config); + if (IS_ERR(priv->vddh_rdev)) { + phydev_err(phydev, "failed to register VDDH regulator\n"); + return PTR_ERR(priv->vddh_rdev); + } + + return 0; +} + +static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) +{ + struct phy_device *phydev = upstream; + __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support); + __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support); + DECLARE_PHY_INTERFACE_MASK(interfaces); + phy_interface_t iface; + + linkmode_zero(phy_support); + phylink_set(phy_support, 1000baseX_Full); + phylink_set(phy_support, 1000baseT_Full); + phylink_set(phy_support, Autoneg); + phylink_set(phy_support, Pause); + phylink_set(phy_support, Asym_Pause); + + linkmode_zero(sfp_support); + sfp_parse_support(phydev->sfp_bus, id, sfp_support, interfaces); + /* Some modules support 10G modes as well as others we support. + * Mask out non-supported modes so the correct interface is picked. + */ + linkmode_and(sfp_support, phy_support, sfp_support); + + if (linkmode_empty(sfp_support)) { + dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n"); + return -EINVAL; + } + + iface = sfp_select_interface(phydev->sfp_bus, sfp_support); + + /* Only 1000Base-X is supported by AR8031/8033 as the downstream SerDes + * interface for use with SFP modules. + * However, some copper modules detected as having a preferred SGMII + * interface do default to and function in 1000Base-X mode, so just + * print a warning and allow such modules, as they may have some chance + * of working. + */ + if (iface == PHY_INTERFACE_MODE_SGMII) + dev_warn(&phydev->mdio.dev, "module may not function if 1000Base-X not supported\n"); + else if (iface != PHY_INTERFACE_MODE_1000BASEX) + return -EINVAL; + + return 0; +} + +static const struct sfp_upstream_ops at8031_sfp_ops = { + .attach = phy_sfp_attach, + .detach = phy_sfp_detach, + .module_insert = at8031_sfp_insert, +}; + +static int at8031_parse_dt(struct phy_device *phydev) +{ + struct device_node *node = phydev->mdio.dev.of_node; + struct at803x_priv *priv = phydev->priv; + int ret; + + if (of_property_read_bool(node, "qca,keep-pll-enabled")) + priv->flags |= AT803X_KEEP_PLL_ENABLED; + + ret = at8031_register_regulators(phydev); + if (ret < 0) + return ret; + + ret = devm_regulator_get_enable_optional(&phydev->mdio.dev, + "vddio"); + if (ret) { + phydev_err(phydev, "failed to get VDDIO regulator\n"); + return ret; + } + + /* Only AR8031/8033 support 1000Base-X for SFP modules */ + return phy_sfp_probe(phydev, &at8031_sfp_ops); +} + +static int at8031_probe(struct phy_device *phydev) +{ + struct at803x_priv *priv; + int mode_cfg; + int ccr; + int ret; + + ret = at803x_probe(phydev); + if (ret) + return ret; + + priv = phydev->priv; + + /* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping + * options. + */ + ret = at8031_parse_dt(phydev); + if (ret) + return ret; + + ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG); + if (ccr < 0) + return ccr; + mode_cfg = ccr & AT803X_MODE_CFG_MASK; + + switch (mode_cfg) { + case AT803X_MODE_CFG_BX1000_RGMII_50OHM: + case AT803X_MODE_CFG_BX1000_RGMII_75OHM: + priv->is_1000basex = true; + fallthrough; + case AT803X_MODE_CFG_FX100_RGMII_50OHM: + case AT803X_MODE_CFG_FX100_RGMII_75OHM: + priv->is_fiber = true; + break; + } + + /* Disable WoL in 1588 register which is enabled + * by default + */ + return phy_modify_mmd(phydev, MDIO_MMD_PCS, + AT803X_PHY_MMD3_WOL_CTRL, + AT803X_WOL_EN, 0); +} + +static int at8031_config_init(struct phy_device *phydev) +{ + struct at803x_priv *priv = phydev->priv; + int ret; + + /* Some bootloaders leave the fiber page selected. + * Switch to the appropriate page (fiber or copper), as otherwise we + * read the PHY capabilities from the wrong page. + */ + phy_lock_mdio_bus(phydev); + ret = at803x_write_page(phydev, + priv->is_fiber ? AT803X_PAGE_FIBER : + AT803X_PAGE_COPPER); + phy_unlock_mdio_bus(phydev); + if (ret) + return ret; + + ret = at8031_pll_config(phydev); + if (ret < 0) + return ret; + + return at803x_config_init(phydev); +} + +static int at8031_set_wol(struct phy_device *phydev, + struct ethtool_wolinfo *wol) +{ + int ret; + + /* First setup MAC address and enable WOL interrupt */ + ret = at803x_set_wol(phydev, wol); + if (ret) + return ret; + + if (wol->wolopts & WAKE_MAGIC) + /* Enable WOL function for 1588 */ + ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, + AT803X_PHY_MMD3_WOL_CTRL, + 0, AT803X_WOL_EN); + else + /* Disable WoL function for 1588 */ + ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, + AT803X_PHY_MMD3_WOL_CTRL, + AT803X_WOL_EN, 0); + + return ret; +} + +static int at8031_config_intr(struct phy_device *phydev) +{ + struct at803x_priv *priv = phydev->priv; + int err, value = 0; + + if (phydev->interrupts == PHY_INTERRUPT_ENABLED && + priv->is_fiber) { + /* Clear any pending interrupts */ + err = at803x_ack_interrupt(phydev); + if (err) + return err; + + value |= AT803X_INTR_ENABLE_LINK_FAIL_BX; + value |= AT803X_INTR_ENABLE_LINK_SUCCESS_BX; + + err = phy_set_bits(phydev, AT803X_INTR_ENABLE, value); + if (err) + return err; + } + + return at803x_config_intr(phydev); +} +/* AR8031 and AR8033 share the same read status logic */ +static int at8031_read_status(struct phy_device *phydev) +{ + struct at803x_priv *priv = phydev->priv; + + if (priv->is_1000basex) + return genphy_c37_read_status(phydev); + + return at803x_read_status(phydev); +} + +/* AR8031 and AR8035 share the same cable test get status reg */ +static int at8031_cable_test_get_status(struct phy_device *phydev, + bool *finished) +{ + return at803x_cable_test_get_status(phydev, finished, 0xf); +} + +/* AR8031 and AR8035 share the same cable test start logic */ +static int at8031_cable_test_start(struct phy_device *phydev) +{ + at803x_cable_test_autoneg(phydev); + phy_write(phydev, MII_CTRL1000, 0); /* we do all the (time consuming) work later */ return 0; } +/* AR8032, AR9331 and QCA9561 share the same cable test get status reg */ +static int at8032_cable_test_get_status(struct phy_device *phydev, + bool *finished) +{ + return at803x_cable_test_get_status(phydev, finished, 0x3); +} + +static int at8035_parse_dt(struct phy_device *phydev) +{ + struct at803x_priv *priv = phydev->priv; + + /* Mask is set by the generic at803x_parse_dt + * if property is set. Assume property is set + * with the mask not zero. + */ + if (priv->clk_25m_mask) { + /* Fixup for the AR8030/AR8035. This chip has another mask and + * doesn't support the DSP reference. Eg. the lowest bit of the + * mask. The upper two bits select the same frequencies. Mask + * the lowest bit here. + * + * Warning: + * There was no datasheet for the AR8030 available so this is + * just a guess. But the AR8035 is listed as pin compatible + * to the AR8030 so there might be a good chance it works on + * the AR8030 too. + */ + priv->clk_25m_reg &= AT8035_CLK_OUT_MASK; + priv->clk_25m_mask &= AT8035_CLK_OUT_MASK; + } + + return 0; +} + +/* AR8030 and AR8035 shared the same special mask for clk_25m */ +static int at8035_probe(struct phy_device *phydev) +{ + int ret; + + ret = at803x_probe(phydev); + if (ret) + return ret; + + return at8035_parse_dt(phydev); +} + static int qca83xx_config_init(struct phy_device *phydev) { u8 switch_revision; @@ -1616,27 +1714,26 @@ static int qca83xx_config_init(struct phy_device *phydev) break; } + /* Following original QCA sourcecode set port to prefer master */ + phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER); + + return 0; +} + +static int qca8327_config_init(struct phy_device *phydev) +{ /* QCA8327 require DAC amplitude adjustment for 100m set to +6%. * Disable on init and enable only with 100m speed following * qca original source code. */ - if (phydev->drv->phy_id == QCA8327_A_PHY_ID || - phydev->drv->phy_id == QCA8327_B_PHY_ID) - at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, - QCA8327_DEBUG_MANU_CTRL_EN, 0); + at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, + QCA8327_DEBUG_MANU_CTRL_EN, 0); - /* Following original QCA sourcecode set port to prefer master */ - phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER); - - return 0; + return qca83xx_config_init(phydev); } static void qca83xx_link_change_notify(struct phy_device *phydev) { - /* QCA8337 doesn't require DAC Amplitude adjustement */ - if (phydev->drv->phy_id == QCA8337_PHY_ID) - return; - /* Set DAC Amplitude adjustment to +6% for 100m on link running */ if (phydev->state == PHY_RUNNING) { if (phydev->speed == SPEED_100) @@ -1672,26 +1769,13 @@ static int qca83xx_resume(struct phy_device *phydev) if (ret) return ret; - msleep(1); + usleep_range(1000, 2000); return 0; } static int qca83xx_suspend(struct phy_device *phydev) { - u16 mask = 0; - - /* Only QCA8337 support actual suspend. - * QCA8327 cause port unreliability when phy suspend - * is set. - */ - if (phydev->drv->phy_id == QCA8337_PHY_ID) { - genphy_suspend(phydev); - } else { - mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX); - phy_modify(phydev, MII_BMCR, mask, 0); - } - at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_GREEN, AT803X_DEBUG_GATE_CLK_IN1000, 0); @@ -1702,6 +1786,27 @@ static int qca83xx_suspend(struct phy_device *phydev) return 0; } +static int qca8337_suspend(struct phy_device *phydev) +{ + /* Only QCA8337 support actual suspend. */ + genphy_suspend(phydev); + + return qca83xx_suspend(phydev); +} + +static int qca8327_suspend(struct phy_device *phydev) +{ + u16 mask = 0; + + /* QCA8327 cause port unreliability when phy suspend + * is set. + */ + mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX); + phy_modify(phydev, MII_BMCR, mask, 0); + + return qca83xx_suspend(phydev); +} + static int qca808x_phy_fast_retrain_config(struct phy_device *phydev) { int ret; @@ -1712,27 +1817,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; } @@ -1769,13 +1874,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; @@ -1801,11 +1907,13 @@ 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) { + struct at803x_ss_mask ss_mask = { 0 }; int ret; ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT); @@ -1813,13 +1921,16 @@ 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) return ret; - ret = at803x_read_specific_status(phydev); + /* qca8081 takes the different bits for speed value from at803x */ + ss_mask.speed_mask = QCA808X_SS_SPEED_MASK; + ss_mask.speed_shift = __bf_shf(QCA808X_SS_SPEED_MASK); + ret = at803x_read_specific_status(phydev, ss_mask); if (ret < 0) return ret; @@ -1840,7 +1951,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); @@ -1868,8 +1979,17 @@ static int qca808x_soft_reset(struct phy_device *phydev) static bool qca808x_cdt_fault_length_valid(int cdt_code) { switch (cdt_code) { - case QCA808X_CDT_STATUS_STAT_SHORT: - case QCA808X_CDT_STATUS_STAT_OPEN: + case QCA808X_CDT_STATUS_STAT_SAME_SHORT: + case QCA808X_CDT_STATUS_STAT_SAME_OPEN: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: return true; default: return false; @@ -1881,17 +2001,28 @@ static int qca808x_cable_test_result_trans(int cdt_code) switch (cdt_code) { case QCA808X_CDT_STATUS_STAT_NORMAL: return ETHTOOL_A_CABLE_RESULT_CODE_OK; - case QCA808X_CDT_STATUS_STAT_SHORT: + case QCA808X_CDT_STATUS_STAT_SAME_SHORT: return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; - case QCA808X_CDT_STATUS_STAT_OPEN: + case QCA808X_CDT_STATUS_STAT_SAME_OPEN: return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: + case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: + return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT; case QCA808X_CDT_STATUS_STAT_FAIL: default: return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; } } -static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair) +static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair, + int result) { int val; u32 cdt_length_reg = 0; @@ -1917,7 +2048,12 @@ static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair) if (val < 0) return val; - return (FIELD_GET(QCA808X_CDT_DIAG_LENGTH, val) * 824) / 10; + if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT) + val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val); + else + val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val); + + return at803x_cdt_fault_length(val); } static int qca808x_cable_test_start(struct phy_device *phydev) @@ -1961,18 +2097,53 @@ static int qca808x_cable_test_start(struct phy_device *phydev) return 0; } +static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair, + u16 status) +{ + int length, result; + u16 pair_code; + + switch (pair) { + case ETHTOOL_A_CABLE_PAIR_A: + pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status); + break; + case ETHTOOL_A_CABLE_PAIR_B: + pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status); + break; + case ETHTOOL_A_CABLE_PAIR_C: + pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status); + break; + case ETHTOOL_A_CABLE_PAIR_D: + pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status); + break; + default: + return -EINVAL; + } + + result = qca808x_cable_test_result_trans(pair_code); + ethnl_cable_test_result(phydev, pair, result); + + if (qca808x_cdt_fault_length_valid(pair_code)) { + length = qca808x_cdt_fault_length(phydev, pair, result); + ethnl_cable_test_fault_length(phydev, pair, length); + } + + return 0; +} + static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished) { int ret, val; - int pair_a, pair_b, pair_c, pair_d; *finished = false; - ret = at803x_cdt_start(phydev, 0); + val = QCA808X_CDT_ENABLE_TEST | + QCA808X_CDT_LENGTH_UNIT; + ret = at803x_cdt_start(phydev, val); if (ret) return ret; - ret = at803x_cdt_wait_for_completion(phydev); + ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST); if (ret) return ret; @@ -1980,32 +2151,21 @@ static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finish if (val < 0) return val; - pair_a = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, val); - pair_b = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, val); - pair_c = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, val); - pair_d = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, val); - - ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A, - qca808x_cable_test_result_trans(pair_a)); - ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_B, - qca808x_cable_test_result_trans(pair_b)); - ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_C, - qca808x_cable_test_result_trans(pair_c)); - 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)); + ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val); + if (ret) + return ret; + + ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val); + if (ret) + return ret; + + ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val); + if (ret) + return ret; + + ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val); + if (ret) + return ret; *finished = true; @@ -2040,14 +2200,41 @@ static int qca808x_get_features(struct phy_device *phydev) return 0; } +static int qca808x_config_aneg(struct phy_device *phydev) +{ + int phy_ctrl = 0; + int ret; + + ret = at803x_prepare_config_aneg(phydev); + if (ret) + return ret; + + /* The reg MII_BMCR also needs to be configured for force mode, the + * genphy_config_aneg is also needed. + */ + if (phydev->autoneg == AUTONEG_DISABLE) + genphy_c45_pma_setup_forced(phydev); + + if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising)) + phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G; + + ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, + MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl); + if (ret < 0) + return ret; + + return __genphy_config_aneg(phydev, ret); +} + static void qca808x_link_change_notify(struct phy_device *phydev) { /* Assert interface sgmii fifo on link down, deassert it on link up, * 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[] = { @@ -2056,7 +2243,7 @@ static struct phy_driver at803x_driver[] = { PHY_ID_MATCH_EXACT(ATH8035_PHY_ID), .name = "Qualcomm Atheros AR8035", .flags = PHY_POLL_CABLE_TEST, - .probe = at803x_probe, + .probe = at8035_probe, .config_aneg = at803x_config_aneg, .config_init = at803x_config_init, .soft_reset = genphy_soft_reset, @@ -2070,14 +2257,14 @@ static struct phy_driver at803x_driver[] = { .handle_interrupt = at803x_handle_interrupt, .get_tunable = at803x_get_tunable, .set_tunable = at803x_set_tunable, - .cable_test_start = at803x_cable_test_start, - .cable_test_get_status = at803x_cable_test_get_status, + .cable_test_start = at8031_cable_test_start, + .cable_test_get_status = at8031_cable_test_get_status, }, { /* Qualcomm Atheros AR8030 */ .phy_id = ATH8030_PHY_ID, .name = "Qualcomm Atheros AR8030", .phy_id_mask = AT8030_PHY_ID_MASK, - .probe = at803x_probe, + .probe = at8035_probe, .config_init = at803x_config_init, .link_change_notify = at803x_link_change_notify, .set_wol = at803x_set_wol, @@ -2092,24 +2279,24 @@ static struct phy_driver at803x_driver[] = { PHY_ID_MATCH_EXACT(ATH8031_PHY_ID), .name = "Qualcomm Atheros AR8031/AR8033", .flags = PHY_POLL_CABLE_TEST, - .probe = at803x_probe, - .config_init = at803x_config_init, + .probe = at8031_probe, + .config_init = at8031_config_init, .config_aneg = at803x_config_aneg, .soft_reset = genphy_soft_reset, - .set_wol = at803x_set_wol, + .set_wol = at8031_set_wol, .get_wol = at803x_get_wol, .suspend = at803x_suspend, .resume = at803x_resume, .read_page = at803x_read_page, .write_page = at803x_write_page, .get_features = at803x_get_features, - .read_status = at803x_read_status, - .config_intr = at803x_config_intr, + .read_status = at8031_read_status, + .config_intr = at8031_config_intr, .handle_interrupt = at803x_handle_interrupt, .get_tunable = at803x_get_tunable, .set_tunable = at803x_set_tunable, - .cable_test_start = at803x_cable_test_start, - .cable_test_get_status = at803x_cable_test_get_status, + .cable_test_start = at8031_cable_test_start, + .cable_test_get_status = at8031_cable_test_get_status, }, { /* Qualcomm Atheros AR8032 */ PHY_ID_MATCH_EXACT(ATH8032_PHY_ID), @@ -2124,7 +2311,7 @@ static struct phy_driver at803x_driver[] = { .config_intr = at803x_config_intr, .handle_interrupt = at803x_handle_interrupt, .cable_test_start = at803x_cable_test_start, - .cable_test_get_status = at803x_cable_test_get_status, + .cable_test_get_status = at8032_cable_test_get_status, }, { /* ATHEROS AR9331 */ PHY_ID_MATCH_EXACT(ATH9331_PHY_ID), @@ -2137,7 +2324,7 @@ static struct phy_driver at803x_driver[] = { .config_intr = at803x_config_intr, .handle_interrupt = at803x_handle_interrupt, .cable_test_start = at803x_cable_test_start, - .cable_test_get_status = at803x_cable_test_get_status, + .cable_test_get_status = at8032_cable_test_get_status, .read_status = at803x_read_status, .soft_reset = genphy_soft_reset, .config_aneg = at803x_config_aneg, @@ -2153,7 +2340,7 @@ static struct phy_driver at803x_driver[] = { .config_intr = at803x_config_intr, .handle_interrupt = at803x_handle_interrupt, .cable_test_start = at803x_cable_test_start, - .cable_test_get_status = at803x_cable_test_get_status, + .cable_test_get_status = at8032_cable_test_get_status, .read_status = at803x_read_status, .soft_reset = genphy_soft_reset, .config_aneg = at803x_config_aneg, @@ -2163,15 +2350,14 @@ static struct phy_driver at803x_driver[] = { .phy_id_mask = QCA8K_PHY_ID_MASK, .name = "Qualcomm Atheros 8337 internal PHY", /* PHY_GBIT_FEATURES */ - .link_change_notify = qca83xx_link_change_notify, .probe = at803x_probe, .flags = PHY_IS_INTERNAL, .config_init = qca83xx_config_init, .soft_reset = genphy_soft_reset, - .get_sset_count = at803x_get_sset_count, - .get_strings = at803x_get_strings, - .get_stats = at803x_get_stats, - .suspend = qca83xx_suspend, + .get_sset_count = qca83xx_get_sset_count, + .get_strings = qca83xx_get_strings, + .get_stats = qca83xx_get_stats, + .suspend = qca8337_suspend, .resume = qca83xx_resume, }, { /* QCA8327-A from switch QCA8327-AL1A */ @@ -2182,12 +2368,12 @@ static struct phy_driver at803x_driver[] = { .link_change_notify = qca83xx_link_change_notify, .probe = at803x_probe, .flags = PHY_IS_INTERNAL, - .config_init = qca83xx_config_init, + .config_init = qca8327_config_init, .soft_reset = genphy_soft_reset, - .get_sset_count = at803x_get_sset_count, - .get_strings = at803x_get_strings, - .get_stats = at803x_get_stats, - .suspend = qca83xx_suspend, + .get_sset_count = qca83xx_get_sset_count, + .get_strings = qca83xx_get_strings, + .get_stats = qca83xx_get_stats, + .suspend = qca8327_suspend, .resume = qca83xx_resume, }, { /* QCA8327-B from switch QCA8327-BL1A */ @@ -2198,12 +2384,12 @@ static struct phy_driver at803x_driver[] = { .link_change_notify = qca83xx_link_change_notify, .probe = at803x_probe, .flags = PHY_IS_INTERNAL, - .config_init = qca83xx_config_init, + .config_init = qca8327_config_init, .soft_reset = genphy_soft_reset, - .get_sset_count = at803x_get_sset_count, - .get_strings = at803x_get_strings, - .get_stats = at803x_get_stats, - .suspend = qca83xx_suspend, + .get_sset_count = qca83xx_get_sset_count, + .get_strings = qca83xx_get_strings, + .get_stats = qca83xx_get_stats, + .suspend = qca8327_suspend, .resume = qca83xx_resume, }, { /* Qualcomm QCA8081 */ @@ -2218,7 +2404,7 @@ static struct phy_driver at803x_driver[] = { .set_wol = at803x_set_wol, .get_wol = at803x_get_wol, .get_features = qca808x_get_features, - .config_aneg = at803x_config_aneg, + .config_aneg = qca808x_config_aneg, .suspend = genphy_suspend, .resume = genphy_resume, .read_status = qca808x_read_status, diff --git a/drivers/net/phy/ax88796b_rust.rs b/drivers/net/phy/ax88796b_rust.rs new file mode 100644 index 0000000000..5c92572962 --- /dev/null +++ b/drivers/net/phy/ax88796b_rust.rs @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2023 FUJITA Tomonori <fujita.tomonori@gmail.com> + +//! Rust Asix PHYs driver +//! +//! C version of this driver: [`drivers/net/phy/ax88796b.c`](./ax88796b.c) +use kernel::{ + c_str, + net::phy::{self, DeviceId, Driver}, + prelude::*, + uapi, +}; + +kernel::module_phy_driver! { + drivers: [PhyAX88772A, PhyAX88772C, PhyAX88796B], + device_table: [ + DeviceId::new_with_driver::<PhyAX88772A>(), + DeviceId::new_with_driver::<PhyAX88772C>(), + DeviceId::new_with_driver::<PhyAX88796B>() + ], + name: "rust_asix_phy", + author: "FUJITA Tomonori <fujita.tomonori@gmail.com>", + description: "Rust Asix PHYs driver", + license: "GPL", +} + +const MII_BMCR: u16 = uapi::MII_BMCR as u16; +const BMCR_SPEED100: u16 = uapi::BMCR_SPEED100 as u16; +const BMCR_FULLDPLX: u16 = uapi::BMCR_FULLDPLX as u16; + +// Performs a software PHY reset using the standard +// BMCR_RESET bit and poll for the reset bit to be cleared. +// Toggle BMCR_RESET bit off to accommodate broken AX8796B PHY implementation +// such as used on the Individual Computers' X-Surf 100 Zorro card. +fn asix_soft_reset(dev: &mut phy::Device) -> Result { + dev.write(uapi::MII_BMCR as u16, 0)?; + dev.genphy_soft_reset() +} + +struct PhyAX88772A; + +#[vtable] +impl Driver for PhyAX88772A { + const FLAGS: u32 = phy::flags::IS_INTERNAL; + const NAME: &'static CStr = c_str!("Asix Electronics AX88772A"); + const PHY_DEVICE_ID: DeviceId = DeviceId::new_with_exact_mask(0x003b1861); + + // AX88772A is not working properly with some old switches (NETGEAR EN 108TP): + // after autoneg is done and the link status is reported as active, the MII_LPA + // register is 0. This issue is not reproducible on AX88772C. + fn read_status(dev: &mut phy::Device) -> Result<u16> { + dev.genphy_update_link()?; + if !dev.is_link_up() { + return Ok(0); + } + // If MII_LPA is 0, phy_resolve_aneg_linkmode() will fail to resolve + // linkmode so use MII_BMCR as default values. + let ret = dev.read(MII_BMCR)?; + + if ret & BMCR_SPEED100 != 0 { + dev.set_speed(uapi::SPEED_100); + } else { + dev.set_speed(uapi::SPEED_10); + } + + let duplex = if ret & BMCR_FULLDPLX != 0 { + phy::DuplexMode::Full + } else { + phy::DuplexMode::Half + }; + dev.set_duplex(duplex); + + dev.genphy_read_lpa()?; + + if dev.is_autoneg_enabled() && dev.is_autoneg_completed() { + dev.resolve_aneg_linkmode(); + } + + Ok(0) + } + + fn suspend(dev: &mut phy::Device) -> Result { + dev.genphy_suspend() + } + + fn resume(dev: &mut phy::Device) -> Result { + dev.genphy_resume() + } + + fn soft_reset(dev: &mut phy::Device) -> Result { + asix_soft_reset(dev) + } + + fn link_change_notify(dev: &mut phy::Device) { + // Reset PHY, otherwise MII_LPA will provide outdated information. + // This issue is reproducible only with some link partner PHYs. + if dev.state() == phy::DeviceState::NoLink { + let _ = dev.init_hw(); + let _ = dev.start_aneg(); + } + } +} + +struct PhyAX88772C; + +#[vtable] +impl Driver for PhyAX88772C { + const FLAGS: u32 = phy::flags::IS_INTERNAL; + const NAME: &'static CStr = c_str!("Asix Electronics AX88772C"); + const PHY_DEVICE_ID: DeviceId = DeviceId::new_with_exact_mask(0x003b1881); + + fn suspend(dev: &mut phy::Device) -> Result { + dev.genphy_suspend() + } + + fn resume(dev: &mut phy::Device) -> Result { + dev.genphy_resume() + } + + fn soft_reset(dev: &mut phy::Device) -> Result { + asix_soft_reset(dev) + } +} + +struct PhyAX88796B; + +#[vtable] +impl Driver for PhyAX88796B { + const NAME: &'static CStr = c_str!("Asix Electronics AX88796B"); + const PHY_DEVICE_ID: DeviceId = DeviceId::new_with_model_mask(0x003b1841); + + fn soft_reset(dev: &mut phy::Device) -> Result { + asix_soft_reset(dev) + } +} diff --git a/drivers/net/phy/bcm-phy-ptp.c b/drivers/net/phy/bcm-phy-ptp.c index cb4b91af5e..617d384d45 100644 --- a/drivers/net/phy/bcm-phy-ptp.c +++ b/drivers/net/phy/bcm-phy-ptp.c @@ -782,16 +782,13 @@ out: } static int bcm_ptp_hwtstamp(struct mii_timestamper *mii_ts, - struct ifreq *ifr) + struct kernel_hwtstamp_config *cfg, + struct netlink_ext_ack *extack) { struct bcm_ptp_private *priv = mii2priv(mii_ts); - struct hwtstamp_config cfg; u16 mode, ctrl; - if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) - return -EFAULT; - - switch (cfg.rx_filter) { + switch (cfg->rx_filter) { case HWTSTAMP_FILTER_NONE: priv->hwts_rx = false; break; @@ -804,14 +801,14 @@ static int bcm_ptp_hwtstamp(struct mii_timestamper *mii_ts, case HWTSTAMP_FILTER_PTP_V2_EVENT: case HWTSTAMP_FILTER_PTP_V2_SYNC: case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: - cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; + cfg->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; priv->hwts_rx = true; break; default: return -ERANGE; } - priv->tx_type = cfg.tx_type; + priv->tx_type = cfg->tx_type; ctrl = priv->hwts_rx ? SLICE_RX_EN : 0; ctrl |= priv->tx_type != HWTSTAMP_TX_OFF ? SLICE_TX_EN : 0; @@ -840,7 +837,7 @@ static int bcm_ptp_hwtstamp(struct mii_timestamper *mii_ts, /* purge existing data */ skb_queue_purge(&priv->tx_queue); - return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; + return 0; } static int bcm_ptp_ts_info(struct mii_timestamper *mii_ts, diff --git a/drivers/net/phy/bcm54140.c b/drivers/net/phy/bcm54140.c index d43076592f..2eea3d09b1 100644 --- a/drivers/net/phy/bcm54140.c +++ b/drivers/net/phy/bcm54140.c @@ -128,6 +128,10 @@ #define BCM54140_DEFAULT_DOWNSHIFT 5 #define BCM54140_MAX_DOWNSHIFT 9 +enum bcm54140_global_phy { + BCM54140_BASE_ADDR = 0, +}; + struct bcm54140_priv { int port; int base_addr; @@ -429,11 +433,13 @@ static int bcm54140_base_read_rdb(struct phy_device *phydev, u16 rdb) int ret; phy_lock_mdio_bus(phydev); - ret = __phy_package_write(phydev, MII_BCM54XX_RDB_ADDR, rdb); + ret = __phy_package_write(phydev, BCM54140_BASE_ADDR, + MII_BCM54XX_RDB_ADDR, rdb); if (ret < 0) goto out; - ret = __phy_package_read(phydev, MII_BCM54XX_RDB_DATA); + ret = __phy_package_read(phydev, BCM54140_BASE_ADDR, + MII_BCM54XX_RDB_DATA); out: phy_unlock_mdio_bus(phydev); @@ -446,11 +452,13 @@ static int bcm54140_base_write_rdb(struct phy_device *phydev, int ret; phy_lock_mdio_bus(phydev); - ret = __phy_package_write(phydev, MII_BCM54XX_RDB_ADDR, rdb); + ret = __phy_package_write(phydev, BCM54140_BASE_ADDR, + MII_BCM54XX_RDB_ADDR, rdb); if (ret < 0) goto out; - ret = __phy_package_write(phydev, MII_BCM54XX_RDB_DATA, val); + ret = __phy_package_write(phydev, BCM54140_BASE_ADDR, + MII_BCM54XX_RDB_DATA, val); out: phy_unlock_mdio_bus(phydev); diff --git a/drivers/net/phy/bcm84881.c b/drivers/net/phy/bcm84881.c index 9717a1626f..f1d47c2640 100644 --- a/drivers/net/phy/bcm84881.c +++ b/drivers/net/phy/bcm84881.c @@ -29,8 +29,19 @@ static int bcm84881_wait_init(struct phy_device *phydev) 100000, 2000000, false); } +static void bcm84881_fill_possible_interfaces(struct phy_device *phydev) +{ + unsigned long *possible = phydev->possible_interfaces; + + __set_bit(PHY_INTERFACE_MODE_SGMII, possible); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, possible); + __set_bit(PHY_INTERFACE_MODE_10GBASER, possible); +} + static int bcm84881_config_init(struct phy_device *phydev) { + bcm84881_fill_possible_interfaces(phydev); + switch (phydev->interface) { case PHY_INTERFACE_MODE_SGMII: case PHY_INTERFACE_MODE_2500BASEX: @@ -39,6 +50,7 @@ static int bcm84881_config_init(struct phy_device *phydev) default: return -ENODEV; } + return 0; } diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c index 3a62710567..312a8bb35d 100644 --- a/drivers/net/phy/broadcom.c +++ b/drivers/net/phy/broadcom.c @@ -1135,6 +1135,8 @@ static struct phy_driver broadcom_drivers[] = { .handle_interrupt = bcm_phy_handle_interrupt, .link_change_notify = bcm54xx_link_change_notify, .led_brightness_set = bcm_phy_led_brightness_set, + .suspend = bcm54xx_suspend, + .resume = bcm54xx_resume, }, { .phy_id = PHY_ID_BCM54616S, .phy_id_mask = 0xfffffff0, diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c index 2657be7cc0..5c42c47dc5 100644 --- a/drivers/net/phy/dp83640.c +++ b/drivers/net/phy/dp83640.c @@ -1207,22 +1207,20 @@ static irqreturn_t dp83640_handle_interrupt(struct phy_device *phydev) return IRQ_HANDLED; } -static int dp83640_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr) +static int dp83640_hwtstamp(struct mii_timestamper *mii_ts, + struct kernel_hwtstamp_config *cfg, + struct netlink_ext_ack *extack) { struct dp83640_private *dp83640 = container_of(mii_ts, struct dp83640_private, mii_ts); - struct hwtstamp_config cfg; u16 txcfg0, rxcfg0; - if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) - return -EFAULT; - - if (cfg.tx_type < 0 || cfg.tx_type > HWTSTAMP_TX_ONESTEP_SYNC) + if (cfg->tx_type < 0 || cfg->tx_type > HWTSTAMP_TX_ONESTEP_SYNC) return -ERANGE; - dp83640->hwts_tx_en = cfg.tx_type; + dp83640->hwts_tx_en = cfg->tx_type; - switch (cfg.rx_filter) { + switch (cfg->rx_filter) { case HWTSTAMP_FILTER_NONE: dp83640->hwts_rx_en = 0; dp83640->layer = 0; @@ -1234,7 +1232,7 @@ static int dp83640_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr) dp83640->hwts_rx_en = 1; dp83640->layer = PTP_CLASS_L4; dp83640->version = PTP_CLASS_V1; - cfg.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT; + cfg->rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT; break; case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: @@ -1242,7 +1240,7 @@ static int dp83640_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr) dp83640->hwts_rx_en = 1; dp83640->layer = PTP_CLASS_L4; dp83640->version = PTP_CLASS_V2; - cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT; + cfg->rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT; break; case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: @@ -1250,7 +1248,7 @@ static int dp83640_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr) dp83640->hwts_rx_en = 1; dp83640->layer = PTP_CLASS_L2; dp83640->version = PTP_CLASS_V2; - cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT; + cfg->rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT; break; case HWTSTAMP_FILTER_PTP_V2_EVENT: case HWTSTAMP_FILTER_PTP_V2_SYNC: @@ -1258,7 +1256,7 @@ static int dp83640_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr) dp83640->hwts_rx_en = 1; dp83640->layer = PTP_CLASS_L4 | PTP_CLASS_L2; dp83640->version = PTP_CLASS_V2; - cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; + cfg->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; break; default: return -ERANGE; @@ -1292,7 +1290,7 @@ static int dp83640_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr) mutex_unlock(&dp83640->clock->extreg_lock); - return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; + return 0; } static void rx_timestamp_work(struct work_struct *work) diff --git a/drivers/net/phy/dp83869.c b/drivers/net/phy/dp83869.c index fa8c6fdcf3..d7aaefb522 100644 --- a/drivers/net/phy/dp83869.c +++ b/drivers/net/phy/dp83869.c @@ -695,7 +695,8 @@ static int dp83869_configure_mode(struct phy_device *phydev, phy_ctrl_val = dp83869->mode; if (phydev->interface == PHY_INTERFACE_MODE_MII) { if (dp83869->mode == DP83869_100M_MEDIA_CONVERT || - dp83869->mode == DP83869_RGMII_100_BASE) { + dp83869->mode == DP83869_RGMII_100_BASE || + dp83869->mode == DP83869_RGMII_COPPER_ETHERNET) { phy_ctrl_val |= DP83869_OP_MODE_MII; } else { phydev_err(phydev, "selected op-mode is not valid with MII mode\n"); diff --git a/drivers/net/phy/dp83tg720.c b/drivers/net/phy/dp83tg720.c new file mode 100644 index 0000000000..326c9770a6 --- /dev/null +++ b/drivers/net/phy/dp83tg720.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Driver for the Texas Instruments DP83TG720 PHY + * Copyright (c) 2023 Pengutronix, Oleksij Rempel <kernel@pengutronix.de> + */ +#include <linux/bitfield.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/phy.h> + +#define DP83TG720S_PHY_ID 0x2000a284 + +/* MDIO_MMD_VEND2 registers */ +#define DP83TG720S_MII_REG_10 0x10 +#define DP83TG720S_STS_MII_INT BIT(7) +#define DP83TG720S_LINK_STATUS BIT(0) + +#define DP83TG720S_PHY_RESET 0x1f +#define DP83TG720S_HW_RESET BIT(15) + +#define DP83TG720S_RGMII_DELAY_CTRL 0x602 +/* In RGMII mode, Enable or disable the internal delay for RXD */ +#define DP83TG720S_RGMII_RX_CLK_SEL BIT(1) +/* In RGMII mode, Enable or disable the internal delay for TXD */ +#define DP83TG720S_RGMII_TX_CLK_SEL BIT(0) + +#define DP83TG720S_SQI_REG_1 0x871 +#define DP83TG720S_SQI_OUT_WORST GENMASK(7, 5) +#define DP83TG720S_SQI_OUT GENMASK(3, 1) + +#define DP83TG720_SQI_MAX 7 + +static int dp83tg720_config_aneg(struct phy_device *phydev) +{ + /* Autoneg is not supported and this PHY supports only one speed. + * We need to care only about master/slave configuration if it was + * changed by user. + */ + return genphy_c45_pma_baset1_setup_master_slave(phydev); +} + +static int dp83tg720_read_status(struct phy_device *phydev) +{ + u16 phy_sts; + int ret; + + phydev->pause = 0; + phydev->asym_pause = 0; + + /* Most of Clause 45 registers are not present, so we can't use + * genphy_c45_read_status() here. + */ + phy_sts = phy_read(phydev, DP83TG720S_MII_REG_10); + phydev->link = !!(phy_sts & DP83TG720S_LINK_STATUS); + if (!phydev->link) { + /* According to the "DP83TC81x, DP83TG72x Software + * Implementation Guide", the PHY needs to be reset after a + * link loss or if no link is created after at least 100ms. + * + * Currently we are polling with the PHY_STATE_TIME (1000ms) + * interval, which is still enough for not automotive use cases. + */ + ret = phy_init_hw(phydev); + if (ret) + return ret; + + /* After HW reset we need to restore master/slave configuration. + */ + ret = dp83tg720_config_aneg(phydev); + if (ret) + return ret; + + phydev->speed = SPEED_UNKNOWN; + phydev->duplex = DUPLEX_UNKNOWN; + } else { + /* PMA/PMD control 1 register (Register 1.0) is present, but it + * doesn't contain the link speed information. + * So genphy_c45_read_pma() can't be used here. + */ + ret = genphy_c45_pma_baset1_read_master_slave(phydev); + if (ret) + return ret; + + phydev->duplex = DUPLEX_FULL; + phydev->speed = SPEED_1000; + } + + return 0; +} + +static int dp83tg720_get_sqi(struct phy_device *phydev) +{ + int ret; + + if (!phydev->link) + return 0; + + ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TG720S_SQI_REG_1); + if (ret < 0) + return ret; + + return FIELD_GET(DP83TG720S_SQI_OUT, ret); +} + +static int dp83tg720_get_sqi_max(struct phy_device *phydev) +{ + return DP83TG720_SQI_MAX; +} + +static int dp83tg720_config_rgmii_delay(struct phy_device *phydev) +{ + u16 rgmii_delay_mask; + u16 rgmii_delay = 0; + + switch (phydev->interface) { + case PHY_INTERFACE_MODE_RGMII: + rgmii_delay = 0; + break; + case PHY_INTERFACE_MODE_RGMII_ID: + rgmii_delay = DP83TG720S_RGMII_RX_CLK_SEL | + DP83TG720S_RGMII_TX_CLK_SEL; + break; + case PHY_INTERFACE_MODE_RGMII_RXID: + rgmii_delay = DP83TG720S_RGMII_RX_CLK_SEL; + break; + case PHY_INTERFACE_MODE_RGMII_TXID: + rgmii_delay = DP83TG720S_RGMII_TX_CLK_SEL; + break; + default: + return 0; + } + + rgmii_delay_mask = DP83TG720S_RGMII_RX_CLK_SEL | + DP83TG720S_RGMII_TX_CLK_SEL; + + return phy_modify_mmd(phydev, MDIO_MMD_VEND2, + DP83TG720S_RGMII_DELAY_CTRL, rgmii_delay_mask, + rgmii_delay); +} + +static int dp83tg720_config_init(struct phy_device *phydev) +{ + int ret; + + /* Software Restart is not enough to recover from a link failure. + * Using Hardware Reset instead. + */ + ret = phy_write(phydev, DP83TG720S_PHY_RESET, DP83TG720S_HW_RESET); + if (ret) + return ret; + + /* Wait until MDC can be used again. + * The wait value of one 1ms is documented in "DP83TG720S-Q1 1000BASE-T1 + * Automotive Ethernet PHY with SGMII and RGMII" datasheet. + */ + usleep_range(1000, 2000); + + if (phy_interface_is_rgmii(phydev)) + return dp83tg720_config_rgmii_delay(phydev); + + return 0; +} + +static struct phy_driver dp83tg720_driver[] = { +{ + PHY_ID_MATCH_MODEL(DP83TG720S_PHY_ID), + .name = "TI DP83TG720S", + + .config_aneg = dp83tg720_config_aneg, + .read_status = dp83tg720_read_status, + .get_features = genphy_c45_pma_read_ext_abilities, + .config_init = dp83tg720_config_init, + .get_sqi = dp83tg720_get_sqi, + .get_sqi_max = dp83tg720_get_sqi_max, + + .suspend = genphy_suspend, + .resume = genphy_resume, +} }; +module_phy_driver(dp83tg720_driver); + +static struct mdio_device_id __maybe_unused dp83tg720_tbl[] = { + { PHY_ID_MATCH_MODEL(DP83TG720S_PHY_ID) }, + { } +}; +MODULE_DEVICE_TABLE(mdio, dp83tg720_tbl); + +MODULE_DESCRIPTION("Texas Instruments DP83TG720S PHY driver"); +MODULE_AUTHOR("Oleksij Rempel <kernel@pengutronix.de>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c index d4bb90d768..ad43e28093 100644 --- a/drivers/net/phy/marvell10g.c +++ b/drivers/net/phy/marvell10g.c @@ -141,13 +141,21 @@ enum { MV_V2_TEMP_UNKNOWN = 0x9600, /* unknown function */ }; +struct mv3310_mactype { + bool valid; + bool fixed_interface; + phy_interface_t interface_10g; +}; + struct mv3310_chip { bool (*has_downshift)(struct phy_device *phydev); void (*init_supported_interfaces)(unsigned long *mask); int (*get_mactype)(struct phy_device *phydev); int (*set_mactype)(struct phy_device *phydev, int mactype); int (*select_mactype)(unsigned long *interfaces); - int (*init_interface)(struct phy_device *phydev, int mactype); + + const struct mv3310_mactype *mactypes; + size_t n_mactypes; #ifdef CONFIG_HWMON int (*hwmon_read_temp_reg)(struct phy_device *phydev); @@ -156,11 +164,10 @@ struct mv3310_chip { struct mv3310_priv { DECLARE_BITMAP(supported_interfaces, PHY_INTERFACE_MODE_MAX); + const struct mv3310_mactype *mactype; u32 firmware_ver; bool has_downshift; - bool rate_match; - phy_interface_t const_interface; struct device *hwmon_dev; char *hwmon_name; @@ -702,70 +709,114 @@ static int mv3310_select_mactype(unsigned long *interfaces) return -1; } -static int mv2110_init_interface(struct phy_device *phydev, int mactype) -{ - struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); - - priv->rate_match = false; - - if (mactype == MV_PMA_21X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH) - priv->rate_match = true; - - if (mactype == MV_PMA_21X0_PORT_CTRL_MACTYPE_USXGMII) - priv->const_interface = PHY_INTERFACE_MODE_USXGMII; - else if (mactype == MV_PMA_21X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH) - priv->const_interface = PHY_INTERFACE_MODE_10GBASER; - else if (mactype == MV_PMA_21X0_PORT_CTRL_MACTYPE_5GBASER || - mactype == MV_PMA_21X0_PORT_CTRL_MACTYPE_5GBASER_NO_SGMII_AN) - priv->const_interface = PHY_INTERFACE_MODE_NA; - else - return -EINVAL; - - return 0; -} - -static int mv3310_init_interface(struct phy_device *phydev, int mactype) -{ - struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); +static const struct mv3310_mactype mv2110_mactypes[] = { + [MV_PMA_21X0_PORT_CTRL_MACTYPE_USXGMII] = { + .valid = true, + .fixed_interface = true, + .interface_10g = PHY_INTERFACE_MODE_USXGMII, + }, + [MV_PMA_21X0_PORT_CTRL_MACTYPE_5GBASER] = { + .valid = true, + .interface_10g = PHY_INTERFACE_MODE_NA, + }, + [MV_PMA_21X0_PORT_CTRL_MACTYPE_5GBASER_NO_SGMII_AN] = { + .valid = true, + .interface_10g = PHY_INTERFACE_MODE_NA, + }, + [MV_PMA_21X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH] = { + .valid = true, + .fixed_interface = true, + .interface_10g = PHY_INTERFACE_MODE_10GBASER, + }, +}; - priv->rate_match = false; - - if (mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH || - mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI_RATE_MATCH || - mactype == MV_V2_3310_PORT_CTRL_MACTYPE_XAUI_RATE_MATCH) - priv->rate_match = true; - - if (mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_USXGMII) - priv->const_interface = PHY_INTERFACE_MODE_USXGMII; - else if (mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH || - mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_NO_SGMII_AN || - mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER) - priv->const_interface = PHY_INTERFACE_MODE_10GBASER; - else if (mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI_RATE_MATCH || - mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI) - priv->const_interface = PHY_INTERFACE_MODE_RXAUI; - else if (mactype == MV_V2_3310_PORT_CTRL_MACTYPE_XAUI_RATE_MATCH || - mactype == MV_V2_3310_PORT_CTRL_MACTYPE_XAUI) - priv->const_interface = PHY_INTERFACE_MODE_XAUI; - else - return -EINVAL; +static const struct mv3310_mactype mv3310_mactypes[] = { + [MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI] = { + .valid = true, + .interface_10g = PHY_INTERFACE_MODE_RXAUI, + }, + [MV_V2_3310_PORT_CTRL_MACTYPE_XAUI_RATE_MATCH] = { + .valid = true, + .fixed_interface = true, + .interface_10g = PHY_INTERFACE_MODE_XAUI, + }, + [MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI_RATE_MATCH] = { + .valid = true, + .fixed_interface = true, + .interface_10g = PHY_INTERFACE_MODE_RXAUI, + }, + [MV_V2_3310_PORT_CTRL_MACTYPE_XAUI] = { + .valid = true, + .interface_10g = PHY_INTERFACE_MODE_XAUI, + }, + [MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER] = { + .valid = true, + .interface_10g = PHY_INTERFACE_MODE_10GBASER, + }, + [MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_NO_SGMII_AN] = { + .valid = true, + .interface_10g = PHY_INTERFACE_MODE_10GBASER, + }, + [MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH] = { + .valid = true, + .fixed_interface = true, + .interface_10g = PHY_INTERFACE_MODE_10GBASER, + }, + [MV_V2_33X0_PORT_CTRL_MACTYPE_USXGMII] = { + .valid = true, + .fixed_interface = true, + .interface_10g = PHY_INTERFACE_MODE_USXGMII, + }, +}; - return 0; -} +static const struct mv3310_mactype mv3340_mactypes[] = { + [MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI] = { + .valid = true, + .interface_10g = PHY_INTERFACE_MODE_RXAUI, + }, + [MV_V2_3340_PORT_CTRL_MACTYPE_RXAUI_NO_SGMII_AN] = { + .valid = true, + .interface_10g = PHY_INTERFACE_MODE_RXAUI, + }, + [MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI_RATE_MATCH] = { + .valid = true, + .fixed_interface = true, + .interface_10g = PHY_INTERFACE_MODE_RXAUI, + }, + [MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER] = { + .valid = true, + .interface_10g = PHY_INTERFACE_MODE_10GBASER, + }, + [MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_NO_SGMII_AN] = { + .valid = true, + .interface_10g = PHY_INTERFACE_MODE_10GBASER, + }, + [MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH] = { + .valid = true, + .fixed_interface = true, + .interface_10g = PHY_INTERFACE_MODE_10GBASER, + }, + [MV_V2_33X0_PORT_CTRL_MACTYPE_USXGMII] = { + .valid = true, + .fixed_interface = true, + .interface_10g = PHY_INTERFACE_MODE_USXGMII, + }, +}; -static int mv3340_init_interface(struct phy_device *phydev, int mactype) +static void mv3310_fill_possible_interfaces(struct phy_device *phydev) { struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); - int err = 0; - - priv->rate_match = false; + unsigned long *possible = phydev->possible_interfaces; + const struct mv3310_mactype *mactype = priv->mactype; - if (mactype == MV_V2_3340_PORT_CTRL_MACTYPE_RXAUI_NO_SGMII_AN) - priv->const_interface = PHY_INTERFACE_MODE_RXAUI; - else - err = mv3310_init_interface(phydev, mactype); + if (mactype->interface_10g != PHY_INTERFACE_MODE_NA) + __set_bit(priv->mactype->interface_10g, possible); - return err; + if (!mactype->fixed_interface) { + __set_bit(PHY_INTERFACE_MODE_5GBASER, possible); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, possible); + __set_bit(PHY_INTERFACE_MODE_SGMII, possible); + } } static int mv3310_config_init(struct phy_device *phydev) @@ -803,12 +854,15 @@ static int mv3310_config_init(struct phy_device *phydev) if (mactype < 0) return mactype; - err = chip->init_interface(phydev, mactype); - if (err) { + if (mactype >= chip->n_mactypes || !chip->mactypes[mactype].valid) { phydev_err(phydev, "MACTYPE configuration invalid\n"); - return err; + return -EINVAL; } + priv->mactype = &chip->mactypes[mactype]; + + mv3310_fill_possible_interfaces(phydev); + /* Enable EDPD mode - saving 600mW */ err = mv3310_set_edpd(phydev, ETHTOOL_PHY_EDPD_DFLT_TX_MSECS); if (err) @@ -935,9 +989,8 @@ static void mv3310_update_interface(struct phy_device *phydev) * * In USXGMII mode the PHY interface mode is also fixed. */ - if (priv->rate_match || - priv->const_interface == PHY_INTERFACE_MODE_USXGMII) { - phydev->interface = priv->const_interface; + if (priv->mactype->fixed_interface) { + phydev->interface = priv->mactype->interface_10g; return; } @@ -949,7 +1002,7 @@ static void mv3310_update_interface(struct phy_device *phydev) */ switch (phydev->speed) { case SPEED_10000: - phydev->interface = priv->const_interface; + phydev->interface = priv->mactype->interface_10g; break; case SPEED_5000: phydev->interface = PHY_INTERFACE_MODE_5GBASER; @@ -1163,7 +1216,9 @@ static const struct mv3310_chip mv3310_type = { .get_mactype = mv3310_get_mactype, .set_mactype = mv3310_set_mactype, .select_mactype = mv3310_select_mactype, - .init_interface = mv3310_init_interface, + + .mactypes = mv3310_mactypes, + .n_mactypes = ARRAY_SIZE(mv3310_mactypes), #ifdef CONFIG_HWMON .hwmon_read_temp_reg = mv3310_hwmon_read_temp_reg, @@ -1176,7 +1231,9 @@ static const struct mv3310_chip mv3340_type = { .get_mactype = mv3310_get_mactype, .set_mactype = mv3310_set_mactype, .select_mactype = mv3310_select_mactype, - .init_interface = mv3340_init_interface, + + .mactypes = mv3340_mactypes, + .n_mactypes = ARRAY_SIZE(mv3340_mactypes), #ifdef CONFIG_HWMON .hwmon_read_temp_reg = mv3310_hwmon_read_temp_reg, @@ -1188,7 +1245,9 @@ static const struct mv3310_chip mv2110_type = { .get_mactype = mv2110_get_mactype, .set_mactype = mv2110_set_mactype, .select_mactype = mv2110_select_mactype, - .init_interface = mv2110_init_interface, + + .mactypes = mv2110_mactypes, + .n_mactypes = ARRAY_SIZE(mv2110_mactypes), #ifdef CONFIG_HWMON .hwmon_read_temp_reg = mv2110_hwmon_read_temp_reg, @@ -1200,7 +1259,9 @@ static const struct mv3310_chip mv2111_type = { .get_mactype = mv2110_get_mactype, .set_mactype = mv2110_set_mactype, .select_mactype = mv2110_select_mactype, - .init_interface = mv2110_init_interface, + + .mactypes = mv2110_mactypes, + .n_mactypes = ARRAY_SIZE(mv2110_mactypes), #ifdef CONFIG_HWMON .hwmon_read_temp_reg = mv2110_hwmon_read_temp_reg, diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 25dcaa49ab..afbad1ad86 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -193,6 +193,10 @@ static void mdiobus_release(struct device *d) bus->state != MDIOBUS_ALLOCATED, "%s: not in RELEASED or ALLOCATED state\n", bus->id); + + if (bus->state == MDIOBUS_RELEASED) + fwnode_handle_put(dev_fwnode(d)); + kfree(bus); } @@ -506,7 +510,7 @@ static int mdiobus_create_device(struct mii_bus *bus, if (IS_ERR(mdiodev)) return -ENODEV; - strncpy(mdiodev->modalias, bi->modalias, + strscpy(mdiodev->modalias, bi->modalias, sizeof(mdiodev->modalias)); mdiodev->bus_match = mdio_device_bus_match; mdiodev->dev.platform_data = (void *)bi->platform_data; @@ -684,6 +688,15 @@ int __mdiobus_register(struct mii_bus *bus, struct module *owner) bus->dev.groups = NULL; dev_set_name(&bus->dev, "%s", bus->id); + /* If the bus state is allocated, we're registering a fresh bus + * that may have a fwnode associated with it. Grab a reference + * to the fwnode. This will be dropped when the bus is released. + * If the bus was set to unregistered, it means that the bus was + * previously registered, and we've already grabbed a reference. + */ + if (bus->state == MDIOBUS_ALLOCATED) + fwnode_handle_get(dev_fwnode(&bus->dev)); + /* We need to set state to MDIOBUS_UNREGISTERED to correctly release * the device in mdiobus_free() * diff --git a/drivers/net/phy/mdio_device.c b/drivers/net/phy/mdio_device.c index 044828d081..73f6539b9e 100644 --- a/drivers/net/phy/mdio_device.c +++ b/drivers/net/phy/mdio_device.c @@ -62,6 +62,7 @@ struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr) mdiodev->device_remove = mdio_device_remove; mdiodev->bus = bus; mdiodev->addr = addr; + mdiodev->reset_state = -1; dev_set_name(&mdiodev->dev, PHY_ID_FMT, bus->id, addr); @@ -122,6 +123,9 @@ void mdio_device_reset(struct mdio_device *mdiodev, int value) if (!mdiodev->reset_gpio && !mdiodev->reset_ctrl) return; + if (mdiodev->reset_state == value) + return; + if (mdiodev->reset_gpio) gpiod_set_value_cansleep(mdiodev->reset_gpio, value); @@ -135,6 +139,8 @@ void mdio_device_reset(struct mdio_device *mdiodev, int value) d = value ? mdiodev->reset_assert_delay : mdiodev->reset_deassert_delay; if (d) fsleep(d); + + mdiodev->reset_state = value; } EXPORT_SYMBOL(mdio_device_reset); diff --git a/drivers/net/phy/mdio_devres.c b/drivers/net/phy/mdio_devres.c index 69b829e6ab..7fd3377dbd 100644 --- a/drivers/net/phy/mdio_devres.c +++ b/drivers/net/phy/mdio_devres.c @@ -131,4 +131,5 @@ int __devm_of_mdiobus_register(struct device *dev, struct mii_bus *mdio, EXPORT_SYMBOL(__devm_of_mdiobus_register); #endif /* CONFIG_OF_MDIO */ +MODULE_DESCRIPTION("Network MDIO bus devres helpers"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/phy/mediatek-ge-soc.c b/drivers/net/phy/mediatek-ge-soc.c index 0f3a1538a8..f4f9412d0c 100644 --- a/drivers/net/phy/mediatek-ge-soc.c +++ b/drivers/net/phy/mediatek-ge-soc.c @@ -216,6 +216,9 @@ #define MTK_PHY_LED_ON_LINK1000 BIT(0) #define MTK_PHY_LED_ON_LINK100 BIT(1) #define MTK_PHY_LED_ON_LINK10 BIT(2) +#define MTK_PHY_LED_ON_LINK (MTK_PHY_LED_ON_LINK10 |\ + MTK_PHY_LED_ON_LINK100 |\ + MTK_PHY_LED_ON_LINK1000) #define MTK_PHY_LED_ON_LINKDOWN BIT(3) #define MTK_PHY_LED_ON_FDX BIT(4) /* Full duplex */ #define MTK_PHY_LED_ON_HDX BIT(5) /* Half duplex */ @@ -231,6 +234,12 @@ #define MTK_PHY_LED_BLINK_100RX BIT(3) #define MTK_PHY_LED_BLINK_10TX BIT(4) #define MTK_PHY_LED_BLINK_10RX BIT(5) +#define MTK_PHY_LED_BLINK_RX (MTK_PHY_LED_BLINK_10RX |\ + MTK_PHY_LED_BLINK_100RX |\ + MTK_PHY_LED_BLINK_1000RX) +#define MTK_PHY_LED_BLINK_TX (MTK_PHY_LED_BLINK_10TX |\ + MTK_PHY_LED_BLINK_100TX |\ + MTK_PHY_LED_BLINK_1000TX) #define MTK_PHY_LED_BLINK_COLLISION BIT(6) #define MTK_PHY_LED_BLINK_RX_CRC_ERR BIT(7) #define MTK_PHY_LED_BLINK_RX_IDLE_ERR BIT(8) @@ -1247,11 +1256,9 @@ static int mt798x_phy_led_hw_control_get(struct phy_device *phydev, u8 index, if (blink < 0) return -EIO; - if ((on & (MTK_PHY_LED_ON_LINK1000 | MTK_PHY_LED_ON_LINK100 | - MTK_PHY_LED_ON_LINK10)) || - (blink & (MTK_PHY_LED_BLINK_1000RX | MTK_PHY_LED_BLINK_100RX | - MTK_PHY_LED_BLINK_10RX | MTK_PHY_LED_BLINK_1000TX | - MTK_PHY_LED_BLINK_100TX | MTK_PHY_LED_BLINK_10TX))) + if ((on & (MTK_PHY_LED_ON_LINK | MTK_PHY_LED_ON_FDX | MTK_PHY_LED_ON_HDX | + MTK_PHY_LED_ON_LINKDOWN)) || + (blink & (MTK_PHY_LED_BLINK_RX | MTK_PHY_LED_BLINK_TX))) set_bit(bit_netdev, &priv->led_state); else clear_bit(bit_netdev, &priv->led_state); @@ -1269,7 +1276,7 @@ static int mt798x_phy_led_hw_control_get(struct phy_device *phydev, u8 index, if (!rules) return 0; - if (on & (MTK_PHY_LED_ON_LINK1000 | MTK_PHY_LED_ON_LINK100 | MTK_PHY_LED_ON_LINK10)) + if (on & MTK_PHY_LED_ON_LINK) *rules |= BIT(TRIGGER_NETDEV_LINK); if (on & MTK_PHY_LED_ON_LINK10) @@ -1287,10 +1294,10 @@ static int mt798x_phy_led_hw_control_get(struct phy_device *phydev, u8 index, if (on & MTK_PHY_LED_ON_HDX) *rules |= BIT(TRIGGER_NETDEV_HALF_DUPLEX); - if (blink & (MTK_PHY_LED_BLINK_1000RX | MTK_PHY_LED_BLINK_100RX | MTK_PHY_LED_BLINK_10RX)) + if (blink & MTK_PHY_LED_BLINK_RX) *rules |= BIT(TRIGGER_NETDEV_RX); - if (blink & (MTK_PHY_LED_BLINK_1000TX | MTK_PHY_LED_BLINK_100TX | MTK_PHY_LED_BLINK_10TX)) + if (blink & MTK_PHY_LED_BLINK_TX) *rules |= BIT(TRIGGER_NETDEV_TX); return 0; @@ -1323,15 +1330,19 @@ static int mt798x_phy_led_hw_control_set(struct phy_device *phydev, u8 index, on |= MTK_PHY_LED_ON_LINK1000; if (rules & BIT(TRIGGER_NETDEV_RX)) { - blink |= MTK_PHY_LED_BLINK_10RX | - MTK_PHY_LED_BLINK_100RX | - MTK_PHY_LED_BLINK_1000RX; + blink |= (on & MTK_PHY_LED_ON_LINK) ? + (((on & MTK_PHY_LED_ON_LINK10) ? MTK_PHY_LED_BLINK_10RX : 0) | + ((on & MTK_PHY_LED_ON_LINK100) ? MTK_PHY_LED_BLINK_100RX : 0) | + ((on & MTK_PHY_LED_ON_LINK1000) ? MTK_PHY_LED_BLINK_1000RX : 0)) : + MTK_PHY_LED_BLINK_RX; } if (rules & BIT(TRIGGER_NETDEV_TX)) { - blink |= MTK_PHY_LED_BLINK_10TX | - MTK_PHY_LED_BLINK_100TX | - MTK_PHY_LED_BLINK_1000TX; + blink |= (on & MTK_PHY_LED_ON_LINK) ? + (((on & MTK_PHY_LED_ON_LINK10) ? MTK_PHY_LED_BLINK_10TX : 0) | + ((on & MTK_PHY_LED_ON_LINK100) ? MTK_PHY_LED_BLINK_100TX : 0) | + ((on & MTK_PHY_LED_ON_LINK1000) ? MTK_PHY_LED_BLINK_1000TX : 0)) : + MTK_PHY_LED_BLINK_TX; } if (blink || on) @@ -1344,9 +1355,7 @@ static int mt798x_phy_led_hw_control_set(struct phy_device *phydev, u8 index, MTK_PHY_LED0_ON_CTRL, MTK_PHY_LED_ON_FDX | MTK_PHY_LED_ON_HDX | - MTK_PHY_LED_ON_LINK10 | - MTK_PHY_LED_ON_LINK100 | - MTK_PHY_LED_ON_LINK1000, + MTK_PHY_LED_ON_LINK, on); if (ret) diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index ca2db4adcb..1f950c8244 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -2006,7 +2006,7 @@ static int kszphy_probe(struct phy_device *phydev) kszphy_parse_led_mode(phydev); - clk = devm_clk_get(&phydev->mdio.dev, "rmii-ref"); + clk = devm_clk_get_optional_enabled(&phydev->mdio.dev, "rmii-ref"); /* NOTE: clk may be NULL if building without CONFIG_HAVE_CLK */ if (!IS_ERR_OR_NULL(clk)) { unsigned long rate = clk_get_rate(clk); @@ -2026,6 +2026,11 @@ static int kszphy_probe(struct phy_device *phydev) rate); return -EINVAL; } + } else if (!clk) { + /* unnamed clock from the generic ethernet-phy binding */ + clk = devm_clk_get_optional_enabled(&phydev->mdio.dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); } if (ksz8041_fiber_mode(phydev)) @@ -2400,24 +2405,23 @@ static void lan8814_flush_fifo(struct phy_device *phydev, bool egress) lanphy_read_page_reg(phydev, 5, PTP_TSU_INT_STS); } -static int lan8814_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr) +static int lan8814_hwtstamp(struct mii_timestamper *mii_ts, + struct kernel_hwtstamp_config *config, + struct netlink_ext_ack *extack) { struct kszphy_ptp_priv *ptp_priv = container_of(mii_ts, struct kszphy_ptp_priv, mii_ts); struct phy_device *phydev = ptp_priv->phydev; struct lan8814_shared_priv *shared = phydev->shared->priv; struct lan8814_ptp_rx_ts *rx_ts, *tmp; - struct hwtstamp_config config; int txcfg = 0, rxcfg = 0; int pkt_ts_enable; + int tx_mod; - if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) - return -EFAULT; + ptp_priv->hwts_tx_type = config->tx_type; + ptp_priv->rx_filter = config->rx_filter; - ptp_priv->hwts_tx_type = config.tx_type; - ptp_priv->rx_filter = config.rx_filter; - - switch (config.rx_filter) { + switch (config->rx_filter) { case HWTSTAMP_FILTER_NONE: ptp_priv->layer = 0; ptp_priv->version = 0; @@ -2459,17 +2463,22 @@ static int lan8814_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr) lanphy_write_page_reg(ptp_priv->phydev, 5, PTP_RX_TIMESTAMP_EN, pkt_ts_enable); lanphy_write_page_reg(ptp_priv->phydev, 5, PTP_TX_TIMESTAMP_EN, pkt_ts_enable); - if (ptp_priv->hwts_tx_type == HWTSTAMP_TX_ONESTEP_SYNC) + tx_mod = lanphy_read_page_reg(ptp_priv->phydev, 5, PTP_TX_MOD); + if (ptp_priv->hwts_tx_type == HWTSTAMP_TX_ONESTEP_SYNC) { + lanphy_write_page_reg(ptp_priv->phydev, 5, PTP_TX_MOD, + tx_mod | PTP_TX_MOD_TX_PTP_SYNC_TS_INSERT_); + } else if (ptp_priv->hwts_tx_type == HWTSTAMP_TX_ON) { lanphy_write_page_reg(ptp_priv->phydev, 5, PTP_TX_MOD, - PTP_TX_MOD_TX_PTP_SYNC_TS_INSERT_); + tx_mod & ~PTP_TX_MOD_TX_PTP_SYNC_TS_INSERT_); + } - if (config.rx_filter != HWTSTAMP_FILTER_NONE) + if (config->rx_filter != HWTSTAMP_FILTER_NONE) lan8814_config_ts_intr(ptp_priv->phydev, true); else lan8814_config_ts_intr(ptp_priv->phydev, false); mutex_lock(&shared->shared_lock); - if (config.rx_filter != HWTSTAMP_FILTER_NONE) + if (config->rx_filter != HWTSTAMP_FILTER_NONE) shared->ref++; else shared->ref--; @@ -2493,7 +2502,7 @@ static int lan8814_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr) lan8814_flush_fifo(ptp_priv->phydev, false); lan8814_flush_fifo(ptp_priv->phydev, true); - return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? -EFAULT : 0; + return 0; } static void lan8814_txtstamp(struct mii_timestamper *mii_ts, @@ -2519,7 +2528,7 @@ static void lan8814_txtstamp(struct mii_timestamper *mii_ts, } } -static void lan8814_get_sig_rx(struct sk_buff *skb, u16 *sig) +static bool lan8814_get_sig_rx(struct sk_buff *skb, u16 *sig) { struct ptp_header *ptp_header; u32 type; @@ -2529,7 +2538,11 @@ static void lan8814_get_sig_rx(struct sk_buff *skb, u16 *sig) ptp_header = ptp_parse_header(skb, type); skb_pull_inline(skb, ETH_HLEN); + if (!ptp_header) + return false; + *sig = (__force u16)(ntohs(ptp_header->sequence_id)); + return true; } static bool lan8814_match_rx_skb(struct kszphy_ptp_priv *ptp_priv, @@ -2541,7 +2554,8 @@ static bool lan8814_match_rx_skb(struct kszphy_ptp_priv *ptp_priv, bool ret = false; u16 skb_sig; - lan8814_get_sig_rx(skb, &skb_sig); + if (!lan8814_get_sig_rx(skb, &skb_sig)) + return ret; /* Iterate over all RX timestamps and match it with the received skbs */ spin_lock_irqsave(&ptp_priv->rx_ts_lock, flags); @@ -2821,7 +2835,7 @@ static int lan8814_ptpci_adjfine(struct ptp_clock_info *ptpci, long scaled_ppm) return 0; } -static void lan8814_get_sig_tx(struct sk_buff *skb, u16 *sig) +static bool lan8814_get_sig_tx(struct sk_buff *skb, u16 *sig) { struct ptp_header *ptp_header; u32 type; @@ -2829,7 +2843,11 @@ static void lan8814_get_sig_tx(struct sk_buff *skb, u16 *sig) type = ptp_classify_raw(skb); ptp_header = ptp_parse_header(skb, type); + if (!ptp_header) + return false; + *sig = (__force u16)(ntohs(ptp_header->sequence_id)); + return true; } static void lan8814_match_tx_skb(struct kszphy_ptp_priv *ptp_priv, @@ -2843,7 +2861,8 @@ static void lan8814_match_tx_skb(struct kszphy_ptp_priv *ptp_priv, spin_lock_irqsave(&ptp_priv->tx_queue.lock, flags); skb_queue_walk_safe(&ptp_priv->tx_queue, skb, skb_tmp) { - lan8814_get_sig_tx(skb, &skb_sig); + if (!lan8814_get_sig_tx(skb, &skb_sig)) + continue; if (memcmp(&skb_sig, &seq_id, sizeof(seq_id))) continue; @@ -2897,7 +2916,8 @@ static bool lan8814_match_skb(struct kszphy_ptp_priv *ptp_priv, spin_lock_irqsave(&ptp_priv->rx_queue.lock, flags); skb_queue_walk_safe(&ptp_priv->rx_queue, skb, skb_tmp) { - lan8814_get_sig_rx(skb, &skb_sig); + if (!lan8814_get_sig_rx(skb, &skb_sig)) + continue; if (memcmp(&skb_sig, &rx_ts->seq_id, sizeof(rx_ts->seq_id))) continue; @@ -3718,21 +3738,19 @@ static void lan8841_ptp_enable_processing(struct kszphy_ptp_priv *ptp_priv, #define LAN8841_PTP_TX_TIMESTAMP_EN 443 #define LAN8841_PTP_TX_MOD 445 -static int lan8841_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr) +static int lan8841_hwtstamp(struct mii_timestamper *mii_ts, + struct kernel_hwtstamp_config *config, + struct netlink_ext_ack *extack) { struct kszphy_ptp_priv *ptp_priv = container_of(mii_ts, struct kszphy_ptp_priv, mii_ts); struct phy_device *phydev = ptp_priv->phydev; - struct hwtstamp_config config; int txcfg = 0, rxcfg = 0; int pkt_ts_enable; - if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) - return -EFAULT; - - ptp_priv->hwts_tx_type = config.tx_type; - ptp_priv->rx_filter = config.rx_filter; + ptp_priv->hwts_tx_type = config->tx_type; + ptp_priv->rx_filter = config->rx_filter; - switch (config.rx_filter) { + switch (config->rx_filter) { case HWTSTAMP_FILTER_NONE: ptp_priv->layer = 0; ptp_priv->version = 0; @@ -3786,13 +3804,13 @@ static int lan8841_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr) /* Now enable/disable the timestamping */ lan8841_ptp_enable_processing(ptp_priv, - config.rx_filter != HWTSTAMP_FILTER_NONE); + config->rx_filter != HWTSTAMP_FILTER_NONE); skb_queue_purge(&ptp_priv->tx_queue); lan8841_ptp_flush_fifo(ptp_priv); - return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? -EFAULT : 0; + return 0; } static bool lan8841_rxtstamp(struct mii_timestamper *mii_ts, diff --git a/drivers/net/phy/mscc/mscc.h b/drivers/net/phy/mscc/mscc.h index 7a962050a4..6a3d8a754e 100644 --- a/drivers/net/phy/mscc/mscc.h +++ b/drivers/net/phy/mscc/mscc.h @@ -416,6 +416,11 @@ struct vsc8531_private { * gpio_lock: used for PHC operations. Common for all PHYs as the load/save GPIO * is shared. */ + +enum vsc85xx_global_phy { + VSC88XX_BASE_ADDR = 0, +}; + struct vsc85xx_shared_private { struct mutex gpio_lock; }; diff --git a/drivers/net/phy/mscc/mscc_main.c b/drivers/net/phy/mscc/mscc_main.c index 4171f01d34..6f74ce0ab1 100644 --- a/drivers/net/phy/mscc/mscc_main.c +++ b/drivers/net/phy/mscc/mscc_main.c @@ -711,7 +711,7 @@ int phy_base_write(struct phy_device *phydev, u32 regnum, u16 val) dump_stack(); } - return __phy_package_write(phydev, regnum, val); + return __phy_package_write(phydev, VSC88XX_BASE_ADDR, regnum, val); } /* phydev->bus->mdio_lock should be locked when using this function */ @@ -722,7 +722,7 @@ int phy_base_read(struct phy_device *phydev, u32 regnum) dump_stack(); } - return __phy_package_read(phydev, regnum); + return __phy_package_read(phydev, VSC88XX_BASE_ADDR, regnum); } u32 vsc85xx_csr_read(struct phy_device *phydev, diff --git a/drivers/net/phy/mscc/mscc_ptp.c b/drivers/net/phy/mscc/mscc_ptp.c index cf728bfd83..eb0b032cb6 100644 --- a/drivers/net/phy/mscc/mscc_ptp.c +++ b/drivers/net/phy/mscc/mscc_ptp.c @@ -1045,19 +1045,17 @@ static void vsc85xx_ts_reset_fifo(struct phy_device *phydev) val); } -static int vsc85xx_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr) +static int vsc85xx_hwtstamp(struct mii_timestamper *mii_ts, + struct kernel_hwtstamp_config *cfg, + struct netlink_ext_ack *extack) { struct vsc8531_private *vsc8531 = container_of(mii_ts, struct vsc8531_private, mii_ts); struct phy_device *phydev = vsc8531->ptp->phydev; - struct hwtstamp_config cfg; bool one_step = false; u32 val; - if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) - return -EFAULT; - - switch (cfg.tx_type) { + switch (cfg->tx_type) { case HWTSTAMP_TX_ONESTEP_SYNC: one_step = true; break; @@ -1069,9 +1067,9 @@ static int vsc85xx_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr) return -ERANGE; } - vsc8531->ptp->tx_type = cfg.tx_type; + vsc8531->ptp->tx_type = cfg->tx_type; - switch (cfg.rx_filter) { + switch (cfg->rx_filter) { case HWTSTAMP_FILTER_NONE: break; case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: @@ -1084,7 +1082,7 @@ static int vsc85xx_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr) return -ERANGE; } - vsc8531->ptp->rx_filter = cfg.rx_filter; + vsc8531->ptp->rx_filter = cfg->rx_filter; mutex_lock(&vsc8531->ts_lock); @@ -1132,7 +1130,7 @@ static int vsc85xx_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr) vsc8531->ptp->configured = 1; mutex_unlock(&vsc8531->ts_lock); - return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; + return 0; } static int vsc85xx_ts_info(struct mii_timestamper *mii_ts, 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 0000000000..550ef08970 --- /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 7ab080ff02..3cf614b4cd 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) { @@ -1022,24 +1005,21 @@ static bool nxp_c45_rxtstamp(struct mii_timestamper *mii_ts, } static int nxp_c45_hwtstamp(struct mii_timestamper *mii_ts, - struct ifreq *ifreq) + struct kernel_hwtstamp_config *cfg, + struct netlink_ext_ack *extack) { struct nxp_c45_phy *priv = container_of(mii_ts, struct nxp_c45_phy, mii_ts); struct phy_device *phydev = priv->phydev; const struct nxp_c45_phy_data *data; - struct hwtstamp_config cfg; - - if (copy_from_user(&cfg, ifreq->ifr_data, sizeof(cfg))) - return -EFAULT; - if (cfg.tx_type < 0 || cfg.tx_type > HWTSTAMP_TX_ON) + if (cfg->tx_type < 0 || cfg->tx_type > HWTSTAMP_TX_ON) return -ERANGE; data = nxp_c45_get_data(phydev); - priv->hwts_tx = cfg.tx_type; + priv->hwts_tx = cfg->tx_type; - switch (cfg.rx_filter) { + switch (cfg->rx_filter) { case HWTSTAMP_FILTER_NONE: priv->hwts_rx = 0; break; @@ -1047,7 +1027,7 @@ static int nxp_c45_hwtstamp(struct mii_timestamper *mii_ts, case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: priv->hwts_rx = 1; - cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT; + cfg->rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT; break; default: return -ERANGE; @@ -1074,7 +1054,7 @@ static int nxp_c45_hwtstamp(struct mii_timestamper *mii_ts, nxp_c45_clear_reg_field(phydev, &data->regmap->irq_egr_ts_en); nxp_c45_no_ptp_irq: - return copy_to_user(ifreq->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; + return 0; } static int nxp_c45_ts_info(struct mii_timestamper *mii_ts, @@ -1218,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) @@ -1289,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; } @@ -1614,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); } @@ -1629,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); @@ -1645,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; @@ -1666,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; } @@ -1679,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 0000000000..f364fca68f --- /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/nxp-tja11xx.c b/drivers/net/phy/nxp-tja11xx.c index a713999651..2c263ae44b 100644 --- a/drivers/net/phy/nxp-tja11xx.c +++ b/drivers/net/phy/nxp-tja11xx.c @@ -415,7 +415,7 @@ static void tja11xx_get_strings(struct phy_device *phydev, u8 *data) int i; for (i = 0; i < ARRAY_SIZE(tja11xx_hw_stats); i++) - ethtool_sprintf(&data, "%s", tja11xx_hw_stats[i].string); + ethtool_puts(&data, tja11xx_hw_stats[i].string); } static void tja11xx_get_stats(struct phy_device *phydev, diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c index 8e6fd4962c..747d14bf15 100644 --- a/drivers/net/phy/phy-c45.c +++ b/drivers/net/phy/phy-c45.c @@ -920,6 +920,79 @@ int genphy_c45_pma_baset1_read_abilities(struct phy_device *phydev) EXPORT_SYMBOL_GPL(genphy_c45_pma_baset1_read_abilities); /** + * genphy_c45_pma_read_ext_abilities - read supported link modes from PMA + * @phydev: target phy_device struct + * + * Read the supported link modes from the PMA/PMD extended ability register + * (Register 1.11). + */ +int genphy_c45_pma_read_ext_abilities(struct phy_device *phydev) +{ + int val; + + val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_EXTABLE); + if (val < 0) + return val; + + linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT, + phydev->supported, + val & MDIO_PMA_EXTABLE_10GBLRM); + linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, + phydev->supported, + val & MDIO_PMA_EXTABLE_10GBT); + linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, + phydev->supported, + val & MDIO_PMA_EXTABLE_10GBKX4); + linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, + phydev->supported, + val & MDIO_PMA_EXTABLE_10GBKR); + linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, + phydev->supported, + val & MDIO_PMA_EXTABLE_1000BT); + linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, + phydev->supported, + val & MDIO_PMA_EXTABLE_1000BKX); + + linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, + phydev->supported, + val & MDIO_PMA_EXTABLE_100BTX); + linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, + phydev->supported, + val & MDIO_PMA_EXTABLE_100BTX); + + linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, + phydev->supported, + val & MDIO_PMA_EXTABLE_10BT); + linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, + phydev->supported, + val & MDIO_PMA_EXTABLE_10BT); + + if (val & MDIO_PMA_EXTABLE_NBT) { + val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, + MDIO_PMA_NG_EXTABLE); + if (val < 0) + return val; + + linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, + phydev->supported, + val & MDIO_PMA_NG_EXTABLE_2_5GBT); + + linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, + phydev->supported, + val & MDIO_PMA_NG_EXTABLE_5GBT); + } + + if (val & MDIO_PMA_EXTABLE_BT1) { + val = genphy_c45_pma_baset1_read_abilities(phydev); + if (val < 0) + return val; + } + + return 0; +} +EXPORT_SYMBOL_GPL(genphy_c45_pma_read_ext_abilities); + +/** * genphy_c45_pma_read_abilities - read supported link modes from PMA * @phydev: target phy_device struct * @@ -962,63 +1035,9 @@ int genphy_c45_pma_read_abilities(struct phy_device *phydev) val & MDIO_PMA_STAT2_10GBER); if (val & MDIO_PMA_STAT2_EXTABLE) { - val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_EXTABLE); + val = genphy_c45_pma_read_ext_abilities(phydev); if (val < 0) return val; - - linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT, - phydev->supported, - val & MDIO_PMA_EXTABLE_10GBLRM); - linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, - phydev->supported, - val & MDIO_PMA_EXTABLE_10GBT); - linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, - phydev->supported, - val & MDIO_PMA_EXTABLE_10GBKX4); - linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, - phydev->supported, - val & MDIO_PMA_EXTABLE_10GBKR); - linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, - phydev->supported, - val & MDIO_PMA_EXTABLE_1000BT); - linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, - phydev->supported, - val & MDIO_PMA_EXTABLE_1000BKX); - - linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, - phydev->supported, - val & MDIO_PMA_EXTABLE_100BTX); - linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, - phydev->supported, - val & MDIO_PMA_EXTABLE_100BTX); - - linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, - phydev->supported, - val & MDIO_PMA_EXTABLE_10BT); - linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, - phydev->supported, - val & MDIO_PMA_EXTABLE_10BT); - - if (val & MDIO_PMA_EXTABLE_NBT) { - val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, - MDIO_PMA_NG_EXTABLE); - if (val < 0) - return val; - - linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, - phydev->supported, - val & MDIO_PMA_NG_EXTABLE_2_5GBT); - - linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, - phydev->supported, - val & MDIO_PMA_NG_EXTABLE_5GBT); - } - - if (val & MDIO_PMA_EXTABLE_BT1) { - val = genphy_c45_pma_baset1_read_abilities(phydev); - if (val < 0) - return val; - } } /* This is optional functionality. If not supported, we may get an error diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c index 966c93cbe6..15f349e599 100644 --- a/drivers/net/phy/phy-core.c +++ b/drivers/net/phy/phy-core.c @@ -540,6 +540,28 @@ static void mmd_phy_indirect(struct mii_bus *bus, int phy_addr, int devad, devad | MII_MMD_CTRL_NOINCR); } +static int mmd_phy_read(struct mii_bus *bus, int phy_addr, bool is_c45, + int devad, u32 regnum) +{ + if (is_c45) + return __mdiobus_c45_read(bus, phy_addr, devad, regnum); + + mmd_phy_indirect(bus, phy_addr, devad, regnum); + /* Read the content of the MMD's selected register */ + return __mdiobus_read(bus, phy_addr, MII_MMD_DATA); +} + +static int mmd_phy_write(struct mii_bus *bus, int phy_addr, bool is_c45, + int devad, u32 regnum, u16 val) +{ + if (is_c45) + return __mdiobus_c45_write(bus, phy_addr, devad, regnum, val); + + mmd_phy_indirect(bus, phy_addr, devad, regnum); + /* Write the data into MMD's selected register */ + return __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val); +} + /** * __phy_read_mmd - Convenience function for reading a register * from an MMD on a given PHY. @@ -551,26 +573,14 @@ static void mmd_phy_indirect(struct mii_bus *bus, int phy_addr, int devad, */ int __phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum) { - int val; - if (regnum > (u16)~0 || devad > 32) return -EINVAL; - if (phydev->drv && phydev->drv->read_mmd) { - val = phydev->drv->read_mmd(phydev, devad, regnum); - } else if (phydev->is_c45) { - val = __mdiobus_c45_read(phydev->mdio.bus, phydev->mdio.addr, - devad, regnum); - } else { - struct mii_bus *bus = phydev->mdio.bus; - int phy_addr = phydev->mdio.addr; - - mmd_phy_indirect(bus, phy_addr, devad, regnum); + if (phydev->drv && phydev->drv->read_mmd) + return phydev->drv->read_mmd(phydev, devad, regnum); - /* Read the content of the MMD's selected register */ - val = __mdiobus_read(bus, phy_addr, MII_MMD_DATA); - } - return val; + return mmd_phy_read(phydev->mdio.bus, phydev->mdio.addr, + phydev->is_c45, devad, regnum); } EXPORT_SYMBOL(__phy_read_mmd); @@ -607,28 +617,14 @@ EXPORT_SYMBOL(phy_read_mmd); */ int __phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val) { - int ret; - if (regnum > (u16)~0 || devad > 32) return -EINVAL; - if (phydev->drv && phydev->drv->write_mmd) { - ret = phydev->drv->write_mmd(phydev, devad, regnum, val); - } else if (phydev->is_c45) { - ret = __mdiobus_c45_write(phydev->mdio.bus, phydev->mdio.addr, - devad, regnum, val); - } else { - struct mii_bus *bus = phydev->mdio.bus; - int phy_addr = phydev->mdio.addr; - - mmd_phy_indirect(bus, phy_addr, devad, regnum); + if (phydev->drv && phydev->drv->write_mmd) + return phydev->drv->write_mmd(phydev, devad, regnum, val); - /* Write the data into MMD's selected register */ - __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val); - - ret = 0; - } - return ret; + return mmd_phy_write(phydev->mdio.bus, phydev->mdio.addr, + phydev->is_c45, devad, regnum, val); } EXPORT_SYMBOL(__phy_write_mmd); @@ -655,6 +651,146 @@ int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val) EXPORT_SYMBOL(phy_write_mmd); /** + * __phy_package_read_mmd - read MMD reg relative to PHY package base addr + * @phydev: The phy_device struct + * @addr_offset: The offset to be added to PHY package base_addr + * @devad: The MMD to read from + * @regnum: The register on the MMD to read + * + * Convenience helper for reading a register of an MMD on a given PHY + * using the PHY package base address. The base address is added to + * the addr_offset value. + * + * Same calling rules as for __phy_read(); + * + * NOTE: It's assumed that the entire PHY package is either C22 or C45. + */ +int __phy_package_read_mmd(struct phy_device *phydev, + unsigned int addr_offset, int devad, + u32 regnum) +{ + int addr = phy_package_address(phydev, addr_offset); + + if (addr < 0) + return addr; + + if (regnum > (u16)~0 || devad > 32) + return -EINVAL; + + return mmd_phy_read(phydev->mdio.bus, addr, phydev->is_c45, devad, + regnum); +} +EXPORT_SYMBOL(__phy_package_read_mmd); + +/** + * phy_package_read_mmd - read MMD reg relative to PHY package base addr + * @phydev: The phy_device struct + * @addr_offset: The offset to be added to PHY package base_addr + * @devad: The MMD to read from + * @regnum: The register on the MMD to read + * + * Convenience helper for reading a register of an MMD on a given PHY + * using the PHY package base address. The base address is added to + * the addr_offset value. + * + * Same calling rules as for phy_read(); + * + * NOTE: It's assumed that the entire PHY package is either C22 or C45. + */ +int phy_package_read_mmd(struct phy_device *phydev, + unsigned int addr_offset, int devad, + u32 regnum) +{ + int addr = phy_package_address(phydev, addr_offset); + int val; + + if (addr < 0) + return addr; + + if (regnum > (u16)~0 || devad > 32) + return -EINVAL; + + phy_lock_mdio_bus(phydev); + val = mmd_phy_read(phydev->mdio.bus, addr, phydev->is_c45, devad, + regnum); + phy_unlock_mdio_bus(phydev); + + return val; +} +EXPORT_SYMBOL(phy_package_read_mmd); + +/** + * __phy_package_write_mmd - write MMD reg relative to PHY package base addr + * @phydev: The phy_device struct + * @addr_offset: The offset to be added to PHY package base_addr + * @devad: The MMD to write to + * @regnum: The register on the MMD to write + * @val: value to write to @regnum + * + * Convenience helper for writing a register of an MMD on a given PHY + * using the PHY package base address. The base address is added to + * the addr_offset value. + * + * Same calling rules as for __phy_write(); + * + * NOTE: It's assumed that the entire PHY package is either C22 or C45. + */ +int __phy_package_write_mmd(struct phy_device *phydev, + unsigned int addr_offset, int devad, + u32 regnum, u16 val) +{ + int addr = phy_package_address(phydev, addr_offset); + + if (addr < 0) + return addr; + + if (regnum > (u16)~0 || devad > 32) + return -EINVAL; + + return mmd_phy_write(phydev->mdio.bus, addr, phydev->is_c45, devad, + regnum, val); +} +EXPORT_SYMBOL(__phy_package_write_mmd); + +/** + * phy_package_write_mmd - write MMD reg relative to PHY package base addr + * @phydev: The phy_device struct + * @addr_offset: The offset to be added to PHY package base_addr + * @devad: The MMD to write to + * @regnum: The register on the MMD to write + * @val: value to write to @regnum + * + * Convenience helper for writing a register of an MMD on a given PHY + * using the PHY package base address. The base address is added to + * the addr_offset value. + * + * Same calling rules as for phy_write(); + * + * NOTE: It's assumed that the entire PHY package is either C22 or C45. + */ +int phy_package_write_mmd(struct phy_device *phydev, + unsigned int addr_offset, int devad, + u32 regnum, u16 val) +{ + int addr = phy_package_address(phydev, addr_offset); + int ret; + + if (addr < 0) + return addr; + + if (regnum > (u16)~0 || devad > 32) + return -EINVAL; + + phy_lock_mdio_bus(phydev); + ret = mmd_phy_write(phydev->mdio.bus, addr, phydev->is_c45, devad, + regnum, val); + phy_unlock_mdio_bus(phydev); + + return ret; +} +EXPORT_SYMBOL(phy_package_write_mmd); + +/** * phy_modify_changed - Function for modifying a PHY register * @phydev: the phy_device struct * @regnum: register number to modify diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index a5fa077650..3376e58e2b 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -325,9 +325,13 @@ EXPORT_SYMBOL(phy_ethtool_ksettings_get); int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd) { struct mii_ioctl_data *mii_data = if_mii(ifr); + struct kernel_hwtstamp_config kernel_cfg; + struct netlink_ext_ack extack = {}; u16 val = mii_data->val_in; bool change_autoneg = false; + struct hwtstamp_config cfg; int prtad, devad; + int ret; switch (cmd) { case SIOCGMIIPHY: @@ -411,8 +415,21 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd) return 0; case SIOCSHWTSTAMP: - if (phydev->mii_ts && phydev->mii_ts->hwtstamp) - return phydev->mii_ts->hwtstamp(phydev->mii_ts, ifr); + if (phydev->mii_ts && phydev->mii_ts->hwtstamp) { + if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) + return -EFAULT; + + hwtstamp_config_to_kernel(&kernel_cfg, &cfg); + ret = phydev->mii_ts->hwtstamp(phydev->mii_ts, &kernel_cfg, &extack); + if (ret) + return ret; + + hwtstamp_config_from_kernel(&cfg, &kernel_cfg); + if (copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg))) + return -EFAULT; + + return 0; + } fallthrough; default: @@ -469,7 +486,7 @@ int __phy_hwtstamp_get(struct phy_device *phydev, if (!phydev) return -ENODEV; - return phy_mii_ioctl(phydev, config->ifr, SIOCGHWTSTAMP); + return -EOPNOTSUPP; } /** @@ -486,7 +503,10 @@ int __phy_hwtstamp_set(struct phy_device *phydev, if (!phydev) return -ENODEV; - return phy_mii_ioctl(phydev, config->ifr, SIOCSHWTSTAMP); + if (phydev->mii_ts && phydev->mii_ts->hwtstamp) + return phydev->mii_ts->hwtstamp(phydev->mii_ts, config, extack); + + return -EOPNOTSUPP; } /** diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 813b753e21..8efa2a136f 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -654,6 +654,7 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id, mdiodev->flags = MDIO_DEVICE_FLAG_PHY; mdiodev->device_free = phy_mdio_device_free; mdiodev->device_remove = phy_mdio_device_remove; + mdiodev->reset_state = -1; dev->speed = SPEED_UNKNOWN; dev->duplex = DUPLEX_UNKNOWN; @@ -1235,18 +1236,19 @@ int phy_init_hw(struct phy_device *phydev) if (phydev->drv->soft_reset) { ret = phydev->drv->soft_reset(phydev); + if (ret < 0) + return ret; + /* see comment in genphy_soft_reset for an explanation */ - if (!ret) - phydev->suspended = 0; + phydev->suspended = 0; } - if (ret < 0) - return ret; - ret = phy_scan_fixups(phydev); if (ret < 0) return ret; + phy_interface_zero(phydev->possible_interfaces); + if (phydev->drv->config_init) { ret = phydev->drv->config_init(phydev); if (ret < 0) @@ -1411,6 +1413,11 @@ int phy_sfp_probe(struct phy_device *phydev, } EXPORT_SYMBOL(phy_sfp_probe); +static bool phy_drv_supports_irq(struct phy_driver *phydrv) +{ + return phydrv->config_intr && phydrv->handle_interrupt; +} + /** * phy_attach_direct - attach a network device to a given PHY device pointer * @dev: network device to attach @@ -1525,6 +1532,9 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, if (phydev->dev_flags & PHY_F_NO_IRQ) phydev->irq = PHY_POLL; + if (!phy_drv_supports_irq(phydev->drv) && phy_interrupt_is_valid(phydev)) + phydev->irq = PHY_POLL; + /* Port is set to PORT_TP by default and the actual PHY driver will set * it to different value depending on the PHY configuration. If we have * the generic PHY driver we can't figure it out, thus set the old @@ -1650,20 +1660,22 @@ EXPORT_SYMBOL_GPL(phy_driver_is_genphy_10g); /** * phy_package_join - join a common PHY group * @phydev: target phy_device struct - * @addr: cookie and PHY address for global register access + * @base_addr: cookie and base PHY address of PHY package for offset + * calculation of global register access * @priv_size: if non-zero allocate this amount of bytes for private data * * This joins a PHY group and provides a shared storage for all phydevs in * this group. This is intended to be used for packages which contain * more than one PHY, for example a quad PHY transceiver. * - * The addr parameter serves as a cookie which has to have the same value - * for all members of one group and as a PHY address to access generic - * registers of a PHY package. Usually, one of the PHY addresses of the - * different PHYs in the package provides access to these global registers. + * The base_addr parameter serves as cookie which has to have the same values + * for all members of one group and as the base PHY address of the PHY package + * for offset calculation to access generic registers of a PHY package. + * Usually, one of the PHY addresses of the different PHYs in the package + * provides access to these global registers. * The address which is given here, will be used in the phy_package_read() - * and phy_package_write() convenience functions. If your PHY doesn't have - * global registers you can just pick any of the PHY addresses. + * and phy_package_write() convenience functions as base and added to the + * passed offset in those functions. * * This will set the shared pointer of the phydev to the shared storage. * If this is the first call for a this cookie the shared storage will be @@ -1673,17 +1685,17 @@ EXPORT_SYMBOL_GPL(phy_driver_is_genphy_10g); * Returns < 1 on error, 0 on success. Esp. calling phy_package_join() * with the same cookie but a different priv_size is an error. */ -int phy_package_join(struct phy_device *phydev, int addr, size_t priv_size) +int phy_package_join(struct phy_device *phydev, int base_addr, size_t priv_size) { struct mii_bus *bus = phydev->mdio.bus; struct phy_package_shared *shared; int ret; - if (addr < 0 || addr >= PHY_MAX_ADDR) + if (base_addr < 0 || base_addr >= PHY_MAX_ADDR) return -EINVAL; mutex_lock(&bus->shared_lock); - shared = bus->shared[addr]; + shared = bus->shared[base_addr]; if (!shared) { ret = -ENOMEM; shared = kzalloc(sizeof(*shared), GFP_KERNEL); @@ -1695,9 +1707,9 @@ int phy_package_join(struct phy_device *phydev, int addr, size_t priv_size) goto err_free; shared->priv_size = priv_size; } - shared->addr = addr; + shared->base_addr = base_addr; refcount_set(&shared->refcnt, 1); - bus->shared[addr] = shared; + bus->shared[base_addr] = shared; } else { ret = -EINVAL; if (priv_size && priv_size != shared->priv_size) @@ -1735,7 +1747,7 @@ void phy_package_leave(struct phy_device *phydev) return; if (refcount_dec_and_mutex_lock(&shared->refcnt, &bus->shared_lock)) { - bus->shared[shared->addr] = NULL; + bus->shared[shared->base_addr] = NULL; mutex_unlock(&bus->shared_lock); kfree(shared->priv); kfree(shared); @@ -1754,7 +1766,8 @@ static void devm_phy_package_leave(struct device *dev, void *res) * devm_phy_package_join - resource managed phy_package_join() * @dev: device that is registering this PHY package * @phydev: target phy_device struct - * @addr: cookie and PHY address for global register access + * @base_addr: cookie and base PHY address of PHY package for offset + * calculation of global register access * @priv_size: if non-zero allocate this amount of bytes for private data * * Managed phy_package_join(). Shared storage fetched by this function, @@ -1762,7 +1775,7 @@ static void devm_phy_package_leave(struct device *dev, void *res) * phy_package_join() for more information. */ int devm_phy_package_join(struct device *dev, struct phy_device *phydev, - int addr, size_t priv_size) + int base_addr, size_t priv_size) { struct phy_device **ptr; int ret; @@ -1772,7 +1785,7 @@ int devm_phy_package_join(struct device *dev, struct phy_device *phydev, if (!ptr) return -ENOMEM; - ret = phy_package_join(phydev, addr, priv_size); + ret = phy_package_join(phydev, base_addr, priv_size); if (!ret) { *ptr = phydev; @@ -2987,11 +3000,6 @@ s32 phy_get_internal_delay(struct phy_device *phydev, struct device *dev, } EXPORT_SYMBOL(phy_get_internal_delay); -static bool phy_drv_supports_irq(struct phy_driver *phydrv) -{ - return phydrv->config_intr && phydrv->handle_interrupt; -} - static int phy_led_set_brightness(struct led_classdev *led_cdev, enum led_brightness value) { diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 25c19496a3..ed0b4ccaa6 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -121,6 +121,19 @@ do { \ }) #endif +static const phy_interface_t phylink_sfp_interface_preference[] = { + PHY_INTERFACE_MODE_25GBASER, + PHY_INTERFACE_MODE_USXGMII, + PHY_INTERFACE_MODE_10GBASER, + PHY_INTERFACE_MODE_5GBASER, + PHY_INTERFACE_MODE_2500BASEX, + PHY_INTERFACE_MODE_SGMII, + PHY_INTERFACE_MODE_1000BASEX, + PHY_INTERFACE_MODE_100BASEX, +}; + +static DECLARE_PHY_INTERFACE_MASK(phylink_sfp_interfaces); + /** * phylink_set_port_modes() - set the port type modes in the ethtool mask * @mask: ethtool link mode mask @@ -689,28 +702,48 @@ static int phylink_validate_mac_and_pcs(struct phylink *pl, return phylink_is_empty_linkmode(supported) ? -EINVAL : 0; } -static int phylink_validate_mask(struct phylink *pl, unsigned long *supported, +static void phylink_validate_one(struct phylink *pl, struct phy_device *phy, + const unsigned long *supported, + const struct phylink_link_state *state, + phy_interface_t interface, + unsigned long *accum_supported, + unsigned long *accum_advertising) +{ + __ETHTOOL_DECLARE_LINK_MODE_MASK(tmp_supported); + struct phylink_link_state tmp_state; + + linkmode_copy(tmp_supported, supported); + + tmp_state = *state; + tmp_state.interface = interface; + + if (phy) + tmp_state.rate_matching = phy_get_rate_matching(phy, interface); + + if (!phylink_validate_mac_and_pcs(pl, tmp_supported, &tmp_state)) { + phylink_dbg(pl, " interface %u (%s) rate match %s supports %*pbl\n", + interface, phy_modes(interface), + phy_rate_matching_to_str(tmp_state.rate_matching), + __ETHTOOL_LINK_MODE_MASK_NBITS, tmp_supported); + + linkmode_or(accum_supported, accum_supported, tmp_supported); + linkmode_or(accum_advertising, accum_advertising, + tmp_state.advertising); + } +} + +static int phylink_validate_mask(struct phylink *pl, struct phy_device *phy, + unsigned long *supported, struct phylink_link_state *state, const unsigned long *interfaces) { __ETHTOOL_DECLARE_LINK_MODE_MASK(all_adv) = { 0, }; __ETHTOOL_DECLARE_LINK_MODE_MASK(all_s) = { 0, }; - __ETHTOOL_DECLARE_LINK_MODE_MASK(s); - struct phylink_link_state t; - int intf; - - for (intf = 0; intf < PHY_INTERFACE_MODE_MAX; intf++) { - if (test_bit(intf, interfaces)) { - linkmode_copy(s, supported); - - t = *state; - t.interface = intf; - if (!phylink_validate_mac_and_pcs(pl, s, &t)) { - linkmode_or(all_s, all_s, s); - linkmode_or(all_adv, all_adv, t.advertising); - } - } - } + int interface; + + for_each_set_bit(interface, interfaces, PHY_INTERFACE_MODE_MAX) + phylink_validate_one(pl, phy, supported, state, interface, + all_s, all_adv); linkmode_copy(supported, all_s); linkmode_copy(state->advertising, all_adv); @@ -724,7 +757,8 @@ static int phylink_validate(struct phylink *pl, unsigned long *supported, const unsigned long *interfaces = pl->config->supported_interfaces; if (state->interface == PHY_INTERFACE_MODE_NA) - return phylink_validate_mask(pl, supported, state, interfaces); + return phylink_validate_mask(pl, NULL, supported, state, + interfaces); if (!test_bit(state->interface, interfaces)) return -EINVAL; @@ -805,7 +839,7 @@ static int phylink_parse_fixedlink(struct phylink *pl, phylink_warn(pl, "fixed link specifies half duplex for %dMbps link?\n", pl->link_config.speed); - bitmap_fill(pl->supported, __ETHTOOL_LINK_MODE_MASK_NBITS); + linkmode_fill(pl->supported); linkmode_copy(pl->link_config.advertising, pl->supported); phylink_validate(pl, pl->supported, &pl->link_config); @@ -849,6 +883,7 @@ static int phylink_parse_mode(struct phylink *pl, { struct fwnode_handle *dn; const char *managed; + unsigned long caps; dn = fwnode_get_named_child_node(fwnode, "fixed-link"); if (dn || fwnode_property_present(fwnode, "fixed-link")) @@ -881,80 +916,18 @@ static int phylink_parse_mode(struct phylink *pl, case PHY_INTERFACE_MODE_RGMII_RXID: case PHY_INTERFACE_MODE_RGMII_TXID: case PHY_INTERFACE_MODE_RTBI: - phylink_set(pl->supported, 10baseT_Half); - phylink_set(pl->supported, 10baseT_Full); - phylink_set(pl->supported, 100baseT_Half); - phylink_set(pl->supported, 100baseT_Full); - phylink_set(pl->supported, 1000baseT_Half); - phylink_set(pl->supported, 1000baseT_Full); - break; - case PHY_INTERFACE_MODE_1000BASEX: - phylink_set(pl->supported, 1000baseX_Full); - break; - case PHY_INTERFACE_MODE_2500BASEX: - phylink_set(pl->supported, 2500baseX_Full); - break; - case PHY_INTERFACE_MODE_5GBASER: - phylink_set(pl->supported, 5000baseT_Full); - break; - case PHY_INTERFACE_MODE_25GBASER: - phylink_set(pl->supported, 25000baseCR_Full); - phylink_set(pl->supported, 25000baseKR_Full); - phylink_set(pl->supported, 25000baseSR_Full); - fallthrough; 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); - phylink_set(pl->supported, 100baseT_Full); - phylink_set(pl->supported, 1000baseT_Half); - phylink_set(pl->supported, 1000baseT_Full); - phylink_set(pl->supported, 1000baseX_Full); - phylink_set(pl->supported, 1000baseKX_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, 10000baseKX4_Full); - phylink_set(pl->supported, 10000baseCR_Full); - phylink_set(pl->supported, 10000baseSR_Full); - phylink_set(pl->supported, 10000baseLR_Full); - phylink_set(pl->supported, 10000baseLRM_Full); - phylink_set(pl->supported, 10000baseER_Full); - break; - case PHY_INTERFACE_MODE_XLGMII: - phylink_set(pl->supported, 25000baseCR_Full); - phylink_set(pl->supported, 25000baseKR_Full); - phylink_set(pl->supported, 25000baseSR_Full); - phylink_set(pl->supported, 40000baseKR4_Full); - phylink_set(pl->supported, 40000baseCR4_Full); - phylink_set(pl->supported, 40000baseSR4_Full); - phylink_set(pl->supported, 40000baseLR4_Full); - phylink_set(pl->supported, 50000baseCR2_Full); - phylink_set(pl->supported, 50000baseKR2_Full); - phylink_set(pl->supported, 50000baseSR2_Full); - phylink_set(pl->supported, 50000baseKR_Full); - phylink_set(pl->supported, 50000baseSR_Full); - phylink_set(pl->supported, 50000baseCR_Full); - phylink_set(pl->supported, 50000baseLR_ER_FR_Full); - phylink_set(pl->supported, 50000baseDR_Full); - phylink_set(pl->supported, 100000baseKR4_Full); - phylink_set(pl->supported, 100000baseSR4_Full); - phylink_set(pl->supported, 100000baseCR4_Full); - phylink_set(pl->supported, 100000baseLR4_ER4_Full); - phylink_set(pl->supported, 100000baseKR2_Full); - phylink_set(pl->supported, 100000baseSR2_Full); - phylink_set(pl->supported, 100000baseCR2_Full); - phylink_set(pl->supported, 100000baseLR2_ER2_FR2_Full); - phylink_set(pl->supported, 100000baseDR2_Full); + caps = ~(MAC_SYM_PAUSE | MAC_ASYM_PAUSE); + caps = phylink_get_capabilities(pl->link_config.interface, caps, + RATE_MATCH_NONE); + phylink_caps_to_linkmodes(pl->supported, caps); break; default: @@ -1101,6 +1074,72 @@ static void phylink_pcs_an_restart(struct phylink *pl) pl->pcs->ops->pcs_an_restart(pl->pcs); } +/** + * phylink_pcs_neg_mode() - helper to determine PCS inband mode + * @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND. + * @interface: interface mode to be used + * @advertising: adertisement ethtool link mode mask + * + * Determines the negotiation mode to be used by the PCS, and returns + * one of: + * + * - %PHYLINK_PCS_NEG_NONE: interface mode does not support inband + * - %PHYLINK_PCS_NEG_OUTBAND: an out of band mode (e.g. reading the PHY) + * will be used. + * - %PHYLINK_PCS_NEG_INBAND_DISABLED: inband mode selected but autoneg + * disabled + * - %PHYLINK_PCS_NEG_INBAND_ENABLED: inband mode selected and autoneg enabled + * + * Note: this is for cases where the PCS itself is involved in negotiation + * (e.g. Clause 37, SGMII and similar) not Clause 73. + */ +static unsigned int phylink_pcs_neg_mode(unsigned int mode, + phy_interface_t interface, + const unsigned long *advertising) +{ + unsigned int neg_mode; + + switch (interface) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_QSGMII: + case PHY_INTERFACE_MODE_QUSGMII: + case PHY_INTERFACE_MODE_USXGMII: + /* These protocols are designed for use with a PHY which + * communicates its negotiation result back to the MAC via + * inband communication. Note: there exist PHYs that run + * with SGMII but do not send the inband data. + */ + if (!phylink_autoneg_inband(mode)) + neg_mode = PHYLINK_PCS_NEG_OUTBAND; + else + neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED; + break; + + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: + /* 1000base-X is designed for use media-side for Fibre + * connections, and thus the Autoneg bit needs to be + * taken into account. We also do this for 2500base-X + * as well, but drivers may not support this, so may + * need to override this. + */ + if (!phylink_autoneg_inband(mode)) + neg_mode = PHYLINK_PCS_NEG_OUTBAND; + else if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, + advertising)) + neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED; + else + neg_mode = PHYLINK_PCS_NEG_INBAND_DISABLED; + break; + + default: + neg_mode = PHYLINK_PCS_NEG_NONE; + break; + } + + return neg_mode; +} + static void phylink_major_config(struct phylink *pl, bool restart, const struct phylink_link_state *state) { @@ -1640,7 +1679,7 @@ struct phylink *phylink_create(struct phylink_config *config, __set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); timer_setup(&pl->link_poll, phylink_fixed_poll, 0); - bitmap_fill(pl->supported, __ETHTOOL_LINK_MODE_MASK_NBITS); + linkmode_fill(pl->supported); linkmode_copy(pl->link_config.advertising, pl->supported); phylink_validate(pl, pl->supported, &pl->link_config); @@ -1739,31 +1778,55 @@ static void phylink_phy_change(struct phy_device *phydev, bool up) phylink_pause_to_str(pl->phy_state.pause)); } -static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy, - phy_interface_t interface) +static int phylink_validate_phy(struct phylink *pl, struct phy_device *phy, + unsigned long *supported, + struct phylink_link_state *state) { - struct phylink_link_state config; - __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); - char *irq_str; - int ret; + DECLARE_PHY_INTERFACE_MASK(interfaces); - /* - * 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. + /* If the PHY provides a bitmap of the interfaces it will be using + * depending on the negotiated media speeds, use this to validate + * which ethtool link modes can be used. */ - phy_support_asym_pause(phy); + if (!phy_interface_empty(phy->possible_interfaces)) { + /* We only care about the union of the PHY's interfaces and + * those which the host supports. + */ + phy_interface_and(interfaces, phy->possible_interfaces, + pl->config->supported_interfaces); - memset(&config, 0, sizeof(config)); - linkmode_copy(supported, phy->supported); - linkmode_copy(config.advertising, phy->advertising); + if (phy_interface_empty(interfaces)) { + phylink_err(pl, "PHY has no common interfaces\n"); + return -EINVAL; + } + + if (phy_on_sfp(phy)) { + /* If the PHY is on a SFP, limit the interfaces to + * those that can be used with a SFP module. + */ + phy_interface_and(interfaces, interfaces, + phylink_sfp_interfaces); + + if (phy_interface_empty(interfaces)) { + phylink_err(pl, "SFP PHY's possible interfaces becomes empty\n"); + return -EINVAL; + } + } + + phylink_dbg(pl, "PHY %s uses interfaces %*pbl, validating %*pbl\n", + phydev_name(phy), + (int)PHY_INTERFACE_MODE_MAX, + phy->possible_interfaces, + (int)PHY_INTERFACE_MODE_MAX, interfaces); + + return phylink_validate_mask(pl, phy, supported, state, + interfaces); + } /* Check whether we would use rate matching for the proposed interface * mode. */ - config.rate_matching = phy_get_rate_matching(phy, interface); + state->rate_matching = phy_get_rate_matching(phy, state->interface); /* Clause 45 PHYs may switch their Serdes lane between, e.g. 10GBASE-R, * 5GBASE-R, 2500BASE-X and SGMII if they are not using rate matching. @@ -1776,15 +1839,38 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy, * against all interface modes, which may lead to more ethtool link * modes being advertised than are actually supported. */ - if (phy->is_c45 && config.rate_matching == RATE_MATCH_NONE && - 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; + if (phy->is_c45 && state->rate_matching == RATE_MATCH_NONE && + state->interface != PHY_INTERFACE_MODE_RXAUI && + state->interface != PHY_INTERFACE_MODE_XAUI && + state->interface != PHY_INTERFACE_MODE_USXGMII) + state->interface = PHY_INTERFACE_MODE_NA; - ret = phylink_validate(pl, supported, &config); + return phylink_validate(pl, supported, state); +} + +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; + + /* + * 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); + + memset(&config, 0, sizeof(config)); + linkmode_copy(supported, phy->supported); + linkmode_copy(config.advertising, phy->advertising); + config.interface = interface; + + ret = phylink_validate_phy(pl, phy, supported, &config); if (ret) { phylink_warn(pl, "validation of %s with support %*pb and advertisement %*pb failed: %pe\n", phy_modes(config.interface), @@ -3005,19 +3091,6 @@ static void phylink_sfp_detach(void *upstream, struct sfp_bus *bus) pl->netdev->sfp_bus = NULL; } -static const phy_interface_t phylink_sfp_interface_preference[] = { - PHY_INTERFACE_MODE_25GBASER, - PHY_INTERFACE_MODE_USXGMII, - PHY_INTERFACE_MODE_10GBASER, - PHY_INTERFACE_MODE_5GBASER, - PHY_INTERFACE_MODE_2500BASEX, - PHY_INTERFACE_MODE_SGMII, - PHY_INTERFACE_MODE_1000BASEX, - PHY_INTERFACE_MODE_100BASEX, -}; - -static DECLARE_PHY_INTERFACE_MASK(phylink_sfp_interfaces); - static phy_interface_t phylink_choose_sfp_interface(struct phylink *pl, const unsigned long *intf) { @@ -3160,7 +3233,8 @@ static int phylink_sfp_config_optical(struct phylink *pl) /* For all the interfaces that are supported, reduce the sfp_support * mask to only those link modes that can be supported. */ - ret = phylink_validate_mask(pl, pl->sfp_support, &config, interfaces); + ret = phylink_validate_mask(pl, NULL, pl->sfp_support, &config, + interfaces); if (ret) { phylink_err(pl, "unsupported SFP module: validation with support %*pb failed\n", __ETHTOOL_LINK_MODE_MASK_NBITS, support); diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c index 208a9393c2..db39dec7f2 100644 --- a/drivers/net/phy/sfp-bus.c +++ b/drivers/net/phy/sfp-bus.c @@ -151,10 +151,6 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, unsigned int br_min, br_nom, br_max; __ETHTOOL_DECLARE_LINK_MODE_MASK(modes) = { 0, }; - phylink_set(modes, Autoneg); - phylink_set(modes, Pause); - phylink_set(modes, Asym_Pause); - /* Decode the bitrate information to MBd */ br_min = br_nom = br_max = 0; if (id->base.br_nominal) { @@ -328,7 +324,7 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, * modules use 2500Mbaud rather than 3100 or 3200Mbaud for * 2500BASE-X, so we allow some slack here. */ - if (bitmap_empty(modes, __ETHTOOL_LINK_MODE_MASK_NBITS) && br_nom) { + if (linkmode_empty(modes) && br_nom) { if (br_min <= 1300 && br_max >= 1200) { phylink_set(modes, 1000baseX_Full); __set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces); @@ -339,6 +335,10 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, } } + phylink_set(modes, Autoneg); + phylink_set(modes, Pause); + phylink_set(modes, Asym_Pause); + if (bus->sfp_quirk && bus->sfp_quirk->modes) bus->sfp_quirk->modes(id, modes, interfaces); diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index 5468bd209f..f75c9eb395 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -191,7 +191,7 @@ static const enum gpiod_flags gpio_flags[] = { * R_PHY_RETRY is the number of attempts. */ #define T_PHY_RETRY msecs_to_jiffies(50) -#define R_PHY_RETRY 12 +#define R_PHY_RETRY 25 /* 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 @@ -275,6 +275,7 @@ struct sfp { 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; unsigned int rs_threshold_kbd; @@ -372,18 +373,28 @@ static void sfp_fixup_10gbaset_30m(struct sfp *sfp) sfp->id.base.extended_cc = SFF8024_ECC_10GBASE_T_SR; } -static void sfp_fixup_rollball_proto(struct sfp *sfp, unsigned int secs) +static void sfp_fixup_rollball(struct sfp *sfp) { sfp->mdio_protocol = MDIO_I2C_ROLLBALL; - sfp->module_t_wait = msecs_to_jiffies(secs * 1000); + + /* RollBall modules may disallow access to PHY registers for up to 25 + * seconds, and the reads return 0xffff before that. Increase the time + * between PHY probe retries from 50ms to 1s so that we will wait for + * the PHY for a sufficient amount of time. + */ + sfp->phy_t_retry = msecs_to_jiffies(1000); } static void sfp_fixup_fs_10gt(struct sfp *sfp) { sfp_fixup_10gbaset_30m(sfp); + sfp_fixup_rollball(sfp); - // These SFPs need 4 seconds before the PHY can be accessed - sfp_fixup_rollball_proto(sfp, 4); + /* 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) @@ -395,12 +406,6 @@ static void sfp_fixup_halny_gsfp(struct sfp *sfp) sfp->state_hw_mask &= ~(SFP_F_TX_FAULT | SFP_F_LOS); } -static void sfp_fixup_rollball(struct sfp *sfp) -{ - // Rollball SFPs need 25 seconds before the PHY can be accessed - sfp_fixup_rollball_proto(sfp, 25); -} - static void sfp_fixup_rollball_cc(struct sfp *sfp) { sfp_fixup_rollball(sfp); @@ -2332,6 +2337,7 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) 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; @@ -2629,7 +2635,11 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event) 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); + sfp_sm_next(sfp, SFP_S_INIT_PHY, + sfp->phy_t_retry); + dev_dbg(sfp->dev, + "no PHY detected, %u tries left\n", + sfp->sm_phy_retries); break; } else { dev_info(sfp->dev, "no PHY detected\n"); @@ -3096,7 +3106,7 @@ static int sfp_probe(struct platform_device *pdev) return 0; } -static int sfp_remove(struct platform_device *pdev) +static void sfp_remove(struct platform_device *pdev) { struct sfp *sfp = platform_get_drvdata(pdev); @@ -3106,8 +3116,6 @@ static int sfp_remove(struct platform_device *pdev) rtnl_lock(); sfp_sm_event(sfp, SFP_E_REMOVE); rtnl_unlock(); - - return 0; } static void sfp_shutdown(struct platform_device *pdev) @@ -3128,7 +3136,7 @@ static void sfp_shutdown(struct platform_device *pdev) static struct platform_driver sfp_driver = { .probe = sfp_probe, - .remove = sfp_remove, + .remove_new = sfp_remove, .shutdown = sfp_shutdown, .driver = { .name = "sfp", diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index 1c7306a1af..150aea7c9c 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -508,7 +508,7 @@ static void smsc_get_strings(struct phy_device *phydev, u8 *data) int i; for (i = 0; i < ARRAY_SIZE(smsc_hw_stats); i++) - ethtool_sprintf(&data, "%s", smsc_hw_stats[i].string); + ethtool_puts(&data, smsc_hw_stats[i].string); } static u64 smsc_get_stat(struct phy_device *phydev, int i) |