Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/net/phy/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 98 kB image not shown  

Quelle  phy_device.c   Sprache: C

 
// 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/ptp_clock_kernel.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>

#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_10gbit_features) __ro_after_init;
EXPORT_SYMBOL_GPL(phy_10gbit_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);

static 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,
};

static 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,
};

static 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,
};

static const int phy_basic_t1s_p2mp_features_array[2] = {
 ETHTOOL_LINK_MODE_TP_BIT,
 ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT,
};

static const int phy_gbit_features_array[2] = {
 ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
 ETHTOOL_LINK_MODE_1000baseT_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(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,
          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(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);

}

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 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;
 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 (phy_uses_state_machine(phydev))
  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 (phy_uses_state_machine(phydev))
  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
 */

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);

 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;
}

/* 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)
{
 return phy_unregister_fixup(PHY_ANY_ID, phy_uid, phy_uid_mask);
}
EXPORT_SYMBOL(phy_unregister_fixup_for_uid);

/* Unregisters a fixup of the PHY with id string bus_id */
int phy_unregister_fixup_for_id(const char *bus_id)
{
 return phy_unregister_fixup(bus_id, PHY_ANY_UID, 0xffffffff);
}
EXPORT_SYMBOL(phy_unregister_fixup_for_id);

/* Returns 1 if fixup matches phydev in bus_id and phy_uid.
 * Fixups can be set to match any in one or more fields.
 */

static int phy_needs_fixup(struct phy_device *phydev, struct phy_fixup *fixup)
{
 if (strcmp(fixup->bus_id, phydev_name(phydev)) != 0)
  if (strcmp(fixup->bus_id, PHY_ANY_ID) != 0)
   return 0;

 if (!phy_id_compare(phydev->phy_id, fixup->phy_uid,
       fixup->phy_uid_mask))
  if (fixup->phy_uid != PHY_ANY_UID)
   return 0;

 return 1;
}

/* Runs any matching fixups for this phydev */
static int phy_scan_fixups(struct phy_device *phydev)
{
 struct phy_fixup *fixup;

 mutex_lock(&phy_fixup_lock);
 list_for_each_entry(fixup, &phy_fixup_list, list) {
  if (phy_needs_fixup(phydev, fixup)) {
   int err = fixup->run(phydev);

   if (err < 0) {
    mutex_unlock(&phy_fixup_lock);
    return err;
   }
   phydev->has_fixups = true;
  }
 }
 mutex_unlock(&phy_fixup_lock);

 return 0;
}

/**
 * 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)
{
 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 (phy_id_compare(phydev->c45_ids.device_ids[i],
        phydrv->phy_id, phydrv->phy_id_mask))
    return 1;
  }

  return 0;
 }

 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
phy_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->phy_id);
}
static DEVICE_ATTR_RO(phy_id);

static ssize_t
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 (phydev->is_internal)
  mode = "internal";
 else
  mode = phy_modes(phydev->interface);

 return sysfs_emit(buf, "%s\n", mode);
}
static DEVICE_ATTR_RO(phy_interface);

static ssize_t
phy_has_fixups_show(struct device *dev, struct device_attribute *attr,
      char *buf)
{
 struct phy_device *phydev = to_phy_device(dev);

 return sysfs_emit(buf, "%d\n", phydev->has_fixups);
}
static DEVICE_ATTR_RO(phy_has_fixups);

static ssize_t phy_dev_flags_show(struct device *dev,
      struct device_attribute *attr,
      char *buf)
{
 struct phy_device *phydev = to_phy_device(dev);

 return sysfs_emit(buf, "0x%08x\n", phydev->dev_flags);
}
static DEVICE_ATTR_RO(phy_dev_flags);

static struct attribute *phy_dev_attrs[] = {
 &dev_attr_phy_id.attr,
 &dev_attr_phy_interface.attr,
 &dev_attr_phy_has_fixups.attr,
 &dev_attr_phy_dev_flags.attr,
 NULL,
};

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_device_groups,
 .release = phy_device_release,
 .pm = pm_ptr(&mdio_bus_phy_pm_ops),
};

static int phy_request_driver_module(struct phy_device *dev, u32 phy_id)
{
 int ret;

 ret = request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT,
        MDIO_ID_ARGS(phy_id));
 /* We only check for failures in executing the usermode binary,
 * not whether a PHY driver module exists for the PHY ID.
 * Accept -ENOENT because this may occur in case no initramfs exists,
 * then modprobe isn't available.
 */

 if (IS_ENABLED(CONFIG_MODULES) && ret < 0 && ret != -ENOENT) {
  phydev_err(dev, "error %d loading PHY driver module for ID 0x%08lx\n",
      ret, (unsigned long)phy_id);
  return ret;
 }

 return 0;
}

struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id,
         bool is_c45,
         struct phy_c45_device_ids *c45_ids)
{
 struct phy_device *dev;
 struct mdio_device *mdiodev;
 int ret = 0;

 /* We allocate the device, and initialize the default values */
 dev = kzalloc(sizeof(*dev), GFP_KERNEL);
 if (!dev)
  return ERR_PTR(-ENOMEM);

 mdiodev = &dev->mdio;
 mdiodev->dev.parent = &bus->dev;
 mdiodev->dev.bus = &mdio_bus_type;
 mdiodev->dev.type = &mdio_bus_phy_type;
 mdiodev->bus = bus;
 mdiodev->bus_match = phy_bus_match;
 mdiodev->addr = addr;
 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->link = 0;
 dev->port = PORT_TP;
 dev->interface = PHY_INTERFACE_MODE_GMII;

 dev->autoneg = AUTONEG_ENABLE;

 dev->pma_extable = -ENODATA;
 dev->is_c45 = is_c45;
 dev->phy_id = phy_id;
 if (c45_ids)
  dev->c45_ids = *c45_ids;
 dev->irq = bus->irq[addr];

