diff options
Diffstat (limited to 'drivers/net/phy/phy_device.c')
| -rw-r--r-- | drivers/net/phy/phy_device.c | 1361 |
1 files changed, 889 insertions, 472 deletions
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 716870a4499c..81984d4ebb7c 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -19,15 +19,21 @@ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/kernel.h> +#include <linux/list.h> #include <linux/mdio.h> #include <linux/mii.h> #include <linux/mm.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/netdevice.h> #include <linux/phy.h> +#include <linux/phylib_stubs.h> #include <linux/phy_led_triggers.h> +#include <linux/phy_link_topology.h> #include <linux/pse-pd/pse.h> #include <linux/property.h> +#include <linux/ptp_clock_kernel.h> +#include <linux/rtnetlink.h> #include <linux/sfp.h> #include <linux/skbuff.h> #include <linux/slab.h> @@ -35,31 +41,49 @@ #include <linux/uaccess.h> #include <linux/unistd.h> +#include "phylib-internal.h" +#include "phy-caps.h" + MODULE_DESCRIPTION("PHY library"); MODULE_AUTHOR("Andy Fleming"); MODULE_LICENSE("GPL"); +#define PHY_ANY_ID "MATCH ANY PHY" +#define PHY_ANY_UID 0xffffffff + +struct phy_fixup { + struct list_head list; + char bus_id[MII_BUS_ID_SIZE + 3]; + u32 phy_uid; + u32 phy_uid_mask; + int (*run)(struct phy_device *phydev); +}; + +static struct phy_driver genphy_c45_driver = { + .phy_id = 0xffffffff, + .phy_id_mask = 0xffffffff, + .name = "Generic Clause 45 PHY", + .read_status = genphy_c45_read_status, +}; + __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_basic_features) __ro_after_init; EXPORT_SYMBOL_GPL(phy_basic_features); __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_basic_t1_features) __ro_after_init; EXPORT_SYMBOL_GPL(phy_basic_t1_features); +__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_basic_t1s_p2mp_features) __ro_after_init; +EXPORT_SYMBOL_GPL(phy_basic_t1s_p2mp_features); + __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_gbit_features) __ro_after_init; EXPORT_SYMBOL_GPL(phy_gbit_features); __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_gbit_fibre_features) __ro_after_init; EXPORT_SYMBOL_GPL(phy_gbit_fibre_features); -__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_gbit_all_ports_features) __ro_after_init; -EXPORT_SYMBOL_GPL(phy_gbit_all_ports_features); - __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_10gbit_features) __ro_after_init; EXPORT_SYMBOL_GPL(phy_10gbit_features); -__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_10gbit_fec_features) __ro_after_init; -EXPORT_SYMBOL_GPL(phy_10gbit_fec_features); - const int phy_basic_ports_array[3] = { ETHTOOL_LINK_MODE_Autoneg_BIT, ETHTOOL_LINK_MODE_TP_BIT, @@ -67,12 +91,7 @@ const int phy_basic_ports_array[3] = { }; EXPORT_SYMBOL_GPL(phy_basic_ports_array); -const int phy_fibre_port_array[1] = { - ETHTOOL_LINK_MODE_FIBRE_BIT, -}; -EXPORT_SYMBOL_GPL(phy_fibre_port_array); - -const int phy_all_ports_features_array[7] = { +static const int phy_all_ports_features_array[7] __initconst = { ETHTOOL_LINK_MODE_Autoneg_BIT, ETHTOOL_LINK_MODE_TP_BIT, ETHTOOL_LINK_MODE_MII_BIT, @@ -81,49 +100,51 @@ const int phy_all_ports_features_array[7] = { ETHTOOL_LINK_MODE_BNC_BIT, ETHTOOL_LINK_MODE_Backplane_BIT, }; -EXPORT_SYMBOL_GPL(phy_all_ports_features_array); -const int phy_10_100_features_array[4] = { +static const int phy_10_100_features_array[4] __initconst = { ETHTOOL_LINK_MODE_10baseT_Half_BIT, ETHTOOL_LINK_MODE_10baseT_Full_BIT, ETHTOOL_LINK_MODE_100baseT_Half_BIT, ETHTOOL_LINK_MODE_100baseT_Full_BIT, }; -EXPORT_SYMBOL_GPL(phy_10_100_features_array); -const int phy_basic_t1_features_array[3] = { +static const int phy_basic_t1_features_array[3] __initconst = { ETHTOOL_LINK_MODE_TP_BIT, ETHTOOL_LINK_MODE_10baseT1L_Full_BIT, ETHTOOL_LINK_MODE_100baseT1_Full_BIT, }; -EXPORT_SYMBOL_GPL(phy_basic_t1_features_array); -const int phy_gbit_features_array[2] = { +static const int phy_basic_t1s_p2mp_features_array[2] __initconst = { + ETHTOOL_LINK_MODE_TP_BIT, + ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT, +}; + +static const int phy_gbit_features_array[2] __initconst = { ETHTOOL_LINK_MODE_1000baseT_Half_BIT, ETHTOOL_LINK_MODE_1000baseT_Full_BIT, }; -EXPORT_SYMBOL_GPL(phy_gbit_features_array); -const int phy_10gbit_features_array[1] = { +static const int phy_eee_cap1_features_array[] __initconst = { + ETHTOOL_LINK_MODE_100baseT_Full_BIT, + ETHTOOL_LINK_MODE_1000baseT_Full_BIT, ETHTOOL_LINK_MODE_10000baseT_Full_BIT, + ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, + ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, + ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, }; -EXPORT_SYMBOL_GPL(phy_10gbit_features_array); -static const int phy_10gbit_fec_features_array[1] = { - ETHTOOL_LINK_MODE_10000baseR_FEC_BIT, -}; - -__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_10gbit_full_features) __ro_after_init; -EXPORT_SYMBOL_GPL(phy_10gbit_full_features); +__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_eee_cap1_features) __ro_after_init; +EXPORT_SYMBOL_GPL(phy_eee_cap1_features); -static const int phy_10gbit_full_features_array[] = { - ETHTOOL_LINK_MODE_10baseT_Full_BIT, - ETHTOOL_LINK_MODE_100baseT_Full_BIT, - ETHTOOL_LINK_MODE_1000baseT_Full_BIT, - ETHTOOL_LINK_MODE_10000baseT_Full_BIT, +static const int phy_eee_cap2_features_array[] __initconst = { + ETHTOOL_LINK_MODE_2500baseT_Full_BIT, + ETHTOOL_LINK_MODE_5000baseT_Full_BIT, }; -static void features_init(void) +__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_eee_cap2_features) __ro_after_init; +EXPORT_SYMBOL_GPL(phy_eee_cap2_features); + +static void __init features_init(void) { /* 10/100 half/full*/ linkmode_set_bit_array(phy_basic_ports_array, @@ -138,6 +159,11 @@ static void features_init(void) ARRAY_SIZE(phy_basic_t1_features_array), phy_basic_t1_features); + /* 10 half, P2MP, TP */ + linkmode_set_bit_array(phy_basic_t1s_p2mp_features_array, + ARRAY_SIZE(phy_basic_t1s_p2mp_features_array), + phy_basic_t1s_p2mp_features); + /* 10/100 half/full + 1000 half/full */ linkmode_set_bit_array(phy_basic_ports_array, ARRAY_SIZE(phy_basic_ports_array), @@ -159,20 +185,7 @@ static void features_init(void) linkmode_set_bit_array(phy_gbit_features_array, ARRAY_SIZE(phy_gbit_features_array), phy_gbit_fibre_features); - linkmode_set_bit_array(phy_fibre_port_array, - ARRAY_SIZE(phy_fibre_port_array), - phy_gbit_fibre_features); - - /* 10/100 half/full + 1000 half/full + TP/MII/FIBRE/AUI/BNC/Backplane*/ - linkmode_set_bit_array(phy_all_ports_features_array, - ARRAY_SIZE(phy_all_ports_features_array), - phy_gbit_all_ports_features); - linkmode_set_bit_array(phy_10_100_features_array, - ARRAY_SIZE(phy_10_100_features_array), - phy_gbit_all_ports_features); - linkmode_set_bit_array(phy_gbit_features_array, - ARRAY_SIZE(phy_gbit_features_array), - phy_gbit_all_ports_features); + linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phy_gbit_fibre_features); /* 10/100 half/full + 1000 half/full + 10G full*/ linkmode_set_bit_array(phy_all_ports_features_array, @@ -184,21 +197,16 @@ static void features_init(void) linkmode_set_bit_array(phy_gbit_features_array, ARRAY_SIZE(phy_gbit_features_array), phy_10gbit_features); - linkmode_set_bit_array(phy_10gbit_features_array, - ARRAY_SIZE(phy_10gbit_features_array), - phy_10gbit_features); + linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, + phy_10gbit_features); + + linkmode_set_bit_array(phy_eee_cap1_features_array, + ARRAY_SIZE(phy_eee_cap1_features_array), + phy_eee_cap1_features); + linkmode_set_bit_array(phy_eee_cap2_features_array, + ARRAY_SIZE(phy_eee_cap2_features_array), + phy_eee_cap2_features); - /* 10/100/1000/10G full */ - linkmode_set_bit_array(phy_all_ports_features_array, - ARRAY_SIZE(phy_all_ports_features_array), - phy_10gbit_full_features); - linkmode_set_bit_array(phy_10gbit_full_features_array, - ARRAY_SIZE(phy_10gbit_full_features_array), - phy_10gbit_full_features); - /* 10G FEC only */ - linkmode_set_bit_array(phy_10gbit_fec_features_array, - ARRAY_SIZE(phy_10gbit_fec_features_array), - phy_10gbit_fec_features); } void phy_device_free(struct phy_device *phydev) @@ -234,6 +242,64 @@ static struct phy_driver genphy_driver; static LIST_HEAD(phy_fixup_list); static DEFINE_MUTEX(phy_fixup_lock); +static bool phy_drv_wol_enabled(struct phy_device *phydev) +{ + struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL }; + + phy_ethtool_get_wol(phydev, &wol); + + return wol.wolopts != 0; +} + +bool phy_may_wakeup(struct phy_device *phydev) +{ + /* If the PHY is using driver-model based wakeup, use that state. */ + if (phy_can_wakeup(phydev)) + return device_may_wakeup(&phydev->mdio.dev); + + return phy_drv_wol_enabled(phydev); +} +EXPORT_SYMBOL_GPL(phy_may_wakeup); + +static void phy_link_change(struct phy_device *phydev, bool up) +{ + struct net_device *netdev = phydev->attached_dev; + + if (up) + netif_carrier_on(netdev); + else + netif_carrier_off(netdev); + phydev->adjust_link(netdev); + if (phydev->mii_ts && phydev->mii_ts->link_state) + phydev->mii_ts->link_state(phydev->mii_ts, phydev); +} + +/** + * phy_uses_state_machine - test whether consumer driver uses PAL state machine + * @phydev: the target PHY device structure + * + * Ultimately, this aims to indirectly determine whether the PHY is attached + * to a consumer which uses the state machine by calling phy_start() and + * phy_stop(). + * + * When the PHY driver consumer uses phylib, it must have previously called + * phy_connect_direct() or one of its derivatives, so that phy_prepare_link() + * has set up a hook for monitoring state changes. + * + * When the PHY driver is used by the MAC driver consumer through phylink (the + * only other provider of a phy_link_change() method), using the PHY state + * machine is not optional. + * + * Return: true if consumer calls phy_start() and phy_stop(), false otherwise. + */ +static bool phy_uses_state_machine(struct phy_device *phydev) +{ + if (phydev->phy_link_change == phy_link_change) + return phydev->attached_dev && phydev->adjust_link; + + return !!phydev->phy_link_change; +} + static bool mdio_bus_phy_may_suspend(struct phy_device *phydev) { struct device_driver *drv = phydev->mdio.dev.driver; @@ -243,6 +309,12 @@ static bool mdio_bus_phy_may_suspend(struct phy_device *phydev) if (!drv || !phydrv->suspend) return false; + /* If the PHY on the mido bus is not attached but has WOL enabled + * we cannot suspend the PHY. + */ + if (!netdev && phy_may_wakeup(phydev)) + return false; + /* PHY not attached? May suspend if the PHY has not already been * suspended as part of a prior call to phy_disconnect() -> * phy_detach() -> phy_suspend() because the parent netdev might be the @@ -251,7 +323,7 @@ static bool mdio_bus_phy_may_suspend(struct phy_device *phydev) if (!netdev) goto out; - if (netdev->wol_enabled) + if (netdev->ethtool->wol_enabled) return false; /* As long as not all affected network drivers support the @@ -294,7 +366,7 @@ static __maybe_unused int mdio_bus_phy_suspend(struct device *dev) * may call phy routines that try to grab the same lock, and that may * lead to a deadlock. */ - if (phydev->attached_dev && phydev->adjust_link) + if (phy_uses_state_machine(phydev)) phy_stop_machine(phydev); if (!mdio_bus_phy_may_suspend(phydev)) @@ -348,7 +420,7 @@ no_resume: } } - if (phydev->attached_dev && phydev->adjust_link) + if (phy_uses_state_machine(phydev)) phy_start_machine(phydev); return 0; @@ -366,8 +438,8 @@ static SIMPLE_DEV_PM_OPS(mdio_bus_phy_pm_ops, mdio_bus_phy_suspend, * comparison * @run: The actual code to be run when a matching PHY is found */ -int phy_register_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask, - int (*run)(struct phy_device *)) +static int phy_register_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask, + int (*run)(struct phy_device *)) { struct phy_fixup *fixup = kzalloc(sizeof(*fixup), GFP_KERNEL); @@ -385,7 +457,6 @@ int phy_register_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask, return 0; } -EXPORT_SYMBOL(phy_register_fixup); /* Registers a fixup to be run on any PHY with the UID in phy_uid */ int phy_register_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask, @@ -422,8 +493,7 @@ int phy_unregister_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask) fixup = list_entry(pos, struct phy_fixup, list); if ((!strcmp(fixup->bus_id, bus_id)) && - ((fixup->phy_uid & phy_uid_mask) == - (phy_uid & phy_uid_mask))) { + phy_id_compare(fixup->phy_uid, phy_uid, phy_uid_mask)) { list_del(&fixup->list); kfree(fixup); ret = 0; @@ -459,8 +529,8 @@ static int phy_needs_fixup(struct phy_device *phydev, struct phy_fixup *fixup) if (strcmp(fixup->bus_id, PHY_ANY_ID) != 0) return 0; - if ((fixup->phy_uid & fixup->phy_uid_mask) != - (phydev->phy_id & fixup->phy_uid_mask)) + if (!phy_id_compare(phydev->phy_id, fixup->phy_uid, + fixup->phy_uid_mask)) if (fixup->phy_uid != PHY_ANY_UID) return 0; @@ -489,34 +559,55 @@ static int phy_scan_fixups(struct phy_device *phydev) return 0; } -static int phy_bus_match(struct device *dev, struct device_driver *drv) +/** + * genphy_match_phy_device - match a PHY device with a PHY driver + * @phydev: target phy_device struct + * @phydrv: target phy_driver struct + * + * Description: Checks whether the given PHY device matches the specified + * PHY driver. For Clause 45 PHYs, iterates over the available device + * identifiers and compares them against the driver's expected PHY ID, + * applying the provided mask. For Clause 22 PHYs, a direct ID comparison + * is performed. + * + * Return: 1 if the PHY device matches the driver, 0 otherwise. + */ +int genphy_match_phy_device(struct phy_device *phydev, + const struct phy_driver *phydrv) { - struct phy_device *phydev = to_phy_device(dev); - struct phy_driver *phydrv = to_phy_driver(drv); - const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids); - int i; - - if (!(phydrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY)) - return 0; - - if (phydrv->match_phy_device) - return phydrv->match_phy_device(phydev); - if (phydev->is_c45) { + const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids); + int i; + for (i = 1; i < num_ids; i++) { if (phydev->c45_ids.device_ids[i] == 0xffffffff) continue; - if ((phydrv->phy_id & phydrv->phy_id_mask) == - (phydev->c45_ids.device_ids[i] & - phydrv->phy_id_mask)) + if (phy_id_compare(phydev->c45_ids.device_ids[i], + phydrv->phy_id, phydrv->phy_id_mask)) return 1; } + return 0; - } else { - return (phydrv->phy_id & phydrv->phy_id_mask) == - (phydev->phy_id & phydrv->phy_id_mask); } + + return phy_id_compare(phydev->phy_id, phydrv->phy_id, + phydrv->phy_id_mask); +} +EXPORT_SYMBOL_GPL(genphy_match_phy_device); + +static int phy_bus_match(struct device *dev, const struct device_driver *drv) +{ + struct phy_device *phydev = to_phy_device(dev); + const struct phy_driver *phydrv = to_phy_driver(drv); + + if (!(phydrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY)) + return 0; + + if (phydrv->match_phy_device) + return phydrv->match_phy_device(phydev, phydrv); + + return genphy_match_phy_device(phydev, phydrv); } static ssize_t @@ -534,7 +625,7 @@ phy_interface_show(struct device *dev, struct device_attribute *attr, char *buf) struct phy_device *phydev = to_phy_device(dev); const char *mode = NULL; - if (phy_is_internal(phydev)) + if (phydev->is_internal) mode = "internal"; else mode = phy_modes(phydev->interface); @@ -570,11 +661,119 @@ static struct attribute *phy_dev_attrs[] = { &dev_attr_phy_dev_flags.attr, NULL, }; -ATTRIBUTE_GROUPS(phy_dev); + +static const struct attribute_group phy_dev_group = { + .attrs = phy_dev_attrs, +}; + +#define MMD_DEVICE_ID_ATTR(n) \ +static ssize_t mmd##n##_device_id_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct phy_device *phydev = to_phy_device(dev); \ + return sysfs_emit(buf, "0x%.8lx\n", \ + (unsigned long)phydev->c45_ids.device_ids[n]); \ +} \ +static DEVICE_ATTR_RO(mmd##n##_device_id) + +MMD_DEVICE_ID_ATTR(1); +MMD_DEVICE_ID_ATTR(2); +MMD_DEVICE_ID_ATTR(3); +MMD_DEVICE_ID_ATTR(4); +MMD_DEVICE_ID_ATTR(5); +MMD_DEVICE_ID_ATTR(6); +MMD_DEVICE_ID_ATTR(7); +MMD_DEVICE_ID_ATTR(8); +MMD_DEVICE_ID_ATTR(9); +MMD_DEVICE_ID_ATTR(10); +MMD_DEVICE_ID_ATTR(11); +MMD_DEVICE_ID_ATTR(12); +MMD_DEVICE_ID_ATTR(13); +MMD_DEVICE_ID_ATTR(14); +MMD_DEVICE_ID_ATTR(15); +MMD_DEVICE_ID_ATTR(16); +MMD_DEVICE_ID_ATTR(17); +MMD_DEVICE_ID_ATTR(18); +MMD_DEVICE_ID_ATTR(19); +MMD_DEVICE_ID_ATTR(20); +MMD_DEVICE_ID_ATTR(21); +MMD_DEVICE_ID_ATTR(22); +MMD_DEVICE_ID_ATTR(23); +MMD_DEVICE_ID_ATTR(24); +MMD_DEVICE_ID_ATTR(25); +MMD_DEVICE_ID_ATTR(26); +MMD_DEVICE_ID_ATTR(27); +MMD_DEVICE_ID_ATTR(28); +MMD_DEVICE_ID_ATTR(29); +MMD_DEVICE_ID_ATTR(30); +MMD_DEVICE_ID_ATTR(31); + +static struct attribute *phy_mmd_attrs[] = { + &dev_attr_mmd1_device_id.attr, + &dev_attr_mmd2_device_id.attr, + &dev_attr_mmd3_device_id.attr, + &dev_attr_mmd4_device_id.attr, + &dev_attr_mmd5_device_id.attr, + &dev_attr_mmd6_device_id.attr, + &dev_attr_mmd7_device_id.attr, + &dev_attr_mmd8_device_id.attr, + &dev_attr_mmd9_device_id.attr, + &dev_attr_mmd10_device_id.attr, + &dev_attr_mmd11_device_id.attr, + &dev_attr_mmd12_device_id.attr, + &dev_attr_mmd13_device_id.attr, + &dev_attr_mmd14_device_id.attr, + &dev_attr_mmd15_device_id.attr, + &dev_attr_mmd16_device_id.attr, + &dev_attr_mmd17_device_id.attr, + &dev_attr_mmd18_device_id.attr, + &dev_attr_mmd19_device_id.attr, + &dev_attr_mmd20_device_id.attr, + &dev_attr_mmd21_device_id.attr, + &dev_attr_mmd22_device_id.attr, + &dev_attr_mmd23_device_id.attr, + &dev_attr_mmd24_device_id.attr, + &dev_attr_mmd25_device_id.attr, + &dev_attr_mmd26_device_id.attr, + &dev_attr_mmd27_device_id.attr, + &dev_attr_mmd28_device_id.attr, + &dev_attr_mmd29_device_id.attr, + &dev_attr_mmd30_device_id.attr, + &dev_attr_mmd31_device_id.attr, + NULL +}; + +static umode_t phy_mmd_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = kobj_to_dev(kobj); + struct phy_device *phydev = to_phy_device(dev); + const int i = index + 1; + + if (!phydev->is_c45) + return 0; + if (i >= ARRAY_SIZE(phydev->c45_ids.device_ids) || + phydev->c45_ids.device_ids[i] == 0xffffffff) + return 0; + + return attr->mode; +} + +static const struct attribute_group phy_mmd_group = { + .name = "c45_phy_ids", + .attrs = phy_mmd_attrs, + .is_visible = phy_mmd_is_visible, +}; + +static const struct attribute_group *phy_device_groups[] = { + &phy_dev_group, + &phy_mmd_group, + NULL, +}; static const struct device_type mdio_bus_phy_type = { .name = "PHY", - .groups = phy_dev_groups, + .groups = phy_device_groups, .release = phy_device_release, .pm = pm_ptr(&mdio_bus_phy_pm_ops), }; @@ -622,11 +821,12 @@ 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; - dev->pause = 0; - dev->asym_pause = 0; + dev->pause = false; + dev->asym_pause = false; dev->link = 0; dev->port = PORT_TP; dev->interface = PHY_INTERFACE_MODE_GMII; @@ -644,6 +844,7 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id, device_initialize(&mdiodev->dev); dev->state = PHY_DOWN; + INIT_LIST_HEAD(&dev->leds); mutex_init(&dev->lock); INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine); @@ -746,7 +947,7 @@ static int get_phy_c45_devs_in_pkg(struct mii_bus *bus, int addr, int dev_addr, * and identifiers in @c45_ids. * * Returns zero on success, %-EIO on bus access error, or %-ENODEV if - * the "devices in package" is invalid. + * the "devices in package" is invalid or no device responds. */ static int get_phy_c45_ids(struct mii_bus *bus, int addr, struct phy_c45_device_ids *c45_ids) @@ -769,7 +970,11 @@ static int get_phy_c45_ids(struct mii_bus *bus, int addr, */ ret = phy_c45_probe_present(bus, addr, i); if (ret < 0) - return -EIO; + /* returning -ENODEV doesn't stop bus + * scanning + */ + return (phy_reg == -EIO || + phy_reg == -ENODEV) ? -ENODEV : -EIO; if (!ret) continue; @@ -932,7 +1137,7 @@ struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45) * probe with C45 to see if we're able to get a valid PHY ID in the C45 * space, if successful, create the C45 PHY device. */ - if (!is_c45 && phy_id == 0 && bus->probe_capabilities >= MDIOBUS_C45) { + if (!is_c45 && phy_id == 0 && bus->read_c45) { r = get_phy_c45_ids(bus, addr, &c45_ids); if (!r) return phy_device_create(bus, addr, phy_id, @@ -1019,35 +1224,24 @@ int phy_get_c45_ids(struct phy_device *phydev) EXPORT_SYMBOL(phy_get_c45_ids); /** - * phy_find_first - finds the first PHY device on the bus + * phy_find_next - finds the next PHY device on the bus * @bus: the target MII bus + * @pos: cursor + * + * Return: next phy_device on the bus, or NULL */ -struct phy_device *phy_find_first(struct mii_bus *bus) +struct phy_device *phy_find_next(struct mii_bus *bus, struct phy_device *pos) { - struct phy_device *phydev; - int addr; + for (int addr = pos ? pos->mdio.addr + 1 : 0; + addr < PHY_MAX_ADDR; addr++) { + struct phy_device *phydev = mdiobus_get_phy(bus, addr); - for (addr = 0; addr < PHY_MAX_ADDR; addr++) { - phydev = mdiobus_get_phy(bus, addr); if (phydev) return phydev; } return NULL; } -EXPORT_SYMBOL(phy_find_first); - -static void phy_link_change(struct phy_device *phydev, bool up) -{ - struct net_device *netdev = phydev->attached_dev; - - if (up) - netif_carrier_on(netdev); - else - netif_carrier_off(netdev); - phydev->adjust_link(netdev); - if (phydev->mii_ts && phydev->mii_ts->link_state) - phydev->mii_ts->link_state(phydev->mii_ts, phydev); -} +EXPORT_SYMBOL_GPL(phy_find_next); /** * phy_prepare_link - prepares the PHY layer to monitor link status @@ -1202,18 +1396,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) @@ -1320,6 +1515,48 @@ phy_standalone_show(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR_RO(phy_standalone); /** + * phy_sfp_connect_phy - Connect the SFP module's PHY to the upstream PHY + * @upstream: pointer to the upstream phy device + * @phy: pointer to the SFP module's phy device + * + * This helper allows keeping track of PHY devices on the link. It adds the + * SFP module's phy to the phy namespace of the upstream phy + * + * Return: 0 on success, otherwise a negative error code. + */ +int phy_sfp_connect_phy(void *upstream, struct phy_device *phy) +{ + struct phy_device *phydev = upstream; + struct net_device *dev = phydev->attached_dev; + + if (dev) + return phy_link_topo_add_phy(dev, phy, PHY_UPSTREAM_PHY, phydev); + + return 0; +} +EXPORT_SYMBOL(phy_sfp_connect_phy); + +/** + * phy_sfp_disconnect_phy - Disconnect the SFP module's PHY from the upstream PHY + * @upstream: pointer to the upstream phy device + * @phy: pointer to the SFP module's phy device + * + * This helper allows keeping track of PHY devices on the link. It removes the + * SFP module's phy to the phy namespace of the upstream phy. As the module phy + * will be destroyed, re-inserting the same module will add a new phy with a + * new index. + */ +void phy_sfp_disconnect_phy(void *upstream, struct phy_device *phy) +{ + struct phy_device *phydev = upstream; + struct net_device *dev = phydev->attached_dev; + + if (dev) + phy_link_topo_del_phy(dev, phy); +} +EXPORT_SYMBOL(phy_sfp_disconnect_phy); + +/** * phy_sfp_attach - attach the SFP bus to the PHY upstream network device * @upstream: pointer to the phy device * @bus: sfp bus representing cage being attached @@ -1378,6 +1615,11 @@ int phy_sfp_probe(struct phy_device *phydev, } EXPORT_SYMBOL(phy_sfp_probe); +static bool phy_drv_supports_irq(const 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 @@ -1399,7 +1641,6 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, struct mii_bus *bus = phydev->mdio.bus; struct device *d = &phydev->mdio.dev; struct module *ndev_owner = NULL; - bool using_genphy = false; int err; /* For Ethernet device drivers that register their own MDIO bus, we @@ -1425,7 +1666,7 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, else d->driver = &genphy_driver.mdiodrv.driver; - using_genphy = true; + phydev->is_genphy_driven = 1; } if (!try_module_get(d->driver->owner)) { @@ -1434,7 +1675,7 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, goto error_put_device; } - if (using_genphy) { + if (phydev->is_genphy_driven) { err = d->driver->probe(d); if (err >= 0) err = device_bind_driver(d); @@ -1456,8 +1697,10 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, if (phydev->sfp_bus_attached) dev->sfp_bus = phydev->sfp_bus; - else if (dev->sfp_bus) - phydev->is_on_sfp_module = true; + + err = phy_link_topo_add_phy(dev, phydev, PHY_UPSTREAM_MAC, dev); + if (err) + goto error; } /* Some Ethernet drivers try to connect to a PHY device before @@ -1487,12 +1730,22 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, phydev->interrupts = PHY_INTERRUPT_DISABLED; + /* PHYs can request to use poll mode even though they have an + * associated interrupt line. This could be the case if they + * detect a broken interrupt handling. + */ + 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 * legacy PORT_MII value. */ - if (using_genphy) + if (phydev->is_genphy_driven) phydev->port = PORT_MII; /* Initial carrier state is off as the phy is about to be @@ -1510,14 +1763,15 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, goto error; phy_resume(phydev); - phy_led_triggers_register(phydev); + if (!phydev->is_on_sfp_module) + phy_led_triggers_register(phydev); /** * If the external phy used by current mac interface is managed by * another mac interface, so we should create a device link between * phy dev and mac dev. */ - if (phydev->mdio.bus->parent && dev->dev.parent != phydev->mdio.bus->parent) + if (dev && phydev->mdio.bus->parent && dev->dev.parent != phydev->mdio.bus->parent) phydev->devlink = device_link_add(dev->dev.parent, &phydev->mdio.dev, DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS); @@ -1530,6 +1784,7 @@ error: error_module_put: module_put(d->driver->owner); + phydev->is_genphy_driven = 0; d->driver = NULL; error_put_device: put_device(d); @@ -1551,7 +1806,6 @@ EXPORT_SYMBOL(phy_attach_direct); struct phy_device *phy_attach(struct net_device *dev, const char *bus_id, phy_interface_t interface) { - struct bus_type *bus = &mdio_bus_type; struct phy_device *phydev; struct device *d; int rc; @@ -1562,7 +1816,7 @@ struct phy_device *phy_attach(struct net_device *dev, const char *bus_id, /* Search the list of PHY devices on the mdio bus for the * PHY with the requested name */ - d = bus_find_device_by_name(bus, NULL, bus_id); + d = bus_find_device_by_name(&mdio_bus_type, NULL, bus_id); if (!d) { pr_err("PHY %s not found\n", bus_id); return ERR_PTR(-ENODEV); @@ -1578,174 +1832,6 @@ struct phy_device *phy_attach(struct net_device *dev, const char *bus_id, } EXPORT_SYMBOL(phy_attach); -static bool phy_driver_is_genphy_kind(struct phy_device *phydev, - struct device_driver *driver) -{ - struct device *d = &phydev->mdio.dev; - bool ret = false; - - if (!phydev->drv) - return ret; - - get_device(d); - ret = d->driver == driver; - put_device(d); - - return ret; -} - -bool phy_driver_is_genphy(struct phy_device *phydev) -{ - return phy_driver_is_genphy_kind(phydev, - &genphy_driver.mdiodrv.driver); -} -EXPORT_SYMBOL_GPL(phy_driver_is_genphy); - -bool phy_driver_is_genphy_10g(struct phy_device *phydev) -{ - return phy_driver_is_genphy_kind(phydev, - &genphy_c45_driver.mdiodrv.driver); -} -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 - * @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 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. - * - * 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 - * allocated. If priv_size is non-zero, the given amount of bytes are - * allocated for the priv member. - * - * 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) -{ - struct mii_bus *bus = phydev->mdio.bus; - struct phy_package_shared *shared; - int ret; - - if (addr < 0 || addr >= PHY_MAX_ADDR) - return -EINVAL; - - mutex_lock(&bus->shared_lock); - shared = bus->shared[addr]; - if (!shared) { - ret = -ENOMEM; - shared = kzalloc(sizeof(*shared), GFP_KERNEL); - if (!shared) - goto err_unlock; - if (priv_size) { - shared->priv = kzalloc(priv_size, GFP_KERNEL); - if (!shared->priv) - goto err_free; - shared->priv_size = priv_size; - } - shared->addr = addr; - refcount_set(&shared->refcnt, 1); - bus->shared[addr] = shared; - } else { - ret = -EINVAL; - if (priv_size && priv_size != shared->priv_size) - goto err_unlock; - refcount_inc(&shared->refcnt); - } - mutex_unlock(&bus->shared_lock); - - phydev->shared = shared; - - return 0; - -err_free: - kfree(shared); -err_unlock: - mutex_unlock(&bus->shared_lock); - return ret; -} -EXPORT_SYMBOL_GPL(phy_package_join); - -/** - * phy_package_leave - leave a common PHY group - * @phydev: target phy_device struct - * - * This leaves a PHY group created by phy_package_join(). If this phydev - * was the last user of the shared data between the group, this data is - * freed. Resets the phydev->shared pointer to NULL. - */ -void phy_package_leave(struct phy_device *phydev) -{ - struct phy_package_shared *shared = phydev->shared; - struct mii_bus *bus = phydev->mdio.bus; - - if (!shared) - return; - - if (refcount_dec_and_mutex_lock(&shared->refcnt, &bus->shared_lock)) { - bus->shared[shared->addr] = NULL; - mutex_unlock(&bus->shared_lock); - kfree(shared->priv); - kfree(shared); - } - - phydev->shared = NULL; -} -EXPORT_SYMBOL_GPL(phy_package_leave); - -static void devm_phy_package_leave(struct device *dev, void *res) -{ - phy_package_leave(*(struct phy_device **)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 - * @priv_size: if non-zero allocate this amount of bytes for private data - * - * Managed phy_package_join(). Shared storage fetched by this function, - * phy_package_leave() is automatically called on driver detach. See - * phy_package_join() for more information. - */ -int devm_phy_package_join(struct device *dev, struct phy_device *phydev, - int addr, size_t priv_size) -{ - struct phy_device **ptr; - int ret; - - ptr = devres_alloc(devm_phy_package_leave, sizeof(*ptr), - GFP_KERNEL); - if (!ptr) - return -ENOMEM; - - ret = phy_package_join(phydev, addr, priv_size); - - if (!ret) { - *ptr = phydev; - devres_add(dev, ptr); - } else { - devres_free(ptr); - } - - return ret; -} -EXPORT_SYMBOL_GPL(devm_phy_package_join); - /** * phy_detach - detach a PHY device from its network device * @phydev: target phy_device struct @@ -1759,8 +1845,10 @@ void phy_detach(struct phy_device *phydev) struct module *ndev_owner = NULL; struct mii_bus *bus; - if (phydev->devlink) + if (phydev->devlink) { device_link_del(phydev->devlink); + phydev->devlink = NULL; + } if (phydev->sysfs_links) { if (dev) @@ -1774,12 +1862,25 @@ void phy_detach(struct phy_device *phydev) phy_suspend(phydev); if (dev) { + struct hwtstamp_provider *hwprov; + + hwprov = rtnl_dereference(dev->hwprov); + /* Disable timestamp if it is the one selected */ + if (hwprov && hwprov->phydev == phydev) { + rcu_assign_pointer(dev->hwprov, NULL); + kfree_rcu(hwprov, rcu_head); + } + phydev->attached_dev->phydev = NULL; phydev->attached_dev = NULL; + phy_link_topo_del_phy(dev, phydev); } + + phydev->phy_link_change = NULL; phydev->phylink = NULL; - phy_led_triggers_unregister(phydev); + if (!phydev->is_on_sfp_module) + phy_led_triggers_unregister(phydev); if (phydev->mdio.dev.driver) module_put(phydev->mdio.dev.driver->owner); @@ -1789,9 +1890,10 @@ void phy_detach(struct phy_device *phydev) * from the generic driver so that there's a chance a * real driver could be loaded */ - if (phy_driver_is_genphy(phydev) || - phy_driver_is_genphy_10g(phydev)) + if (phydev->is_genphy_driven) { device_release_driver(&phydev->mdio.dev); + phydev->is_genphy_driven = 0; + } /* Assert the reset signal */ phy_device_reset(phydev, 1); @@ -1812,20 +1914,20 @@ EXPORT_SYMBOL(phy_detach); int phy_suspend(struct phy_device *phydev) { - struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL }; struct net_device *netdev = phydev->attached_dev; - struct phy_driver *phydrv = phydev->drv; + const struct phy_driver *phydrv = phydev->drv; int ret; - if (phydev->suspended) + if (phydev->suspended || !phydrv) return 0; + phydev->wol_enabled = phy_may_wakeup(phydev) || + (netdev && netdev->ethtool->wol_enabled); /* If the device has WOL enabled, we cannot suspend the PHY */ - phy_ethtool_get_wol(phydev, &wol); - if (wol.wolopts || (netdev && netdev->wol_enabled)) + if (phydev->wol_enabled && !(phydrv->flags & PHY_ALWAYS_CALL_SUSPEND)) return -EBUSY; - if (!phydrv || !phydrv->suspend) + if (!phydrv->suspend) return 0; ret = phydrv->suspend(phydev); @@ -1838,7 +1940,7 @@ EXPORT_SYMBOL(phy_suspend); int __phy_resume(struct phy_device *phydev) { - struct phy_driver *phydrv = phydev->drv; + const struct phy_driver *phydrv = phydev->drv; int ret; lockdep_assert_held(&phydev->lock); @@ -1866,41 +1968,6 @@ int phy_resume(struct phy_device *phydev) } EXPORT_SYMBOL(phy_resume); -int phy_loopback(struct phy_device *phydev, bool enable) -{ - int ret = 0; - - if (!phydev->drv) - return -EIO; - - mutex_lock(&phydev->lock); - - if (enable && phydev->loopback_enabled) { - ret = -EBUSY; - goto out; - } - - if (!enable && !phydev->loopback_enabled) { - ret = -EINVAL; - goto out; - } - - if (phydev->drv->set_loopback) - ret = phydev->drv->set_loopback(phydev, enable); - else - ret = genphy_loopback(phydev, enable); - - if (ret) - goto out; - - phydev->loopback_enabled = enable; - -out: - mutex_unlock(&phydev->lock); - return ret; -} -EXPORT_SYMBOL(phy_loopback); - /** * phy_reset_after_clk_enable - perform a PHY reset if needed * @phydev: target phy_device struct @@ -1930,22 +1997,20 @@ EXPORT_SYMBOL(phy_reset_after_clk_enable); /** * genphy_config_advert - sanitize and advertise auto-negotiation parameters * @phydev: target phy_device struct + * @advert: auto-negotiation parameters to advertise * * Description: Writes MII_ADVERTISE with the appropriate values, * after sanitizing the values to make sure we only advertise * what is supported. Returns < 0 on error, 0 if the PHY's advertisement * hasn't changed, and > 0 if it has changed. */ -static int genphy_config_advert(struct phy_device *phydev) +static int genphy_config_advert(struct phy_device *phydev, + const unsigned long *advert) { int err, bmsr, changed = 0; u32 adv; - /* Only allow advertising what this PHY supports */ - linkmode_and(phydev->advertising, phydev->advertising, - phydev->supported); - - adv = linkmode_adv_to_mii_adv_t(phydev->advertising); + adv = linkmode_adv_to_mii_adv_t(advert); /* Setup standard advertisement */ err = phy_modify_changed(phydev, MII_ADVERTISE, @@ -1968,7 +2033,7 @@ static int genphy_config_advert(struct phy_device *phydev) if (!(bmsr & BMSR_ESTATEN)) return changed; - adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising); + adv = linkmode_adv_to_mii_ctrl1000_t(advert); err = phy_modify_changed(phydev, MII_CTRL1000, ADVERTISE_1000FULL | ADVERTISE_1000HALF, @@ -2016,29 +2081,6 @@ static int genphy_c37_config_advert(struct phy_device *phydev) } /** - * genphy_config_eee_advert - disable unwanted eee mode advertisement - * @phydev: target phy_device struct - * - * Description: Writes MDIO_AN_EEE_ADV after disabling unsupported energy - * efficent ethernet modes. Returns 0 if the PHY's advertisement hasn't - * changed, and 1 if it has changed. - */ -int genphy_config_eee_advert(struct phy_device *phydev) -{ - int err; - - /* Nothing to disable */ - if (!phydev->eee_broken_modes) - return 0; - - err = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, - phydev->eee_broken_modes, 0); - /* If the call failed, we assume that EEE is not supported */ - return err < 0 ? 0 : err; -} -EXPORT_SYMBOL(genphy_config_eee_advert); - -/** * genphy_setup_forced - configures/forces speed/duplex from @phydev * @phydev: target phy_device struct * @@ -2050,8 +2092,8 @@ int genphy_setup_forced(struct phy_device *phydev) { u16 ctl; - phydev->pause = 0; - phydev->asym_pause = 0; + phydev->pause = false; + phydev->asym_pause = false; ctl = mii_bmcr_encode_fixed(phydev->speed, phydev->duplex); @@ -2192,9 +2234,15 @@ EXPORT_SYMBOL(genphy_check_and_restart_aneg); */ int __genphy_config_aneg(struct phy_device *phydev, bool changed) { + __ETHTOOL_DECLARE_LINK_MODE_MASK(fixed_advert); + const struct link_capabilities *c; + unsigned long *advert; int err; - if (genphy_config_eee_advert(phydev)) + err = genphy_c45_an_config_eee_aneg(phydev); + if (err < 0) + return err; + else if (err) changed = true; err = genphy_setup_master_slave(phydev); @@ -2203,10 +2251,26 @@ int __genphy_config_aneg(struct phy_device *phydev, bool changed) else if (err) changed = true; - if (AUTONEG_ENABLE != phydev->autoneg) + if (phydev->autoneg == AUTONEG_ENABLE) { + /* Only allow advertising what this PHY supports */ + linkmode_and(phydev->advertising, phydev->advertising, + phydev->supported); + advert = phydev->advertising; + } else if (phydev->speed < SPEED_1000) { return genphy_setup_forced(phydev); + } else { + linkmode_zero(fixed_advert); - err = genphy_config_advert(phydev); + c = phy_caps_lookup(phydev->speed, phydev->duplex, + phydev->supported, true); + if (c) + linkmode_and(fixed_advert, phydev->supported, + c->linkmodes); + + advert = fixed_advert; + } + + err = genphy_config_advert(phydev, advert); if (err < 0) /* error */ return err; else if (err) @@ -2436,8 +2500,8 @@ int genphy_read_status(struct phy_device *phydev) phydev->master_slave_state = MASTER_SLAVE_STATE_UNSUPPORTED; phydev->speed = SPEED_UNKNOWN; phydev->duplex = DUPLEX_UNKNOWN; - phydev->pause = 0; - phydev->asym_pause = 0; + phydev->pause = false; + phydev->asym_pause = false; if (phydev->is_gigabit_capable) { err = genphy_read_master_slave(phydev); @@ -2464,12 +2528,15 @@ EXPORT_SYMBOL(genphy_read_status); /** * genphy_c37_read_status - check the link status and update current link state * @phydev: target phy_device struct + * @changed: pointer where to store if link changed * * Description: Check the link, then figure out the current state * by comparing what we advertise with what the link partner * advertises. This function is for Clause 37 1000Base-X mode. + * + * If link has changed, @changed is set to true, false otherwise. */ -int genphy_c37_read_status(struct phy_device *phydev) +int genphy_c37_read_status(struct phy_device *phydev, bool *changed) { int lpa, err, old_link = phydev->link; @@ -2479,12 +2546,16 @@ int genphy_c37_read_status(struct phy_device *phydev) return err; /* why bother the PHY if nothing can have changed */ - if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) + if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) { + *changed = false; return 0; + } + /* Signal link has changed */ + *changed = true; phydev->duplex = DUPLEX_UNKNOWN; - phydev->pause = 0; - phydev->asym_pause = 0; + phydev->pause = false; + phydev->asym_pause = false; if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) { lpa = phy_read(phydev, MII_LPA); @@ -2616,6 +2687,11 @@ int genphy_read_abilities(struct phy_device *phydev) phydev->supported, val & ESTATUS_1000_XFULL); } + /* This is optional functionality. If not supported, we may get an error + * which should be ignored. + */ + genphy_c45_read_eee_abilities(phydev); + return 0; } EXPORT_SYMBOL(genphy_read_abilities); @@ -2649,11 +2725,17 @@ int genphy_resume(struct phy_device *phydev) } EXPORT_SYMBOL(genphy_resume); -int genphy_loopback(struct phy_device *phydev, bool enable) +int genphy_loopback(struct phy_device *phydev, bool enable, int speed) { if (enable) { - u16 val, ctl = BMCR_LOOPBACK; - int ret; + u16 ctl = BMCR_LOOPBACK; + int ret, val; + + if (speed == SPEED_10 || speed == SPEED_100 || + speed == SPEED_1000) + phydev->speed = speed; + else if (speed) + return -EINVAL; ctl |= mii_bmcr_encode_fixed(phydev->speed, phydev->duplex); @@ -2716,6 +2798,67 @@ void phy_advertise_supported(struct phy_device *phydev) EXPORT_SYMBOL(phy_advertise_supported); /** + * phy_advertise_eee_all - Advertise all supported EEE modes + * @phydev: target phy_device struct + * + * Description: Per default phylib preserves the EEE advertising at the time of + * phy probing, which might be a subset of the supported EEE modes. Use this + * function when all supported EEE modes should be advertised. This does not + * trigger auto-negotiation, so must be called before phy_start()/ + * phylink_start() which will start auto-negotiation. + */ +void phy_advertise_eee_all(struct phy_device *phydev) +{ + linkmode_copy(phydev->advertising_eee, phydev->supported_eee); +} +EXPORT_SYMBOL_GPL(phy_advertise_eee_all); + +/** + * phy_support_eee - Set initial EEE policy configuration + * @phydev: Target phy_device struct + * + * This function configures the initial policy for Energy Efficient Ethernet + * (EEE) on the specified PHY device, influencing that EEE capabilities are + * advertised before the link is established. It should be called during PHY + * registration by the MAC driver and/or the PHY driver (for SmartEEE PHYs) + * if MAC supports LPI or PHY is capable to compensate missing LPI functionality + * of the MAC. + * + * The function sets default EEE policy parameters, including preparing the PHY + * to advertise EEE capabilities based on hardware support. + * + * It also sets the expected configuration for Low Power Idle (LPI) in the MAC + * driver. If the PHY framework determines that both local and remote + * advertisements support EEE, and the negotiated link mode is compatible with + * EEE, it will set enable_tx_lpi = true. The MAC driver is expected to act on + * this setting by enabling the LPI timer if enable_tx_lpi is set. + */ +void phy_support_eee(struct phy_device *phydev) +{ + linkmode_copy(phydev->advertising_eee, phydev->supported_eee); + phydev->eee_cfg.tx_lpi_enabled = true; + phydev->eee_cfg.eee_enabled = true; +} +EXPORT_SYMBOL(phy_support_eee); + +/** + * phy_disable_eee - Disable EEE for the PHY + * @phydev: Target phy_device struct + * + * This function is used by MAC drivers for MAC's which don't support EEE. + * It disables EEE on the PHY layer. + */ +void phy_disable_eee(struct phy_device *phydev) +{ + linkmode_zero(phydev->advertising_eee); + phydev->eee_cfg.tx_lpi_enabled = false; + phydev->eee_cfg.eee_enabled = false; + /* don't let userspace re-enable EEE advertisement */ + linkmode_fill(phydev->eee_disabled_modes); +} +EXPORT_SYMBOL_GPL(phy_disable_eee); + +/** * phy_support_sym_pause - Enable support of symmetrical pause * @phydev: target phy_device struct * @@ -2841,19 +2984,12 @@ void phy_get_pause(struct phy_device *phydev, bool *tx_pause, bool *rx_pause) EXPORT_SYMBOL(phy_get_pause); #if IS_ENABLED(CONFIG_OF_MDIO) -static int phy_get_int_delay_property(struct device *dev, const char *name) +static int phy_get_u32_property(struct device *dev, const char *name, u32 *val) { - s32 int_delay; - int ret; - - ret = device_property_read_u32(dev, name, &int_delay); - if (ret) - return ret; - - return int_delay; + return device_property_read_u32(dev, name, val); } #else -static int phy_get_int_delay_property(struct device *dev, const char *name) +static int phy_get_u32_property(struct device *dev, const char *name, u32 *val) { return -EINVAL; } @@ -2862,7 +2998,6 @@ static int phy_get_int_delay_property(struct device *dev, const char *name) /** * phy_get_internal_delay - returns the index of the internal delay * @phydev: phy_device struct - * @dev: pointer to the devices device struct * @delay_values: array of delays the PHY supports * @size: the size of the delay array * @is_rx: boolean to indicate to get the rx internal delay @@ -2875,15 +3010,16 @@ static int phy_get_int_delay_property(struct device *dev, const char *name) * array then size = 0 and the value of the delay property is returned. * Return -EINVAL if the delay is invalid or cannot be found. */ -s32 phy_get_internal_delay(struct phy_device *phydev, struct device *dev, - const int *delay_values, int size, bool is_rx) +s32 phy_get_internal_delay(struct phy_device *phydev, const int *delay_values, + int size, bool is_rx) { - s32 delay; - int i; + struct device *dev = &phydev->mdio.dev; + int i, ret; + u32 delay; if (is_rx) { - delay = phy_get_int_delay_property(dev, "rx-internal-delay-ps"); - if (delay < 0 && size == 0) { + ret = phy_get_u32_property(dev, "rx-internal-delay-ps", &delay); + if (ret < 0 && size == 0) { if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) return 1; @@ -2892,8 +3028,8 @@ s32 phy_get_internal_delay(struct phy_device *phydev, struct device *dev, } } else { - delay = phy_get_int_delay_property(dev, "tx-internal-delay-ps"); - if (delay < 0 && size == 0) { + ret = phy_get_u32_property(dev, "tx-internal-delay-ps", &delay); + if (ret < 0 && size == 0) { if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) return 1; @@ -2902,10 +3038,10 @@ s32 phy_get_internal_delay(struct phy_device *phydev, struct device *dev, } } - if (delay < 0) - return delay; + if (ret < 0) + return ret; - if (delay && size == 0) + if (size == 0) return delay; if (delay < delay_values[0] || delay > delay_values[size - 1]) { @@ -2938,9 +3074,255 @@ 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) +/** + * phy_get_tx_amplitude_gain - stores tx amplitude gain in @val + * @phydev: phy_device struct + * @dev: pointer to the devices device struct + * @linkmode: linkmode for which the tx amplitude gain should be retrieved + * @val: tx amplitude gain + * + * Returns: 0 on success, < 0 on failure + */ +int phy_get_tx_amplitude_gain(struct phy_device *phydev, struct device *dev, + enum ethtool_link_mode_bit_indices linkmode, + u32 *val) +{ + switch (linkmode) { + case ETHTOOL_LINK_MODE_100baseT_Full_BIT: + return phy_get_u32_property(dev, + "tx-amplitude-100base-tx-percent", + val); + default: + return -EINVAL; + } +} +EXPORT_SYMBOL_GPL(phy_get_tx_amplitude_gain); + +/** + * phy_get_mac_termination - stores MAC termination in @val + * @phydev: phy_device struct + * @dev: pointer to the devices device struct + * @val: MAC termination + * + * Returns: 0 on success, < 0 on failure + */ +int phy_get_mac_termination(struct phy_device *phydev, struct device *dev, + u32 *val) { - return phydrv->config_intr && phydrv->handle_interrupt; + return phy_get_u32_property(dev, "mac-termination-ohms", val); +} +EXPORT_SYMBOL_GPL(phy_get_mac_termination); + +static int phy_led_set_brightness(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct phy_led *phyled = to_phy_led(led_cdev); + struct phy_device *phydev = phyled->phydev; + int err; + + mutex_lock(&phydev->lock); + err = phydev->drv->led_brightness_set(phydev, phyled->index, value); + mutex_unlock(&phydev->lock); + + return err; +} + +static int phy_led_blink_set(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct phy_led *phyled = to_phy_led(led_cdev); + struct phy_device *phydev = phyled->phydev; + int err; + + mutex_lock(&phydev->lock); + err = phydev->drv->led_blink_set(phydev, phyled->index, + delay_on, delay_off); + mutex_unlock(&phydev->lock); + + return err; +} + +static __maybe_unused struct device * +phy_led_hw_control_get_device(struct led_classdev *led_cdev) +{ + struct phy_led *phyled = to_phy_led(led_cdev); + struct phy_device *phydev = phyled->phydev; + + if (phydev->attached_dev) + return &phydev->attached_dev->dev; + return NULL; +} + +static int __maybe_unused +phy_led_hw_control_get(struct led_classdev *led_cdev, + unsigned long *rules) +{ + struct phy_led *phyled = to_phy_led(led_cdev); + struct phy_device *phydev = phyled->phydev; + int err; + + mutex_lock(&phydev->lock); + err = phydev->drv->led_hw_control_get(phydev, phyled->index, rules); + mutex_unlock(&phydev->lock); + + return err; +} + +static int __maybe_unused +phy_led_hw_control_set(struct led_classdev *led_cdev, + unsigned long rules) +{ + struct phy_led *phyled = to_phy_led(led_cdev); + struct phy_device *phydev = phyled->phydev; + int err; + + mutex_lock(&phydev->lock); + err = phydev->drv->led_hw_control_set(phydev, phyled->index, rules); + mutex_unlock(&phydev->lock); + + return err; +} + +static __maybe_unused int phy_led_hw_is_supported(struct led_classdev *led_cdev, + unsigned long rules) +{ + struct phy_led *phyled = to_phy_led(led_cdev); + struct phy_device *phydev = phyled->phydev; + int err; + + mutex_lock(&phydev->lock); + err = phydev->drv->led_hw_is_supported(phydev, phyled->index, rules); + mutex_unlock(&phydev->lock); + + return err; +} + +static void phy_leds_unregister(struct phy_device *phydev) +{ + struct phy_led *phyled, *tmp; + + list_for_each_entry_safe(phyled, tmp, &phydev->leds, list) { + led_classdev_unregister(&phyled->led_cdev); + list_del(&phyled->list); + } +} + +static int of_phy_led(struct phy_device *phydev, + struct device_node *led) +{ + struct device *dev = &phydev->mdio.dev; + struct led_init_data init_data = {}; + struct led_classdev *cdev; + unsigned long modes = 0; + struct phy_led *phyled; + u32 index; + int err; + + phyled = devm_kzalloc(dev, sizeof(*phyled), GFP_KERNEL); + if (!phyled) + return -ENOMEM; + + cdev = &phyled->led_cdev; + phyled->phydev = phydev; + + err = of_property_read_u32(led, "reg", &index); + if (err) + return err; + if (index > U8_MAX) + return -EINVAL; + + if (of_property_read_bool(led, "active-high")) + set_bit(PHY_LED_ACTIVE_HIGH, &modes); + if (of_property_read_bool(led, "active-low")) + set_bit(PHY_LED_ACTIVE_LOW, &modes); + if (of_property_read_bool(led, "inactive-high-impedance")) + set_bit(PHY_LED_INACTIVE_HIGH_IMPEDANCE, &modes); + + if (WARN_ON(modes & BIT(PHY_LED_ACTIVE_LOW) && + modes & BIT(PHY_LED_ACTIVE_HIGH))) + return -EINVAL; + + if (modes) { + /* Return error if asked to set polarity modes but not supported */ + if (!phydev->drv->led_polarity_set) + return -EINVAL; + + err = phydev->drv->led_polarity_set(phydev, index, modes); + if (err) + return err; + } + + phyled->index = index; + if (phydev->drv->led_brightness_set) + cdev->brightness_set_blocking = phy_led_set_brightness; + if (phydev->drv->led_blink_set) + cdev->blink_set = phy_led_blink_set; + +#ifdef CONFIG_LEDS_TRIGGERS + if (phydev->drv->led_hw_is_supported && + phydev->drv->led_hw_control_set && + phydev->drv->led_hw_control_get) { + cdev->hw_control_is_supported = phy_led_hw_is_supported; + cdev->hw_control_set = phy_led_hw_control_set; + cdev->hw_control_get = phy_led_hw_control_get; + cdev->hw_control_trigger = "netdev"; + } + + cdev->hw_control_get_device = phy_led_hw_control_get_device; +#endif + cdev->max_brightness = 1; + init_data.devicename = dev_name(&phydev->mdio.dev); + init_data.fwnode = of_fwnode_handle(led); + init_data.devname_mandatory = true; + + err = led_classdev_register_ext(dev, cdev, &init_data); + if (err) + return err; + + list_add(&phyled->list, &phydev->leds); + + return 0; +} + +static int of_phy_leds(struct phy_device *phydev) +{ + struct device_node *node = phydev->mdio.dev.of_node; + struct device_node *leds; + int err; + + if (!IS_ENABLED(CONFIG_OF_MDIO)) + return 0; + + if (!node) + return 0; + + leds = of_get_child_by_name(node, "leds"); + if (!leds) + return 0; + + /* Check if the PHY driver have at least an OP to + * set the LEDs. + */ + if (!(phydev->drv->led_brightness_set || + phydev->drv->led_blink_set || + phydev->drv->led_hw_control_set)) { + phydev_dbg(phydev, "ignoring leds node defined with no PHY driver support\n"); + goto exit; + } + + for_each_available_child_of_node_scoped(leds, led) { + err = of_phy_led(phydev, led); + if (err) { + of_node_put(leds); + phy_leds_unregister(phydev); + return err; + } + } + +exit: + of_node_put(leds); + return 0; } /** @@ -2992,18 +3374,6 @@ struct phy_device *fwnode_phy_find_device(struct fwnode_handle *phy_fwnode) EXPORT_SYMBOL(fwnode_phy_find_device); /** - * device_phy_find_device - For the given device, get the phy_device - * @dev: Pointer to the given device - * - * Refer return conditions of fwnode_phy_find_device(). - */ -struct phy_device *device_phy_find_device(struct device *dev) -{ - return fwnode_phy_find_device(dev_fwnode(dev)); -} -EXPORT_SYMBOL_GPL(device_phy_find_device); - -/** * fwnode_get_phy_node - Get the phy_node using the named reference. * @fwnode: Pointer to fwnode from which phy_node has to be obtained. * @@ -3012,18 +3382,18 @@ EXPORT_SYMBOL_GPL(device_phy_find_device); * and "phy-device" are not supported in ACPI. DT supports all the three * named references to the phy node. */ -struct fwnode_handle *fwnode_get_phy_node(struct fwnode_handle *fwnode) +struct fwnode_handle *fwnode_get_phy_node(const struct fwnode_handle *fwnode) { struct fwnode_handle *phy_node; /* Only phy-handle is used for ACPI */ phy_node = fwnode_find_reference(fwnode, "phy-handle", 0); - if (is_acpi_node(fwnode) || !IS_ERR(phy_node)) + if (!IS_ERR(phy_node) || is_acpi_node(fwnode)) return phy_node; phy_node = fwnode_find_reference(fwnode, "phy", 0); - if (IS_ERR(phy_node)) - phy_node = fwnode_find_reference(fwnode, "phy-device", 0); - return phy_node; + if (!IS_ERR(phy_node)) + return phy_node; + return fwnode_find_reference(fwnode, "phy-device", 0); } EXPORT_SYMBOL_GPL(fwnode_get_phy_node); @@ -3031,9 +3401,7 @@ EXPORT_SYMBOL_GPL(fwnode_get_phy_node); * phy_probe - probe and init a PHY device * @dev: device to probe and init * - * Description: Take care of setting up the phy_device structure, - * set the state to READY (the driver's init function should - * set it to STARTING if needed). + * Take care of setting up the phy_device structure, set the state to READY. */ static int phy_probe(struct device *dev) { @@ -3053,8 +3421,6 @@ static int phy_probe(struct device *dev) if (phydrv->flags & PHY_IS_INTERNAL) phydev->is_internal = true; - mutex_lock(&phydev->lock); - /* Deassert the reset signal */ phy_device_reset(phydev, 0); @@ -3064,12 +3430,16 @@ static int phy_probe(struct device *dev) goto out; } + phy_disable_interrupts(phydev); + /* Start out supporting everything. Eventually, * a controller will attach, and may modify one * or both of these values */ - if (phydrv->features) + if (phydrv->features) { linkmode_copy(phydev->supported, phydrv->features); + genphy_c45_read_eee_abilities(phydev); + } else if (phydrv->get_features) err = phydrv->get_features(phydev); else if (phydev->is_c45) @@ -3094,11 +3464,32 @@ static int phy_probe(struct device *dev) of_set_phy_supported(phydev); phy_advertise_supported(phydev); - /* Get the EEE modes we want to prohibit. We will ask - * the PHY stop advertising these mode later on + /* Get PHY default EEE advertising modes and handle them as potentially + * safe initial configuration. */ + err = genphy_c45_read_eee_adv(phydev, phydev->advertising_eee); + if (err) + goto out; + + /* Get the EEE modes we want to prohibit. */ of_set_phy_eee_broken(phydev); + /* Some PHYs may advertise, by default, not support EEE modes. So, + * we need to clean them. In addition remove all disabled EEE modes. + */ + linkmode_and(phydev->advertising_eee, phydev->supported_eee, + phydev->advertising_eee); + linkmode_andnot(phydev->advertising_eee, phydev->advertising_eee, + phydev->eee_disabled_modes); + + /* There is no "enabled" flag. If PHY is advertising, assume it is + * kind of enabled. + */ + phydev->eee_cfg.eee_enabled = !linkmode_empty(phydev->advertising_eee); + + /* Get master/slave strap overrides */ + of_set_phy_timing_role(phydev); + /* The Pause Frame bits indicate that the PHY can support passing * pause frames. During autonegotiation, the PHYs will determine if * they should allow pause frames to pass. The MAC driver should then @@ -3121,13 +3512,17 @@ static int phy_probe(struct device *dev) /* Set the state to READY by default */ phydev->state = PHY_READY; + /* Get the LEDs from the device tree, and instantiate standard + * LEDs for them. + */ + if (IS_ENABLED(CONFIG_PHYLIB_LEDS) && !phy_driver_is_genphy(phydev)) + err = of_phy_leds(phydev); + out: - /* Assert the reset signal */ + /* Re-assert the reset signal on error */ if (err) phy_device_reset(phydev, 1); - mutex_unlock(&phydev->lock); - return err; } @@ -3137,9 +3532,10 @@ static int phy_remove(struct device *dev) cancel_delayed_work_sync(&phydev->state_queue); - mutex_lock(&phydev->lock); + if (IS_ENABLED(CONFIG_PHYLIB_LEDS) && !phy_driver_is_genphy(phydev)) + phy_leds_unregister(phydev); + phydev->state = PHY_DOWN; - mutex_unlock(&phydev->lock); sfp_bus_del_upstream(phydev->sfp_bus); phydev->sfp_bus = NULL; @@ -3155,22 +3551,13 @@ static int phy_remove(struct device *dev) return 0; } -static void phy_shutdown(struct device *dev) -{ - struct phy_device *phydev = to_phy_device(dev); - - if (phydev->state == PHY_READY || !phydev->attached_dev) - return; - - phy_disable_interrupts(phydev); -} - /** * phy_driver_register - register a phy_driver with the PHY layer * @new_driver: new phy_driver to register * @owner: module owning this PHY */ -int phy_driver_register(struct phy_driver *new_driver, struct module *owner) +static int phy_driver_register(struct phy_driver *new_driver, + struct module *owner) { int retval; @@ -3198,7 +3585,6 @@ int phy_driver_register(struct phy_driver *new_driver, struct module *owner) new_driver->mdiodrv.driver.bus = &mdio_bus_type; new_driver->mdiodrv.driver.probe = phy_probe; new_driver->mdiodrv.driver.remove = phy_remove; - new_driver->mdiodrv.driver.shutdown = phy_shutdown; new_driver->mdiodrv.driver.owner = owner; new_driver->mdiodrv.driver.probe_type = PROBE_FORCE_SYNCHRONOUS; @@ -3214,7 +3600,11 @@ int phy_driver_register(struct phy_driver *new_driver, struct module *owner) return 0; } -EXPORT_SYMBOL(phy_driver_register); + +static void phy_driver_unregister(struct phy_driver *drv) +{ + driver_unregister(&drv->mdiodrv.driver); +} int phy_drivers_register(struct phy_driver *new_driver, int n, struct module *owner) @@ -3233,12 +3623,6 @@ int phy_drivers_register(struct phy_driver *new_driver, int n, } EXPORT_SYMBOL(phy_drivers_register); -void phy_driver_unregister(struct phy_driver *drv) -{ - driver_unregister(&drv->mdiodrv.driver); -} -EXPORT_SYMBOL(phy_driver_unregister); - void phy_drivers_unregister(struct phy_driver *drv, int n) { int i; @@ -3262,31 +3646,62 @@ static const struct ethtool_phy_ops phy_ethtool_phy_ops = { .get_sset_count = phy_ethtool_get_sset_count, .get_strings = phy_ethtool_get_strings, .get_stats = phy_ethtool_get_stats, + .get_plca_cfg = phy_ethtool_get_plca_cfg, + .set_plca_cfg = phy_ethtool_set_plca_cfg, + .get_plca_status = phy_ethtool_get_plca_status, .start_cable_test = phy_start_cable_test, .start_cable_test_tdr = phy_start_cable_test_tdr, }; +static const struct phylib_stubs __phylib_stubs = { + .hwtstamp_get = __phy_hwtstamp_get, + .hwtstamp_set = __phy_hwtstamp_set, + .get_phy_stats = __phy_ethtool_get_phy_stats, + .get_link_ext_stats = __phy_ethtool_get_link_ext_stats, +}; + +static void phylib_register_stubs(void) +{ + phylib_stubs = &__phylib_stubs; +} + +static void phylib_unregister_stubs(void) +{ + phylib_stubs = NULL; +} + static int __init phy_init(void) { int rc; - rc = mdio_bus_init(); + rtnl_lock(); + ethtool_set_ethtool_phy_ops(&phy_ethtool_phy_ops); + phylib_register_stubs(); + rtnl_unlock(); + + rc = phy_caps_init(); if (rc) - return rc; + goto err_ethtool_phy_ops; - ethtool_set_ethtool_phy_ops(&phy_ethtool_phy_ops); features_init(); rc = phy_driver_register(&genphy_c45_driver, THIS_MODULE); if (rc) - goto err_c45; + goto err_ethtool_phy_ops; rc = phy_driver_register(&genphy_driver, THIS_MODULE); - if (rc) { - phy_driver_unregister(&genphy_c45_driver); + if (rc) + goto err_c45; + + return 0; + err_c45: - mdio_bus_exit(); - } + phy_driver_unregister(&genphy_c45_driver); +err_ethtool_phy_ops: + rtnl_lock(); + phylib_unregister_stubs(); + ethtool_set_ethtool_phy_ops(NULL); + rtnl_unlock(); return rc; } @@ -3295,8 +3710,10 @@ static void __exit phy_exit(void) { phy_driver_unregister(&genphy_c45_driver); phy_driver_unregister(&genphy_driver); - mdio_bus_exit(); + rtnl_lock(); + phylib_unregister_stubs(); ethtool_set_ethtool_phy_ops(NULL); + rtnl_unlock(); } subsys_initcall(phy_init); |
