Commit 9747e231 authored by David S. Miller's avatar David S. Miller

Merge branch 'phylib-support-for-MV88X3310-10G-phy'

Russell King says:

====================
net: Add phylib support for MV88X3310 10G phy

This patch series adds support for the Marvell 88x3310 PHY found on
the SolidRun Macchiatobin board.

The first patch introduces a set of generic Clause 45 PHY helpers that
C45 PHY drivers can make use of if they wish.

Patch 2 ensures that the Clause 22 aneg_done function will not be
called for incompatible Clause 45 PHYs.

Patch 3 fixes the aneg restart to be compatible with C45 PHYs - it can
currently only cope with C22 PHYs.

Patch 4 moves the "gen10g" driver into the Clause 45 code, grouping all
core clause 45 code together.

Patch 5 adds the phy_interface_t types for XAUI and 10GBase-KR links.
As 10GBase-KR appears to be compatible with XFI and SFI, XFI and SFI,
I currently see no reason to add XFI and SFI interface modes.  There
seems to be vendor code out there using these, but they all alias back
to the same hardware settings.

Patch 6 adds support for the MV88X3310 PHY, which supports both the
copper and fiber interfaces.  It should be noted that the MV88X3310
automatically switches its MAC facing interface between 10GBase-KR
and SGMII depending on the negotiated speed.  This was discussed with
Florian, and we agreed to update the phy interface mode depending on
the properties of the actual link mode to the PHY.