 dev_set_name(&mdiodev->dev, PHY_ID_FMT, bus->id, addr);
 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);

 /* Request the appropriate module unconditionally; don't
 * bother trying to do so only if it isn't already loaded,
 * because that gets complicated. A hotplug event would have
 * done an unconditional modprobe anyway.
 * We don't do normal hotplug because it won't work for MDIO
 * -- because it relies on the device staying around for long
 * enough for the driver to get loaded. With MDIO, the NIC
 * driver will get bored and give up as soon as it finds that
 * there's no driver _already_ loaded.
 */

 if (is_c45 && c45_ids) {
  const int num_ids = ARRAY_SIZE(c45_ids->device_ids);
  int i;

  for (i = 1; i < num_ids; i++) {
   if (c45_ids->device_ids[i] == 0xffffffff)
    continue;

   ret = phy_request_driver_module(dev,
      c45_ids->device_ids[i]);
   if (ret)
    break;
  }
 } else {
  ret = phy_request_driver_module(dev, phy_id);
 }

 if (ret) {
  put_device(&mdiodev->dev);
  dev = ERR_PTR(ret);
 }

 return dev;
}
EXPORT_SYMBOL(phy_device_create);

/* phy_c45_probe_present - checks to see if a MMD is present in the package
 * @bus: the target MII bus
 * @prtad: PHY package address on the MII bus
 * @devad: PHY device (MMD) address
 *
 * Read the MDIO_STAT2 register, and check whether a device is responding
 * at this address.
 *
 * Returns: negative error number on bus access error, zero if no device
 * is responding, or positive if a device is present.
 */

static int phy_c45_probe_present(struct mii_bus *bus, int prtad, int devad)
{
 int stat2;

 stat2 = mdiobus_c45_read(bus, prtad, devad, MDIO_STAT2);
 if (stat2 < 0)
  return stat2;

 return (stat2 & MDIO_STAT2_DEVPRST) == MDIO_STAT2_DEVPRST_VAL;
}

/* get_phy_c45_devs_in_pkg - reads a MMD's devices in package registers.
 * @bus: the target MII bus
 * @addr: PHY address on the MII bus
 * @dev_addr: MMD address in the PHY.
 * @devices_in_package: where to store the devices in package information.
 *
 * Description: reads devices in package registers of a MMD at @dev_addr
 * from PHY at @addr on @bus.
 *
 * Returns: 0 on success, -EIO on failure.
 */

static int get_phy_c45_devs_in_pkg(struct mii_bus *bus, int addr, int dev_addr,
       u32 *devices_in_package)
{
 int phy_reg;

 phy_reg = mdiobus_c45_read(bus, addr, dev_addr, MDIO_DEVS2);
 if (phy_reg < 0)
  return -EIO;
 *devices_in_package = phy_reg << 16;

 phy_reg = mdiobus_c45_read(bus, addr, dev_addr, MDIO_DEVS1);
 if (phy_reg < 0)
  return -EIO;
 *devices_in_package |= phy_reg;

 return 0;
}

/**
 * get_phy_c45_ids - reads the specified addr for its 802.3-c45 IDs.
 * @bus: the target MII bus
 * @addr: PHY address on the MII bus
 * @c45_ids: where to store the c45 ID information.
 *
 * Read the PHY "devices in package". If this appears to be valid, read
 * the PHY identifiers for each device. Return the "devices in package"
 * and identifiers in @c45_ids.
 *
 * Returns zero on success, %-EIO on bus access error, or %-ENODEV if
 * 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)
{
 const int num_ids = ARRAY_SIZE(c45_ids->device_ids);
 u32 devs_in_pkg = 0;
 int i, ret, phy_reg;

 /* Find first non-zero Devices In package. Device zero is reserved
 * for 802.3 c45 complied PHYs, so don't probe it at first.
 */

 for (i = 1; i < MDIO_MMD_NUM && (devs_in_pkg == 0 ||
      (devs_in_pkg & 0x1fffffff) == 0x1fffffff); i++) {
  if (i == MDIO_MMD_VEND1 || i == MDIO_MMD_VEND2) {
   /* Check that there is a device present at this
 * address before reading the devices-in-package
 * register to avoid reading garbage from the PHY.
 * Some PHYs (88x3310) vendor space is not IEEE802.3
 * compliant.
 */

   ret = phy_c45_probe_present(bus, addr, i);
   if (ret < 0)
    /* returning -ENODEV doesn't stop bus
 * scanning
 */

    return (phy_reg == -EIO ||
     phy_reg == -ENODEV) ? -ENODEV : -EIO;

   if (!ret)
    continue;
  }
  phy_reg = get_phy_c45_devs_in_pkg(bus, addr, i, &devs_in_pkg);
  if (phy_reg < 0)
   return -EIO;
 }

 if ((devs_in_pkg & 0x1fffffff) == 0x1fffffff) {
  /* If mostly Fs, there is no device there, then let's probe
 * MMD 0, as some 10G PHYs have zero Devices In package,
 * e.g. Cortina CS4315/CS4340 PHY.
 */

  phy_reg = get_phy_c45_devs_in_pkg(bus, addr, 0, &devs_in_pkg);
  if (phy_reg < 0)
   return -EIO;

  /* no device there, let's get out of here */
  if ((devs_in_pkg & 0x1fffffff) == 0x1fffffff)
   return -ENODEV;
 }

 /* Now probe Device Identifiers for each device present. */
 for (i = 1; i < num_ids; i++) {
  if (!(devs_in_pkg & (1 << i)))
   continue;

  if (i == MDIO_MMD_VEND1 || i == MDIO_MMD_VEND2) {
   /* Probe the "Device Present" bits for the vendor MMDs
 * to ignore these if they do not contain IEEE 802.3
 * registers.
 */

   ret = phy_c45_probe_present(bus, addr, i);
   if (ret < 0)
    return ret;

   if (!ret)
    continue;
  }

  phy_reg = mdiobus_c45_read(bus, addr, i, MII_PHYSID1);
  if (phy_reg < 0)
   return -EIO;
  c45_ids->device_ids[i] = phy_reg << 16;

  phy_reg = mdiobus_c45_read(bus, addr, i, MII_PHYSID2);
  if (phy_reg < 0)
   return -EIO;
  c45_ids->device_ids[i] |= phy_reg;
 }

 c45_ids->devices_in_package = devs_in_pkg;
 /* Bit 0 doesn't represent a device, it indicates c22 regs presence */
 c45_ids->mmds_present = devs_in_pkg & ~BIT(0);

 return 0;
}

