Commit 23470933 authored by David S. Miller's avatar David S. Miller

Merge branch 'phylink-and-sfp-support'

Russell King says:

====================
phylink and sfp support

This patch series introduces generic support for SFP sockets found on
various Marvell based platforms.  The idea here is to provide common
SFP socket support which can be re-used by network drivers as
appropriate, rather than each network driver having to re-implement
SFP socket support.

SFP sockets typically use other system resources, eg, I2C buses to read
identifying information, and GPIOs to monitor socket state and control
the socket.  Meanwhile, some network drivers drive multiple ethernet
ports from one instantiation of the driver.

It is not desirable to block the initialisation of a network driver
(thus denying other ports from being operational) if the resources
for the SFP socket are not yet available.  This means that an element
of independence between the SFP support code and the driver is
required.

More than that, SFP modules effectively bring hotplug PHYs to
networking - SFP copper modules normally contain a standard PHY
accessed over the I2C bus, and it is desirable to read their state
so network drivers can be appropriately configured.

To add to the complexity, SFP modules can be connected in at least
two places:

1. Directly to the serdes output of a MAC with no intervening PHY.
   For example:

     mvneta ----> SFP socket

2. To a PHY, for example:

     mvpp2 ---> PHY ---> copper
                 |
                 `-----> SFP socket

This code supports both setups, although it's not fully implemented
with scenario (2).

Moreover, the link presented by the SFP module can be one of the
10Gbase-R family (for SFP+ sockets), SGMII or 1000base-X (for SFP
sockets) depending on the module, and network drivers need to
reconfigure themselves accordingly for the link to come up.

For example, if the MAC is configured for SGMII and a fibre module
is plugged in, the link won't come up until the MAC is reconfigured
for 1000base-X mode.

The SFP code manages the SFP socket - detecting the module, reading
the identifying information, and managing the control and status
signals.  Importantly, it disables the SFP module transmitter when
the MAC is down, so that the laser is turned off (but that is not
a guarantee.)

phylink provides the mechanisms necessary to manage the link modes,
based on the SFP module type, and supports hot-plugging of the PHY
without needing the MAC driver to be brought up and down on
transitions.  phylink also supports the classical static PHY and
fixed-link modes.

I currently (but not included in this series) have code to convert
mvneta to use phylink, and the out of tree mvpp2x driver.  I have
nothing for the mvpp2 driver at present as that driver is only
recently becoming functional on 10G hardware, and is missing a lot
of features that are necessary to make things work correctly.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 21e27f2d 73970055
...@@ -106,6 +106,16 @@ config MDIO_HISI_FEMAC ...@@ -106,6 +106,16 @@ config MDIO_HISI_FEMAC
This module provides a driver for the MDIO busses found in the This module provides a driver for the MDIO busses found in the
Hisilicon SoC that have an Fast Ethernet MAC. Hisilicon SoC that have an Fast Ethernet MAC.
config MDIO_I2C
tristate
depends on I2C
help
Support I2C based PHYs. This provides a MDIO bus bridged
to I2C to allow PHYs connected in I2C mode to be accessed
using the existing infrastructure.
This is library mode.
config MDIO_MOXART config MDIO_MOXART
tristate "MOXA ART MDIO interface support" tristate "MOXA ART MDIO interface support"
depends on ARCH_MOXART depends on ARCH_MOXART
...@@ -159,6 +169,16 @@ menuconfig PHYLIB ...@@ -159,6 +169,16 @@ menuconfig PHYLIB
devices. This option provides infrastructure for devices. This option provides infrastructure for
managing PHY devices. managing PHY devices.
config PHYLINK
tristate
depends on NETDEVICES
select PHYLIB
select SWPHY
help
PHYlink models the link between the PHY and MAC, allowing fixed
configuration links, PHYs, and Serdes links with MAC level
autonegotiation modes.
if PHYLIB if PHYLIB
config SWPHY config SWPHY
...@@ -180,6 +200,11 @@ config LED_TRIGGER_PHY ...@@ -180,6 +200,11 @@ config LED_TRIGGER_PHY
comment "MII PHY device drivers" comment "MII PHY device drivers"
config SFP
tristate "SFP cage support"
depends on I2C && PHYLINK
select MDIO_I2C
config AMD_PHY config AMD_PHY
tristate "AMD PHYs" tristate "AMD PHYs"
---help--- ---help---
......
...@@ -18,6 +18,7 @@ endif ...@@ -18,6 +18,7 @@ endif
libphy-$(CONFIG_SWPHY) += swphy.o libphy-$(CONFIG_SWPHY) += swphy.o
libphy-$(CONFIG_LED_TRIGGER_PHY) += phy_led_triggers.o libphy-$(CONFIG_LED_TRIGGER_PHY) += phy_led_triggers.o
obj-$(CONFIG_PHYLINK) += phylink.o
obj-$(CONFIG_PHYLIB) += libphy.o obj-$(CONFIG_PHYLIB) += libphy.o
obj-$(CONFIG_MDIO_BCM_IPROC) += mdio-bcm-iproc.o obj-$(CONFIG_MDIO_BCM_IPROC) += mdio-bcm-iproc.o
...@@ -30,12 +31,17 @@ obj-$(CONFIG_MDIO_BUS_MUX_MMIOREG) += mdio-mux-mmioreg.o ...@@ -30,12 +31,17 @@ obj-$(CONFIG_MDIO_BUS_MUX_MMIOREG) += mdio-mux-mmioreg.o
obj-$(CONFIG_MDIO_CAVIUM) += mdio-cavium.o obj-$(CONFIG_MDIO_CAVIUM) += mdio-cavium.o
obj-$(CONFIG_MDIO_GPIO) += mdio-gpio.o obj-$(CONFIG_MDIO_GPIO) += mdio-gpio.o
obj-$(CONFIG_MDIO_HISI_FEMAC) += mdio-hisi-femac.o obj-$(CONFIG_MDIO_HISI_FEMAC) += mdio-hisi-femac.o
obj-$(CONFIG_MDIO_I2C) += mdio-i2c.o
obj-$(CONFIG_MDIO_MOXART) += mdio-moxart.o obj-$(CONFIG_MDIO_MOXART) += mdio-moxart.o
obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.o obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.o
obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o
obj-$(CONFIG_MDIO_THUNDER) += mdio-thunder.o obj-$(CONFIG_MDIO_THUNDER) += mdio-thunder.o
obj-$(CONFIG_MDIO_XGENE) += mdio-xgene.o obj-$(CONFIG_MDIO_XGENE) += mdio-xgene.o
obj-$(CONFIG_SFP) += sfp.o
sfp-obj-$(CONFIG_SFP) += sfp-bus.o
obj-y += $(sfp-obj-y) $(sfp-obj-m)
obj-$(CONFIG_AMD_PHY) += amd.o obj-$(CONFIG_AMD_PHY) += amd.o
obj-$(CONFIG_AQUANTIA_PHY) += aquantia.o obj-$(CONFIG_AQUANTIA_PHY) += aquantia.o
obj-$(CONFIG_AT803X_PHY) += at803x.o obj-$(CONFIG_AT803X_PHY) += at803x.o
......
/*
* MDIO I2C bridge
*
* Copyright (C) 2015-2016 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Network PHYs can appear on I2C buses when they are part of SFP module.
* This driver exposes these PHYs to the networking PHY code, allowing
* our PHY drivers access to these PHYs, and so allowing configuration
* of their settings.
*/
#include <linux/i2c.h>
#include <linux/phy.h>
#include "mdio-i2c.h"
/*
* I2C bus addresses 0x50 and 0x51 are normally an EEPROM, which is
* specified to be present in SFP modules. These correspond with PHY
* addresses 16 and 17. Disallow access to these "phy" addresses.
*/
static bool i2c_mii_valid_phy_id(int phy_id)
{
return phy_id != 0x10 && phy_id != 0x11;
}
static unsigned int i2c_mii_phy_addr(int phy_id)
{
return phy_id + 0x40;
}
static int i2c_mii_read(struct mii_bus *bus, int phy_id, int reg)
{
struct i2c_adapter *i2c = bus->priv;
struct i2c_msg msgs[2];
u8 data[2], dev_addr = reg;
int bus_addr, ret;
if (!i2c_mii_valid_phy_id(phy_id))
return 0xffff;
bus_addr = i2c_mii_phy_addr(phy_id);
msgs[0].addr = bus_addr;
msgs[0].flags = 0;
msgs[0].len = 1;
msgs[0].buf = &dev_addr;
msgs[1].addr = bus_addr;
msgs[1].flags = I2C_M_RD;
msgs[1].len = sizeof(data);
msgs[1].buf = data;
ret = i2c_transfer(i2c, msgs, ARRAY_SIZE(msgs));
if (ret != ARRAY_SIZE(msgs))
return 0xffff;
return data[0] << 8 | data[1];
}
static int i2c_mii_write(struct mii_bus *bus, int phy_id, int reg, u16 val)
{
struct i2c_adapter *i2c = bus->priv;
struct i2c_msg msg;
int ret;
u8 data[3];
if (!i2c_mii_valid_phy_id(phy_id))
return 0;
data[0] = reg;
data[1] = val >> 8;
data[2] = val;
msg.addr = i2c_mii_phy_addr(phy_id);
msg.flags = 0;
msg.len = 3;
msg.buf = data;
ret = i2c_transfer(i2c, &msg, 1);
return ret < 0 ? ret : 0;
}
struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c)
{
struct mii_bus *mii;
if (!i2c_check_functionality(i2c, I2C_FUNC_I2C))
return ERR_PTR(-EINVAL);
mii = mdiobus_alloc();
if (!mii)
return ERR_PTR(-ENOMEM);
snprintf(mii->id, MII_BUS_ID_SIZE, "i2c:%s", dev_name(parent));
mii->parent = parent;
mii->read = i2c_mii_read;
mii->write = i2c_mii_write;
mii->priv = i2c;
return mii;
}
EXPORT_SYMBOL_GPL(mdio_i2c_alloc);
MODULE_AUTHOR("Russell King");
MODULE_DESCRIPTION("MDIO I2C bridge library");
MODULE_LICENSE("GPL v2");
/*
* MDIO I2C bridge
*
* Copyright (C) 2015 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef MDIO_I2C_H
#define MDIO_I2C_H
struct device;
struct i2c_adapter;
struct mii_bus;
struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c);
#endif
...@@ -9,6 +9,186 @@ ...@@ -9,6 +9,186 @@
#include <linux/export.h> #include <linux/export.h>
#include <linux/phy.h> #include <linux/phy.h>
const char *phy_speed_to_str(int speed)
{
switch (speed) {
case SPEED_10:
return "10Mbps";
case SPEED_100:
return "100Mbps";
case SPEED_1000:
return "1Gbps";
case SPEED_2500:
return "2.5Gbps";
case SPEED_5000:
return "5Gbps";
case SPEED_10000:
return "10Gbps";
case SPEED_14000:
return "14Gbps";
case SPEED_20000:
return "20Gbps";
case SPEED_25000:
return "25Gbps";
case SPEED_40000:
return "40Gbps";
case SPEED_50000:
return "50Gbps";
case SPEED_56000:
return "56Gbps";
case SPEED_100000:
return "100Gbps";
case SPEED_UNKNOWN:
return "Unknown";
default:
return "Unsupported (update phy-core.c)";
}
}
EXPORT_SYMBOL_GPL(phy_speed_to_str);
const char *phy_duplex_to_str(unsigned int duplex)
{
if (duplex == DUPLEX_HALF)
return "Half";
if (duplex == DUPLEX_FULL)
return "Full";
if (duplex == DUPLEX_UNKNOWN)
return "Unknown";
return "Unsupported (update phy-core.c)";
}
EXPORT_SYMBOL_GPL(phy_duplex_to_str);
/* A mapping of all SUPPORTED settings to speed/duplex. This table
* must be grouped by speed and sorted in descending match priority
* - iow, descending speed. */
static const struct phy_setting settings[] = {
{
.speed = SPEED_10000,
.duplex = DUPLEX_FULL,
.bit = ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
},
{
.speed = SPEED_10000,
.duplex = DUPLEX_FULL,
.bit = ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
},
{
.speed = SPEED_10000,
.duplex = DUPLEX_FULL,
.bit = ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
},
{
.speed = SPEED_2500,
.duplex = DUPLEX_FULL,
.bit = ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
},
{
.speed = SPEED_1000,
.duplex = DUPLEX_FULL,
.bit = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
},
{
.speed = SPEED_1000,
.duplex = DUPLEX_FULL,
.bit = ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
},
{
.speed = SPEED_1000,
.duplex = DUPLEX_FULL,
.bit = ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
},
{
.speed = SPEED_1000,
.duplex = DUPLEX_HALF,
.bit = ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
},
{
.speed = SPEED_100,
.duplex = DUPLEX_FULL,
.bit = ETHTOOL_LINK_MODE_100baseT_Full_BIT,
},
{
.speed = SPEED_100,
.duplex = DUPLEX_HALF,
.bit = ETHTOOL_LINK_MODE_100baseT_Half_BIT,
},
{
.speed = SPEED_10,
.duplex = DUPLEX_FULL,
.bit = ETHTOOL_LINK_MODE_10baseT_Full_BIT,
},
{
.speed = SPEED_10,
.duplex = DUPLEX_HALF,
.bit = ETHTOOL_LINK_MODE_10baseT_Half_BIT,
},
};
/**
* phy_lookup_setting - lookup a PHY setting
* @speed: speed to match
* @duplex: duplex to match
* @mask: allowed link modes
* @maxbit: bit size of link modes
* @exact: an exact match is required
*
* Search the settings array for a setting that matches the speed and
* duplex, and which is supported.
*
* If @exact is unset, either an exact match or %NULL for no match will
* be returned.
*
* If @exact is set, an exact match, the fastest supported setting at
* or below the specified speed, the slowest supported setting, or if
* they all fail, %NULL will be returned.
*/
const struct phy_setting *
phy_lookup_setting(int speed, int duplex, const unsigned long *mask,
size_t maxbit, bool exact)
{
const struct phy_setting *p, *match = NULL, *last = NULL;
int i;
for (i = 0, p = settings; i < ARRAY_SIZE(settings); i++, p++) {
if (p->bit < maxbit && test_bit(p->bit, mask)) {
last = p;
if (p->speed == speed && p->duplex == duplex) {
/* Exact match for speed and duplex */
match = p;
break;
} else if (!exact) {
if (!match && p->speed <= speed)
/* Candidate */
match = p;
if (p->speed < speed)
break;
}
}
}
if (!match && !exact)
match = last;
return match;
}
EXPORT_SYMBOL_GPL(phy_lookup_setting);
size_t phy_speeds(unsigned int *speeds, size_t size,
unsigned long *mask, size_t maxbit)
{
size_t count;
int i;
for (i = 0, count = 0; i < ARRAY_SIZE(settings) && count < size; i++)
if (settings[i].bit < maxbit &&
test_bit(settings[i].bit, mask) &&
(count == 0 || speeds[count - 1] != settings[i].speed))
speeds[count++] = settings[i].speed;
return count;
}
static void mmd_phy_indirect(struct mii_bus *bus, int phy_addr, int devad, static void mmd_phy_indirect(struct mii_bus *bus, int phy_addr, int devad,
u16 regnum) u16 regnum)
{ {
......
...@@ -38,42 +38,6 @@ ...@@ -38,42 +38,6 @@
#include <asm/irq.h> #include <asm/irq.h>
static const char *phy_speed_to_str(int speed)
{
switch (speed) {
case SPEED_10:
return "10Mbps";
case SPEED_100:
return "100Mbps";
case SPEED_1000:
return "1Gbps";
case SPEED_2500:
return "2.5Gbps";
case SPEED_5000:
return "5Gbps";
case SPEED_10000:
return "10Gbps";
case SPEED_14000:
return "14Gbps";
case SPEED_20000:
return "20Gbps";
case SPEED_25000:
return "25Gbps";
case SPEED_40000:
return "40Gbps";
case SPEED_50000:
return "50Gbps";
case SPEED_56000:
return "56Gbps";
case SPEED_100000:
return "100Gbps";
case SPEED_UNKNOWN:
return "Unknown";
default:
return "Unsupported (update phy.c)";
}
}
#define PHY_STATE_STR(_state) \ #define PHY_STATE_STR(_state) \
case PHY_##_state: \ case PHY_##_state: \
return __stringify(_state); \ return __stringify(_state); \
...@@ -109,7 +73,7 @@ void phy_print_status(struct phy_device *phydev) ...@@ -109,7 +73,7 @@ void phy_print_status(struct phy_device *phydev)
netdev_info(phydev->attached_dev, netdev_info(phydev->attached_dev,
"Link is Up - %s/%s - flow control %s\n", "Link is Up - %s/%s - flow control %s\n",
phy_speed_to_str(phydev->speed), phy_speed_to_str(phydev->speed),
DUPLEX_FULL == phydev->duplex ? "Full" : "Half", phy_duplex_to_str(phydev->duplex),
phydev->pause ? "rx/tx" : "off"); phydev->pause ? "rx/tx" : "off");
} else { } else {
netdev_info(phydev->attached_dev, "Link is Down\n"); netdev_info(phydev->attached_dev, "Link is Down\n");
...@@ -193,123 +157,6 @@ int phy_aneg_done(struct phy_device *phydev) ...@@ -193,123 +157,6 @@ int phy_aneg_done(struct phy_device *phydev)
} }
EXPORT_SYMBOL(phy_aneg_done); EXPORT_SYMBOL(phy_aneg_done);
/* A structure for mapping a particular speed and duplex
* combination to a particular SUPPORTED and ADVERTISED value
*/
struct phy_setting {
int speed;
int duplex;
u32 setting;
};
/* A mapping of all SUPPORTED settings to speed/duplex. This table
* must be grouped by speed and sorted in descending match priority
* - iow, descending speed. */
static const struct phy_setting settings[] = {
{
.speed = SPEED_10000,
.duplex = DUPLEX_FULL,
.setting = SUPPORTED_10000baseKR_Full,
},
{
.speed = SPEED_10000,
.duplex = DUPLEX_FULL,
.setting = SUPPORTED_10000baseKX4_Full,
},
{
.speed = SPEED_10000,
.duplex = DUPLEX_FULL,
.setting = SUPPORTED_10000baseT_Full,
},
{
.speed = SPEED_2500,
.duplex = DUPLEX_FULL,
.setting = SUPPORTED_2500baseX_Full,
},
{
.speed = SPEED_1000,
.duplex = DUPLEX_FULL,
.setting = SUPPORTED_1000baseKX_Full,
},
{
.speed = SPEED_1000,
.duplex = DUPLEX_FULL,
.setting = SUPPORTED_1000baseT_Full,
},
{
.speed = SPEED_1000,
.duplex = DUPLEX_HALF,
.setting = SUPPORTED_1000baseT_Half,
},
{
.speed = SPEED_100,
.duplex = DUPLEX_FULL,
.setting = SUPPORTED_100baseT_Full,
},
{
.speed = SPEED_100,
.duplex = DUPLEX_HALF,
.setting = SUPPORTED_100baseT_Half,
},
{
.speed = SPEED_10,
.duplex = DUPLEX_FULL,
.setting = SUPPORTED_10baseT_Full,
},
{
.speed = SPEED_10,
.duplex = DUPLEX_HALF,
.setting = SUPPORTED_10baseT_Half,
},
};
/**
* phy_lookup_setting - lookup a PHY setting
* @speed: speed to match
* @duplex: duplex to match
* @features: allowed link modes
* @exact: an exact match is required
*
* Search the settings array for a setting that matches the speed and
* duplex, and which is supported.
*
* If @exact is unset, either an exact match or %NULL for no match will
* be returned.
*
* If @exact is set, an exact match, the fastest supported setting at
* or below the specified speed, the slowest supported setting, or if
* they all fail, %NULL will be returned.
*/
static const struct phy_setting *
phy_lookup_setting(int speed, int duplex, u32 features, bool exact)
{
const struct phy_setting *p, *match = NULL, *last = NULL;
int i;
for (i = 0, p = settings; i < ARRAY_SIZE(settings); i++, p++) {
if (p->setting & features) {
last = p;
if (p->speed == speed && p->duplex == duplex) {
/* Exact match for speed and duplex */
match = p;
break;
} else if (!exact) {
if (!match && p->speed <= speed)
/* Candidate */
match = p;
if (p->speed < speed)
break;
}
}
}
if (!match && !exact)
match = last;
return match;
}
/** /**
* phy_find_valid - find a PHY setting that matches the requested parameters * phy_find_valid - find a PHY setting that matches the requested parameters
* @speed: desired speed * @speed: desired speed
...@@ -326,7 +173,9 @@ phy_lookup_setting(int speed, int duplex, u32 features, bool exact) ...@@ -326,7 +173,9 @@ phy_lookup_setting(int speed, int duplex, u32 features, bool exact)
static const struct phy_setting * static const struct phy_setting *
phy_find_valid(int speed, int duplex, u32 supported) phy_find_valid(int speed, int duplex, u32 supported)
{ {
return phy_lookup_setting(speed, duplex, supported, false); unsigned long mask = supported;
return phy_lookup_setting(speed, duplex, &mask, BITS_PER_LONG, false);
} }
/** /**
...@@ -343,16 +192,9 @@ unsigned int phy_supported_speeds(struct phy_device *phy, ...@@ -343,16 +192,9 @@ unsigned int phy_supported_speeds(struct phy_device *phy,
unsigned int *speeds, unsigned int *speeds,
unsigned int size) unsigned int size)
{ {
unsigned int count = 0; unsigned long supported = phy->supported;
unsigned int idx = 0;
for (idx = 0; idx < ARRAY_SIZE(settings) && count < size; idx++) return phy_speeds(speeds, size, &supported, BITS_PER_LONG);
/* Assumes settings are grouped by speed */
if ((settings[idx].setting & phy->supported) &&
(count == 0 || speeds[count - 1] != settings[idx].speed))
speeds[count++] = settings[idx].speed;
return count;
} }
/** /**
...@@ -366,7 +208,9 @@ unsigned int phy_supported_speeds(struct phy_device *phy, ...@@ -366,7 +208,9 @@ unsigned int phy_supported_speeds(struct phy_device *phy,
*/ */
static inline bool phy_check_valid(int speed, int duplex, u32 features) static inline bool phy_check_valid(int speed, int duplex, u32 features)
{ {
return !!phy_lookup_setting(speed, duplex, features, true); unsigned long mask = features;
return !!phy_lookup_setting(speed, duplex, &mask, BITS_PER_LONG, true);
} }
/** /**
...@@ -712,6 +556,7 @@ void phy_start_machine(struct phy_device *phydev) ...@@ -712,6 +556,7 @@ void phy_start_machine(struct phy_device *phydev)
{ {
queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, HZ); queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, HZ);
} }
EXPORT_SYMBOL_GPL(phy_start_machine);
/** /**
* phy_trigger_machine - trigger the state machine to run * phy_trigger_machine - trigger the state machine to run
...@@ -1021,9 +866,15 @@ void phy_start(struct phy_device *phydev) ...@@ -1021,9 +866,15 @@ void phy_start(struct phy_device *phydev)
} }
EXPORT_SYMBOL(phy_start); EXPORT_SYMBOL(phy_start);
static void phy_adjust_link(struct phy_device *phydev) static void phy_link_up(struct phy_device *phydev)
{ {
phydev->adjust_link(phydev->attached_dev); phydev->phy_link_change(phydev, true, true);
phy_led_trigger_change_speed(phydev);
}
static void phy_link_down(struct phy_device *phydev, bool do_carrier)
{
phydev->phy_link_change(phydev, false, do_carrier);
phy_led_trigger_change_speed(phydev); phy_led_trigger_change_speed(phydev);
} }
...@@ -1068,8 +919,7 @@ void phy_state_machine(struct work_struct *work) ...@@ -1068,8 +919,7 @@ void phy_state_machine(struct work_struct *work)
/* If the link is down, give up on negotiation for now */ /* If the link is down, give up on negotiation for now */
if (!phydev->link) { if (!phydev->link) {
phydev->state = PHY_NOLINK; phydev->state = PHY_NOLINK;
netif_carrier_off(phydev->attached_dev); phy_link_down(phydev, true);
phy_adjust_link(phydev);
break; break;
} }
...@@ -1081,9 +931,7 @@ void phy_state_machine(struct work_struct *work) ...@@ -1081,9 +931,7 @@ void phy_state_machine(struct work_struct *work)
/* If AN is done, we're running */ /* If AN is done, we're running */
if (err > 0) { if (err > 0) {
phydev->state = PHY_RUNNING; phydev->state = PHY_RUNNING;
netif_carrier_on(phydev->attached_dev); phy_link_up(phydev);
phy_adjust_link(phydev);
} else if (0 == phydev->link_timeout--) } else if (0 == phydev->link_timeout--)
needs_aneg = true; needs_aneg = true;
break; break;
...@@ -1108,8 +956,7 @@ void phy_state_machine(struct work_struct *work) ...@@ -1108,8 +956,7 @@ void phy_state_machine(struct work_struct *work)
} }
} }
phydev->state = PHY_RUNNING; phydev->state = PHY_RUNNING;
netif_carrier_on(phydev->attached_dev); phy_link_up(phydev);
phy_adjust_link(phydev);
} }
break; break;
case PHY_FORCING: case PHY_FORCING:
...@@ -1119,13 +966,12 @@ void phy_state_machine(struct work_struct *work) ...@@ -1119,13 +966,12 @@ void phy_state_machine(struct work_struct *work)
if (phydev->link) { if (phydev->link) {
phydev->state = PHY_RUNNING; phydev->state = PHY_RUNNING;
netif_carrier_on(phydev->attached_dev); phy_link_up(phydev);
} else { } else {
if (0 == phydev->link_timeout--) if (0 == phydev->link_timeout--)
needs_aneg = true; needs_aneg = true;
phy_link_down(phydev, false);
} }
phy_adjust_link(phydev);
break; break;
case PHY_RUNNING: case PHY_RUNNING:
/* Only register a CHANGE if we are polling and link changed /* Only register a CHANGE if we are polling and link changed
...@@ -1157,14 +1003,12 @@ void phy_state_machine(struct work_struct *work) ...@@ -1157,14 +1003,12 @@ void phy_state_machine(struct work_struct *work)
if (phydev->link) { if (phydev->link) {
phydev->state = PHY_RUNNING; phydev->state = PHY_RUNNING;
netif_carrier_on(phydev->attached_dev); phy_link_up(phydev);
} else { } else {
phydev->state = PHY_NOLINK; phydev->state = PHY_NOLINK;
netif_carrier_off(phydev->attached_dev); phy_link_down(phydev, true);
} }
phy_adjust_link(phydev);
if (phy_interrupt_is_valid(phydev)) if (phy_interrupt_is_valid(phydev))
err = phy_config_interrupt(phydev, err = phy_config_interrupt(phydev,
PHY_INTERRUPT_ENABLED); PHY_INTERRUPT_ENABLED);
...@@ -1172,8 +1016,7 @@ void phy_state_machine(struct work_struct *work) ...@@ -1172,8 +1016,7 @@ void phy_state_machine(struct work_struct *work)
case PHY_HALTED: case PHY_HALTED:
if (phydev->link) { if (phydev->link) {
phydev->link = 0; phydev->link = 0;
netif_carrier_off(phydev->attached_dev); phy_link_down(phydev, true);
phy_adjust_link(phydev);
do_suspend = true; do_suspend = true;
} }
break; break;
...@@ -1193,11 +1036,11 @@ void phy_state_machine(struct work_struct *work) ...@@ -1193,11 +1036,11 @@ void phy_state_machine(struct work_struct *work)
if (phydev->link) { if (phydev->link) {
phydev->state = PHY_RUNNING; phydev->state = PHY_RUNNING;
netif_carrier_on(phydev->attached_dev); phy_link_up(phydev);
} else { } else {
phydev->state = PHY_NOLINK; phydev->state = PHY_NOLINK;
phy_link_down(phydev, false);
} }
phy_adjust_link(phydev);
} else { } else {
phydev->state = PHY_AN; phydev->state = PHY_AN;
phydev->link_timeout = PHY_AN_TIMEOUT; phydev->link_timeout = PHY_AN_TIMEOUT;
...@@ -1209,11 +1052,11 @@ void phy_state_machine(struct work_struct *work) ...@@ -1209,11 +1052,11 @@ void phy_state_machine(struct work_struct *work)
if (phydev->link) { if (phydev->link) {
phydev->state = PHY_RUNNING; phydev->state = PHY_RUNNING;
netif_carrier_on(phydev->attached_dev); phy_link_up(phydev);
} else { } else {
phydev->state = PHY_NOLINK; phydev->state = PHY_NOLINK;
phy_link_down(phydev, false);
} }
phy_adjust_link(phydev);
} }
break; break;
} }
......
...@@ -688,6 +688,19 @@ struct phy_device *phy_find_first(struct mii_bus *bus) ...@@ -688,6 +688,19 @@ struct phy_device *phy_find_first(struct mii_bus *bus)
} }
EXPORT_SYMBOL(phy_find_first); EXPORT_SYMBOL(phy_find_first);
static void phy_link_change(struct phy_device *phydev, bool up, bool do_carrier)
{
struct net_device *netdev = phydev->attached_dev;
if (do_carrier) {
if (up)
netif_carrier_on(netdev);
else
netif_carrier_off(netdev);
}
phydev->adjust_link(netdev);
}
/** /**
* phy_prepare_link - prepares the PHY layer to monitor link status * phy_prepare_link - prepares the PHY layer to monitor link status
* @phydev: target phy_device struct * @phydev: target phy_device struct
...@@ -951,6 +964,7 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, ...@@ -951,6 +964,7 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
goto error; goto error;
} }
phydev->phy_link_change = phy_link_change;
phydev->attached_dev = dev; phydev->attached_dev = dev;
dev->phydev = phydev; dev->phydev = phydev;
...@@ -1070,6 +1084,7 @@ void phy_detach(struct phy_device *phydev) ...@@ -1070,6 +1084,7 @@ void phy_detach(struct phy_device *phydev)
phydev->attached_dev->phydev = NULL; phydev->attached_dev->phydev = NULL;
phydev->attached_dev = NULL; phydev->attached_dev = NULL;
phy_suspend(phydev); phy_suspend(phydev);
phydev->phylink = NULL;
phy_led_triggers_unregister(phydev); phy_led_triggers_unregister(phydev);
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
#ifndef SFP_H
#define SFP_H
#include <linux/ethtool.h>
#include <linux/sfp.h>
struct sfp;
struct sfp_socket_ops {
void (*start)(struct sfp *sfp);
void (*stop)(struct sfp *sfp);
int (*module_info)(struct sfp *sfp, struct ethtool_modinfo *modinfo);
int (*module_eeprom)(struct sfp *sfp, struct ethtool_eeprom *ee,
u8 *data);
};
int sfp_add_phy(struct sfp_bus *bus, struct phy_device *phydev);
void sfp_remove_phy(struct sfp_bus *bus);
void sfp_link_up(struct sfp_bus *bus);
void sfp_link_down(struct sfp_bus *bus);
int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id);
void sfp_module_remove(struct sfp_bus *bus);
int sfp_link_configure(struct sfp_bus *bus, const struct sfp_eeprom_id *id);
struct sfp_bus *sfp_register_socket(struct device *dev, struct sfp *sfp,
const struct sfp_socket_ops *ops);
void sfp_unregister_socket(struct sfp_bus *bus);
#endif
...@@ -182,6 +182,7 @@ static inline const char *phy_modes(phy_interface_t interface) ...@@ -182,6 +182,7 @@ static inline const char *phy_modes(phy_interface_t interface)
#define MII_ADDR_C45 (1<<30) #define MII_ADDR_C45 (1<<30)
struct device; struct device;
struct phylink;
struct sk_buff; struct sk_buff;
/* /*
...@@ -469,11 +470,13 @@ struct phy_device { ...@@ -469,11 +470,13 @@ struct phy_device {
struct mutex lock; struct mutex lock;
struct phylink *phylink;
struct net_device *attached_dev; struct net_device *attached_dev;
u8 mdix; u8 mdix;
u8 mdix_ctrl; u8 mdix_ctrl;
void (*phy_link_change)(struct phy_device *, bool up, bool do_carrier);
void (*adjust_link)(struct net_device *dev); void (*adjust_link)(struct net_device *dev);
}; };
#define to_phy_device(d) container_of(to_mdio_device(d), \ #define to_phy_device(d) container_of(to_mdio_device(d), \
...@@ -667,6 +670,24 @@ struct phy_fixup { ...@@ -667,6 +670,24 @@ struct phy_fixup {
int (*run)(struct phy_device *phydev); int (*run)(struct phy_device *phydev);
}; };
const char *phy_speed_to_str(int speed);
const char *phy_duplex_to_str(unsigned int duplex);
/* A structure for mapping a particular speed and duplex
* combination to a particular SUPPORTED and ADVERTISED value
*/
struct phy_setting {
u32 speed;
u8 duplex;
u8 bit;
};
const struct phy_setting *
phy_lookup_setting(int speed, int duplex, const unsigned long *mask,
size_t maxbit, bool exact);
size_t phy_speeds(unsigned int *speeds, size_t size,
unsigned long *mask, size_t maxbit);
/** /**
* phy_read_mmd - Convenience function for reading a register * phy_read_mmd - Convenience function for reading a register
* from an MMD on a given PHY. * from an MMD on a given PHY.
......
#ifndef NETDEV_PCS_H
#define NETDEV_PCS_H
#include <linux/phy.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
struct device_node;
struct ethtool_cmd;
struct net_device;
enum {
MLO_PAUSE_NONE,
MLO_PAUSE_ASYM = BIT(0),
MLO_PAUSE_SYM = BIT(1),
MLO_PAUSE_RX = BIT(2),
MLO_PAUSE_TX = BIT(3),
MLO_PAUSE_TXRX_MASK = MLO_PAUSE_TX | MLO_PAUSE_RX,
MLO_PAUSE_AN = BIT(4),
MLO_AN_PHY = 0, /* Conventional PHY */
MLO_AN_FIXED, /* Fixed-link mode */
MLO_AN_SGMII, /* Cisco SGMII protocol */
MLO_AN_8023Z, /* 1000base-X protocol */
};
static inline bool phylink_autoneg_inband(unsigned int mode)
{
return mode == MLO_AN_SGMII || mode == MLO_AN_8023Z;
}
struct phylink_link_state {
__ETHTOOL_DECLARE_LINK_MODE_MASK(advertising);
__ETHTOOL_DECLARE_LINK_MODE_MASK(lp_advertising);
phy_interface_t interface; /* PHY_INTERFACE_xxx */
int speed;
int duplex;
int pause;
unsigned int link:1;
unsigned int an_enabled:1;
unsigned int an_complete:1;
};
struct phylink_mac_ops {
/**
* validate: validate and update the link configuration
* @ndev: net_device structure associated with MAC
* @config: configuration to validate
*
* Update the %config->supported and %config->advertised masks
* clearing bits that can not be supported.
*
* Note: the PHY may be able to transform from one connection
* technology to another, so, eg, don't clear 1000BaseX just
* because the MAC is unable to support it. This is more about
* clearing unsupported speeds and duplex settings.
*
* If the %config->interface mode is %PHY_INTERFACE_MODE_1000BASEX
* or %PHY_INTERFACE_MODE_2500BASEX, select the appropriate mode
* based on %config->advertised and/or %config->speed.
*/
void (*validate)(struct net_device *ndev, unsigned long *supported,
struct phylink_link_state *state);
/* Read the current link state from the hardware */
int (*mac_link_state)(struct net_device *, struct phylink_link_state *);
/* Configure the MAC */
/**
* mac_config: configure the MAC for the selected mode and state
* @ndev: net_device structure for the MAC
* @mode: one of MLO_AN_FIXED, MLO_AN_PHY, MLO_AN_8023Z, MLO_AN_SGMII
* @state: state structure
*
* The action performed depends on the currently selected mode:
*
* %MLO_AN_FIXED, %MLO_AN_PHY:
* set the specified speed, duplex, pause mode, and phy interface
* mode in the provided @state.
* %MLO_AN_8023Z:
* place the link in 1000base-X mode, advertising the parameters
* given in advertising in @state.
* %MLO_AN_SGMII:
* place the link in Cisco SGMII mode - there is no advertisment
* to make as the PHY communicates the speed and duplex to the
* MAC over the in-band control word. Configuration of the pause
* mode is as per MLO_AN_PHY since this is not included.
*/
void (*mac_config)(struct net_device *ndev, unsigned int mode,
const struct phylink_link_state *state);
/**
* mac_an_restart: restart 802.3z BaseX autonegotiation
* @ndev: net_device structure for the MAC
*/
void (*mac_an_restart)(struct net_device *ndev);
void (*mac_link_down)(struct net_device *, unsigned int mode);
void (*mac_link_up)(struct net_device *, unsigned int mode,
struct phy_device *);
};
struct phylink *phylink_create(struct net_device *, struct device_node *,
phy_interface_t iface, const struct phylink_mac_ops *ops);
void phylink_destroy(struct phylink *);
int phylink_connect_phy(struct phylink *, struct phy_device *);
int phylink_of_phy_connect(struct phylink *, struct device_node *);
void phylink_disconnect_phy(struct phylink *);
void phylink_mac_change(struct phylink *, bool up);
void phylink_start(struct phylink *);
void phylink_stop(struct phylink *);
void phylink_ethtool_get_wol(struct phylink *, struct ethtool_wolinfo *);
int phylink_ethtool_set_wol(struct phylink *, struct ethtool_wolinfo *);
int phylink_ethtool_ksettings_get(struct phylink *,
struct ethtool_link_ksettings *);
int phylink_ethtool_ksettings_set(struct phylink *,
const struct ethtool_link_ksettings *);
int phylink_ethtool_nway_reset(struct phylink *);
void phylink_ethtool_get_pauseparam(struct phylink *,
struct ethtool_pauseparam *);
int phylink_ethtool_set_pauseparam(struct phylink *,
struct ethtool_pauseparam *);
int phylink_ethtool_get_module_info(struct phylink *, struct ethtool_modinfo *);
int phylink_ethtool_get_module_eeprom(struct phylink *,
struct ethtool_eeprom *, u8 *);
int phylink_init_eee(struct phylink *, bool);
int phylink_get_eee_err(struct phylink *);
int phylink_ethtool_get_eee(struct phylink *, struct ethtool_eee *);
int phylink_ethtool_set_eee(struct phylink *, struct ethtool_eee *);
int phylink_mii_ioctl(struct phylink *, struct ifreq *, int);
#define phylink_zero(bm) \
bitmap_zero(bm, __ETHTOOL_LINK_MODE_MASK_NBITS)
#define __phylink_do_bit(op, bm, mode) \
op(ETHTOOL_LINK_MODE_ ## mode ## _BIT, bm)
#define phylink_set(bm, mode) __phylink_do_bit(__set_bit, bm, mode)
#define phylink_clear(bm, mode) __phylink_do_bit(__clear_bit, bm, mode)
#define phylink_test(bm, mode) __phylink_do_bit(test_bit, bm, mode)
void phylink_set_port_modes(unsigned long *bits);
#endif
This diff is collapsed.
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