3838 lines · 102.0 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0+/* Framework for finding and configuring PHYs. * Also contains generic PHY driver * * Author: Andy Fleming * * Copyright (c) 2004 Freescale Semiconductor, Inc. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/acpi.h>#include <linux/bitmap.h>#include <linux/delay.h>#include <linux/errno.h>#include <linux/etherdevice.h>#include <linux/ethtool.h>#include <linux/init.h>#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/rtnetlink.h>#include <linux/sfp.h>#include <linux/skbuff.h>#include <linux/slab.h>#include <linux/string.h>#include <linux/uaccess.h>#include <linux/unistd.h> MODULE_DESCRIPTION("PHY library");MODULE_AUTHOR("Andy Fleming");MODULE_LICENSE("GPL"); __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,	ETHTOOL_LINK_MODE_MII_BIT,};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] = {	ETHTOOL_LINK_MODE_Autoneg_BIT,	ETHTOOL_LINK_MODE_TP_BIT,	ETHTOOL_LINK_MODE_MII_BIT,	ETHTOOL_LINK_MODE_FIBRE_BIT,	ETHTOOL_LINK_MODE_AUI_BIT,	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] = {	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] = {	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_basic_t1s_p2mp_features_array[2] = {	ETHTOOL_LINK_MODE_TP_BIT,	ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT,};EXPORT_SYMBOL_GPL(phy_basic_t1s_p2mp_features_array); const int phy_gbit_features_array[2] = {	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] = {	ETHTOOL_LINK_MODE_10000baseT_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); 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_cap1_features_array[] = {	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,}; __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_eee_cap1_features) __ro_after_init;EXPORT_SYMBOL_GPL(phy_eee_cap1_features); static const int phy_eee_cap2_features_array[] = {	ETHTOOL_LINK_MODE_2500baseT_Full_BIT,	ETHTOOL_LINK_MODE_5000baseT_Full_BIT,}; __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_eee_cap2_features) __ro_after_init;EXPORT_SYMBOL_GPL(phy_eee_cap2_features); static void features_init(void){	/* 10/100 half/full*/	linkmode_set_bit_array(phy_basic_ports_array,			       ARRAY_SIZE(phy_basic_ports_array),			       phy_basic_features);	linkmode_set_bit_array(phy_10_100_features_array,			       ARRAY_SIZE(phy_10_100_features_array),			       phy_basic_features); 	/* 100 full, TP */	linkmode_set_bit_array(phy_basic_t1_features_array,			       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),			       phy_gbit_features);	linkmode_set_bit_array(phy_10_100_features_array,			       ARRAY_SIZE(phy_10_100_features_array),			       phy_gbit_features);	linkmode_set_bit_array(phy_gbit_features_array,			       ARRAY_SIZE(phy_gbit_features_array),			       phy_gbit_features); 	/* 10/100 half/full + 1000 half/full + fibre*/	linkmode_set_bit_array(phy_basic_ports_array,			       ARRAY_SIZE(phy_basic_ports_array),			       phy_gbit_fibre_features);	linkmode_set_bit_array(phy_10_100_features_array,			       ARRAY_SIZE(phy_10_100_features_array),			       phy_gbit_fibre_features);	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); 	/* 10/100 half/full + 1000 half/full + 10G full*/	linkmode_set_bit_array(phy_all_ports_features_array,			       ARRAY_SIZE(phy_all_ports_features_array),			       phy_10gbit_features);	linkmode_set_bit_array(phy_10_100_features_array,			       ARRAY_SIZE(phy_10_100_features_array),			       phy_10gbit_features);	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); 	/* 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);	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); } void phy_device_free(struct phy_device *phydev){	put_device(&phydev->mdio.dev);}EXPORT_SYMBOL(phy_device_free); static void phy_mdio_device_free(struct mdio_device *mdiodev){	struct phy_device *phydev; 	phydev = container_of(mdiodev, struct phy_device, mdio);	phy_device_free(phydev);} static void phy_device_release(struct device *dev){	fwnode_handle_put(dev->fwnode);	kfree(to_phy_device(dev));} static void phy_mdio_device_remove(struct mdio_device *mdiodev){	struct phy_device *phydev; 	phydev = container_of(mdiodev, struct phy_device, mdio);	phy_device_remove(phydev);} 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;} static bool mdio_bus_phy_may_suspend(struct phy_device *phydev){	struct device_driver *drv = phydev->mdio.dev.driver;	struct phy_driver *phydrv = to_phy_driver(drv);	struct net_device *netdev = phydev->attached_dev; 	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_drv_wol_enabled(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	 * MDIO bus driver and clock gated at this point.	 */	if (!netdev)		goto out; 	if (netdev->ethtool->wol_enabled)		return false; 	/* As long as not all affected network drivers support the	 * wol_enabled flag, let's check for hints that WoL is enabled.	 * Don't suspend PHY if the attached netdev parent may wake up.	 * The parent may point to a PCI device, as in tg3 driver.	 */	if (netdev->dev.parent && device_may_wakeup(netdev->dev.parent))		return false; 	/* Also don't suspend PHY if the netdev itself may wakeup. This	 * is the case for devices w/o underlaying pwr. mgmt. aware bus,	 * e.g. SoC devices.	 */	if (device_may_wakeup(&netdev->dev))		return false; out:	return !phydev->suspended;} static __maybe_unused int mdio_bus_phy_suspend(struct device *dev){	struct phy_device *phydev = to_phy_device(dev); 	if (phydev->mac_managed_pm)		return 0; 	/* Wakeup interrupts may occur during the system sleep transition when	 * the PHY is inaccessible. Set flag to postpone handling until the PHY	 * has resumed. Wait for concurrent interrupt handler to complete.	 */	if (phy_interrupt_is_valid(phydev)) {		phydev->irq_suspended = 1;		synchronize_irq(phydev->irq);	} 	/* We must stop the state machine manually, otherwise it stops out of	 * control, possibly with the phydev->lock held. Upon resume, netdev	 * 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)		phy_stop_machine(phydev); 	if (!mdio_bus_phy_may_suspend(phydev))		return 0; 	phydev->suspended_by_mdio_bus = 1; 	return phy_suspend(phydev);} static __maybe_unused int mdio_bus_phy_resume(struct device *dev){	struct phy_device *phydev = to_phy_device(dev);	int ret; 	if (phydev->mac_managed_pm)		return 0; 	if (!phydev->suspended_by_mdio_bus)		goto no_resume; 	phydev->suspended_by_mdio_bus = 0; 	/* If we managed to get here with the PHY state machine in a state	 * neither PHY_HALTED, PHY_READY nor PHY_UP, this is an indication	 * that something went wrong and we should most likely be using	 * MAC managed PM, but we are not.	 */	WARN_ON(phydev->state != PHY_HALTED && phydev->state != PHY_READY &&		phydev->state != PHY_UP); 	ret = phy_init_hw(phydev);	if (ret < 0)		return ret; 	ret = phy_resume(phydev);	if (ret < 0)		return ret;no_resume:	if (phy_interrupt_is_valid(phydev)) {		phydev->irq_suspended = 0;		synchronize_irq(phydev->irq); 		/* Rerun interrupts which were postponed by phy_interrupt()		 * because they occurred during the system sleep transition.		 */		if (phydev->irq_rerun) {			phydev->irq_rerun = 0;			enable_irq(phydev->irq);			irq_wake_thread(phydev->irq, phydev);		}	} 	if (phydev->attached_dev && phydev->adjust_link)		phy_start_machine(phydev); 	return 0;} static SIMPLE_DEV_PM_OPS(mdio_bus_phy_pm_ops, mdio_bus_phy_suspend,			 mdio_bus_phy_resume); /** * phy_register_fixup - creates a new phy_fixup and adds it to the list * @bus_id: A string which matches phydev->mdio.dev.bus_id (or PHY_ANY_ID) * @phy_uid: Used to match against phydev->phy_id (the UID of the PHY) *	It can also be PHY_ANY_UID * @phy_uid_mask: Applied to phydev->phy_id and fixup->phy_uid before *	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 *)){	struct phy_fixup *fixup = kzalloc(sizeof(*fixup), GFP_KERNEL); 	if (!fixup)		return -ENOMEM; 	strscpy(fixup->bus_id, bus_id, sizeof(fixup->bus_id));	fixup->phy_uid = phy_uid;	fixup->phy_uid_mask = phy_uid_mask;	fixup->run = run; 	mutex_lock(&phy_fixup_lock);	list_add_tail(&fixup->list, &phy_fixup_list);	mutex_unlock(&phy_fixup_lock); 	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,			       int (*run)(struct phy_device *)){	return phy_register_fixup(PHY_ANY_ID, phy_uid, phy_uid_mask, run);}EXPORT_SYMBOL(phy_register_fixup_for_uid); /* Registers a fixup to be run on the PHY with id string bus_id */int phy_register_fixup_for_id(const char *bus_id,			      int (*run)(struct phy_device *)){	return phy_register_fixup(bus_id, PHY_ANY_UID, 0xffffffff, run);}EXPORT_SYMBOL(phy_register_fixup_for_id); /** * phy_unregister_fixup - remove a phy_fixup from the list * @bus_id: A string matches fixup->bus_id (or PHY_ANY_ID) in phy_fixup_list * @phy_uid: A phy id matches fixup->phy_id (or PHY_ANY_UID) in phy_fixup_list * @phy_uid_mask: Applied to phy_uid and fixup->phy_uid before comparison */int phy_unregister_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask){	struct list_head *pos, *n;	struct phy_fixup *fixup;	int ret; 	ret = -ENODEV; 	mutex_lock(&phy_fixup_lock);	list_for_each_safe(pos, n, &phy_fixup_list) {		fixup = list_entry(pos, struct phy_fixup, list); 		if ((!strcmp(fixup->bus_id, bus_id)) &&		    phy_id_compare(fixup->phy_uid, phy_uid, phy_uid_mask)) {			list_del(&fixup->list);			kfree(fixup);			ret = 0;			break;		}	}	mutex_unlock(&phy_fixup_lock); 	return ret;}EXPORT_SYMBOL(phy_unregister_fixup); /* Unregisters a fixup of any PHY with the UID in phy_uid */int phy_unregister_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask)

Lines 1–500 of 3838.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t