/**
 * get_phy_c22_id - reads the specified addr for its clause 22 ID.
 * @bus: the target MII bus
 * @addr: PHY address on the MII bus
 * @phy_id: where to store the ID retrieved.
 *
 * Read the 802.3 clause 22 PHY ID from the PHY at @addr on the @bus,
 * placing it in @phy_id. Return zero on successful read and the ID is
 * valid, %-EIO on bus access error, or %-ENODEV if no device responds
 * or invalid ID.
 */

static int get_phy_c22_id(struct mii_bus *bus, int addr, u32 *phy_id)
{
 int phy_reg;

 /* Grab the bits from PHYIR1, and put them in the upper half */
 phy_reg = mdiobus_read(bus, addr, MII_PHYSID1);
 if (phy_reg < 0) {
  /* returning -ENODEV doesn't stop bus scanning */
  return (phy_reg == -EIO || phy_reg == -ENODEV) ? -ENODEV : -EIO;
 }

 *phy_id = phy_reg << 16;

 /* Grab the bits from PHYIR2, and put them in the lower half */
 phy_reg = mdiobus_read(bus, addr, MII_PHYSID2);
 if (phy_reg < 0) {
  /* returning -ENODEV doesn't stop bus scanning */
  return (phy_reg == -EIO || phy_reg == -ENODEV) ? -ENODEV : -EIO;
 }

 *phy_id |= phy_reg;

 /* If the phy_id is mostly Fs, there is no device there */
 if ((*phy_id & 0x1fffffff) == 0x1fffffff)
  return -ENODEV;

 return 0;
}

/* Extract the phy ID from the compatible string of the form
 * ethernet-phy-idAAAA.BBBB.
 */

int fwnode_get_phy_id(struct fwnode_handle *fwnode, u32 *phy_id)
{
 unsigned int upper, lower;
 const char *cp;
 int ret;

 ret = fwnode_property_read_string(fwnode, "compatible", &cp);
 if (ret)
  return ret;

 if (sscanf(cp, "ethernet-phy-id%4x.%4x", &upper, &lower) != 2)
  return -EINVAL;

 *phy_id = ((upper & GENMASK(15, 0)) << 16) | (lower & GENMASK(15, 0));
 return 0;
}
EXPORT_SYMBOL(fwnode_get_phy_id);

/**
 * get_phy_device - reads the specified PHY device and returns its @phy_device
 *     struct
 * @bus: the target MII bus
 * @addr: PHY address on the MII bus
 * @is_c45: If true the PHY uses the 802.3 clause 45 protocol
 *
 * Probe for a PHY at @addr on @bus.
 *
 * When probing for a clause 22 PHY, then read the ID registers. If we find
 * a valid ID, allocate and return a &struct phy_device.
 *
 * When probing for a clause 45 PHY, read the "devices in package" registers.
 * If the "devices in package" appears valid, read the ID registers for each
 * MMD, allocate and return a &struct phy_device.
 *
 * Returns an allocated &struct phy_device on success, %-ENODEV if there is
 * no PHY present, or %-EIO on bus access error.
 */

struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45)
{
 struct phy_c45_device_ids c45_ids;
 u32 phy_id = 0;
 int r;

 c45_ids.devices_in_package = 0;
 c45_ids.mmds_present = 0;
 memset(c45_ids.device_ids, 0xff, sizeof(c45_ids.device_ids));

 if (is_c45)
  r = get_phy_c45_ids(bus, addr, &c45_ids);
 else
  r = get_phy_c22_id(bus, addr, &phy_id);

 if (r)
  return ERR_PTR(r);

 /* PHY device such as the Marvell Alaska 88E2110 will return a PHY ID
 * of 0 when probed using get_phy_c22_id() with no error. Proceed to
 * 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->read_c45) {
  r = get_phy_c45_ids(bus, addr, &c45_ids);
  if (!r)
   return phy_device_create(bus, addr, phy_id,
       true, &c45_ids);
 }

 return phy_device_create(bus, addr, phy_id, is_c45, &c45_ids);
}
EXPORT_SYMBOL(get_phy_device);

/**
 * phy_device_register - Register the phy device on the MDIO bus
 * @phydev: phy_device structure to be added to the MDIO bus
 */

int phy_device_register(struct phy_device *phydev)
{
 int err;

 err = mdiobus_register_device(&phydev->mdio);
 if (err)
  return err;

 /* Deassert the reset signal */
 phy_device_reset(phydev, 0);

 /* Run all of the fixups for this PHY */
 err = phy_scan_fixups(phydev);
 if (err) {
  phydev_err(phydev, "failed to initialize\n");
  goto out;
 }

 err = device_add(&phydev->mdio.dev);
 if (err) {
  phydev_err(phydev, "failed to add\n");
  goto out;
 }

 return 0;

 out:
 /* Assert the reset signal */
 phy_device_reset(phydev, 1);

 mdiobus_unregister_device(&phydev->mdio);
 return err;
}
EXPORT_SYMBOL(phy_device_register);

/**
 * phy_device_remove - Remove a previously registered phy device from the MDIO bus
 * @phydev: phy_device structure to remove
 *
 * This doesn't free the phy_device itself, it merely reverses the effects
 * of phy_device_register(). Use phy_device_free() to free the device
 * after calling this function.
 */

void phy_device_remove(struct phy_device *phydev)
{
 unregister_mii_timestamper(phydev->mii_ts);
 pse_control_put(phydev->psec);

 device_del(&phydev->mdio.dev);

 /* Assert the reset signal */
 phy_device_reset(phydev, 1);

 mdiobus_unregister_device(&phydev->mdio);
}
EXPORT_SYMBOL(phy_device_remove);

/**
 * phy_get_c45_ids - Read 802.3-c45 IDs for phy device.
 * @phydev: phy_device structure to read 802.3-c45 IDs
 *
 * Returns zero on success, %-EIO on bus access error, or %-ENODEV if
 * the "devices in package" is invalid.
 */

int phy_get_c45_ids(struct phy_device *phydev)
{
 return get_phy_c45_ids(phydev->mdio.bus, phydev->mdio.addr,
          &phydev->c45_ids);
}
EXPORT_SYMBOL(phy_get_c45_ids);

/**
 * phy_find_first - finds the first PHY device on the bus
 * @bus: the target MII bus
 */

struct phy_device *phy_find_first(struct mii_bus *bus)
{
 struct phy_device *phydev;
 int 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);

/**
 * phy_prepare_link - prepares the PHY layer to monitor link status
 * @phydev: target phy_device struct
 * @handler: callback function for link status change notifications
 *
 * Description: Tells the PHY infrastructure to handle the
 *   gory details on monitoring link status (whether through
 *   polling or an interrupt), and to call back to the
 *   connected device driver when the link status changes.
 *   If you want to monitor your own link state, don't call
 *   this function.
 */

static void phy_prepare_link(struct phy_device *phydev,
        void (*handler)(struct net_device *))
{
 phydev->adjust_link = handler;
}

/**
 * phy_connect_direct - connect an ethernet device to a specific phy_device
 * @dev: the network device to connect
 * @phydev: the pointer to the phy device
 * @handler: callback function for state change notifications
 * @interface: PHY device's interface
 */

int phy_connect_direct(struct net_device *dev, struct phy_device *phydev,
         void (*handler)(struct net_device *),
         phy_interface_t interface)
{
 int rc;

 if (!dev)
  return -EINVAL;

 rc = phy_attach_direct(dev, phydev, phydev->dev_flags, interface);
 if (rc)
  return rc;

 phy_prepare_link(phydev, handler);
 if (phy_interrupt_is_valid(phydev))
  phy_request_interrupt(phydev);

 return 0;
}
EXPORT_SYMBOL(phy_connect_direct);

/**
 * phy_connect - connect an ethernet device to a PHY device
 * @dev: the network device to connect
 * @bus_id: the id string of the PHY device to connect
 * @handler: callback function for state change notifications
 * @interface: PHY device's interface
 *
 * Description: Convenience function for connecting ethernet
 *   devices to PHY devices.  The default behavior is for
 *   the PHY infrastructure to handle everything, and only notify
 *   the connected driver when the link status changes.  If you
 *   don't want, or can't use the provided functionality, you may
 *   choose to call only the subset of functions which provide
 *   the desired functionality.
 */

struct phy_device *phy_connect(struct net_device *dev, const char *bus_id,
          void (*handler)(struct net_device *),
          phy_interface_t interface)
{
 struct phy_device *phydev;
 struct device *d;
 int rc;

 /* Search the list of PHY devices on the mdio bus for the
 * PHY with the requested name
 */

 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);
 }
 phydev = to_phy_device(d);

 rc = phy_connect_direct(dev, phydev, handler, interface);
 put_device(d);
 if (rc)
  return ERR_PTR(rc);

 return phydev;
}
EXPORT_SYMBOL(phy_connect);

/**
 * phy_disconnect - disable interrupts, stop state machine, and detach a PHY
 *     device
 * @phydev: target phy_device struct
 */

void phy_disconnect(struct phy_device *phydev)
{
 if (phy_is_started(phydev))
  phy_stop(phydev);

 if (phy_interrupt_is_valid(phydev))
  phy_free_interrupt(phydev);

 phydev->adjust_link = NULL;

 phy_detach(phydev);
}
EXPORT_SYMBOL(phy_disconnect);

/**
 * phy_poll_reset - Safely wait until a PHY reset has properly completed
 * @phydev: The PHY device to poll
 *
 * Description: According to IEEE 802.3, Section 2, Subsection 22.2.4.1.1, as
 *   published in 2008, a PHY reset may take up to 0.5 seconds.  The MII BMCR
 *   register must be polled until the BMCR_RESET bit clears.
 *
 *   Furthermore, any attempts to write to PHY registers may have no effect
 *   or even generate MDIO bus errors until this is complete.
 *
 *   Some PHYs (such as the Marvell 88E1111) don't entirely conform to the
 *   standard and do not fully reset after the BMCR_RESET bit is set, and may
 *   even *REQUIRE* a soft-reset to properly restart autonegotiation.  In an
 *   effort to support such broken PHYs, this function is separate from the
 *   standard phy_init_hw() which will zero all the other bits in the BMCR
 *   and reapply all driver-specific and board-specific fixups.
 */

static int phy_poll_reset(struct phy_device *phydev)
{
 /* Poll until the reset bit clears (50ms per retry == 0.6 sec) */
 int ret, val;

 ret = phy_read_poll_timeout(phydev, MII_BMCR, val, !(val & BMCR_RESET),
        50000, 600000, true);
 if (ret)
  return ret;
 /* Some chips (smsc911x) may still need up to another 1ms after the
 * BMCR_RESET bit is cleared before they are usable.
 */

 msleep(1);
 return 0;
}

int phy_init_hw(struct phy_device *phydev)
{
 int ret = 0;

 /* Deassert the reset signal */
 phy_device_reset(phydev, 0);

 if (!phydev->drv)
  return 0;

 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 */
  phydev->suspended = 0;
 }

 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)
   return ret;
 }

 if (phydev->drv->config_intr) {
  ret = phydev->drv->config_intr(phydev);
  if (ret < 0)
   return ret;
 }

 return 0;
}
EXPORT_SYMBOL(phy_init_hw);

void phy_attached_info(struct phy_device *phydev)
{
 phy_attached_print(phydev, NULL);
}
EXPORT_SYMBOL(phy_attached_info);

#define ATTACHED_FMT "attached PHY driver %s(mii_bus:phy_addr=%s, irq=%s)"
char *phy_attached_info_irq(struct phy_device *phydev)
{
 char *irq_str;
 char irq_num[8];

 switch(phydev->irq) {
 case PHY_POLL:
  irq_str = "POLL";
  break;
 case PHY_MAC_INTERRUPT:
  irq_str = "MAC";
  break;
 default:
  snprintf(irq_num, sizeof(irq_num), "%d", phydev->irq);
  irq_str = irq_num;
  break;
 }

 return kasprintf(GFP_KERNEL, "%s", irq_str);
}
EXPORT_SYMBOL(phy_attached_info_irq);

void phy_attached_print(struct phy_device *phydev, const char *fmt, ...)
{
 const char *unbound = phydev->drv ? "" : "[unbound] ";
 char *irq_str = phy_attached_info_irq(phydev);

 if (!fmt) {
  phydev_info(phydev, ATTACHED_FMT "\n", unbound,
       phydev_name(phydev), irq_str);
 } else {
  va_list ap;

  phydev_info(phydev, ATTACHED_FMT, unbound,
       phydev_name(phydev), irq_str);

  va_start(ap, fmt);
  vprintk(fmt, ap);
  va_end(ap);
 }
 kfree(irq_str);
}
EXPORT_SYMBOL(phy_attached_print);

static void phy_sysfs_create_links(struct phy_device *phydev)
{
 struct net_device *dev = phydev->attached_dev;
 int err;

 if (!dev)
  return;

 err = sysfs_create_link(&phydev->mdio.dev.kobj, &dev->dev.kobj,
    "attached_dev");
 if (err)
  return;

 err = sysfs_create_link_nowarn(&dev->dev.kobj,
           &phydev->mdio.dev.kobj,
           "phydev");
 if (err) {
  dev_err(&dev->dev, "could not add device link to %s err %d\n",
   kobject_name(&phydev->mdio.dev.kobj),
   err);
  /* non-fatal - some net drivers can use one netdevice
 * with more then one phy
 */

 }

 phydev->sysfs_links = true;
}

static ssize_t
phy_standalone_show(struct device *dev, struct device_attribute *attr,
      char *buf)
{
 struct phy_device *phydev = to_phy_device(dev);

 return sysfs_emit(buf, "%d\n", !phydev->attached_dev);
}
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
 *
 * This is used to fill in the sfp_upstream_ops .attach member.
 */

void phy_sfp_attach(void *upstream, struct sfp_bus *bus)
{
 struct phy_device *phydev = upstream;

 if (phydev->attached_dev)
  phydev->attached_dev->sfp_bus = bus;
 phydev->sfp_bus_attached = true;
}
EXPORT_SYMBOL(phy_sfp_attach);

/**
 * phy_sfp_detach - detach the SFP bus from the PHY upstream network device
 * @upstream: pointer to the phy device
 * @bus: sfp bus representing cage being attached
 *
 * This is used to fill in the sfp_upstream_ops .detach member.
 */

void phy_sfp_detach(void *upstream, struct sfp_bus *bus)
{
 struct phy_device *phydev = upstream;

 if (phydev->attached_dev)
  phydev->attached_dev->sfp_bus = NULL;
 phydev->sfp_bus_attached = false;
}
EXPORT_SYMBOL(phy_sfp_detach);

/**
 * phy_sfp_probe - probe for a SFP cage attached to this PHY device
 * @phydev: Pointer to phy_device
 * @ops: SFP's upstream operations
 */

int phy_sfp_probe(struct phy_device *phydev,
    const struct sfp_upstream_ops *ops)
{
 struct sfp_bus *bus;
 int ret = 0;

 if (phydev->mdio.dev.fwnode) {
  bus = sfp_bus_find_fwnode(phydev->mdio.dev.fwnode);
  if (IS_ERR(bus))
   return PTR_ERR(bus);

  phydev->sfp_bus = bus;

  ret = sfp_bus_add_upstream(bus, phydev, ops);
  sfp_bus_put(bus);
 }
 return ret;
}
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
 * @phydev: Pointer to phy_device to attach
 * @flags: PHY device's dev_flags
 * @interface: PHY device's interface
 *
 * Description: Called by drivers to attach to a particular PHY
 *     device. The phy_device is found, and properly hooked up
 *     to the phy_driver.  If no driver is attached, then a
 *     generic driver is used.  The phy_device is given a ptr to
 *     the attaching device, and given a callback for link status
 *     change.  The phy_device is returned to the attaching driver.
 *     This function takes a reference on the phy device.
 */