v2:
- update sysfs-class-net-phydev documentation
- avoid genphy_aneg_done for non-C22 PHYs
- expand comment about 0x30 constant
- add comment about lack of reset
- configure driver using MARVELL_10G_PHY
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 92046578 20b2af32
...@@ -32,5 +32,5 @@ Description: ...@@ -32,5 +32,5 @@ Description:
<empty> (not available), mii, gmii, sgmii, tbi, rev-mii, <empty> (not available), mii, gmii, sgmii, tbi, rev-mii,
rmii, rgmii, rgmii-id, rgmii-rxid, rgmii-txid, rtbi, smii rmii, rgmii, rgmii-id, rgmii-rxid, rgmii-txid, rtbi, smii
xgmii, moca, qsgmii, trgmii, 1000base-x, 2500base-x, rxaui, xgmii, moca, qsgmii, trgmii, 1000base-x, 2500base-x, rxaui,
unknown xaui, 10gbase-kr, unknown
...@@ -32,6 +32,8 @@ The following properties are common to the Ethernet controllers: ...@@ -32,6 +32,8 @@ The following properties are common to the Ethernet controllers:
* "2000base-x", * "2000base-x",
* "2500base-x", * "2500base-x",
* "rxaui" * "rxaui"
* "xaui"
* "10gbase-kr" (10GBASE-KR, XFI, SFI)
- phy-connection-type: the same as "phy-mode" property but described in ePAPR; - phy-connection-type: the same as "phy-mode" property but described in ePAPR;
- phy-handle: phandle, specifies a reference to a node representing a PHY - phy-handle: phandle, specifies a reference to a node representing a PHY
device; this property is described in ePAPR and so preferred; device; this property is described in ePAPR and so preferred;
......
...@@ -7979,6 +7979,12 @@ S: Maintained ...@@ -7979,6 +7979,12 @@ S: Maintained
F: drivers/net/ethernet/marvell/mv643xx_eth.* F: drivers/net/ethernet/marvell/mv643xx_eth.*
F: include/linux/mv643xx.h F: include/linux/mv643xx.h
MARVELL MV88X3310 PHY DRIVER
M: Russell King <rmk@armlinux.org.uk>
L: netdev@vger.kernel.org
S: Maintained
F: drivers/net/phy/marvell10g.c
MARVELL MVNETA ETHERNET DRIVER MARVELL MVNETA ETHERNET DRIVER
M: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> M: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
L: netdev@vger.kernel.org L: netdev@vger.kernel.org
......
...@@ -292,6 +292,11 @@ config MARVELL_PHY ...@@ -292,6 +292,11 @@ config MARVELL_PHY
---help--- ---help---
Currently has a driver for the 88E1011S Currently has a driver for the 88E1011S
config MARVELL_10G_PHY
tristate "Marvell Alaska 10Gbit PHYs"
---help---
Support for the Marvell Alaska MV88X3310 and compatible PHYs.
config MESON_GXL_PHY config MESON_GXL_PHY
tristate "Amlogic Meson GXL Internal PHY" tristate "Amlogic Meson GXL Internal PHY"
depends on ARCH_MESON || COMPILE_TEST depends on ARCH_MESON || COMPILE_TEST
......
# Makefile for Linux PHY drivers and MDIO bus drivers # Makefile for Linux PHY drivers and MDIO bus drivers
libphy-y := phy.o phy-core.o phy_device.o libphy-y := phy.o phy-c45.o phy-core.o phy_device.o
mdio-bus-y += mdio_bus.o mdio_device.o mdio-bus-y += mdio_bus.o mdio_device.o
ifdef CONFIG_MDIO_DEVICE ifdef CONFIG_MDIO_DEVICE
...@@ -57,6 +57,7 @@ obj-$(CONFIG_INTEL_XWAY_PHY) += intel-xway.o ...@@ -57,6 +57,7 @@ obj-$(CONFIG_INTEL_XWAY_PHY) += intel-xway.o
obj-$(CONFIG_LSI_ET1011C_PHY) += et1011c.o obj-$(CONFIG_LSI_ET1011C_PHY) += et1011c.o
obj-$(CONFIG_LXT_PHY) += lxt.o obj-$(CONFIG_LXT_PHY) += lxt.o
obj-$(CONFIG_MARVELL_PHY) += marvell.o obj-$(CONFIG_MARVELL_PHY) += marvell.o
obj-$(CONFIG_MARVELL_10G_PHY) += marvell10g.o
obj-$(CONFIG_MESON_GXL_PHY) += meson-gxl.o obj-$(CONFIG_MESON_GXL_PHY) += meson-gxl.o
obj-$(CONFIG_MICREL_KS8995MA) += spi_ks8995.o obj-$(CONFIG_MICREL_KS8995MA) += spi_ks8995.o
obj-$(CONFIG_MICREL_PHY) += micrel.o obj-$(CONFIG_MICREL_PHY) += micrel.o
......
/*
* Marvell 10G 88x3310 PHY driver
*
* Based upon the ID registers, this PHY appears to be a mixture of IPs
* from two different companies.
*
* There appears to be several different data paths through the PHY which
* are automatically managed by the PHY. The following has been determined
* via observation and experimentation:
*
* SGMII PHYXS -- BASE-T PCS -- 10G PMA -- AN -- Copper (for <= 1G)
* 10GBASE-KR PHYXS -- BASE-T PCS -- 10G PMA -- AN -- Copper (for 10G)
* 10GBASE-KR PHYXS -- BASE-R PCS -- Fiber
*
* If both the fiber and copper ports are connected, the first to gain
* link takes priority and the other port is completely locked out.
*/
#include <linux/phy.h>
enum {
MV_PCS_BASE_T = 0x0000,
MV_PCS_BASE_R = 0x1000,
MV_PCS_1000BASEX = 0x2000,
/* These registers appear at 0x800X and 0xa00X - the 0xa00X control
* registers appear to set themselves to the 0x800X when AN is
* restarted, but status registers appear readable from either.
*/
MV_AN_CTRL1000 = 0x8000, /* 1000base-T control register */
MV_AN_STAT1000 = 0x8001, /* 1000base-T status register */
/* This register appears to reflect the copper status */
MV_AN_RESULT = 0xa016,
MV_AN_RESULT_SPD_10 = BIT(12),
MV_AN_RESULT_SPD_100 = BIT(13),
MV_AN_RESULT_SPD_1000 = BIT(14),
MV_AN_RESULT_SPD_10000 = BIT(15),
};
static int mv3310_modify(struct phy_device *phydev, int devad, u16 reg,
u16 mask, u16 bits)
{
int old, val, ret;
old = phy_read_mmd(phydev, devad, reg);
if (old < 0)
return old;
val = (old & ~mask) | (bits & mask);
if (val == old)
return 0;
ret = phy_write_mmd(phydev, devad, reg, val);
return ret < 0 ? ret : 1;
}
static int mv3310_probe(struct phy_device *phydev)
{
u32 mmd_mask = MDIO_DEVS_PMAPMD | MDIO_DEVS_AN;
if (!phydev->is_c45 ||
(phydev->c45_ids.devices_in_package & mmd_mask) != mmd_mask)
return -ENODEV;
return 0;
}
/*
* Resetting the MV88X3310 causes it to become non-responsive. Avoid
* setting the reset bit(s).
*/
static int mv3310_soft_reset(struct phy_device *phydev)
{
return 0;
}
static int mv3310_config_init(struct phy_device *phydev)
{
__ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = { 0, };
u32 mask;
int val;
/* Check that the PHY interface type is compatible */
if (phydev->interface != PHY_INTERFACE_MODE_SGMII &&
phydev->interface != PHY_INTERFACE_MODE_XGMII &&
phydev->interface != PHY_INTERFACE_MODE_XAUI &&
phydev->interface != PHY_INTERFACE_MODE_RXAUI &&
phydev->interface != PHY_INTERFACE_MODE_10GKR)
return -ENODEV;
__set_bit(ETHTOOL_LINK_MODE_Pause_BIT, supported);
__set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, supported);
if (phydev->c45_ids.devices_in_package & MDIO_DEVS_AN) {
val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
if (val < 0)
return val;
if (val & MDIO_AN_STAT1_ABLE)
__set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, supported);
}
val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_STAT2);
if (val < 0)
return val;
/* Ethtool does not support the WAN mode bits */
if (val & (MDIO_PMA_STAT2_10GBSR | MDIO_PMA_STAT2_10GBLR |
MDIO_PMA_STAT2_10GBER | MDIO_PMA_STAT2_10GBLX4 |
MDIO_PMA_STAT2_10GBSW | MDIO_PMA_STAT2_10GBLW |
MDIO_PMA_STAT2_10GBEW))
__set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, supported);
if (val & MDIO_PMA_STAT2_10GBSR)
__set_bit(ETHTOOL_LINK_MODE_10000baseSR_Full_BIT, supported);
if (val & MDIO_PMA_STAT2_10GBLR)
__set_bit(ETHTOOL_LINK_MODE_10000baseLR_Full_BIT, supported);
if (val & MDIO_PMA_STAT2_10GBER)
__set_bit(ETHTOOL_LINK_MODE_10000baseER_Full_BIT, supported);
if (val & MDIO_PMA_STAT2_EXTABLE) {
val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_EXTABLE);
if (val < 0)
return val;
if (val & (MDIO_PMA_EXTABLE_10GBT | MDIO_PMA_EXTABLE_1000BT |
MDIO_PMA_EXTABLE_100BTX | MDIO_PMA_EXTABLE_10BT))
__set_bit(ETHTOOL_LINK_MODE_TP_BIT, supported);
if (val & MDIO_PMA_EXTABLE_10GBLRM)
__set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, supported);
if (val & (MDIO_PMA_EXTABLE_10GBKX4 | MDIO_PMA_EXTABLE_10GBKR |
MDIO_PMA_EXTABLE_1000BKX))
__set_bit(ETHTOOL_LINK_MODE_Backplane_BIT, supported);
if (val & MDIO_PMA_EXTABLE_10GBLRM)
__set_bit(ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT,
supported);
if (val & MDIO_PMA_EXTABLE_10GBT)
__set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
supported);
if (val & MDIO_PMA_EXTABLE_10GBKX4)
__set_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
supported);
if (val & MDIO_PMA_EXTABLE_10GBKR)
__set_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
supported);
if (val & MDIO_PMA_EXTABLE_1000BT)
__set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
supported);
if (val & MDIO_PMA_EXTABLE_1000BKX)
__set_bit(ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
supported);
if (val & MDIO_PMA_EXTABLE_100BTX)
__set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
supported);
if (val & MDIO_PMA_EXTABLE_10BT)
__set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
supported);
}
if (!ethtool_convert_link_mode_to_legacy_u32(&mask, supported))
dev_warn(&phydev->mdio.dev,
"PHY supports (%*pb) more modes than phylib supports, some modes not supported.\n",
__ETHTOOL_LINK_MODE_MASK_NBITS, supported);
phydev->supported &= mask;
phydev->advertising &= phydev->supported;
return 0;
}
static int mv3310_config_aneg(struct phy_device *phydev)
{
bool changed = false;
u32 advertising;
int ret;
if (phydev->autoneg == AUTONEG_DISABLE) {
ret = genphy_c45_pma_setup_forced(phydev);
if (ret < 0)
return ret;
return genphy_c45_an_disable_aneg(phydev);
}
phydev->advertising &= phydev->supported;
advertising = phydev->advertising;
ret = mv3310_modify(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE,
ADVERTISE_ALL | ADVERTISE_100BASE4 |
ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM,
ethtool_adv_to_mii_adv_t(advertising));
if (ret < 0)
return ret;
if (ret > 0)
changed = true;
ret = mv3310_modify(phydev, MDIO_MMD_AN, MV_AN_CTRL1000,
ADVERTISE_1000FULL | ADVERTISE_1000HALF,
ethtool_adv_to_mii_ctrl1000_t(advertising));
if (ret < 0)
return ret;
if (ret > 0)
changed = true;
/* 10G control register */
ret = mv3310_modify(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
MDIO_AN_10GBT_CTRL_ADV10G,
advertising & ADVERTISED_10000baseT_Full ?
MDIO_AN_10GBT_CTRL_ADV10G : 0);
if (ret < 0)
return ret;
if (ret > 0)
changed = true;
if (changed)
ret = genphy_c45_restart_aneg(phydev);
return ret;
}
static int mv3310_aneg_done(struct phy_device *phydev)
{
int val;
val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_BASE_R + MDIO_STAT1);
if (val < 0)
return val;
if (val & MDIO_STAT1_LSTATUS)
return 1;
return genphy_c45_aneg_done(phydev);
}
/* 10GBASE-ER,LR,LRM,SR do not support autonegotiation. */
static int mv3310_read_10gbr_status(struct phy_device *phydev)
{
phydev->link = 1;
phydev->speed = SPEED_10000;
phydev->duplex = DUPLEX_FULL;
if (phydev->interface == PHY_INTERFACE_MODE_SGMII)
phydev->interface = PHY_INTERFACE_MODE_10GKR;
return 0;
}
static int mv3310_read_status(struct phy_device *phydev)
{
u32 mmd_mask = phydev->c45_ids.devices_in_package;
int val;
/* The vendor devads do not report link status. Avoid the PHYXS
* instance as there are three, and its status depends on the MAC
* being appropriately configured for the negotiated speed.
*/
mmd_mask &= ~(BIT(MDIO_MMD_VEND1) | BIT(MDIO_MMD_VEND2) |
BIT(MDIO_MMD_PHYXS));
phydev->speed = SPEED_UNKNOWN;
phydev->duplex = DUPLEX_UNKNOWN;
phydev->lp_advertising = 0;
phydev->link = 0;
phydev->pause = 0;
phydev->asym_pause = 0;
val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_BASE_R + MDIO_STAT1);
if (val < 0)
return val;
if (val & MDIO_STAT1_LSTATUS)
return mv3310_read_10gbr_status(phydev);
val = genphy_c45_read_link(phydev, mmd_mask);
if (val < 0)
return val;
phydev->link = val > 0 ? 1 : 0;
val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
if (val < 0)
return val;
if (val & MDIO_AN_STAT1_COMPLETE) {
val = genphy_c45_read_lpa(phydev);
if (val < 0)
return val;
/* Read the link partner's 1G advertisment */
val = phy_read_mmd(phydev, MDIO_MMD_AN, MV_AN_STAT1000);
if (val < 0)
return val;
phydev->lp_advertising |= mii_stat1000_to_ethtool_lpa_t(val);
if (phydev->autoneg == AUTONEG_ENABLE) {
val = phy_read_mmd(phydev, MDIO_MMD_AN, MV_AN_RESULT);
if (val < 0)
return val;
if (val & MV_AN_RESULT_SPD_10000)
phydev->speed = SPEED_10000;
else if (val & MV_AN_RESULT_SPD_1000)
phydev->speed = SPEED_1000;
else if (val & MV_AN_RESULT_SPD_100)
phydev->speed = SPEED_100;
else if (val & MV_AN_RESULT_SPD_10)
phydev->speed = SPEED_10;
phydev->duplex = DUPLEX_FULL;
}
}
if (phydev->autoneg != AUTONEG_ENABLE) {
val = genphy_c45_read_pma(phydev);
if (val < 0)
return val;
}
if ((phydev->interface == PHY_INTERFACE_MODE_SGMII ||
phydev->interface == PHY_INTERFACE_MODE_10GKR) && phydev->link) {
/* The PHY automatically switches its serdes interface (and
* active PHYXS instance) between Cisco SGMII and 10GBase-KR
* modes according to the speed. Florian suggests setting
* phydev->interface to communicate this to the MAC. Only do
* this if we are already in either SGMII or 10GBase-KR mode.
*/
if (phydev->speed == SPEED_10000)
phydev->interface = PHY_INTERFACE_MODE_10GKR;
else if (phydev->speed >= SPEED_10 &&
phydev->speed < SPEED_10000)
phydev->interface = PHY_INTERFACE_MODE_SGMII;
}
return 0;
}
static struct phy_driver mv3310_drivers[] = {
{
.phy_id = 0x002b09aa,
.phy_id_mask = 0xffffffff,
.name = "mv88x3310",
.features = SUPPORTED_10baseT_Full |
SUPPORTED_100baseT_Full |
SUPPORTED_1000baseT_Full |
SUPPORTED_Autoneg |
SUPPORTED_TP |
SUPPORTED_FIBRE |
SUPPORTED_10000baseT_Full |
SUPPORTED_Backplane,
.probe = mv3310_probe,
.soft_reset = mv3310_soft_reset,
.config_init = mv3310_config_init,
.config_aneg = mv3310_config_aneg,
.aneg_done = mv3310_aneg_done,
.read_status = mv3310_read_status,
},
};
module_phy_driver(mv3310_drivers);
static struct mdio_device_id __maybe_unused mv3310_tbl[] = {
{ 0x002b09aa, 0xffffffff },
{ },
};
MODULE_DEVICE_TABLE(mdio, mv3310_tbl);
MODULE_DESCRIPTION("Marvell Alaska X 10Gigabit Ethernet PHY driver (MV88X3310)");
MODULE_LICENSE("GPL");
/*
* Clause 45 PHY support
*/
#include <linux/ethtool.h>
#include <linux/export.h>
#include <linux/mdio.h>
#include <linux/mii.h>
#include <linux/phy.h>
/**
* genphy_c45_setup_forced - configures a forced speed
* @phydev: target phy_device struct
*/
int genphy_c45_pma_setup_forced(struct phy_device *phydev)
{
int ctrl1, ctrl2, ret;
/* Half duplex is not supported */
if (phydev->duplex != DUPLEX_FULL)
return -EINVAL;
ctrl1 = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1);
if (ctrl1 < 0)
return ctrl1;
ctrl2 = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL2);
if (ctrl2 < 0)
return ctrl2;
ctrl1 &= ~MDIO_CTRL1_SPEEDSEL;
/*
* PMA/PMD type selection is 1.7.5:0 not 1.7.3:0. See 45.2.1.6.1
* in 802.3-2012 and 802.3-2015.
*/
ctrl2 &= ~(MDIO_PMA_CTRL2_TYPE | 0x30);
switch (phydev->speed) {
case SPEED_10:
ctrl2 |= MDIO_PMA_CTRL2_10BT;
break;
case SPEED_100:
ctrl1 |= MDIO_PMA_CTRL1_SPEED100;
ctrl2 |= MDIO_PMA_CTRL2_100BTX;
break;
case SPEED_1000:
ctrl1 |= MDIO_PMA_CTRL1_SPEED1000;
/* Assume 1000base-T */
ctrl2 |= MDIO_PMA_CTRL2_1000BT;
break;
case SPEED_10000:
ctrl1 |= MDIO_CTRL1_SPEED10G;
/* Assume 10Gbase-T */
ctrl2 |= MDIO_PMA_CTRL2_10GBT;
break;
default:
return -EINVAL;
}
ret = phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1, ctrl1);
if (ret < 0)
return ret;
return phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL2, ctrl2);
}
EXPORT_SYMBOL_GPL(genphy_c45_pma_setup_forced);
/**
* genphy_c45_an_disable_aneg - disable auto-negotiation
* @phydev: target phy_device struct
*
* Disable auto-negotiation in the Clause 45 PHY. The link parameters
* parameters are controlled through the PMA/PMD MMD registers.
*
* Returns zero on success, negative errno code on failure.
*/
int genphy_c45_an_disable_aneg(struct phy_device *phydev)
{
int val;
val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1);
if (val < 0)
return val;
val &= ~(MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART);
return phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, val);
}
EXPORT_SYMBOL_GPL(genphy_c45_an_disable_aneg);
/**
* genphy_c45_restart_aneg - Enable and restart auto-negotiation
* @phydev: target phy_device struct
*
* This assumes that the auto-negotiation MMD is present.
*
* Enable and restart auto-negotiation.
*/
int genphy_c45_restart_aneg(struct phy_device *phydev)
{
int val;
val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1);
if (val < 0)
return val;
val |= MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART;
return phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, val);
}
EXPORT_SYMBOL_GPL(genphy_c45_restart_aneg);
/**
* genphy_c45_aneg_done - return auto-negotiation complete status
* @phydev: target phy_device struct
*
* This assumes that the auto-negotiation MMD is present.
*
* Reads the status register from the auto-negotiation MMD, returning:
* - positive if auto-negotiation is complete
* - negative errno code on error
* - zero otherwise
*/
int genphy_c45_aneg_done(struct phy_device *phydev)
{
int val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
return val < 0 ? val : val & MDIO_AN_STAT1_COMPLETE ? 1 : 0;
}
EXPORT_SYMBOL_GPL(genphy_c45_aneg_done);
/**
* genphy_c45_read_link - read the overall link status from the MMDs
* @phydev: target phy_device struct
* @mmd_mask: MMDs to read status from
*
* Read the link status from the specified MMDs, and if they all indicate
* that the link is up, return positive. If an error is encountered,
* a negative errno will be returned, otherwise zero.
*/
int genphy_c45_read_link(struct phy_device *phydev, u32 mmd_mask)
{
int val, devad;
bool link = true;
while (mmd_mask) {
devad = __ffs(mmd_mask);
mmd_mask &= ~BIT(devad);
/* The link state is latched low so that momentary link
* drops can be detected. Do not double-read the status
* register if the link is down.
*/
val = phy_read_mmd(phydev, devad, MDIO_STAT1);
if (val < 0)
return val;
if (!(val & MDIO_STAT1_LSTATUS))
link = false;
}
return link;
}
EXPORT_SYMBOL_GPL(genphy_c45_read_link);
/**
* genphy_c45_read_lpa - read the link partner advertisment and pause
* @phydev: target phy_device struct
*
* Read the Clause 45 defined base (7.19) and 10G (7.33) status registers,
* filling in the link partner advertisment, pause and asym_pause members
* in @phydev. This assumes that the auto-negotiation MMD is present, and
* the backplane bit (7.48.0) is clear. Clause 45 PHY drivers are expected
* to fill in the remainder of the link partner advert from vendor registers.
*/
int genphy_c45_read_lpa(struct phy_device *phydev)
{
int val;
/* Read the link partner's base page advertisment */
val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPA);
if (val < 0)
return val;
phydev->lp_advertising = mii_lpa_to_ethtool_lpa_t(val);
phydev->pause = val & LPA_PAUSE_CAP ? 1 : 0;
phydev->asym_pause = val & LPA_PAUSE_ASYM ? 1 : 0;
/* Read the link partner's 10G advertisment */
val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT);
if (val < 0)
return val;
if (val & MDIO_AN_10GBT_STAT_LP10G)
phydev->lp_advertising |= ADVERTISED_10000baseT_Full;
return 0;
}
EXPORT_SYMBOL_GPL(genphy_c45_read_lpa);
/**
* genphy_c45_read_pma - read link speed etc from PMA
* @phydev: target phy_device struct
*/
int genphy_c45_read_pma(struct phy_device *phydev)
{
int val;
val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1);
if (val < 0)
return val;
switch (val & MDIO_CTRL1_SPEEDSEL) {
case 0:
phydev->speed = SPEED_10;
break;
case MDIO_PMA_CTRL1_SPEED100:
phydev->speed = SPEED_100;
break;
case MDIO_PMA_CTRL1_SPEED1000:
phydev->speed = SPEED_1000;
break;
case MDIO_CTRL1_SPEED10G:
phydev->speed = SPEED_10000;
break;
default:
phydev->speed = SPEED_UNKNOWN;
break;
}
phydev->duplex = DUPLEX_FULL;
return 0;
}
EXPORT_SYMBOL_GPL(genphy_c45_read_pma);
/* The gen10g_* functions are the old Clause 45 stub */
static int gen10g_config_aneg(struct phy_device *phydev)
{
return 0;
}
static int gen10g_read_status(struct phy_device *phydev)
{
u32 mmd_mask = phydev->c45_ids.devices_in_package;
int ret;
/* For now just lie and say it's 10G all the time */
phydev->speed = SPEED_10000;
phydev->duplex = DUPLEX_FULL;
/* Avoid reading the vendor MMDs */
mmd_mask &= ~(BIT(MDIO_MMD_VEND1) | BIT(MDIO_MMD_VEND2));
ret = genphy_c45_read_link(phydev, mmd_mask);
phydev->link = ret > 0 ? 1 : 0;
return 0;
}
static int gen10g_soft_reset(struct phy_device *phydev)
{
/* Do nothing for now */
return 0;
}
static int gen10g_config_init(struct phy_device *phydev)
{
/* Temporarily just say we support everything */
phydev->supported = SUPPORTED_10000baseT_Full;
phydev->advertising = SUPPORTED_10000baseT_Full;
return 0;
}
static int gen10g_suspend(struct phy_device *phydev)
{
return 0;
}
static int gen10g_resume(struct phy_device *phydev)
{
return 0;
}
struct phy_driver genphy_10g_driver = {
.phy_id = 0xffffffff,
.phy_id_mask = 0xffffffff,
.name = "Generic 10G PHY",
.soft_reset = gen10g_soft_reset,
.config_init = gen10g_config_init,
.features = 0,
.config_aneg = gen10g_config_aneg,
.read_status = gen10g_read_status,
.suspend = gen10g_suspend,
.resume = gen10g_resume,
};
...@@ -149,6 +149,25 @@ static int phy_config_interrupt(struct phy_device *phydev, u32 interrupts) ...@@ -149,6 +149,25 @@ static int phy_config_interrupt(struct phy_device *phydev, u32 interrupts)
return 0; return 0;
} }
/**
* phy_restart_aneg - restart auto-negotiation
* @phydev: target phy_device struct
*
* Restart the autonegotiation on @phydev. Returns >= 0 on success or
* negative errno on error.
*/
int phy_restart_aneg(struct phy_device *phydev)
{
int ret;
if (phydev->is_c45 && !(phydev->c45_ids.devices_in_package & BIT(0)))
ret = genphy_c45_restart_aneg(phydev);
else
ret = genphy_restart_aneg(phydev);
return ret;
}
EXPORT_SYMBOL_GPL(phy_restart_aneg);
/** /**
* phy_aneg_done - return auto-negotiation status * phy_aneg_done - return auto-negotiation status
...@@ -163,6 +182,12 @@ int phy_aneg_done(struct phy_device *phydev) ...@@ -163,6 +182,12 @@ int phy_aneg_done(struct phy_device *phydev)
if (phydev->drv && phydev->drv->aneg_done) if (phydev->drv && phydev->drv->aneg_done)
return phydev->drv->aneg_done(phydev); return phydev->drv->aneg_done(phydev);
/* Avoid genphy_aneg_done() if the Clause 45 PHY does not
* implement Clause 22 registers
*/
if (phydev->is_c45 && !(phydev->c45_ids.devices_in_package & BIT(0)))
return -EINVAL;
return genphy_aneg_done(phydev); return genphy_aneg_done(phydev);
} }
EXPORT_SYMBOL(phy_aneg_done); EXPORT_SYMBOL(phy_aneg_done);
...@@ -1391,7 +1416,7 @@ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data) ...@@ -1391,7 +1416,7 @@ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data)
/* Restart autonegotiation so the new modes get sent to the /* Restart autonegotiation so the new modes get sent to the
* link partner. * link partner.
*/ */
ret = genphy_restart_aneg(phydev); ret = phy_restart_aneg(phydev);
if (ret < 0) if (ret < 0)
return ret; return ret;
} }
...@@ -1450,6 +1475,6 @@ int phy_ethtool_nway_reset(struct net_device *ndev) ...@@ -1450,6 +1475,6 @@ int phy_ethtool_nway_reset(struct net_device *ndev)
if (!phydev->drv) if (!phydev->drv)
return -EIO; return -EIO;
return genphy_restart_aneg(phydev); return phy_restart_aneg(phydev);
} }
EXPORT_SYMBOL(phy_ethtool_nway_reset); EXPORT_SYMBOL(phy_ethtool_nway_reset);
...@@ -69,13 +69,8 @@ static void phy_mdio_device_remove(struct mdio_device *mdiodev) ...@@ -69,13 +69,8 @@ static void phy_mdio_device_remove(struct mdio_device *mdiodev)
phy_device_remove(phydev); phy_device_remove(phydev);
} }
enum genphy_driver { static struct phy_driver genphy_driver;
GENPHY_DRV_1G, extern struct phy_driver genphy_10g_driver;
GENPHY_DRV_10G,
GENPHY_DRV_MAX
};
static struct phy_driver genphy_driver[GENPHY_DRV_MAX];
static LIST_HEAD(phy_fixup_list); static LIST_HEAD(phy_fixup_list);
static DEFINE_MUTEX(phy_fixup_lock); static DEFINE_MUTEX(phy_fixup_lock);
...@@ -928,11 +923,9 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, ...@@ -928,11 +923,9 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
*/ */
if (!d->driver) { if (!d->driver) {
if (phydev->is_c45) if (phydev->is_c45)
d->driver = d->driver = &genphy_10g_driver.mdiodrv.driver;
&genphy_driver[GENPHY_DRV_10G].mdiodrv.driver;
else else
d->driver = d->driver = &genphy_driver.mdiodrv.driver;
&genphy_driver[GENPHY_DRV_1G].mdiodrv.driver;
using_genphy = true; using_genphy = true;
} }
...@@ -1069,7 +1062,6 @@ void phy_detach(struct phy_device *phydev) ...@@ -1069,7 +1062,6 @@ void phy_detach(struct phy_device *phydev)
struct net_device *dev = phydev->attached_dev; struct net_device *dev = phydev->attached_dev;
struct module *ndev_owner = dev->dev.parent->driver->owner; struct module *ndev_owner = dev->dev.parent->driver->owner;
struct mii_bus *bus; struct mii_bus *bus;
int i;
if (phydev->sysfs_links) { if (phydev->sysfs_links) {
sysfs_remove_link(&dev->dev.kobj, "phydev"); sysfs_remove_link(&dev->dev.kobj, "phydev");
...@@ -1088,13 +1080,9 @@ void phy_detach(struct phy_device *phydev) ...@@ -1088,13 +1080,9 @@ void phy_detach(struct phy_device *phydev)
* from the generic driver so that there's a chance a * from the generic driver so that there's a chance a
* real driver could be loaded * real driver could be loaded
*/ */
for (i = 0; i < ARRAY_SIZE(genphy_driver); i++) { if (phydev->mdio.dev.driver == &genphy_10g_driver.mdiodrv.driver ||
if (phydev->mdio.dev.driver == phydev->mdio.dev.driver == &genphy_driver.mdiodrv.driver)
&genphy_driver[i].mdiodrv.driver) { device_release_driver(&phydev->mdio.dev);
device_release_driver(&phydev->mdio.dev);
break;
}
}
/* /*
* The phydev might go away on the put_device() below, so avoid * The phydev might go away on the put_device() below, so avoid
...@@ -1368,11 +1356,6 @@ int genphy_aneg_done(struct phy_device *phydev) ...@@ -1368,11 +1356,6 @@ int genphy_aneg_done(struct phy_device *phydev)
} }
EXPORT_SYMBOL(genphy_aneg_done); EXPORT_SYMBOL(genphy_aneg_done);
static int gen10g_config_aneg(struct phy_device *phydev)
{
return 0;
}
/** /**
* genphy_update_link - update link status in @phydev * genphy_update_link - update link status in @phydev
* @phydev: target phy_device struct * @phydev: target phy_device struct
...@@ -1506,33 +1489,6 @@ int genphy_read_status(struct phy_device *phydev) ...@@ -1506,33 +1489,6 @@ int genphy_read_status(struct phy_device *phydev)
} }
EXPORT_SYMBOL(genphy_read_status); EXPORT_SYMBOL(genphy_read_status);
static int gen10g_read_status(struct phy_device *phydev)
{
int devad, reg;
u32 mmd_mask = phydev->c45_ids.devices_in_package;
phydev->link = 1;
/* For now just lie and say it's 10G all the time */
phydev->speed = SPEED_10000;
phydev->duplex = DUPLEX_FULL;
for (devad = 0; mmd_mask; devad++, mmd_mask = mmd_mask >> 1) {
if (!(mmd_mask & 1))
continue;
/* Read twice because link state is latched and a
* read moves the current state into the register
*/
phy_read_mmd(phydev, devad, MDIO_STAT1);
reg = phy_read_mmd(phydev, devad, MDIO_STAT1);
if (reg < 0 || !(reg & MDIO_STAT1_LSTATUS))
phydev->link = 0;
}
return 0;
}
/** /**
* genphy_soft_reset - software reset the PHY via BMCR_RESET bit * genphy_soft_reset - software reset the PHY via BMCR_RESET bit
* @phydev: target phy_device struct * @phydev: target phy_device struct
...@@ -1598,21 +1554,6 @@ int genphy_config_init(struct phy_device *phydev) ...@@ -1598,21 +1554,6 @@ int genphy_config_init(struct phy_device *phydev)
} }
EXPORT_SYMBOL(genphy_config_init); EXPORT_SYMBOL(genphy_config_init);
static int gen10g_soft_reset(struct phy_device *phydev)
{
/* Do nothing for now */
return 0;
}
static int gen10g_config_init(struct phy_device *phydev)
{
/* Temporarily just say we support everything */
phydev->supported = SUPPORTED_10000baseT_Full;
phydev->advertising = SUPPORTED_10000baseT_Full;
return 0;
}
int genphy_suspend(struct phy_device *phydev) int genphy_suspend(struct phy_device *phydev)
{ {
int value; int value;
...@@ -1628,11 +1569,6 @@ int genphy_suspend(struct phy_device *phydev) ...@@ -1628,11 +1569,6 @@ int genphy_suspend(struct phy_device *phydev)
} }
EXPORT_SYMBOL(genphy_suspend); EXPORT_SYMBOL(genphy_suspend);
static int gen10g_suspend(struct phy_device *phydev)
{
return 0;
}
int genphy_resume(struct phy_device *phydev) int genphy_resume(struct phy_device *phydev)
{ {
int value; int value;
...@@ -1648,11 +1584,6 @@ int genphy_resume(struct phy_device *phydev) ...@@ -1648,11 +1584,6 @@ int genphy_resume(struct phy_device *phydev)
} }
EXPORT_SYMBOL(genphy_resume); EXPORT_SYMBOL(genphy_resume);
static int gen10g_resume(struct phy_device *phydev)
{
return 0;
}
static int __set_phy_supported(struct phy_device *phydev, u32 max_speed) static int __set_phy_supported(struct phy_device *phydev, u32 max_speed)
{ {
/* The default values for phydev->supported are provided by the PHY /* The default values for phydev->supported are provided by the PHY
...@@ -1884,8 +1815,7 @@ void phy_drivers_unregister(struct phy_driver *drv, int n) ...@@ -1884,8 +1815,7 @@ void phy_drivers_unregister(struct phy_driver *drv, int n)
} }
EXPORT_SYMBOL(phy_drivers_unregister); EXPORT_SYMBOL(phy_drivers_unregister);
static struct phy_driver genphy_driver[] = { static struct phy_driver genphy_driver = {
{
.phy_id = 0xffffffff, .phy_id = 0xffffffff,
.phy_id_mask = 0xffffffff, .phy_id_mask = 0xffffffff,
.name = "Generic PHY", .name = "Generic PHY",
...@@ -1899,18 +1829,7 @@ static struct phy_driver genphy_driver[] = { ...@@ -1899,18 +1829,7 @@ static struct phy_driver genphy_driver[] = {
.read_status = genphy_read_status, .read_status = genphy_read_status,
.suspend = genphy_suspend, .suspend = genphy_suspend,
.resume = genphy_resume, .resume = genphy_resume,
}, { };
.phy_id = 0xffffffff,
.phy_id_mask = 0xffffffff,
.name = "Generic 10G PHY",
.soft_reset = gen10g_soft_reset,
.config_init = gen10g_config_init,
.features = 0,
.config_aneg = gen10g_config_aneg,
.read_status = gen10g_read_status,
.suspend = gen10g_suspend,
.resume = gen10g_resume,
} };
static int __init phy_init(void) static int __init phy_init(void)
{ {
...@@ -1920,18 +1839,24 @@ static int __init phy_init(void) ...@@ -1920,18 +1839,24 @@ static int __init phy_init(void)
if (rc) if (rc)
return rc; return rc;
rc = phy_drivers_register(genphy_driver, rc = phy_driver_register(&genphy_10g_driver, THIS_MODULE);
ARRAY_SIZE(genphy_driver), THIS_MODULE);
if (rc) if (rc)
goto err_10g;
rc = phy_driver_register(&genphy_driver, THIS_MODULE);
if (rc) {
phy_driver_unregister(&genphy_10g_driver);
err_10g:
mdio_bus_exit(); mdio_bus_exit();
}
return rc; return rc;
} }
static void __exit phy_exit(void) static void __exit phy_exit(void)
{ {
phy_drivers_unregister(genphy_driver, phy_driver_unregister(&genphy_10g_driver);
ARRAY_SIZE(genphy_driver)); phy_driver_unregister(&genphy_driver);
mdio_bus_exit(); mdio_bus_exit();
} }
......
...@@ -83,6 +83,9 @@ typedef enum { ...@@ -83,6 +83,9 @@ typedef enum {
PHY_INTERFACE_MODE_1000BASEX, PHY_INTERFACE_MODE_1000BASEX,
PHY_INTERFACE_MODE_2500BASEX, PHY_INTERFACE_MODE_2500BASEX,
PHY_INTERFACE_MODE_RXAUI, PHY_INTERFACE_MODE_RXAUI,
PHY_INTERFACE_MODE_XAUI,
/* 10GBASE-KR, XFI, SFI - single lane 10G Serdes */
PHY_INTERFACE_MODE_10GKR,
PHY_INTERFACE_MODE_MAX, PHY_INTERFACE_MODE_MAX,
} phy_interface_t; } phy_interface_t;
...@@ -149,6 +152,10 @@ static inline const char *phy_modes(phy_interface_t interface) ...@@ -149,6 +152,10 @@ static inline const char *phy_modes(phy_interface_t interface)
return "2500base-x"; return "2500base-x";
case PHY_INTERFACE_MODE_RXAUI: case PHY_INTERFACE_MODE_RXAUI:
return "rxaui"; return "rxaui";
case PHY_INTERFACE_MODE_XAUI:
return "xaui";
case PHY_INTERFACE_MODE_10GKR:
return "10gbase-kr";
default: default:
return "unknown"; return "unknown";
} }
...@@ -804,6 +811,7 @@ int phy_start_aneg(struct phy_device *phydev); ...@@ -804,6 +811,7 @@ int phy_start_aneg(struct phy_device *phydev);
int phy_aneg_done(struct phy_device *phydev); int phy_aneg_done(struct phy_device *phydev);
int phy_stop_interrupts(struct phy_device *phydev); int phy_stop_interrupts(struct phy_device *phydev);
int phy_restart_aneg(struct phy_device *phydev);
static inline int phy_read_status(struct phy_device *phydev) static inline int phy_read_status(struct phy_device *phydev)
{ {
...@@ -827,6 +835,8 @@ static inline const char *phydev_name(const struct phy_device *phydev) ...@@ -827,6 +835,8 @@ static inline const char *phydev_name(const struct phy_device *phydev)
void phy_attached_print(struct phy_device *phydev, const char *fmt, ...) void phy_attached_print(struct phy_device *phydev, const char *fmt, ...)
__printf(2, 3); __printf(2, 3);
void phy_attached_info(struct phy_device *phydev); void phy_attached_info(struct phy_device *phydev);
/* Clause 22 PHY */
int genphy_config_init(struct phy_device *phydev); int genphy_config_init(struct phy_device *phydev);
int genphy_setup_forced(struct phy_device *phydev); int genphy_setup_forced(struct phy_device *phydev);
int genphy_restart_aneg(struct phy_device *phydev); int genphy_restart_aneg(struct phy_device *phydev);
...@@ -841,6 +851,16 @@ static inline int genphy_no_soft_reset(struct phy_device *phydev) ...@@ -841,6 +851,16 @@ static inline int genphy_no_soft_reset(struct phy_device *phydev)
{ {
return 0; return 0;
} }
/* Clause 45 PHY */
int genphy_c45_restart_aneg(struct phy_device *phydev);
int genphy_c45_aneg_done(struct phy_device *phydev);
int genphy_c45_read_link(struct phy_device *phydev, u32 mmd_mask);
int genphy_c45_read_lpa(struct phy_device *phydev);
int genphy_c45_read_pma(struct phy_device *phydev);
int genphy_c45_pma_setup_forced(struct phy_device *phydev);
int genphy_c45_an_disable_aneg(struct phy_device *phydev);
void phy_driver_unregister(struct phy_driver *drv); void phy_driver_unregister(struct phy_driver *drv);
void phy_drivers_unregister(struct phy_driver *drv, int n); void phy_drivers_unregister(struct phy_driver *drv, int n);
int phy_driver_register(struct phy_driver *new_driver, struct module *owner); int phy_driver_register(struct phy_driver *new_driver, struct module *owner);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment