123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308 |
- From 03855caf93f7332a3f320228ba1a0e7baae8a749 Mon Sep 17 00:00:00 2001
- From: Tim Harvey <tharvey@gateworks.com>
- Date: Thu, 15 May 2014 12:36:23 -0700
- Subject: [PATCH] net: igb: register mii_bus for SerDes w/ external phy
- If an i210 is configured for 1000BASE-BX link_mode and has an external phy
- specified, then register an mii bus using the external phy address as
- a mask.
- An i210 hooked to an external standard phy will be configured with a link_mo
- of SGMII in which case phy ops will be configured and used internall in the
- igb driver for link status. However, in certain cases one might be using a
- backplane SerDes connection to something that talks on the mdio bus but is
- not a standard phy, such as a switch. In this case by registering an mdio
- bus a phy driver can manage the device.
- Signed-off-by: Tim Harvey <tharvey@gateworks.com>
- ---
- drivers/net/ethernet/intel/igb/e1000_82575.c | 15 +++
- drivers/net/ethernet/intel/igb/e1000_hw.h | 7 ++
- drivers/net/ethernet/intel/igb/igb_main.c | 168 ++++++++++++++++++++++++++-
- 3 files changed, 185 insertions(+), 5 deletions(-)
- --- a/drivers/net/ethernet/intel/igb/e1000_82575.c
- +++ b/drivers/net/ethernet/intel/igb/e1000_82575.c
- @@ -613,13 +613,25 @@ static s32 igb_get_invariants_82575(stru
- switch (link_mode) {
- case E1000_CTRL_EXT_LINK_MODE_1000BASE_KX:
- hw->phy.media_type = e1000_media_type_internal_serdes;
- + if (igb_sgmii_uses_mdio_82575(hw)) {
- + u32 mdicnfg = rd32(E1000_MDICNFG);
- + mdicnfg &= E1000_MDICNFG_PHY_MASK;
- + hw->phy.addr = mdicnfg >> E1000_MDICNFG_PHY_SHIFT;
- + hw_dbg("1000BASE_KX w/ external MDIO device at 0x%x\n",
- + hw->phy.addr);
- + } else {
- + hw_dbg("1000BASE_KX");
- + }
- break;
- case E1000_CTRL_EXT_LINK_MODE_SGMII:
- /* Get phy control interface type set (MDIO vs. I2C)*/
- if (igb_sgmii_uses_mdio_82575(hw)) {
- hw->phy.media_type = e1000_media_type_copper;
- dev_spec->sgmii_active = true;
- + hw_dbg("SGMII with external MDIO PHY");
- break;
- + } else {
- + hw_dbg("SGMII with external I2C PHY");
- }
- /* fall through for I2C based SGMII */
- case E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES:
- @@ -636,8 +648,11 @@ static s32 igb_get_invariants_82575(stru
- hw->phy.media_type = e1000_media_type_copper;
- dev_spec->sgmii_active = true;
- }
- + hw_dbg("SERDES with external SFP");
-
- break;
- + } else {
- + hw_dbg("SERDES");
- }
-
- /* do not change link mode for 100BaseFX */
- --- a/drivers/net/ethernet/intel/igb/e1000_hw.h
- +++ b/drivers/net/ethernet/intel/igb/e1000_hw.h
- @@ -27,6 +27,7 @@
- #include <linux/delay.h>
- #include <linux/io.h>
- #include <linux/netdevice.h>
- +#include <linux/phy.h>
-
- #include "e1000_regs.h"
- #include "e1000_defines.h"
- @@ -543,6 +544,12 @@ struct e1000_hw {
- struct e1000_mbx_info mbx;
- struct e1000_host_mng_dhcp_cookie mng_cookie;
-
- +#ifdef CONFIG_PHYLIB
- + /* Phylib and MDIO interface */
- + struct mii_bus *mii_bus;
- + struct phy_device *phy_dev;
- + phy_interface_t phy_interface;
- +#endif
- union {
- struct e1000_dev_spec_82575 _82575;
- } dev_spec;
- --- a/drivers/net/ethernet/intel/igb/igb_main.c
- +++ b/drivers/net/ethernet/intel/igb/igb_main.c
- @@ -41,6 +41,7 @@
- #include <linux/if_vlan.h>
- #include <linux/pci.h>
- #include <linux/pci-aspm.h>
- +#include <linux/phy.h>
- #include <linux/delay.h>
- #include <linux/interrupt.h>
- #include <linux/ip.h>
- @@ -2217,6 +2218,126 @@ static s32 igb_init_i2c(struct igb_adapt
- return status;
- }
-
- +
- +#ifdef CONFIG_PHYLIB
- +/*
- + * MMIO/PHYdev support
- + */
- +
- +static int igb_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
- +{
- + struct e1000_hw *hw = bus->priv;
- + u16 out;
- + int err;
- +
- + err = igb_read_reg_gs40g(hw, mii_id, regnum, &out);
- + if (err)
- + return err;
- + return out;
- +}
- +
- +static int igb_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
- + u16 val)
- +{
- + struct e1000_hw *hw = bus->priv;
- +
- + return igb_write_reg_gs40g(hw, mii_id, regnum, val);
- +}
- +
- +static int igb_enet_mdio_reset(struct mii_bus *bus)
- +{
- + udelay(300);
- + return 0;
- +}
- +
- +static void igb_enet_mii_link(struct net_device *netdev)
- +{
- +}
- +
- +/* Probe the mdio bus for phys and connect them */
- +static int igb_enet_mii_probe(struct net_device *netdev)
- +{
- + struct igb_adapter *adapter = netdev_priv(netdev);
- + struct e1000_hw *hw = &adapter->hw;
- + struct phy_device *phy_dev = NULL;
- + int phy_id;
- +
- + /* check for attached phy */
- + for (phy_id = 0; (phy_id < PHY_MAX_ADDR); phy_id++) {
- + if (hw->mii_bus->phy_map[phy_id]) {
- + phy_dev = hw->mii_bus->phy_map[phy_id];
- + break;
- + }
- + }
- + if (!phy_dev) {
- + netdev_err(netdev, "no PHY found\n");
- + return -ENODEV;
- + }
- +
- + hw->phy_interface = PHY_INTERFACE_MODE_RGMII;
- + phy_dev = phy_connect(netdev, dev_name(&phy_dev->dev),
- + igb_enet_mii_link, hw->phy_interface);
- + if (IS_ERR(phy_dev)) {
- + netdev_err(netdev, "could not attach to PHY\n");
- + return PTR_ERR(phy_dev);
- + }
- +
- + hw->phy_dev = phy_dev;
- + netdev_info(netdev, "igb PHY driver [%s] (mii_bus:phy_addr=%s)\n",
- + hw->phy_dev->drv->name, dev_name(&hw->phy_dev->dev));
- +
- + return 0;
- +}
- +
- +/* Create and register mdio bus */
- +static int igb_enet_mii_init(struct pci_dev *pdev)
- +{
- + struct mii_bus *mii_bus;
- + struct net_device *netdev = pci_get_drvdata(pdev);
- + struct igb_adapter *adapter = netdev_priv(netdev);
- + struct e1000_hw *hw = &adapter->hw;
- + int err;
- +
- + mii_bus = mdiobus_alloc();
- + if (mii_bus == NULL) {
- + err = -ENOMEM;
- + goto err_out;
- + }
- +
- + mii_bus->name = "igb_enet_mii_bus";
- + mii_bus->read = igb_enet_mdio_read;
- + mii_bus->write = igb_enet_mdio_write;
- + mii_bus->reset = igb_enet_mdio_reset;
- + snprintf(mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
- + pci_name(pdev), hw->device_id + 1);
- + mii_bus->priv = hw;
- + mii_bus->parent = &pdev->dev;
- + mii_bus->phy_mask = ~(1 << hw->phy.addr);
- +
- + err = mdiobus_register(mii_bus);
- + if (err) {
- + printk(KERN_ERR "failed to register mii_bus: %d\n", err);
- + goto err_out_free_mdiobus;
- + }
- + hw->mii_bus = mii_bus;
- +
- + return 0;
- +
- +err_out_free_mdiobus:
- + mdiobus_free(mii_bus);
- +err_out:
- + return err;
- +}
- +
- +static void igb_enet_mii_remove(struct e1000_hw *hw)
- +{
- + if (hw->mii_bus) {
- + mdiobus_unregister(hw->mii_bus);
- + mdiobus_free(hw->mii_bus);
- + }
- +}
- +#endif /* CONFIG_PHYLIB */
- +
- /**
- * igb_probe - Device Initialization Routine
- * @pdev: PCI device information struct
- @@ -2641,6 +2762,13 @@ static int igb_probe(struct pci_dev *pde
- }
- }
- pm_runtime_put_noidle(&pdev->dev);
- +
- +#ifdef CONFIG_PHYLIB
- + /* create and register the mdio bus if using ext phy */
- + if (rd32(E1000_MDICNFG) & E1000_MDICNFG_EXT_MDIO)
- + igb_enet_mii_init(pdev);
- +#endif
- +
- return 0;
-
- err_register:
- @@ -2788,6 +2916,10 @@ static void igb_remove(struct pci_dev *p
- struct e1000_hw *hw = &adapter->hw;
-
- pm_runtime_get_noresume(&pdev->dev);
- +#ifdef CONFIG_PHYLIB
- + if (rd32(E1000_MDICNFG) & E1000_MDICNFG_EXT_MDIO)
- + igb_enet_mii_remove(hw);
- +#endif
- #ifdef CONFIG_IGB_HWMON
- igb_sysfs_exit(adapter);
- #endif
- @@ -3113,6 +3245,12 @@ static int __igb_open(struct net_device
- if (!resuming)
- pm_runtime_put(&pdev->dev);
-
- +#ifdef CONFIG_PHYLIB
- + /* Probe and connect to PHY if using ext phy */
- + if (rd32(E1000_MDICNFG) & E1000_MDICNFG_EXT_MDIO)
- + igb_enet_mii_probe(netdev);
- +#endif
- +
- /* start the watchdog. */
- hw->mac.get_link_status = 1;
- schedule_work(&adapter->watchdog_task);
- @@ -7106,21 +7244,41 @@ void igb_alloc_rx_buffers(struct igb_rin
- static int igb_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
- {
- struct igb_adapter *adapter = netdev_priv(netdev);
- + struct e1000_hw *hw = &adapter->hw;
- struct mii_ioctl_data *data = if_mii(ifr);
-
- - if (adapter->hw.phy.media_type != e1000_media_type_copper)
- + if (adapter->hw.phy.media_type != e1000_media_type_copper &&
- + !(rd32(E1000_MDICNFG) & E1000_MDICNFG_EXT_MDIO))
- return -EOPNOTSUPP;
-
- switch (cmd) {
- case SIOCGMIIPHY:
- - data->phy_id = adapter->hw.phy.addr;
- + data->phy_id = hw->phy.addr;
- break;
- case SIOCGMIIREG:
- - if (igb_read_phy_reg(&adapter->hw, data->reg_num & 0x1F,
- - &data->val_out))
- - return -EIO;
- + if (hw->mac.type == e1000_i210 || hw->mac.type == e1000_i211) {
- + if (igb_read_reg_gs40g(&adapter->hw, data->phy_id,
- + data->reg_num & 0x1F,
- + &data->val_out))
- + return -EIO;
- + } else {
- + if (igb_read_phy_reg(&adapter->hw, data->reg_num & 0x1F,
- + &data->val_out))
- + return -EIO;
- + }
- break;
- case SIOCSMIIREG:
- + if (hw->mac.type == e1000_i210 || hw->mac.type == e1000_i211) {
- + if (igb_write_reg_gs40g(hw, data->phy_id,
- + data->reg_num & 0x1F,
- + data->val_in))
- + return -EIO;
- + } else {
- + if (igb_write_phy_reg(hw, data->reg_num & 0x1F,
- + data->val_in))
- + return -EIO;
- + }
- + break;
- default:
- return -EOPNOTSUPP;
- }
|