int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
        u32 flags, phy_interface_t interface)
{
 struct mii_bus *bus = phydev->mdio.bus;
 struct device *d = &phydev->mdio.dev;
 struct module *ndev_owner = NULL;
 int err;

 /* For Ethernet device drivers that register their own MDIO bus, we
 * will have bus->owner match ndev_mod, so we do not want to increment
 * our own module->refcnt here, otherwise we would not be able to
 * unload later on.
 */

 if (dev)
  ndev_owner = dev->dev.parent->driver->owner;
 if (ndev_owner != bus->owner && !try_module_get(bus->owner)) {
  phydev_err(phydev, "failed to get the bus module\n");
  return -EIO;
 }

 get_device(d);

 /* Assume that if there is no driver, that it doesn't
 * exist, and we should use the genphy driver.
 */

 if (!d->driver) {
  if (phydev->is_c45)
   d->driver = &genphy_c45_driver.mdiodrv.driver;
  else
   d->driver = &genphy_driver.mdiodrv.driver;

  phydev->is_genphy_driven = 1;
 }

 if (!try_module_get(d->driver->owner)) {
  phydev_err(phydev, "failed to get the device driver module\n");
  err = -EIO;
  goto error_put_device;
 }

 if (phydev->is_genphy_driven) {
  err = d->driver->probe(d);
  if (err >= 0)
   err = device_bind_driver(d);

  if (err)
   goto error_module_put;
 }

 if (phydev->attached_dev) {
  dev_err(&dev->dev, "PHY already attached\n");
  err = -EBUSY;
  goto error;
 }

 phydev->phy_link_change = phy_link_change;
 if (dev) {
  phydev->attached_dev = dev;
  dev->phydev = phydev;

  if (phydev->sfp_bus_attached)
   dev->sfp_bus = phydev->sfp_bus;

  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
 * calling register_netdevice() -> netdev_register_kobject() and
 * does the dev->dev.kobj initialization. Here we only check for
 * success which indicates that the network device kobject is
 * ready. Once we do that we still need to keep track of whether
 * links were successfully set up or not for phy_detach() to
 * remove them accordingly.
 */

 phydev->sysfs_links = false;

 phy_sysfs_create_links(phydev);

 if (!phydev->attached_dev) {
  err = sysfs_create_file(&phydev->mdio.dev.kobj,
     &dev_attr_phy_standalone.attr);
  if (err)
   phydev_err(phydev, "error creating 'phy_standalone' sysfs entry\n");
 }

 phydev->dev_flags |= flags;

 phydev->interface = interface;

 phydev->state = PHY_READY;

 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 (phydev->is_genphy_driven)
  phydev->port = PORT_MII;

 /* Initial carrier state is off as the phy is about to be
 * (re)initialized.
 */

 if (dev)
  netif_carrier_off(phydev->attached_dev);

 /* Do initial configuration here, now that
 * we have certain key parameters
 * (dev_flags and interface)
 */

 err = phy_init_hw(phydev);
 if (err)
  goto error;

 phy_resume(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 (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);

 return err;

error:
 /* phy_detach() does all of the cleanup below */
 phy_detach(phydev);
 return err;

error_module_put:
 module_put(d->driver->owner);
 phydev->is_genphy_driven = 0;
 d->driver = NULL;
error_put_device:
 put_device(d);
 if (ndev_owner != bus->owner)
  module_put(bus->owner);
 return err;
}
EXPORT_SYMBOL(phy_attach_direct);

/**
 * phy_attach - attach a network device to a particular PHY device
 * @dev: network device to attach
 * @bus_id: Bus ID of PHY device to attach
 * @interface: PHY device's interface
 *
 * Description: Same as phy_attach_direct() except that a PHY bus_id
 *     string is passed instead of a pointer to a struct phy_device.
 */

struct phy_device *phy_attach(struct net_device *dev, const char *bus_id,
         phy_interface_t interface)
{
 struct phy_device *phydev;
 struct device *d;
 int rc;

 if (!dev)
  return ERR_PTR(-EINVAL);

 /* Search the list of PHY devices on the mdio bus for the
 * PHY with the requested name
 */

 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);
 }
 phydev = to_phy_device(d);

 rc = phy_attach_direct(dev, phydev, phydev->dev_flags, interface);
 put_device(d);
 if (rc)
  return ERR_PTR(rc);

 return phydev;
}
EXPORT_SYMBOL(phy_attach);

/**
 * phy_detach - detach a PHY device from its network device
 * @phydev: target phy_device struct
 *
 * This detaches the phy device from its network device and the phy
 * driver, and drops the reference count taken in phy_attach_direct().
 */

void phy_detach(struct phy_device *phydev)
{
 struct net_device *dev = phydev->attached_dev;
 struct module *ndev_owner = NULL;
 struct mii_bus *bus;

 if (phydev->devlink) {
  device_link_del(phydev->devlink);
  phydev->devlink = NULL;
 }

 if (phydev->sysfs_links) {
  if (dev)
   sysfs_remove_link(&dev->dev.kobj, "phydev");
  sysfs_remove_link(&phydev->mdio.dev.kobj, "attached_dev");
 }

 if (!phydev->attached_dev)
  sysfs_remove_file(&phydev->mdio.dev.kobj,
      &dev_attr_phy_standalone.attr);

 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;

 if (!phydev->is_on_sfp_module)
  phy_led_triggers_unregister(phydev);

 if (phydev->mdio.dev.driver)
  module_put(phydev->mdio.dev.driver->owner);

 /* If the device had no specific driver before (i.e. - it
 * was using the generic driver), we unbind the device
 * from the generic driver so that there's a chance a
 * real driver could be loaded
 */

 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);

 /*
 * The phydev might go away on the put_device() below, so avoid
 * a use-after-free bug by reading the underlying bus first.
 */

 bus = phydev->mdio.bus;

 put_device(&phydev->mdio.dev);
 if (dev)
  ndev_owner = dev->dev.parent->driver->owner;
 if (ndev_owner != bus->owner)
  module_put(bus->owner);
}
EXPORT_SYMBOL(phy_detach);

int phy_suspend(struct phy_device *phydev)
{
 struct net_device *netdev = phydev->attached_dev;
 const struct phy_driver *phydrv = phydev->drv;
 int ret;

 if (phydev->suspended || !phydrv)
  return 0;

 phydev->wol_enabled = phy_drv_wol_enabled(phydev) ||
         (netdev && netdev->ethtool->wol_enabled);
 /* If the device has WOL enabled, we cannot suspend the PHY */
 if (phydev->wol_enabled && !(phydrv->flags & PHY_ALWAYS_CALL_SUSPEND))
  return -EBUSY;

 if (!phydrv->suspend)
  return 0;

 ret = phydrv->suspend(phydev);
 if (!ret)
  phydev->suspended = true;

 return ret;
}
EXPORT_SYMBOL(phy_suspend);

int __phy_resume(struct phy_device *phydev)
{
 const struct phy_driver *phydrv = phydev->drv;
 int ret;

 lockdep_assert_held(&phydev->lock);

 if (!phydrv || !phydrv->resume)
  return 0;

 ret = phydrv->resume(phydev);
 if (!ret)
  phydev->suspended = false;

 return ret;
}
EXPORT_SYMBOL(__phy_resume);

int phy_resume(struct phy_device *phydev)
{
 int ret;

 mutex_lock(&phydev->lock);
 ret = __phy_resume(phydev);
 mutex_unlock(&phydev->lock);

 return ret;
}
EXPORT_SYMBOL(phy_resume);

/**
 * phy_reset_after_clk_enable - perform a PHY reset if needed
 * @phydev: target phy_device struct
 *
 * Description: Some PHYs are known to need a reset after their refclk was
 *   enabled. This function evaluates the flags and perform the reset if it's
 *   needed. Returns < 0 on error, 0 if the phy wasn't reset and 1 if the phy
 *   was reset.
 */

