Skip to content

Commit

Permalink
Merge branch 'phylink-and-sfp-support'
Browse files Browse the repository at this point in the history
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: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Aug 7, 2017
2 parents 21e27f2 + 7397005 commit 2347093
Show file tree
Hide file tree
Showing 14 changed files with 3,867 additions and 187 deletions.
25 changes: 25 additions & 0 deletions drivers/net/phy/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,16 @@ config MDIO_HISI_FEMAC
This module provides a driver for the MDIO busses found in the
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
tristate "MOXA ART MDIO interface support"
depends on ARCH_MOXART
Expand Down Expand Up @@ -159,6 +169,16 @@ menuconfig PHYLIB
devices. This option provides infrastructure for
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

config SWPHY
Expand All @@ -180,6 +200,11 @@ config LED_TRIGGER_PHY

comment "MII PHY device drivers"

config SFP
tristate "SFP cage support"
depends on I2C && PHYLINK
select MDIO_I2C

config AMD_PHY
tristate "AMD PHYs"
---help---
Expand Down
6 changes: 6 additions & 0 deletions drivers/net/phy/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ endif
libphy-$(CONFIG_SWPHY) += swphy.o
libphy-$(CONFIG_LED_TRIGGER_PHY) += phy_led_triggers.o

obj-$(CONFIG_PHYLINK) += phylink.o
obj-$(CONFIG_PHYLIB) += libphy.o

obj-$(CONFIG_MDIO_BCM_IPROC) += mdio-bcm-iproc.o
Expand All @@ -30,12 +31,17 @@ obj-$(CONFIG_MDIO_BUS_MUX_MMIOREG) += mdio-mux-mmioreg.o
obj-$(CONFIG_MDIO_CAVIUM) += mdio-cavium.o
obj-$(CONFIG_MDIO_GPIO) += mdio-gpio.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_OCTEON) += mdio-octeon.o
obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o
obj-$(CONFIG_MDIO_THUNDER) += mdio-thunder.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_AQUANTIA_PHY) += aquantia.o
obj-$(CONFIG_AT803X_PHY) += at803x.o
Expand Down
109 changes: 109 additions & 0 deletions drivers/net/phy/mdio-i2c.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* 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");
19 changes: 19 additions & 0 deletions drivers/net/phy/mdio-i2c.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* 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
180 changes: 180 additions & 0 deletions drivers/net/phy/phy-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,186 @@
#include <linux/export.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,
u16 regnum)
{
Expand Down
Loading

0 comments on commit 2347093

Please sign in to comment.