int phy_reset_after_clk_enable(struct phy_device *phydev)
{
 if (!phydev || !phydev->drv)
  return -ENODEV;

 if (phydev->drv->flags & PHY_RST_AFTER_CLK_EN) {
  phy_device_reset(phydev, 1);
  phy_device_reset(phydev, 0);
  return 1;
 }

 return 0;
}
EXPORT_SYMBOL(phy_reset_after_clk_enable);

/* Generic PHY support and helper functions */

/**
 * 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,
    const unsigned long *advert)
{
 int err, bmsr, changed = 0;
 u32 adv;

 adv = linkmode_adv_to_mii_adv_t(advert);

 /* Setup standard advertisement */
 err = phy_modify_changed(phydev, MII_ADVERTISE,
     ADVERTISE_ALL | ADVERTISE_100BASE4 |
     ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM,
     adv);
 if (err < 0)
  return err;
 if (err > 0)
  changed = 1;

 bmsr = phy_read(phydev, MII_BMSR);
 if (bmsr < 0)
  return bmsr;

 /* Per 802.3-2008, Section 22.2.4.2.16 Extended status all
 * 1000Mbits/sec capable PHYs shall have the BMSR_ESTATEN bit set to a
 * logical 1.
 */

 if (!(bmsr & BMSR_ESTATEN))
  return changed;

 adv = linkmode_adv_to_mii_ctrl1000_t(advert);

 err = phy_modify_changed(phydev, MII_CTRL1000,
     ADVERTISE_1000FULL | ADVERTISE_1000HALF,
     adv);
 if (err < 0)
  return err;
 if (err > 0)
  changed = 1;

 return changed;
}

/**
 * genphy_c37_config_advert - sanitize and advertise auto-negotiation parameters
 * @phydev: target phy_device struct
 *
 * 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. This function is intended
 *   for Clause 37 1000Base-X mode.
 */

static int genphy_c37_config_advert(struct phy_device *phydev)
{
 u16 adv = 0;

 /* Only allow advertising what this PHY supports */
 linkmode_and(phydev->advertising, phydev->advertising,
       phydev->supported);

 if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
         phydev->advertising))
  adv |= ADVERTISE_1000XFULL;
 if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT,
         phydev->advertising))
  adv |= ADVERTISE_1000XPAUSE;
 if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
         phydev->advertising))
  adv |= ADVERTISE_1000XPSE_ASYM;

 return phy_modify_changed(phydev, MII_ADVERTISE,
      ADVERTISE_1000XFULL | ADVERTISE_1000XPAUSE |
      ADVERTISE_1000XHALF | ADVERTISE_1000XPSE_ASYM,
      adv);
}

/**
 * genphy_setup_forced - configures/forces speed/duplex from @phydev
 * @phydev: target phy_device struct
 *
 * Description: Configures MII_BMCR to force speed/duplex
 *   to the values in phydev. Assumes that the values are valid.
 *   Please see phy_sanitize_settings().
 */

int genphy_setup_forced(struct phy_device *phydev)
{
 u16 ctl;

 phydev->pause = 0;
 phydev->asym_pause = 0;

 ctl = mii_bmcr_encode_fixed(phydev->speed, phydev->duplex);

 return phy_modify(phydev, MII_BMCR,
     ~(BMCR_LOOPBACK | BMCR_ISOLATE | BMCR_PDOWN), ctl);
}
EXPORT_SYMBOL(genphy_setup_forced);

static int genphy_setup_master_slave(struct phy_device *phydev)
{
 u16 ctl = 0;

 if (!phydev->is_gigabit_capable)
  return 0;

 switch (phydev->master_slave_set) {
 case MASTER_SLAVE_CFG_MASTER_PREFERRED:
  ctl |= CTL1000_PREFER_MASTER;
  break;
 case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
  break;
 case MASTER_SLAVE_CFG_MASTER_FORCE:
  ctl |= CTL1000_AS_MASTER;
  fallthrough;
 case MASTER_SLAVE_CFG_SLAVE_FORCE:
  ctl |= CTL1000_ENABLE_MASTER;
  break;
 case MASTER_SLAVE_CFG_UNKNOWN:
 case MASTER_SLAVE_CFG_UNSUPPORTED:
  return 0;
 default:
  phydev_warn(phydev, "Unsupported Master/Slave mode\n");
  return -EOPNOTSUPP;
 }

 return phy_modify_changed(phydev, MII_CTRL1000,
      (CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER |
       CTL1000_PREFER_MASTER), ctl);
}

int genphy_read_master_slave(struct phy_device *phydev)
{
 int cfg, state;
 int val;

 phydev->master_slave_get = MASTER_SLAVE_CFG_UNKNOWN;
 phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN;

 val = phy_read(phydev, MII_CTRL1000);
 if (val < 0)
  return val;

 if (val & CTL1000_ENABLE_MASTER) {
  if (val & CTL1000_AS_MASTER)
   cfg = MASTER_SLAVE_CFG_MASTER_FORCE;
  else
   cfg = MASTER_SLAVE_CFG_SLAVE_FORCE;
 } else {
  if (val & CTL1000_PREFER_MASTER)
   cfg = MASTER_SLAVE_CFG_MASTER_PREFERRED;
  else
   cfg = MASTER_SLAVE_CFG_SLAVE_PREFERRED;
 }

 val = phy_read(phydev, MII_STAT1000);
 if (val < 0)
  return val;

 if (val & LPA_1000MSFAIL) {
  state = MASTER_SLAVE_STATE_ERR;
 } else if (phydev->link) {
  /* this bits are valid only for active link */
  if (val & LPA_1000MSRES)
   state = MASTER_SLAVE_STATE_MASTER;
  else
   state = MASTER_SLAVE_STATE_SLAVE;
 } else {
  state = MASTER_SLAVE_STATE_UNKNOWN;
 }

 phydev->master_slave_get = cfg;
 phydev->master_slave_state = state;

 return 0;
}
EXPORT_SYMBOL(genphy_read_master_slave);

/**
 * genphy_restart_aneg - Enable and Restart Autonegotiation
 * @phydev: target phy_device struct
 */

int genphy_restart_aneg(struct phy_device *phydev)
{
 /* Don't isolate the PHY if we're negotiating */
 return phy_modify(phydev, MII_BMCR, BMCR_ISOLATE,
     BMCR_ANENABLE | BMCR_ANRESTART);
}
EXPORT_SYMBOL(genphy_restart_aneg);

/**
 * genphy_check_and_restart_aneg - Enable and restart auto-negotiation
 * @phydev: target phy_device struct
 * @restart: whether aneg restart is requested
 *
 * Check, and restart auto-negotiation if needed.
 */

int genphy_check_and_restart_aneg(struct phy_device *phydev, bool restart)
{
 int ret;

 if (!restart) {
  /* Advertisement hasn't changed, but maybe aneg was never on to
 * begin with?  Or maybe phy was isolated?
 */

  ret = phy_read(phydev, MII_BMCR);
  if (ret < 0)
   return ret;

  if (!(ret & BMCR_ANENABLE) || (ret & BMCR_ISOLATE))
   restart = true;
 }

 if (restart)
  return genphy_restart_aneg(phydev);

 return 0;
}
EXPORT_SYMBOL(genphy_check_and_restart_aneg);

/**
 * __genphy_config_aneg - restart auto-negotiation or write BMCR
 * @phydev: target phy_device struct
 * @changed: whether autoneg is requested
 *
 * Description: If auto-negotiation is enabled, we configure the
 *   advertising, and then restart auto-negotiation.  If it is not
 *   enabled, then we write the BMCR.
 */

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;

 err = genphy_c45_an_config_eee_aneg(phydev);
 if (err < 0)
  return err;
 else if (err)
  changed = true;

 err = genphy_setup_master_slave(phydev);
 if (err < 0)
  return err;
 else if (err)
  changed = true;

 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);

  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)
  changed = true;

 return genphy_check_and_restart_aneg(phydev, changed);
}
EXPORT_SYMBOL(__genphy_config_aneg);

/**
 * genphy_c37_config_aneg - restart auto-negotiation or write BMCR
 * @phydev: target phy_device struct
 *
 * Description: If auto-negotiation is enabled, we configure the
 *   advertising, and then restart auto-negotiation.  If it is not
 *   enabled, then we write the BMCR. This function is intended
 *   for use with Clause 37 1000Base-X mode.
 */

int genphy_c37_config_aneg(struct phy_device *phydev)
{
 int err, changed;

 if (phydev->autoneg != AUTONEG_ENABLE)
  return genphy_setup_forced(phydev);

 err = phy_modify(phydev, MII_BMCR, BMCR_SPEED1000 | BMCR_SPEED100,
    BMCR_SPEED1000);
 if (err)
  return err;

 changed = genphy_c37_config_advert(phydev);
 if (changed < 0) /* error */
  return changed;

 if (!changed) {
  /* Advertisement hasn't changed, but maybe aneg was never on to
 * begin with?  Or maybe phy was isolated?
 */

  int ctl = phy_read(phydev, MII_BMCR);

  if (ctl < 0)
   return ctl;

  if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE))
   changed = 1; /* do restart aneg */
 }

 /* Only restart aneg if we are advertising something different
 * than we were before.
 */

 if (changed > 0)
  return genphy_restart_aneg(phydev);

 return 0;
}
EXPORT_SYMBOL(genphy_c37_config_aneg);

/**
 * genphy_aneg_done - return auto-negotiation status
 * @phydev: target phy_device struct
 *
 * Description: Reads the status register and returns 0 either if
 *   auto-negotiation is incomplete, or if there was an error.
 *   Returns BMSR_ANEGCOMPLETE if auto-negotiation is done.
 */

int genphy_aneg_done(struct phy_device *phydev)
{
 int retval = phy_read(phydev, MII_BMSR);

 return (retval < 0) ? retval : (retval & BMSR_ANEGCOMPLETE);
}
EXPORT_SYMBOL(genphy_aneg_done);

/**
 * genphy_update_link - update link status in @phydev
 * @phydev: target phy_device struct
 *
 * Description: Update the value in phydev->link to reflect the
 *   current link value.  In order to do this, we need to read
 *   the status register twice, keeping the second value.
 */

int genphy_update_link(struct phy_device *phydev)
{
 int status = 0, bmcr;

 bmcr = phy_read(phydev, MII_BMCR);
 if (bmcr < 0)
  return bmcr;

 /* Autoneg is being started, therefore disregard BMSR value and
 * report link as down.
 */

 if (bmcr & BMCR_ANRESTART)
  goto done;

 /* The link state is latched low so that momentary link
 * drops can be detected. Do not double-read the status
 * in polling mode to detect such short link drops except
 * the link was already down.
 */

 if (!phy_polling_mode(phydev) || !phydev->link) {
  status = phy_read(phydev, MII_BMSR);
  if (status < 0)
   return status;
  else if (status & BMSR_LSTATUS)
   goto done;
 }

 /* Read link and autonegotiation status */
 status = phy_read(phydev, MII_BMSR);
 if (status < 0)
  return status;
done:
 phydev->link = status & BMSR_LSTATUS ? 1 : 0;
 phydev->autoneg_complete = status & BMSR_ANEGCOMPLETE ? 1 : 0;

 /* Consider the case that autoneg was started and "aneg complete"
 * bit has been reset, but "link up" bit not yet.
 */

 if (phydev->autoneg == AUTONEG_ENABLE && !phydev->autoneg_complete)
  phydev->link = 0;

 return 0;
}
EXPORT_SYMBOL(genphy_update_link);

int genphy_read_lpa(struct phy_device *phydev)
{
 int lpa, lpagb;

 if (phydev->autoneg == AUTONEG_ENABLE) {
  if (!phydev->autoneg_complete) {
   mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising,
       0);
   mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, 0);
   return 0;
  }

  if (phydev->is_gigabit_capable) {
   lpagb = phy_read(phydev, MII_STAT1000);
   if (lpagb < 0)
    return lpagb;

   if (lpagb & LPA_1000MSFAIL) {
    int adv = phy_read(phydev, MII_CTRL1000);

    if (adv < 0)
     return adv;

    if (adv & CTL1000_ENABLE_MASTER)
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=94 H=96 G=94

¤ Dauer der Verarbeitung: 0.29 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.