mvebu: add support for SFP

Add patches for SFP support and package it for ClearFog. Tested with a
Juniper SFP module.

Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
Acked-by: Felix Fietkau <nbd@nbd.name>
v19.07.3_mercusys_ac12_duma
Jonas Gorski 8 years ago
parent a454756c91
commit 4aa5d3e60d

@ -272,6 +272,7 @@ CONFIG_MAGIC_SYSRQ=y
CONFIG_MANGLE_BOOTARGS=y
CONFIG_MARVELL_PHY=y
CONFIG_MDIO_BOARDINFO=y
CONFIG_MDIO_I2C=y
CONFIG_MEMORY=y
CONFIG_MIGHT_HAVE_CACHE_L2X0=y
CONFIG_MIGHT_HAVE_PCI=y
@ -341,6 +342,7 @@ CONFIG_PCI_MVEBU=y
CONFIG_PERF_USE_VMALLOC=y
CONFIG_PGTABLE_LEVELS=2
CONFIG_PHYLIB=y
CONFIG_PHYLINK=y
CONFIG_PINCTRL=y
CONFIG_PINCTRL_ARMADA_370=y
CONFIG_PINCTRL_ARMADA_38X=y
@ -378,6 +380,7 @@ CONFIG_SENSORS_PWM_FAN=y
CONFIG_SENSORS_TMP421=y
CONFIG_SERIAL_8250_DW=y
CONFIG_SERIAL_8250_FSL=y
CONFIG_SFP=y
CONFIG_SMP=y
CONFIG_SMP_ON_UP=y
CONFIG_SOC_BUS=y
@ -389,6 +392,7 @@ CONFIG_SRAM=y
CONFIG_SRCU=y
CONFIG_SWCONFIG=y
CONFIG_SWIOTLB=y
CONFIG_SWPHY=y
CONFIG_SWP_EMULATE=y
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
CONFIG_THERMAL=y

@ -0,0 +1,306 @@
From 4d5621372f6e7ddbfd5879602f82073987bcc722 Mon Sep 17 00:00:00 2001
From: Russell King <rmk+kernel@arm.linux.org.uk>
Date: Sun, 20 Sep 2015 09:57:10 +0100
Subject: [PATCH 709/744] phy: move fixed_phy MII register generation to a
library
Move the fixed_phy MII register generation to a library to allow other
software phy implementations to use this code.
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
drivers/net/phy/Kconfig | 4 ++
drivers/net/phy/Makefile | 3 +-
drivers/net/phy/fixed_phy.c | 95 ++-------------------------------
drivers/net/phy/swphy.c | 126 ++++++++++++++++++++++++++++++++++++++++++++
drivers/net/phy/swphy.h | 8 +++
5 files changed, 143 insertions(+), 93 deletions(-)
create mode 100644 drivers/net/phy/swphy.c
create mode 100644 drivers/net/phy/swphy.h
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -26,6 +26,9 @@ config SWCONFIG_LEDS
bool "Switch LED trigger support"
depends on (SWCONFIG && LEDS_TRIGGERS)
+config SWPHY
+ bool
+
comment "MII PHY device drivers"
config AQUANTIA_PHY
@@ -205,6 +208,7 @@ config RTL8306_PHY
config FIXED_PHY
tristate "Driver for MDIO Bus/PHY emulation with fixed speed/link PHYs"
depends on PHYLIB
+ select SWPHY
---help---
Adds the platform "fixed" MDIO Bus to cover the boards that use
PHYs that are not connected to the real MDIO bus.
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -1,6 +1,7 @@
# Makefile for Linux PHY drivers
-libphy-objs := phy.o phy_device.o mdio_bus.o
+libphy-y := phy.o phy_device.o mdio_bus.o
+libphy-$(CONFIG_SWPHY) += swphy.o
obj-$(CONFIG_MDIO_BOARDINFO) += mdio-boardinfo.o
--- a/drivers/net/phy/fixed_phy.c
+++ b/drivers/net/phy/fixed_phy.c
@@ -24,6 +24,8 @@
#include <linux/of.h>
#include <linux/gpio.h>
+#include "swphy.h"
+
#define MII_REGS_NUM 29
struct fixed_mdio_bus {
@@ -49,101 +51,10 @@ static struct fixed_mdio_bus platform_fm
static int fixed_phy_update_regs(struct fixed_phy *fp)
{
- u16 bmsr = BMSR_ANEGCAPABLE;
- u16 bmcr = 0;
- u16 lpagb = 0;
- u16 lpa = 0;
-
if (gpio_is_valid(fp->link_gpio))
fp->status.link = !!gpio_get_value_cansleep(fp->link_gpio);
- if (fp->status.duplex) {
- switch (fp->status.speed) {
- case 1000:
- bmsr |= BMSR_ESTATEN;
- break;
- case 100:
- bmsr |= BMSR_100FULL;
- break;
- case 10:
- bmsr |= BMSR_10FULL;
- break;
- default:
- break;
- }
- } else {
- switch (fp->status.speed) {
- case 1000:
- bmsr |= BMSR_ESTATEN;
- break;
- case 100:
- bmsr |= BMSR_100HALF;
- break;
- case 10:
- bmsr |= BMSR_10HALF;
- break;
- default:
- break;
- }
- }
-
- if (fp->status.link) {
- bmsr |= BMSR_LSTATUS | BMSR_ANEGCOMPLETE;
-
- if (fp->status.duplex) {
- bmcr |= BMCR_FULLDPLX;
-
- switch (fp->status.speed) {
- case 1000:
- bmcr |= BMCR_SPEED1000;
- lpagb |= LPA_1000FULL;
- break;
- case 100:
- bmcr |= BMCR_SPEED100;
- lpa |= LPA_100FULL;
- break;
- case 10:
- lpa |= LPA_10FULL;
- break;
- default:
- pr_warn("fixed phy: unknown speed\n");
- return -EINVAL;
- }
- } else {
- switch (fp->status.speed) {
- case 1000:
- bmcr |= BMCR_SPEED1000;
- lpagb |= LPA_1000HALF;
- break;
- case 100:
- bmcr |= BMCR_SPEED100;
- lpa |= LPA_100HALF;
- break;
- case 10:
- lpa |= LPA_10HALF;
- break;
- default:
- pr_warn("fixed phy: unknown speed\n");
- return -EINVAL;
- }
- }
-
- if (fp->status.pause)
- lpa |= LPA_PAUSE_CAP;
-
- if (fp->status.asym_pause)
- lpa |= LPA_PAUSE_ASYM;
- }
-
- fp->regs[MII_PHYSID1] = 0;
- fp->regs[MII_PHYSID2] = 0;
-
- fp->regs[MII_BMSR] = bmsr;
- fp->regs[MII_BMCR] = bmcr;
- fp->regs[MII_LPA] = lpa;
- fp->regs[MII_STAT1000] = lpagb;
-
- return 0;
+ return swphy_update_regs(fp->regs, &fp->status);
}
static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num)
--- /dev/null
+++ b/drivers/net/phy/swphy.c
@@ -0,0 +1,126 @@
+/*
+ * Software PHY emulation
+ *
+ * Code taken from fixed_phy.c by Russell King <rmk+kernel@arm.linux.org.uk>
+ *
+ * Author: Vitaly Bordug <vbordug@ru.mvista.com>
+ * Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * Copyright (c) 2006-2007 MontaVista Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+#include <linux/export.h>
+#include <linux/mii.h>
+#include <linux/phy.h>
+#include <linux/phy_fixed.h>
+
+#include "swphy.h"
+
+/**
+ * swphy_update_regs - update MII register array with fixed phy state
+ * @regs: array of 32 registers to update
+ * @state: fixed phy status
+ *
+ * Update the array of MII registers with the fixed phy link, speed,
+ * duplex and pause mode settings.
+ */
+int swphy_update_regs(u16 *regs, const struct fixed_phy_status *state)
+{
+ u16 bmsr = BMSR_ANEGCAPABLE;
+ u16 bmcr = 0;
+ u16 lpagb = 0;
+ u16 lpa = 0;
+
+ if (state->duplex) {
+ switch (state->speed) {
+ case 1000:
+ bmsr |= BMSR_ESTATEN;
+ break;
+ case 100:
+ bmsr |= BMSR_100FULL;
+ break;
+ case 10:
+ bmsr |= BMSR_10FULL;
+ break;
+ default:
+ break;
+ }
+ } else {
+ switch (state->speed) {
+ case 1000:
+ bmsr |= BMSR_ESTATEN;
+ break;
+ case 100:
+ bmsr |= BMSR_100HALF;
+ break;
+ case 10:
+ bmsr |= BMSR_10HALF;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (state->link) {
+ bmsr |= BMSR_LSTATUS | BMSR_ANEGCOMPLETE;
+
+ if (state->duplex) {
+ bmcr |= BMCR_FULLDPLX;
+
+ switch (state->speed) {
+ case 1000:
+ bmcr |= BMCR_SPEED1000;
+ lpagb |= LPA_1000FULL;
+ break;
+ case 100:
+ bmcr |= BMCR_SPEED100;
+ lpa |= LPA_100FULL;
+ break;
+ case 10:
+ lpa |= LPA_10FULL;
+ break;
+ default:
+ pr_warn("swphy: unknown speed\n");
+ return -EINVAL;
+ }
+ } else {
+ switch (state->speed) {
+ case 1000:
+ bmcr |= BMCR_SPEED1000;
+ lpagb |= LPA_1000HALF;
+ break;
+ case 100:
+ bmcr |= BMCR_SPEED100;
+ lpa |= LPA_100HALF;
+ break;
+ case 10:
+ lpa |= LPA_10HALF;
+ break;
+ default:
+ pr_warn("swphy: unknown speed\n");
+ return -EINVAL;
+ }
+ }
+
+ if (state->pause)
+ lpa |= LPA_PAUSE_CAP;
+
+ if (state->asym_pause)
+ lpa |= LPA_PAUSE_ASYM;
+ }
+
+ regs[MII_PHYSID1] = 0;
+ regs[MII_PHYSID2] = 0;
+
+ regs[MII_BMSR] = bmsr;
+ regs[MII_BMCR] = bmcr;
+ regs[MII_LPA] = lpa;
+ regs[MII_STAT1000] = lpagb;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(swphy_update_regs);
--- /dev/null
+++ b/drivers/net/phy/swphy.h
@@ -0,0 +1,8 @@
+#ifndef SWPHY_H
+#define SWPHY_H
+
+struct fixed_phy_status;
+
+int swphy_update_regs(u16 *regs, const struct fixed_phy_status *state);
+
+#endif

@ -0,0 +1,203 @@
From cd834fe430f030a63bfa9277bba194e8eef4dbd0 Mon Sep 17 00:00:00 2001
From: Russell King <rmk+kernel@arm.linux.org.uk>
Date: Sun, 20 Sep 2015 10:18:59 +0100
Subject: [PATCH 710/744] phy: convert swphy register generation to tabular
form
Convert the swphy register generation to tabular form which allows us
to eliminate multiple switch() statements. This results in a smaller
object code size, more efficient, and easier to add support for faster
speeds.
Before:
Idx Name Size VMA LMA File off Algn
0 .text 00000164 00000000 00000000 00000034 2**2
text data bss dec hex filename
388 0 0 388 184 swphy.o
After:
Idx Name Size VMA LMA File off Algn
0 .text 000000fc 00000000 00000000 00000034 2**2
5 .rodata 00000028 00000000 00000000 00000138 2**2
text data bss dec hex filename
324 0 0 324 144 swphy.o
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
drivers/net/phy/swphy.c | 143 ++++++++++++++++++++++++++----------------------
1 file changed, 78 insertions(+), 65 deletions(-)
--- a/drivers/net/phy/swphy.c
+++ b/drivers/net/phy/swphy.c
@@ -20,6 +20,72 @@
#include "swphy.h"
+struct swmii_regs {
+ u16 bmcr;
+ u16 bmsr;
+ u16 lpa;
+ u16 lpagb;
+};
+
+enum {
+ SWMII_SPEED_10 = 0,
+ SWMII_SPEED_100,
+ SWMII_SPEED_1000,
+ SWMII_DUPLEX_HALF = 0,
+ SWMII_DUPLEX_FULL,
+};
+
+/*
+ * These two tables get bitwise-anded together to produce the final result.
+ * This means the speed table must contain both duplex settings, and the
+ * duplex table must contain all speed settings.
+ */
+static const struct swmii_regs speed[] = {
+ [SWMII_SPEED_10] = {
+ .bmcr = BMCR_FULLDPLX,
+ .lpa = LPA_10FULL | LPA_10HALF,
+ },
+ [SWMII_SPEED_100] = {
+ .bmcr = BMCR_FULLDPLX | BMCR_SPEED100,
+ .bmsr = BMSR_100FULL | BMSR_100HALF,
+ .lpa = LPA_100FULL | LPA_100HALF,
+ },
+ [SWMII_SPEED_1000] = {
+ .bmcr = BMCR_FULLDPLX | BMCR_SPEED1000,
+ .bmsr = BMSR_ESTATEN,
+ .lpagb = LPA_1000FULL | LPA_1000HALF,
+ },
+};
+
+static const struct swmii_regs duplex[] = {
+ [SWMII_DUPLEX_HALF] = {
+ .bmcr = ~BMCR_FULLDPLX,
+ .bmsr = BMSR_ESTATEN | BMSR_100HALF,
+ .lpa = LPA_10HALF | LPA_100HALF,
+ .lpagb = LPA_1000HALF,
+ },
+ [SWMII_DUPLEX_FULL] = {
+ .bmcr = ~0,
+ .bmsr = BMSR_ESTATEN | BMSR_100FULL,
+ .lpa = LPA_10FULL | LPA_100FULL,
+ .lpagb = LPA_1000FULL,
+ },
+};
+
+static int swphy_decode_speed(int speed)
+{
+ switch (speed) {
+ case 1000:
+ return SWMII_SPEED_1000;
+ case 100:
+ return SWMII_SPEED_100;
+ case 10:
+ return SWMII_SPEED_10;
+ default:
+ return -EINVAL;
+ }
+}
+
/**
* swphy_update_regs - update MII register array with fixed phy state
* @regs: array of 32 registers to update
@@ -30,81 +96,28 @@
*/
int swphy_update_regs(u16 *regs, const struct fixed_phy_status *state)
{
+ int speed_index, duplex_index;
u16 bmsr = BMSR_ANEGCAPABLE;
u16 bmcr = 0;
u16 lpagb = 0;
u16 lpa = 0;
- if (state->duplex) {
- switch (state->speed) {
- case 1000:
- bmsr |= BMSR_ESTATEN;
- break;
- case 100:
- bmsr |= BMSR_100FULL;
- break;
- case 10:
- bmsr |= BMSR_10FULL;
- break;
- default:
- break;
- }
- } else {
- switch (state->speed) {
- case 1000:
- bmsr |= BMSR_ESTATEN;
- break;
- case 100:
- bmsr |= BMSR_100HALF;
- break;
- case 10:
- bmsr |= BMSR_10HALF;
- break;
- default:
- break;
- }
+ speed_index = swphy_decode_speed(state->speed);
+ if (speed_index < 0) {
+ pr_warn("swphy: unknown speed\n");
+ return -EINVAL;
}
+ duplex_index = state->duplex ? SWMII_DUPLEX_FULL : SWMII_DUPLEX_HALF;
+
+ bmsr |= speed[speed_index].bmsr & duplex[duplex_index].bmsr;
+
if (state->link) {
bmsr |= BMSR_LSTATUS | BMSR_ANEGCOMPLETE;
- if (state->duplex) {
- bmcr |= BMCR_FULLDPLX;
-
- switch (state->speed) {
- case 1000:
- bmcr |= BMCR_SPEED1000;
- lpagb |= LPA_1000FULL;
- break;
- case 100:
- bmcr |= BMCR_SPEED100;
- lpa |= LPA_100FULL;
- break;
- case 10:
- lpa |= LPA_10FULL;
- break;
- default:
- pr_warn("swphy: unknown speed\n");
- return -EINVAL;
- }
- } else {
- switch (state->speed) {
- case 1000:
- bmcr |= BMCR_SPEED1000;
- lpagb |= LPA_1000HALF;
- break;
- case 100:
- bmcr |= BMCR_SPEED100;
- lpa |= LPA_100HALF;
- break;
- case 10:
- lpa |= LPA_10HALF;
- break;
- default:
- pr_warn("swphy: unknown speed\n");
- return -EINVAL;
- }
- }
+ bmcr |= speed[speed_index].bmcr & duplex[duplex_index].bmcr;
+ lpa |= speed[speed_index].lpa & duplex[duplex_index].lpa;
+ lpagb |= speed[speed_index].lpagb & duplex[duplex_index].lpagb;
if (state->pause)
lpa |= LPA_PAUSE_CAP;

@ -0,0 +1,138 @@
From e07630ad84c7dc145863f079f108154fb7c975e7 Mon Sep 17 00:00:00 2001
From: Russell King <rmk+kernel@arm.linux.org.uk>
Date: Sun, 20 Sep 2015 11:12:15 +0100
Subject: [PATCH 711/744] phy: separate swphy state validation from register
generation
Separate out the generation of MII registers from the state validation.
This allows us to simplify the error handing in fixed_phy() by allowing
earlier error detection.
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
drivers/net/phy/fixed_phy.c | 15 +++++++--------
drivers/net/phy/swphy.c | 33 ++++++++++++++++++++++++++-------
drivers/net/phy/swphy.h | 3 ++-
3 files changed, 35 insertions(+), 16 deletions(-)
--- a/drivers/net/phy/fixed_phy.c
+++ b/drivers/net/phy/fixed_phy.c
@@ -49,12 +49,12 @@ static struct fixed_mdio_bus platform_fm
.phys = LIST_HEAD_INIT(platform_fmb.phys),
};
-static int fixed_phy_update_regs(struct fixed_phy *fp)
+static void fixed_phy_update_regs(struct fixed_phy *fp)
{
if (gpio_is_valid(fp->link_gpio))
fp->status.link = !!gpio_get_value_cansleep(fp->link_gpio);
- return swphy_update_regs(fp->regs, &fp->status);
+ swphy_update_regs(fp->regs, &fp->status);
}
static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num)
@@ -161,6 +161,10 @@ int fixed_phy_add(unsigned int irq, int
struct fixed_mdio_bus *fmb = &platform_fmb;
struct fixed_phy *fp;
+ ret = swphy_validate_state(status);
+ if (ret < 0)
+ return ret;
+
fp = kzalloc(sizeof(*fp), GFP_KERNEL);
if (!fp)
return -ENOMEM;
@@ -180,17 +184,12 @@ int fixed_phy_add(unsigned int irq, int
goto err_regs;
}
- ret = fixed_phy_update_regs(fp);
- if (ret)
- goto err_gpio;
+ fixed_phy_update_regs(fp);
list_add_tail(&fp->node, &fmb->phys);
return 0;
-err_gpio:
- if (gpio_is_valid(fp->link_gpio))
- gpio_free(fp->link_gpio);
err_regs:
kfree(fp);
return ret;
--- a/drivers/net/phy/swphy.c
+++ b/drivers/net/phy/swphy.c
@@ -87,6 +87,29 @@ static int swphy_decode_speed(int speed)
}
/**
+ * swphy_validate_state - validate the software phy status
+ * @state: software phy status
+ *
+ * This checks that we can represent the state stored in @state can be
+ * represented in the emulated MII registers. Returns 0 if it can,
+ * otherwise returns -EINVAL.
+ */
+int swphy_validate_state(const struct fixed_phy_status *state)
+{
+ int err;
+
+ if (state->link) {
+ err = swphy_decode_speed(state->speed);
+ if (err < 0) {
+ pr_warn("swphy: unknown speed\n");
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(swphy_validate_state);
+
+/**
* swphy_update_regs - update MII register array with fixed phy state
* @regs: array of 32 registers to update
* @state: fixed phy status
@@ -94,7 +117,7 @@ static int swphy_decode_speed(int speed)
* Update the array of MII registers with the fixed phy link, speed,
* duplex and pause mode settings.
*/
-int swphy_update_regs(u16 *regs, const struct fixed_phy_status *state)
+void swphy_update_regs(u16 *regs, const struct fixed_phy_status *state)
{
int speed_index, duplex_index;
u16 bmsr = BMSR_ANEGCAPABLE;
@@ -103,10 +126,8 @@ int swphy_update_regs(u16 *regs, const s
u16 lpa = 0;
speed_index = swphy_decode_speed(state->speed);
- if (speed_index < 0) {
- pr_warn("swphy: unknown speed\n");
- return -EINVAL;
- }
+ if (WARN_ON(speed_index < 0))
+ return;
duplex_index = state->duplex ? SWMII_DUPLEX_FULL : SWMII_DUPLEX_HALF;
@@ -133,7 +154,5 @@ int swphy_update_regs(u16 *regs, const s
regs[MII_BMCR] = bmcr;
regs[MII_LPA] = lpa;
regs[MII_STAT1000] = lpagb;
-
- return 0;
}
EXPORT_SYMBOL_GPL(swphy_update_regs);
--- a/drivers/net/phy/swphy.h
+++ b/drivers/net/phy/swphy.h
@@ -3,6 +3,7 @@
struct fixed_phy_status;
-int swphy_update_regs(u16 *regs, const struct fixed_phy_status *state);
+int swphy_validate_state(const struct fixed_phy_status *state);
+void swphy_update_regs(u16 *regs, const struct fixed_phy_status *state);
#endif

@ -0,0 +1,204 @@
From e0f33a88243329da1aa5a90fe10ab25c9fb0a091 Mon Sep 17 00:00:00 2001
From: Russell King <rmk+kernel@arm.linux.org.uk>
Date: Sun, 20 Sep 2015 11:28:39 +0100
Subject: [PATCH 712/744] phy: generate swphy registers on the fly
Generate software phy registers as and when requested, rather than
duplicating the state in fixed_phy. This allows us to eliminate
the duplicate storage of of the same data, which is only different
in format.
As fixed_phy_update_regs() no longer updates register state, rename
it to fixed_phy_update().
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
drivers/net/phy/fixed_phy.c | 31 +++++-------------------------
drivers/net/phy/swphy.c | 47 ++++++++++++++++++++++++++++++++-------------
drivers/net/phy/swphy.h | 2 +-
3 files changed, 40 insertions(+), 40 deletions(-)
--- a/drivers/net/phy/fixed_phy.c
+++ b/drivers/net/phy/fixed_phy.c
@@ -26,8 +26,6 @@
#include "swphy.h"
-#define MII_REGS_NUM 29
-
struct fixed_mdio_bus {
int irqs[PHY_MAX_ADDR];
struct mii_bus *mii_bus;
@@ -36,7 +34,6 @@ struct fixed_mdio_bus {
struct fixed_phy {
int addr;
- u16 regs[MII_REGS_NUM];
struct phy_device *phydev;
struct fixed_phy_status status;
int (*link_update)(struct net_device *, struct fixed_phy_status *);
@@ -49,12 +46,10 @@ static struct fixed_mdio_bus platform_fm
.phys = LIST_HEAD_INIT(platform_fmb.phys),
};
-static void fixed_phy_update_regs(struct fixed_phy *fp)
+static void fixed_phy_update(struct fixed_phy *fp)
{
if (gpio_is_valid(fp->link_gpio))
fp->status.link = !!gpio_get_value_cansleep(fp->link_gpio);
-
- swphy_update_regs(fp->regs, &fp->status);
}
static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num)
@@ -62,29 +57,15 @@ static int fixed_mdio_read(struct mii_bu
struct fixed_mdio_bus *fmb = bus->priv;
struct fixed_phy *fp;
- if (reg_num >= MII_REGS_NUM)
- return -1;
-
- /* We do not support emulating Clause 45 over Clause 22 register reads
- * return an error instead of bogus data.
- */
- switch (reg_num) {
- case MII_MMD_CTRL:
- case MII_MMD_DATA:
- return -1;
- default:
- break;
- }
-
list_for_each_entry(fp, &fmb->phys, node) {
if (fp->addr == phy_addr) {
/* Issue callback if user registered it. */
if (fp->link_update) {
fp->link_update(fp->phydev->attached_dev,
&fp->status);
- fixed_phy_update_regs(fp);
+ fixed_phy_update(fp);
}
- return fp->regs[reg_num];
+ return swphy_read_reg(reg_num, &fp->status);
}
}
@@ -144,7 +125,7 @@ int fixed_phy_update_state(struct phy_de
_UPD(pause);
_UPD(asym_pause);
#undef _UPD
- fixed_phy_update_regs(fp);
+ fixed_phy_update(fp);
return 0;
}
}
@@ -169,8 +150,6 @@ int fixed_phy_add(unsigned int irq, int
if (!fp)
return -ENOMEM;
- memset(fp->regs, 0xFF, sizeof(fp->regs[0]) * MII_REGS_NUM);
-
fmb->irqs[phy_addr] = irq;
fp->addr = phy_addr;
@@ -184,7 +163,7 @@ int fixed_phy_add(unsigned int irq, int
goto err_regs;
}
- fixed_phy_update_regs(fp);
+ fixed_phy_update(fp);
list_add_tail(&fp->node, &fmb->phys);
--- a/drivers/net/phy/swphy.c
+++ b/drivers/net/phy/swphy.c
@@ -20,6 +20,8 @@
#include "swphy.h"
+#define MII_REGS_NUM 29
+
struct swmii_regs {
u16 bmcr;
u16 bmsr;
@@ -110,14 +112,13 @@ int swphy_validate_state(const struct fi
EXPORT_SYMBOL_GPL(swphy_validate_state);
/**
- * swphy_update_regs - update MII register array with fixed phy state
- * @regs: array of 32 registers to update
+ * swphy_read_reg - return a MII register from the fixed phy state
+ * @reg: MII register
* @state: fixed phy status
*
- * Update the array of MII registers with the fixed phy link, speed,
- * duplex and pause mode settings.
+ * Return the MII @reg register generated from the fixed phy state @state.
*/
-void swphy_update_regs(u16 *regs, const struct fixed_phy_status *state)
+int swphy_read_reg(int reg, const struct fixed_phy_status *state)
{
int speed_index, duplex_index;
u16 bmsr = BMSR_ANEGCAPABLE;
@@ -125,9 +126,12 @@ void swphy_update_regs(u16 *regs, const
u16 lpagb = 0;
u16 lpa = 0;
+ if (reg > MII_REGS_NUM)
+ return -1;
+
speed_index = swphy_decode_speed(state->speed);
if (WARN_ON(speed_index < 0))
- return;
+ return 0;
duplex_index = state->duplex ? SWMII_DUPLEX_FULL : SWMII_DUPLEX_HALF;
@@ -147,12 +151,29 @@ void swphy_update_regs(u16 *regs, const
lpa |= LPA_PAUSE_ASYM;
}
- regs[MII_PHYSID1] = 0;
- regs[MII_PHYSID2] = 0;
+ switch (reg) {
+ case MII_BMCR:
+ return bmcr;
+ case MII_BMSR:
+ return bmsr;
+ case MII_PHYSID1:
+ case MII_PHYSID2:
+ return 0;
+ case MII_LPA:
+ return lpa;
+ case MII_STAT1000:
+ return lpagb;
+
+ /*
+ * We do not support emulating Clause 45 over Clause 22 register
+ * reads. Return an error instead of bogus data.
+ */
+ case MII_MMD_CTRL:
+ case MII_MMD_DATA:
+ return -1;
- regs[MII_BMSR] = bmsr;
- regs[MII_BMCR] = bmcr;
- regs[MII_LPA] = lpa;
- regs[MII_STAT1000] = lpagb;
+ default:
+ return 0xffff;
+ }
}
-EXPORT_SYMBOL_GPL(swphy_update_regs);
+EXPORT_SYMBOL_GPL(swphy_read_reg);
--- a/drivers/net/phy/swphy.h
+++ b/drivers/net/phy/swphy.h
@@ -4,6 +4,6 @@
struct fixed_phy_status;
int swphy_validate_state(const struct fixed_phy_status *state);
-void swphy_update_regs(u16 *regs, const struct fixed_phy_status *state);
+int swphy_read_reg(int reg, const struct fixed_phy_status *state);
#endif

@ -0,0 +1,92 @@
From c36739c3cfd277a4cc9820a29dd0f4b7fbac795b Mon Sep 17 00:00:00 2001
From: Russell King <rmk+kernel@arm.linux.org.uk>
Date: Sun, 20 Sep 2015 18:31:36 +0100
Subject: [PATCH 713/744] phy: improve safety of fixed-phy MII register reading
There is no prevention of a concurrent call to both fixed_mdio_read()
and fixed_phy_update_state(), which can result in the state being
modified while it's being inspected. Fix this by using a seqcount
to detect modifications, and memcpy()ing the state.
We remain slightly naughty here, calling link_update() and updating
the link status within the read-side loop - which would need rework
of the design to change.
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
drivers/net/phy/fixed_phy.c | 28 +++++++++++++++++++++-------
1 file changed, 21 insertions(+), 7 deletions(-)
--- a/drivers/net/phy/fixed_phy.c
+++ b/drivers/net/phy/fixed_phy.c
@@ -23,6 +23,7 @@
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/gpio.h>
+#include <linux/seqlock.h>
#include "swphy.h"
@@ -35,6 +36,7 @@ struct fixed_mdio_bus {
struct fixed_phy {
int addr;
struct phy_device *phydev;
+ seqcount_t seqcount;
struct fixed_phy_status status;
int (*link_update)(struct net_device *, struct fixed_phy_status *);
struct list_head node;
@@ -59,13 +61,21 @@ static int fixed_mdio_read(struct mii_bu
list_for_each_entry(fp, &fmb->phys, node) {
if (fp->addr == phy_addr) {
- /* Issue callback if user registered it. */
- if (fp->link_update) {
- fp->link_update(fp->phydev->attached_dev,
- &fp->status);
- fixed_phy_update(fp);
- }
- return swphy_read_reg(reg_num, &fp->status);
+ struct fixed_phy_status state;
+ int s;
+
+ do {
+ s = read_seqcount_begin(&fp->seqcount);
+ /* Issue callback if user registered it. */
+ if (fp->link_update) {
+ fp->link_update(fp->phydev->attached_dev,
+ &fp->status);
+ fixed_phy_update(fp);
+ }
+ state = fp->status;
+ } while (read_seqcount_retry(&fp->seqcount, s));
+
+ return swphy_read_reg(reg_num, &state);
}
}
@@ -117,6 +127,7 @@ int fixed_phy_update_state(struct phy_de
list_for_each_entry(fp, &fmb->phys, node) {
if (fp->addr == phydev->addr) {
+ write_seqcount_begin(&fp->seqcount);
#define _UPD(x) if (changed->x) \
fp->status.x = status->x
_UPD(link);
@@ -126,6 +137,7 @@ int fixed_phy_update_state(struct phy_de
_UPD(asym_pause);
#undef _UPD
fixed_phy_update(fp);
+ write_seqcount_end(&fp->seqcount);
return 0;
}
}
@@ -150,6 +162,8 @@ int fixed_phy_add(unsigned int irq, int
if (!fp)
return -ENOMEM;
+ seqcount_init(&fp->seqcount);
+
fmb->irqs[phy_addr] = irq;
fp->addr = phy_addr;

@ -0,0 +1,183 @@
From d8b4e728f598d3c8a9b219d4679d5de350caa082 Mon Sep 17 00:00:00 2001
From: Russell King <rmk+kernel@arm.linux.org.uk>
Date: Fri, 18 Sep 2015 14:42:16 +0100
Subject: [PATCH 714/744] phy: provide a hook for link up/link down events
Sometimes, we need to do additional work between the PHY coming up and
marking the carrier present - for example, we may need to wait for the
PHY to MAC link to finish negotiation. This changes phylib to provide
a notification function pointer which avoids the built-in
netif_carrier_on() and netif_carrier_off() functions.
Standard ->adjust_link functionality is provided by hooking a helper
into the new ->phy_link_change method.
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
drivers/net/phy/phy.c | 42 ++++++++++++++++++++++--------------------
drivers/net/phy/phy_device.c | 14 ++++++++++++++
include/linux/phy.h | 1 +
3 files changed, 37 insertions(+), 20 deletions(-)
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -847,6 +847,16 @@ void phy_start(struct phy_device *phydev
}
EXPORT_SYMBOL(phy_start);
+static void phy_link_up(struct phy_device *phydev)
+{
+ phydev->phy_link_change(phydev, true, true);
+}
+
+static void phy_link_down(struct phy_device *phydev, bool do_carrier)
+{
+ phydev->phy_link_change(phydev, false, do_carrier);
+}
+
/**
* phy_state_machine - Handle the state machine
* @work: work_struct that describes the work to be done
@@ -888,8 +898,7 @@ void phy_state_machine(struct work_struc
/* If the link is down, give up on negotiation for now */
if (!phydev->link) {
phydev->state = PHY_NOLINK;
- netif_carrier_off(phydev->attached_dev);
- phydev->adjust_link(phydev->attached_dev);
+ phy_link_down(phydev, true);
break;
}
@@ -901,9 +910,7 @@ void phy_state_machine(struct work_struc
/* If AN is done, we're running */
if (err > 0) {
phydev->state = PHY_RUNNING;
- netif_carrier_on(phydev->attached_dev);
- phydev->adjust_link(phydev->attached_dev);
-
+ phy_link_up(phydev);
} else if (0 == phydev->link_timeout--)
needs_aneg = true;
break;
@@ -928,8 +935,7 @@ void phy_state_machine(struct work_struc
}
}
phydev->state = PHY_RUNNING;
- netif_carrier_on(phydev->attached_dev);
- phydev->adjust_link(phydev->attached_dev);
+ phy_link_up(phydev);
}
break;
case PHY_FORCING:
@@ -939,13 +945,12 @@ void phy_state_machine(struct work_struc
if (phydev->link) {
phydev->state = PHY_RUNNING;
- netif_carrier_on(phydev->attached_dev);
+ phy_link_up(phydev);
} else {
if (0 == phydev->link_timeout--)
needs_aneg = true;
+ phy_link_down(phydev, false);
}
-
- phydev->adjust_link(phydev->attached_dev);
break;
case PHY_RUNNING:
/* Only register a CHANGE if we are polling or ignoring
@@ -968,14 +973,12 @@ void phy_state_machine(struct work_struc
if (phydev->link) {
phydev->state = PHY_RUNNING;
- netif_carrier_on(phydev->attached_dev);
+ phy_link_up(phydev);
} else {
phydev->state = PHY_NOLINK;
- netif_carrier_off(phydev->attached_dev);
+ phy_link_down(phydev, true);
}
- phydev->adjust_link(phydev->attached_dev);
-
if (phy_interrupt_is_valid(phydev))
err = phy_config_interrupt(phydev,
PHY_INTERRUPT_ENABLED);
@@ -983,8 +986,7 @@ void phy_state_machine(struct work_struc
case PHY_HALTED:
if (phydev->link) {
phydev->link = 0;
- netif_carrier_off(phydev->attached_dev);
- phydev->adjust_link(phydev->attached_dev);
+ phy_link_down(phydev, true);
do_suspend = true;
}
break;
@@ -1004,11 +1006,11 @@ void phy_state_machine(struct work_struc
if (phydev->link) {
phydev->state = PHY_RUNNING;
- netif_carrier_on(phydev->attached_dev);
+ phy_link_up(phydev);
} else {
phydev->state = PHY_NOLINK;
+ phy_link_down(phydev, false);
}
- phydev->adjust_link(phydev->attached_dev);
} else {
phydev->state = PHY_AN;
phydev->link_timeout = PHY_AN_TIMEOUT;
@@ -1020,11 +1022,11 @@ void phy_state_machine(struct work_struc
if (phydev->link) {
phydev->state = PHY_RUNNING;
- netif_carrier_on(phydev->attached_dev);
+ phy_link_up(phydev);
} else {
phydev->state = PHY_NOLINK;
+ phy_link_down(phydev, false);
}
- phydev->adjust_link(phydev->attached_dev);
}
break;
}
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -441,6 +441,19 @@ struct phy_device *phy_find_first(struct
}
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
* @phydev: target phy_device struct
@@ -659,6 +672,7 @@ int phy_attach_direct(struct net_device
goto error;
}
+ phydev->phy_link_change = phy_link_change;
phydev->attached_dev = dev;
dev->phydev = phydev;
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -433,6 +433,7 @@ struct phy_device {
u8 mdix;
+ void (*phy_link_change)(struct phy_device *, bool up, bool do_carrier);
void (*adjust_link)(struct net_device *dev);
};
#define to_phy_device(d) container_of(d, struct phy_device, dev)

@ -0,0 +1,26 @@
From 5eed0bf3bc3e69b20a13d8ffcdf97cb720391637 Mon Sep 17 00:00:00 2001
From: Russell King <rmk+kernel@arm.linux.org.uk>
Date: Thu, 1 Oct 2015 00:34:08 +0100
Subject: [PATCH 735/744] phy: marvell: 88E1512: add flow control support
The Marvell PHYs support pause frame advertisments, so we should not be
masking their support off. Add the necessary flag to the Marvell PHY
to allow any MAC level pause frame support to be advertised.
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
drivers/net/phy/marvell.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/drivers/net/phy/marvell.c
+++ b/drivers/net/phy/marvell.c
@@ -1142,7 +1142,7 @@ static struct phy_driver marvell_drivers
.phy_id = MARVELL_PHY_ID_88E1510,
.phy_id_mask = MARVELL_PHY_ID_MASK,
.name = "Marvell 88E1510",
- .features = PHY_GBIT_FEATURES,
+ .features = PHY_GBIT_FEATURES | SUPPORTED_Pause,
.flags = PHY_HAS_INTERRUPT,
.config_aneg = &m88e1510_config_aneg,
.read_status = &marvell_read_status,

@ -0,0 +1,25 @@
From f2a9687b39cda3fb67ecd5eaa88e3545e78c982c Mon Sep 17 00:00:00 2001
From: Russell King <rmk+kernel@arm.linux.org.uk>
Date: Fri, 16 Oct 2015 12:18:41 +0100
Subject: [PATCH 715/744] phy: export phy_start_machine() for phylink
phylink will need phy_start_machine exported, so lets export it as a
GPL symbol. Documentation/networking/phy.txt indicates that this
should be a PHY API function.
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
drivers/net/phy/phy.c | 1 +
1 file changed, 1 insertion(+)
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -568,6 +568,7 @@ void phy_start_machine(struct phy_device
{
queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, HZ);
}
+EXPORT_SYMBOL_GPL(phy_start_machine);
/**
* phy_stop_machine - stop the PHY state machine tracking

@ -0,0 +1,44 @@
From 5c77cc2ffd5deb4762d9551409472f2441297fe7 Mon Sep 17 00:00:00 2001
From: Russell King <rmk+kernel@arm.linux.org.uk>
Date: Sun, 18 Oct 2015 19:51:10 +0100
Subject: [PATCH 716/744] phy: export phy_speed_to_str() for phylink
phylink would like to reuse phy_speed_to_str() to convert the speed
to a string. Add a prototype and export this helper function.
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
drivers/net/phy/phy.c | 3 ++-
include/linux/phy.h | 1 +
2 files changed, 3 insertions(+), 1 deletion(-)
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -38,7 +38,7 @@
#include <asm/irq.h>
-static const char *phy_speed_to_str(int speed)
+const char *phy_speed_to_str(int speed)
{
switch (speed) {
case SPEED_10:
@@ -57,6 +57,7 @@ static const char *phy_speed_to_str(int
return "Unsupported (update phy.c)";
}
}
+EXPORT_SYMBOL_GPL(phy_speed_to_str);
#define PHY_STATE_STR(_state) \
case PHY_##_state: \
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -812,6 +812,7 @@ int phy_ethtool_gset(struct phy_device *
int phy_ethtool_ioctl(struct phy_device *phydev, void *useraddr);
int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd);
int phy_start_interrupts(struct phy_device *phydev);
+const char *phy_speed_to_str(int speed);
void phy_print_status(struct phy_device *phydev);
void phy_device_free(struct phy_device *phydev);
int phy_set_max_speed(struct phy_device *phydev, u32 max_speed);

@ -0,0 +1,163 @@
From 7f36ac946bfbd4090b8b94be3661b41ac73e21f4 Mon Sep 17 00:00:00 2001
From: Russell King <rmk+kernel@arm.linux.org.uk>
Date: Fri, 25 Sep 2015 17:43:52 +0100
Subject: [PATCH 717/744] phy: add I2C mdio bus
Add an I2C MDIO bus bridge library, to allow phylib to access PHYs which
are connected to an I2C bus instead of the more conventional MDIO bus.
Such PHYs can be found in SFP adapters and SFF modules.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
drivers/net/phy/Kconfig | 10 ++++++
drivers/net/phy/Makefile | 1 +
drivers/net/phy/mdio-i2c.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++
drivers/net/phy/mdio-i2c.h | 19 ++++++++++
4 files changed, 120 insertions(+)
create mode 100644 drivers/net/phy/mdio-i2c.c
create mode 100644 drivers/net/phy/mdio-i2c.h
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -233,6 +233,16 @@ config MDIO_GPIO
To compile this driver as a module, choose M here: the module
will be called mdio-gpio.
+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_OCTEON
tristate "Support for MDIO buses on Octeon and ThunderX SOCs"
depends on 64BIT
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_SWCONFIG_B53) += b53/
obj-$(CONFIG_FIXED_PHY) += fixed_phy.o
obj-$(CONFIG_MDIO_BITBANG) += mdio-bitbang.o
obj-$(CONFIG_MDIO_GPIO) += mdio-gpio.o
+obj-$(CONFIG_MDIO_I2C) += mdio-i2c.o
obj-$(CONFIG_NATIONAL_PHY) += national.o
obj-$(CONFIG_DP83640_PHY) += dp83640.o
obj-$(CONFIG_DP83848_PHY) += dp83848.o
--- /dev/null
+++ b/drivers/net/phy/mdio-i2c.c
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+#include <linux/i2c.h>
+#include <linux/phy.h>
+
+#include "mdio-i2c.h"
+
+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;
+
+ bus_addr = 0x40 + phy_id;
+ if (bus_addr == 0x50 || bus_addr == 0x51)
+ return 0xffff;
+
+ 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 bus_addr, ret;
+ u8 data[3];
+
+ bus_addr = 0x40 + phy_id;
+ if (bus_addr == 0x50 || bus_addr == 0x51)
+ return 0;
+
+ data[0] = reg;
+ data[1] = val >> 8;
+ data[2] = val;
+
+ msg.addr = bus_addr;
+ 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");
--- /dev/null
+++ b/drivers/net/phy/mdio-i2c.h
@@ -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

@ -0,0 +1,156 @@
From 0a0c4b3dd4f34df4532f254a5940b520015d766f Mon Sep 17 00:00:00 2001
From: Russell King <rmk+kernel@arm.linux.org.uk>
Date: Thu, 24 Sep 2015 11:01:13 +0100
Subject: [PATCH 719/744] phylink: add hooks for SFP support
Add support to phylink for SFP, which needs to control and configure
the ethernet MAC link state. Specifically, SFP needs to:
1. set the negotiation mode between SGMII and 1000base-X
2. attach and detach the module PHY
3. prevent the link coming up when errors are reported
In the absence of a PHY, we also need to set the ethtool port type
according to the module plugged in.
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
drivers/net/phy/phylink.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++
include/linux/phylink.h | 6 ++++
2 files changed, 88 insertions(+)
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -11,6 +11,7 @@
#include <linux/ethtool.h>
#include <linux/export.h>
#include <linux/gpio/consumer.h>
+#include <linux/list.h>
#include <linux/netdevice.h>
#include <linux/of.h>
#include <linux/of_mdio.h>
@@ -29,11 +30,16 @@
(ADVERTISED_TP | ADVERTISED_MII | ADVERTISED_FIBRE | \
ADVERTISED_BNC | ADVERTISED_AUI | ADVERTISED_Backplane)
+static LIST_HEAD(phylinks);
+static DEFINE_MUTEX(phylink_mutex);
+
enum {
PHYLINK_DISABLE_STOPPED,
+ PHYLINK_DISABLE_LINK,
};
struct phylink {
+ struct list_head node;
struct net_device *netdev;
const struct phylink_mac_ops *ops;
struct mutex config_mutex;
@@ -341,12 +347,20 @@ struct phylink *phylink_create(struct ne
return ERR_PTR(ret);
}
+ mutex_lock(&phylink_mutex);
+ list_add_tail(&pl->node, &phylinks);
+ mutex_unlock(&phylink_mutex);
+
return pl;
}
EXPORT_SYMBOL_GPL(phylink_create);
void phylink_destroy(struct phylink *pl)
{
+ mutex_lock(&phylink_mutex);
+ list_del(&pl->node);
+ mutex_unlock(&phylink_mutex);
+
cancel_work_sync(&pl->resolve);
kfree(pl);
}
@@ -813,4 +827,72 @@ int phylink_mii_ioctl(struct phylink *pl
}
EXPORT_SYMBOL_GPL(phylink_mii_ioctl);
+
+
+void phylink_disable(struct phylink *pl)
+{
+ set_bit(PHYLINK_DISABLE_LINK, &pl->phylink_disable_state);
+ flush_work(&pl->resolve);
+
+ netif_carrier_off(pl->netdev);
+}
+EXPORT_SYMBOL_GPL(phylink_disable);
+
+void phylink_enable(struct phylink *pl)
+{
+ clear_bit(PHYLINK_DISABLE_LINK, &pl->phylink_disable_state);
+ phylink_run_resolve(pl);
+}
+EXPORT_SYMBOL_GPL(phylink_enable);
+
+void phylink_set_link_port(struct phylink *pl, u32 support, u8 port)
+{
+ WARN_ON(support & ~SUPPORTED_INTERFACES);
+
+ mutex_lock(&pl->config_mutex);
+ pl->link_port_support = support;
+ pl->link_port = port;
+ mutex_unlock(&pl->config_mutex);
+}
+EXPORT_SYMBOL_GPL(phylink_set_link_port);
+
+int phylink_set_link_an_mode(struct phylink *pl, unsigned int mode)
+{
+ int ret = 0;
+
+ mutex_lock(&pl->config_mutex);
+ if (pl->link_an_mode != mode) {
+ ret = phylink_get_support(pl, mode);
+ if (ret == 0) {
+ if (!test_bit(PHYLINK_DISABLE_STOPPED,
+ &pl->phylink_disable_state))
+ phylink_mac_config(pl, &pl->link_config);
+
+ netdev_info(pl->netdev, "switched to %s link mode\n",
+ phylink_an_mode_str(mode));
+ }
+ }
+ mutex_unlock(&pl->config_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phylink_set_link_an_mode);
+
+struct phylink *phylink_lookup_by_netdev(struct net_device *ndev)
+{
+ struct phylink *pl, *found = NULL;
+
+ mutex_lock(&phylink_mutex);
+ list_for_each_entry(pl, &phylinks, node)
+ if (pl->netdev == ndev) {
+ found = pl;
+ break;
+ }
+
+ mutex_unlock(&phylink_mutex);
+
+ return found;
+}
+EXPORT_SYMBOL_GPL(phylink_lookup_by_netdev);
+
MODULE_LICENSE("GPL");
--- a/include/linux/phylink.h
+++ b/include/linux/phylink.h
@@ -67,4 +67,10 @@ int phylink_ethtool_get_settings(struct
int phylink_ethtool_set_settings(struct phylink *, struct ethtool_cmd *);
int phylink_mii_ioctl(struct phylink *, struct ifreq *, int);
+void phylink_set_link_port(struct phylink *pl, u32 support, u8 port);
+int phylink_set_link_an_mode(struct phylink *pl, unsigned int mode);
+void phylink_disable(struct phylink *pl);
+void phylink_enable(struct phylink *pl);
+struct phylink *phylink_lookup_by_netdev(struct net_device *ndev);
+
#endif

@ -0,0 +1,283 @@
From 66c248886538d7ee97ef2fe498061f857d4c906a Mon Sep 17 00:00:00 2001
From: Russell King <rmk+kernel@arm.linux.org.uk>
Date: Sun, 13 Sep 2015 01:06:31 +0100
Subject: [PATCH 721/744] sfp: display SFP module information
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
drivers/net/phy/sfp.c | 247 +++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 246 insertions(+), 1 deletion(-)
--- a/drivers/net/phy/sfp.c
+++ b/drivers/net/phy/sfp.c
@@ -248,6 +248,182 @@ static unsigned int sfp_check(void *buf,
return check;
}
+static const char *sfp_link_len(char *buf, size_t size, unsigned int length,
+ unsigned int multiplier)
+{
+ if (length == 0)
+ return "unsupported/unspecified";
+
+ if (length == 255) {
+ *buf++ = '>';
+ size -= 1;
+ length -= 1;
+ }
+
+ length *= multiplier;
+
+ if (length >= 1000)
+ snprintf(buf, size, "%u.%0*ukm",
+ length / 1000,
+ multiplier > 100 ? 1 :
+ multiplier > 10 ? 2 : 3,
+ length % 1000);
+ else
+ snprintf(buf, size, "%um", length);
+
+ return buf;
+}
+
+struct bitfield {
+ unsigned int mask;
+ unsigned int val;
+ const char *str;
+};
+
+static const struct bitfield sfp_options[] = {
+ {
+ .mask = SFP_OPTIONS_HIGH_POWER_LEVEL,
+ .val = SFP_OPTIONS_HIGH_POWER_LEVEL,
+ .str = "hpl",
+ }, {
+ .mask = SFP_OPTIONS_PAGING_A2,
+ .val = SFP_OPTIONS_PAGING_A2,
+ .str = "paginga2",
+ }, {
+ .mask = SFP_OPTIONS_RETIMER,
+ .val = SFP_OPTIONS_RETIMER,
+ .str = "retimer",
+ }, {
+ .mask = SFP_OPTIONS_COOLED_XCVR,
+ .val = SFP_OPTIONS_COOLED_XCVR,
+ .str = "cooled",
+ }, {
+ .mask = SFP_OPTIONS_POWER_DECL,
+ .val = SFP_OPTIONS_POWER_DECL,
+ .str = "powerdecl",
+ }, {
+ .mask = SFP_OPTIONS_RX_LINEAR_OUT,
+ .val = SFP_OPTIONS_RX_LINEAR_OUT,
+ .str = "rxlinear",
+ }, {
+ .mask = SFP_OPTIONS_RX_DECISION_THRESH,
+ .val = SFP_OPTIONS_RX_DECISION_THRESH,
+ .str = "rxthresh",
+ }, {
+ .mask = SFP_OPTIONS_TUNABLE_TX,
+ .val = SFP_OPTIONS_TUNABLE_TX,
+ .str = "tunabletx",
+ }, {
+ .mask = SFP_OPTIONS_RATE_SELECT,
+ .val = SFP_OPTIONS_RATE_SELECT,
+ .str = "ratesel",
+ }, {
+ .mask = SFP_OPTIONS_TX_DISABLE,
+ .val = SFP_OPTIONS_TX_DISABLE,
+ .str = "txdisable",
+ }, {
+ .mask = SFP_OPTIONS_TX_FAULT,
+ .val = SFP_OPTIONS_TX_FAULT,
+ .str = "txfault",
+ }, {
+ .mask = SFP_OPTIONS_LOS_INVERTED,
+ .val = SFP_OPTIONS_LOS_INVERTED,
+ .str = "los-",
+ }, {
+ .mask = SFP_OPTIONS_LOS_NORMAL,
+ .val = SFP_OPTIONS_LOS_NORMAL,
+ .str = "los+",
+ }, { }
+};
+
+static const struct bitfield diagmon[] = {
+ {
+ .mask = SFP_DIAGMON_DDM,
+ .val = SFP_DIAGMON_DDM,
+ .str = "ddm",
+ }, {
+ .mask = SFP_DIAGMON_INT_CAL,
+ .val = SFP_DIAGMON_INT_CAL,
+ .str = "intcal",
+ }, {
+ .mask = SFP_DIAGMON_EXT_CAL,
+ .val = SFP_DIAGMON_EXT_CAL,
+ .str = "extcal",
+ }, {
+ .mask = SFP_DIAGMON_RXPWR_AVG,
+ .val = SFP_DIAGMON_RXPWR_AVG,
+ .str = "rxpwravg",
+ }, { }
+};
+
+static const char *sfp_bitfield(char *out, size_t outsz, const struct bitfield *bits, unsigned int val)
+{
+ char *p = out;
+ int n;
+
+ *p = '\0';
+ while (bits->mask) {
+ if ((val & bits->mask) == bits->val) {
+ n = snprintf(p, outsz, "%s%s",
+ out != p ? ", " : "",
+ bits->str);
+ if (n == outsz)
+ break;
+ p += n;
+ outsz -= n;
+ }
+ bits++;
+ }
+
+ return out;
+}
+
+static const char *sfp_connector(unsigned int connector)
+{
+ switch (connector) {
+ case SFP_CONNECTOR_UNSPEC:
+ return "unknown/unspecified";
+ case SFP_CONNECTOR_SC:
+ return "SC";
+ case SFP_CONNECTOR_FIBERJACK:
+ return "Fiberjack";
+ case SFP_CONNECTOR_LC:
+ return "LC";
+ case SFP_CONNECTOR_MT_RJ:
+ return "MT-RJ";
+ case SFP_CONNECTOR_MU:
+ return "MU";
+ case SFP_CONNECTOR_SG:
+ return "SG";
+ case SFP_CONNECTOR_OPTICAL_PIGTAIL:
+ return "Optical pigtail";
+ case SFP_CONNECTOR_HSSDC_II:
+ return "HSSDC II";
+ case SFP_CONNECTOR_COPPER_PIGTAIL:
+ return "Copper pigtail";
+ default:
+ return "unknown";
+ }
+}
+
+static const char *sfp_encoding(unsigned int encoding)
+{
+ switch (encoding) {
+ case SFP_ENCODING_UNSPEC:
+ return "unspecified";
+ case SFP_ENCODING_8B10B:
+ return "8b10b";
+ case SFP_ENCODING_4B5B:
+ return "4b5b";
+ case SFP_ENCODING_NRZ:
+ return "NRZ";
+ case SFP_ENCODING_MANCHESTER:
+ return "MANCHESTER";
+ default:
+ return "unknown";
+ }
+}
+
/* Helpers */
static void sfp_module_tx_disable(struct sfp *sfp)
{
@@ -426,6 +602,7 @@ static int sfp_sm_mod_probe(struct sfp *
char sn[17];
char date[9];
char rev[5];
+ char options[80];
u8 check;
int err;
@@ -462,10 +639,78 @@ static int sfp_sm_mod_probe(struct sfp *
rev[4] = '\0';
memcpy(sn, sfp->id.ext.vendor_sn, 16);
sn[16] = '\0';
- memcpy(date, sfp->id.ext.datecode, 8);
+ date[0] = sfp->id.ext.datecode[4];
+ date[1] = sfp->id.ext.datecode[5];
+ date[2] = '-';
+ date[3] = sfp->id.ext.datecode[2];
+ date[4] = sfp->id.ext.datecode[3];
+ date[5] = '-';
+ date[6] = sfp->id.ext.datecode[0];
+ date[7] = sfp->id.ext.datecode[1];
date[8] = '\0';
dev_info(sfp->dev, "module %s %s rev %s sn %s dc %s\n", vendor, part, rev, sn, date);
+ dev_info(sfp->dev, " %s connector, encoding %s, nominal bitrate %u.%uGbps +%u%% -%u%%\n",
+ sfp_connector(sfp->id.base.connector),
+ sfp_encoding(sfp->id.base.encoding),
+ sfp->id.base.br_nominal / 10,
+ sfp->id.base.br_nominal % 10,
+ sfp->id.ext.br_max, sfp->id.ext.br_min);
+ dev_info(sfp->dev, " 1000BaseSX%c 1000BaseLX%c 1000BaseCX%c 1000BaseT%c 100BaseTLX%c 1000BaseFX%c BaseBX10%c BasePX%c\n",
+ sfp->id.base.e1000_base_sx ? '+' : '-',
+ sfp->id.base.e1000_base_lx ? '+' : '-',
+ sfp->id.base.e1000_base_cx ? '+' : '-',
+ sfp->id.base.e1000_base_t ? '+' : '-',
+ sfp->id.base.e100_base_lx ? '+' : '-',
+ sfp->id.base.e100_base_fx ? '+' : '-',
+ sfp->id.base.e_base_bx10 ? '+' : '-',
+ sfp->id.base.e_base_px ? '+' : '-');
+
+ if (!sfp->id.base.sfp_ct_passive && !sfp->id.base.sfp_ct_active &&
+ !sfp->id.base.e1000_base_t) {
+ char len_9um[16], len_om[16];
+
+ dev_info(sfp->dev, " Wavelength %unm, fiber lengths:\n",
+ be16_to_cpup(&sfp->id.base.optical_wavelength));
+
+ if (sfp->id.base.link_len[0] == 255)
+ strcpy(len_9um, ">254km");
+ else if (sfp->id.base.link_len[1] && sfp->id.base.link_len[1] != 255)
+ sprintf(len_9um, "%um",
+ sfp->id.base.link_len[1] * 100);
+ else if (sfp->id.base.link_len[0])
+ sprintf(len_9um, "%ukm", sfp->id.base.link_len[0]);
+ else if (sfp->id.base.link_len[1] == 255)
+ strcpy(len_9um, ">25.4km");
+ else
+ strcpy(len_9um, "unsupported");
+
+ dev_info(sfp->dev, " 9µm SM : %s\n", len_9um);
+ dev_info(sfp->dev, " 62.5µm MM OM1: %s\n",
+ sfp_link_len(len_om, sizeof(len_om),
+ sfp->id.base.link_len[3], 10));
+ dev_info(sfp->dev, " 50µm MM OM2: %s\n",
+ sfp_link_len(len_om, sizeof(len_om),
+ sfp->id.base.link_len[2], 10));
+ dev_info(sfp->dev, " 50µm MM OM3: %s\n",
+ sfp_link_len(len_om, sizeof(len_om),
+ sfp->id.base.link_len[5], 10));
+ dev_info(sfp->dev, " 50µm MM OM4: %s\n",
+ sfp_link_len(len_om, sizeof(len_om),
+ sfp->id.base.link_len[4], 10));
+ } else {
+ char len[16];
+ dev_info(sfp->dev, " Copper length: %s\n",
+ sfp_link_len(len, sizeof(len),
+ sfp->id.base.link_len[4], 1));
+ }
+
+ dev_info(sfp->dev, " Options: %s\n",
+ sfp_bitfield(options, sizeof(options), sfp_options,
+ be16_to_cpu(sfp->id.ext.options)));
+ dev_info(sfp->dev, " Diagnostics: %s\n",
+ sfp_bitfield(options, sizeof(options), diagmon,
+ sfp->id.ext.diagmon));
/* We only support SFP modules, not the legacy GBIC modules. */
if (sfp->id.base.phys_id != SFP_PHYS_ID_SFP ||

@ -0,0 +1,697 @@
From e268be0ddc666f4a98db462cbed2a97637e82b5c Mon Sep 17 00:00:00 2001
From: Russell King <rmk+kernel@arm.linux.org.uk>
Date: Wed, 16 Sep 2015 21:27:10 +0100
Subject: [PATCH 722/744] net: mvneta: convert to phylink
Convert mvneta to use phylink, which models the MAC to PHY link in
a generic, reusable form.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
drivers/net/ethernet/marvell/Kconfig | 2 +-
drivers/net/ethernet/marvell/mvneta.c | 451 +++++++++++++++++-----------------
2 files changed, 227 insertions(+), 226 deletions(-)
--- a/drivers/net/ethernet/marvell/Kconfig
+++ b/drivers/net/ethernet/marvell/Kconfig
@@ -58,7 +58,7 @@ config MVNETA
tristate "Marvell Armada 370/38x/XP network interface support"
depends on PLAT_ORION
select MVMDIO
- select FIXED_PHY
+ select PHYLINK
---help---
This driver supports the network interface units in the
Marvell ARMADA XP, ARMADA 370 and ARMADA 38x SoC family.
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -28,6 +28,7 @@
#include <linux/of_mdio.h>
#include <linux/of_net.h>
#include <linux/phy.h>
+#include <linux/phylink.h>
#include <linux/platform_device.h>
#include <linux/skbuff.h>
#include <net/hwbm.h>
@@ -188,6 +189,7 @@
#define MVNETA_GMAC_CTRL_0 0x2c00
#define MVNETA_GMAC_MAX_RX_SIZE_SHIFT 2
#define MVNETA_GMAC_MAX_RX_SIZE_MASK 0x7ffc
+#define MVNETA_GMAC0_PORT_1000BASE_X BIT(1)
#define MVNETA_GMAC0_PORT_ENABLE BIT(0)
#define MVNETA_GMAC_CTRL_2 0x2c08
#define MVNETA_GMAC2_INBAND_AN_ENABLE BIT(0)
@@ -203,13 +205,19 @@
#define MVNETA_GMAC_TX_FLOW_CTRL_ENABLE BIT(5)
#define MVNETA_GMAC_RX_FLOW_CTRL_ACTIVE BIT(6)
#define MVNETA_GMAC_TX_FLOW_CTRL_ACTIVE BIT(7)
+#define MVNETA_GMAC_AN_COMPLETE BIT(11)
+#define MVNETA_GMAC_SYNC_OK BIT(14)
#define MVNETA_GMAC_AUTONEG_CONFIG 0x2c0c
#define MVNETA_GMAC_FORCE_LINK_DOWN BIT(0)
#define MVNETA_GMAC_FORCE_LINK_PASS BIT(1)
#define MVNETA_GMAC_INBAND_AN_ENABLE BIT(2)
+#define MVNETA_GMAC_AN_BYPASS_ENABLE BIT(3)
+#define MVNETA_GMAC_INBAND_RESTART_AN BIT(4)
#define MVNETA_GMAC_CONFIG_MII_SPEED BIT(5)
#define MVNETA_GMAC_CONFIG_GMII_SPEED BIT(6)
#define MVNETA_GMAC_AN_SPEED_EN BIT(7)
+#define MVNETA_GMAC_CONFIG_FLOW_CTRL BIT(8)
+#define MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL BIT(9)
#define MVNETA_GMAC_AN_FLOW_CTRL_EN BIT(11)
#define MVNETA_GMAC_CONFIG_FULL_DUPLEX BIT(12)
#define MVNETA_GMAC_AN_DUPLEX_EN BIT(13)
@@ -396,15 +404,9 @@ struct mvneta_port {
u16 tx_ring_size;
u16 rx_ring_size;
- struct mii_bus *mii_bus;
- struct phy_device *phy_dev;
- phy_interface_t phy_interface;
- struct device_node *phy_node;
- unsigned int link;
- unsigned int duplex;
- unsigned int speed;
+ struct device_node *dn;
unsigned int tx_csum_limit;
- unsigned int use_inband_status:1;
+ struct phylink *phylink;
struct mvneta_bm *bm_priv;
struct mvneta_bm_pool *pool_long;
@@ -1236,44 +1238,6 @@ static void mvneta_set_other_mcast_table
mvreg_write(pp, MVNETA_DA_FILT_OTH_MCAST + offset, val);
}
-static void mvneta_set_autoneg(struct mvneta_port *pp, int enable)
-{
- u32 val;
-
- if (enable) {
- val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
- val &= ~(MVNETA_GMAC_FORCE_LINK_PASS |
- MVNETA_GMAC_FORCE_LINK_DOWN |
- MVNETA_GMAC_AN_FLOW_CTRL_EN);
- val |= MVNETA_GMAC_INBAND_AN_ENABLE |
- MVNETA_GMAC_AN_SPEED_EN |
- MVNETA_GMAC_AN_DUPLEX_EN;
- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
-
- val = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER);
- val |= MVNETA_GMAC_1MS_CLOCK_ENABLE;
- mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, val);
-
- val = mvreg_read(pp, MVNETA_GMAC_CTRL_2);
- val |= MVNETA_GMAC2_INBAND_AN_ENABLE;
- mvreg_write(pp, MVNETA_GMAC_CTRL_2, val);
- } else {
- val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
- val &= ~(MVNETA_GMAC_INBAND_AN_ENABLE |
- MVNETA_GMAC_AN_SPEED_EN |
- MVNETA_GMAC_AN_DUPLEX_EN);
- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
-
- val = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER);
- val &= ~MVNETA_GMAC_1MS_CLOCK_ENABLE;
- mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, val);
-
- val = mvreg_read(pp, MVNETA_GMAC_CTRL_2);
- val &= ~MVNETA_GMAC2_INBAND_AN_ENABLE;
- mvreg_write(pp, MVNETA_GMAC_CTRL_2, val);
- }
-}
-
static void mvneta_percpu_unmask_interrupt(void *arg)
{
struct mvneta_port *pp = arg;
@@ -1421,7 +1385,6 @@ static void mvneta_defaults_set(struct m
val &= ~MVNETA_PHY_POLLING_ENABLE;
mvreg_write(pp, MVNETA_UNIT_CONTROL, val);
- mvneta_set_autoneg(pp, pp->use_inband_status);
mvneta_set_ucast_table(pp, -1);
mvneta_set_special_mcast_table(pp, -1);
mvneta_set_other_mcast_table(pp, -1);
@@ -2614,26 +2577,11 @@ static irqreturn_t mvneta_isr(int irq, v
return IRQ_HANDLED;
}
-static int mvneta_fixed_link_update(struct mvneta_port *pp,
- struct phy_device *phy)
+static void mvneta_link_change(struct mvneta_port *pp)
{
- struct fixed_phy_status status;
- struct fixed_phy_status changed = {};
u32 gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS);
- status.link = !!(gmac_stat & MVNETA_GMAC_LINK_UP);
- if (gmac_stat & MVNETA_GMAC_SPEED_1000)
- status.speed = SPEED_1000;
- else if (gmac_stat & MVNETA_GMAC_SPEED_100)
- status.speed = SPEED_100;
- else
- status.speed = SPEED_10;
- status.duplex = !!(gmac_stat & MVNETA_GMAC_FULL_DUPLEX);
- changed.link = 1;
- changed.speed = 1;
- changed.duplex = 1;
- fixed_phy_update_state(phy, &status, &changed);
- return 0;
+ phylink_mac_change(pp->phylink, !!(gmac_stat & MVNETA_GMAC_LINK_UP));
}
/* NAPI handler
@@ -2662,12 +2610,11 @@ static int mvneta_poll(struct napi_struc
u32 cause_misc = mvreg_read(pp, MVNETA_INTR_MISC_CAUSE);
mvreg_write(pp, MVNETA_INTR_MISC_CAUSE, 0);
- if (pp->use_inband_status && (cause_misc &
- (MVNETA_CAUSE_PHY_STATUS_CHANGE |
- MVNETA_CAUSE_LINK_CHANGE |
- MVNETA_CAUSE_PSC_SYNC_CHANGE))) {
- mvneta_fixed_link_update(pp, pp->phy_dev);
- }
+
+ if (cause_misc & (MVNETA_CAUSE_PHY_STATUS_CHANGE |
+ MVNETA_CAUSE_LINK_CHANGE |
+ MVNETA_CAUSE_PSC_SYNC_CHANGE))
+ mvneta_link_change(pp);
}
/* Release Tx descriptors */
@@ -2983,7 +2930,7 @@ static void mvneta_start_dev(struct mvne
MVNETA_CAUSE_LINK_CHANGE |
MVNETA_CAUSE_PSC_SYNC_CHANGE);
- phy_start(pp->phy_dev);
+ phylink_start(pp->phylink);
netif_tx_start_all_queues(pp->dev);
}
@@ -2991,7 +2938,7 @@ static void mvneta_stop_dev(struct mvnet
{
unsigned int cpu;
- phy_stop(pp->phy_dev);
+ phylink_stop(pp->phylink);
for_each_online_cpu(cpu) {
struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu);
@@ -3161,99 +3108,219 @@ static int mvneta_set_mac_addr(struct ne
return 0;
}
-static void mvneta_adjust_link(struct net_device *ndev)
+static int mvneta_mac_support(struct net_device *ndev, unsigned int mode,
+ struct phylink_link_state *state)
+{
+ switch (mode) {
+ case MLO_AN_8023Z:
+ state->supported = SUPPORTED_1000baseT_Full |
+ SUPPORTED_Autoneg | SUPPORTED_Pause;
+ state->advertising = ADVERTISED_1000baseT_Full |
+ ADVERTISED_Autoneg | ADVERTISED_Pause;
+ state->an_enabled = 1;
+ break;
+
+ case MLO_AN_FIXED:
+ break;
+
+ default:
+ state->supported = PHY_10BT_FEATURES |
+ PHY_100BT_FEATURES |
+ SUPPORTED_1000baseT_Full |
+ SUPPORTED_Autoneg;
+ state->advertising = ADVERTISED_10baseT_Half |
+ ADVERTISED_10baseT_Full |
+ ADVERTISED_100baseT_Half |
+ ADVERTISED_100baseT_Full |
+ ADVERTISED_1000baseT_Full |
+ ADVERTISED_Autoneg;
+ state->an_enabled = 1;
+ break;
+ }
+ return 0;
+}
+
+static int mvneta_mac_link_state(struct net_device *ndev,
+ struct phylink_link_state *state)
{
struct mvneta_port *pp = netdev_priv(ndev);
- struct phy_device *phydev = pp->phy_dev;
- int status_change = 0;
+ u32 gmac_stat;
- if (phydev->link) {
- if ((pp->speed != phydev->speed) ||
- (pp->duplex != phydev->duplex)) {
- u32 val;
-
- val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
- val &= ~(MVNETA_GMAC_CONFIG_MII_SPEED |
- MVNETA_GMAC_CONFIG_GMII_SPEED |
- MVNETA_GMAC_CONFIG_FULL_DUPLEX);
-
- if (phydev->duplex)
- val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX;
-
- if (phydev->speed == SPEED_1000)
- val |= MVNETA_GMAC_CONFIG_GMII_SPEED;
- else if (phydev->speed == SPEED_100)
- val |= MVNETA_GMAC_CONFIG_MII_SPEED;
+ gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS);
- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
+ if (gmac_stat & MVNETA_GMAC_SPEED_1000)
+ state->speed = SPEED_1000;
+ else if (gmac_stat & MVNETA_GMAC_SPEED_100)
+ state->speed = SPEED_100;
+ else
+ state->speed = SPEED_10;
- pp->duplex = phydev->duplex;
- pp->speed = phydev->speed;
- }
+ state->an_complete = !!(gmac_stat & MVNETA_GMAC_AN_COMPLETE);
+ state->sync = !!(gmac_stat & MVNETA_GMAC_SYNC_OK);
+ state->link = !!(gmac_stat & MVNETA_GMAC_LINK_UP);
+ state->duplex = !!(gmac_stat & MVNETA_GMAC_FULL_DUPLEX);
+
+ state->pause = 0;
+ if (gmac_stat & MVNETA_GMAC_RX_FLOW_CTRL_ENABLE)
+ state->pause |= MLO_PAUSE_RX;
+ if (gmac_stat & MVNETA_GMAC_TX_FLOW_CTRL_ENABLE)
+ state->pause |= MLO_PAUSE_TX;
+
+ return 1;
+}
+
+static void mvneta_mac_an_restart(struct net_device *ndev, unsigned int mode)
+{
+ struct mvneta_port *pp = netdev_priv(ndev);
+
+ if (mode == MLO_AN_8023Z) {
+ u32 gmac_an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
+
+ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG,
+ gmac_an | MVNETA_GMAC_INBAND_RESTART_AN);
+ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG,
+ gmac_an & ~MVNETA_GMAC_INBAND_RESTART_AN);
}
+}
- if (phydev->link != pp->link) {
- if (!phydev->link) {
- pp->duplex = -1;
- pp->speed = 0;
- }
+static void mvneta_mac_config(struct net_device *ndev, unsigned int mode,
+ const struct phylink_link_state *state)
+{
+ struct mvneta_port *pp = netdev_priv(ndev);
+ u32 new_ctrl0, gmac_ctrl0 = mvreg_read(pp, MVNETA_GMAC_CTRL_0);
+ u32 new_ctrl2, gmac_ctrl2 = mvreg_read(pp, MVNETA_GMAC_CTRL_2);
+ u32 new_clk, gmac_clk = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER);
+ u32 new_an, gmac_an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
+
+ new_ctrl0 = gmac_ctrl0 & ~MVNETA_GMAC0_PORT_1000BASE_X;
+ new_ctrl2 = gmac_ctrl2 & ~MVNETA_GMAC2_INBAND_AN_ENABLE;
+ new_clk = gmac_clk & ~MVNETA_GMAC_1MS_CLOCK_ENABLE;
+ new_an = gmac_an & ~(MVNETA_GMAC_INBAND_AN_ENABLE |
+ MVNETA_GMAC_INBAND_RESTART_AN |
+ MVNETA_GMAC_CONFIG_MII_SPEED |
+ MVNETA_GMAC_CONFIG_GMII_SPEED |
+ MVNETA_GMAC_AN_SPEED_EN |
+ MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL |
+ MVNETA_GMAC_CONFIG_FLOW_CTRL |
+ MVNETA_GMAC_AN_FLOW_CTRL_EN |
+ MVNETA_GMAC_CONFIG_FULL_DUPLEX |
+ MVNETA_GMAC_AN_DUPLEX_EN);
+
+ if (state->advertising & ADVERTISED_Pause)
+ new_an |= MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL;
+
+ switch (mode) {
+ case MLO_AN_SGMII:
+ /* SGMII mode receives the state from the PHY */
+ new_ctrl2 |= MVNETA_GMAC2_INBAND_AN_ENABLE;
+ new_clk |= MVNETA_GMAC_1MS_CLOCK_ENABLE;
+ new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN |
+ MVNETA_GMAC_FORCE_LINK_PASS)) |
+ MVNETA_GMAC_INBAND_AN_ENABLE |
+ MVNETA_GMAC_AN_SPEED_EN |
+ MVNETA_GMAC_AN_DUPLEX_EN;
+ break;
+
+ case MLO_AN_8023Z:
+ /* 802.3z negotiation - only 1000base-X */
+ new_ctrl0 |= MVNETA_GMAC0_PORT_1000BASE_X;
+ new_clk |= MVNETA_GMAC_1MS_CLOCK_ENABLE;
+ new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN |
+ MVNETA_GMAC_FORCE_LINK_PASS)) |
+ MVNETA_GMAC_INBAND_AN_ENABLE |
+ MVNETA_GMAC_CONFIG_GMII_SPEED |
+ /* The MAC only supports FD mode */
+ MVNETA_GMAC_CONFIG_FULL_DUPLEX;
+
+ if (state->an_enabled)
+ new_an |= MVNETA_GMAC_AN_FLOW_CTRL_EN;
+ break;
- pp->link = phydev->link;
- status_change = 1;
+ default:
+ /* Phy or fixed speed */
+ if (state->duplex)
+ new_an |= MVNETA_GMAC_CONFIG_FULL_DUPLEX;
+
+ if (state->speed == SPEED_1000)
+ new_an |= MVNETA_GMAC_CONFIG_GMII_SPEED;
+ else if (state->speed == SPEED_100)
+ new_an |= MVNETA_GMAC_CONFIG_MII_SPEED;
+ break;
}
- if (status_change) {
- if (phydev->link) {
- if (!pp->use_inband_status) {
- u32 val = mvreg_read(pp,
- MVNETA_GMAC_AUTONEG_CONFIG);
- val &= ~MVNETA_GMAC_FORCE_LINK_DOWN;
- val |= MVNETA_GMAC_FORCE_LINK_PASS;
- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG,
- val);
- }
- mvneta_port_up(pp);
- } else {
- if (!pp->use_inband_status) {
- u32 val = mvreg_read(pp,
- MVNETA_GMAC_AUTONEG_CONFIG);
- val &= ~MVNETA_GMAC_FORCE_LINK_PASS;
- val |= MVNETA_GMAC_FORCE_LINK_DOWN;
- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG,
- val);
- }
- mvneta_port_down(pp);
- }
- phy_print_status(phydev);
+ /* Armada 370 documentation says we can only change the port mode
+ * and in-band enable when the link is down, so force it down
+ * while making these changes. We also do this for GMAC_CTRL2 */
+ if ((new_ctrl0 ^ gmac_ctrl0) & MVNETA_GMAC0_PORT_1000BASE_X ||
+ (new_ctrl2 ^ gmac_ctrl2) & MVNETA_GMAC2_INBAND_AN_ENABLE ||
+ (new_an ^ gmac_an) & MVNETA_GMAC_INBAND_AN_ENABLE) {
+ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG,
+ (gmac_an & ~MVNETA_GMAC_FORCE_LINK_PASS) |
+ MVNETA_GMAC_FORCE_LINK_DOWN);
+ }
+
+ if (new_ctrl0 != gmac_ctrl0)
+ mvreg_write(pp, MVNETA_GMAC_CTRL_0, new_ctrl0);
+ if (new_ctrl2 != gmac_ctrl2)
+ mvreg_write(pp, MVNETA_GMAC_CTRL_2, new_ctrl2);
+ if (new_clk != gmac_clk)
+ mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, new_clk);
+ if (new_an != gmac_an)
+ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, new_an);
+}
+
+static void mvneta_mac_link_down(struct net_device *ndev, unsigned int mode)
+{
+ struct mvneta_port *pp = netdev_priv(ndev);
+ u32 val;
+
+ mvneta_port_down(pp);
+
+ if (mode == MLO_AN_PHY || mode == MLO_AN_FIXED) {
+ val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
+ val &= ~MVNETA_GMAC_FORCE_LINK_PASS;
+ val |= MVNETA_GMAC_FORCE_LINK_DOWN;
+ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
}
}
-static int mvneta_mdio_probe(struct mvneta_port *pp)
+static void mvneta_mac_link_up(struct net_device *ndev, unsigned int mode,
+ struct phy_device *phy)
{
- struct phy_device *phy_dev;
+ struct mvneta_port *pp = netdev_priv(ndev);
+ u32 val;
+
+ if (mode == MLO_AN_PHY || mode == MLO_AN_FIXED) {
+ val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
+ val &= ~MVNETA_GMAC_FORCE_LINK_DOWN;
+ val |= MVNETA_GMAC_FORCE_LINK_PASS;
+ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
+ }
- phy_dev = of_phy_connect(pp->dev, pp->phy_node, mvneta_adjust_link, 0,
- pp->phy_interface);
- if (!phy_dev) {
- netdev_err(pp->dev, "could not find the PHY\n");
- return -ENODEV;
- }
-
- phy_dev->supported &= PHY_GBIT_FEATURES;
- phy_dev->advertising = phy_dev->supported;
-
- pp->phy_dev = phy_dev;
- pp->link = 0;
- pp->duplex = 0;
- pp->speed = 0;
+ mvneta_port_up(pp);
+}
- return 0;
+static const struct phylink_mac_ops mvneta_phylink_ops = {
+ .mac_get_support = mvneta_mac_support,
+ .mac_link_state = mvneta_mac_link_state,
+ .mac_an_restart = mvneta_mac_an_restart,
+ .mac_config = mvneta_mac_config,
+ .mac_link_down = mvneta_mac_link_down,
+ .mac_link_up = mvneta_mac_link_up,
+};
+
+static int mvneta_mdio_probe(struct mvneta_port *pp)
+{
+ int err = phylink_of_phy_connect(pp->phylink, pp->dn);
+ if (err)
+ netdev_err(pp->dev, "could not attach PHY\n");
+
+ return err;
}
static void mvneta_mdio_remove(struct mvneta_port *pp)
{
- phy_disconnect(pp->phy_dev);
- pp->phy_dev = NULL;
+ phylink_disconnect_phy(pp->phylink);
}
/* Electing a CPU must be done in an atomic way: it should be done
@@ -3501,10 +3568,7 @@ static int mvneta_ioctl(struct net_devic
{
struct mvneta_port *pp = netdev_priv(dev);
- if (!pp->phy_dev)
- return -ENOTSUPP;
-
- return phy_mii_ioctl(pp->phy_dev, ifr, cmd);
+ return phylink_mii_ioctl(pp->phylink, ifr, cmd);
}
/* Ethtool methods */
@@ -3514,54 +3578,15 @@ int mvneta_ethtool_get_settings(struct n
{
struct mvneta_port *pp = netdev_priv(dev);
- if (!pp->phy_dev)
- return -ENODEV;
-
- return phy_ethtool_gset(pp->phy_dev, cmd);
+ return phylink_ethtool_get_settings(pp->phylink, cmd);
}
/* Set settings (phy address, speed) for ethtools */
int mvneta_ethtool_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct mvneta_port *pp = netdev_priv(dev);
- struct phy_device *phydev = pp->phy_dev;
-
- if (!phydev)
- return -ENODEV;
- if ((cmd->autoneg == AUTONEG_ENABLE) != pp->use_inband_status) {
- u32 val;
-
- mvneta_set_autoneg(pp, cmd->autoneg == AUTONEG_ENABLE);
-
- if (cmd->autoneg == AUTONEG_DISABLE) {
- val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
- val &= ~(MVNETA_GMAC_CONFIG_MII_SPEED |
- MVNETA_GMAC_CONFIG_GMII_SPEED |
- MVNETA_GMAC_CONFIG_FULL_DUPLEX);
-
- if (phydev->duplex)
- val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX;
-
- if (phydev->speed == SPEED_1000)
- val |= MVNETA_GMAC_CONFIG_GMII_SPEED;
- else if (phydev->speed == SPEED_100)
- val |= MVNETA_GMAC_CONFIG_MII_SPEED;
-
- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
- }
-
- pp->use_inband_status = (cmd->autoneg == AUTONEG_ENABLE);
- netdev_info(pp->dev, "autoneg status set to %i\n",
- pp->use_inband_status);
-
- if (netif_running(dev)) {
- mvneta_port_down(pp);
- mvneta_port_up(pp);
- }
- }
-
- return phy_ethtool_sset(pp->phy_dev, cmd);
+ return phylink_ethtool_set_settings(pp->phylink, cmd);
}
/* Set interrupt coalescing for ethtools */
@@ -3669,7 +3694,8 @@ static void mvneta_ethtool_update_stats(
{
const struct mvneta_statistic *s;
void __iomem *base = pp->base;
- u32 high, low, val;
+ u32 high, low;
+ u64 val;
u64 val64;
int i;
@@ -3964,14 +3990,13 @@ static int mvneta_probe(struct platform_
const struct mbus_dram_target_info *dram_target_info;
struct resource *res;
struct device_node *dn = pdev->dev.of_node;
- struct device_node *phy_node;
struct device_node *bm_node;
struct mvneta_port *pp;
struct net_device *dev;
+ struct phylink *phylink;
const char *dt_mac_addr;
char hw_mac_addr[ETH_ALEN];
const char *mac_from;
- const char *managed;
int tx_csum_limit;
int phy_mode;
int err;
@@ -3987,31 +4012,11 @@ static int mvneta_probe(struct platform_
goto err_free_netdev;
}
- phy_node = of_parse_phandle(dn, "phy", 0);
- if (!phy_node) {
- if (!of_phy_is_fixed_link(dn)) {
- dev_err(&pdev->dev, "no PHY specified\n");
- err = -ENODEV;
- goto err_free_irq;
- }
-
- err = of_phy_register_fixed_link(dn);
- if (err < 0) {
- dev_err(&pdev->dev, "cannot register fixed PHY\n");
- goto err_free_irq;
- }
-
- /* In the case of a fixed PHY, the DT node associated
- * to the PHY is the Ethernet MAC DT node.
- */
- phy_node = of_node_get(dn);
- }
-
phy_mode = of_get_phy_mode(dn);
if (phy_mode < 0) {
dev_err(&pdev->dev, "incorrect phy-mode\n");
err = -EINVAL;
- goto err_put_phy_node;
+ goto err_free_irq;
}
dev->tx_queue_len = MVNETA_MAX_TXD;
@@ -4022,12 +4027,7 @@ static int mvneta_probe(struct platform_
pp = netdev_priv(dev);
spin_lock_init(&pp->lock);
- pp->phy_node = phy_node;
- pp->phy_interface = phy_mode;
-
- err = of_property_read_string(dn, "managed", &managed);
- pp->use_inband_status = (err == 0 &&
- strcmp(managed, "in-band-status") == 0);
+ pp->dn = dn;
pp->cpu_notifier.notifier_call = mvneta_percpu_notifier;
pp->rxq_def = rxq_def;
@@ -4037,7 +4037,7 @@ static int mvneta_probe(struct platform_
pp->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(pp->clk)) {
err = PTR_ERR(pp->clk);
- goto err_put_phy_node;
+ goto err_free_irq;
}
clk_prepare_enable(pp->clk);
@@ -4140,6 +4140,14 @@ static int mvneta_probe(struct platform_
dev->priv_flags |= IFF_UNICAST_FLT | IFF_LIVE_ADDR_CHANGE;
dev->gso_max_segs = MVNETA_MAX_TSO_SEGS;
+ phylink = phylink_create(dev, dn, phy_mode, &mvneta_phylink_ops);
+ if (IS_ERR(phylink)) {
+ err = PTR_ERR(phylink);
+ goto err_free_stats;
+ }
+
+ pp->phylink = phylink;
+
err = register_netdev(dev);
if (err < 0) {
dev_err(&pdev->dev, "failed to register\n");
@@ -4151,13 +4159,6 @@ static int mvneta_probe(struct platform_
platform_set_drvdata(pdev, pp->dev);
- if (pp->use_inband_status) {
- struct phy_device *phy = of_phy_find_device(dn);
-
- mvneta_fixed_link_update(pp, phy);
-
- put_device(&phy->dev);
- }
return 0;
@@ -4169,13 +4170,13 @@ err_netdev:
1 << pp->id);
}
err_free_stats:
+ if (pp->phylink)
+ phylink_destroy(pp->phylink);
free_percpu(pp->stats);
err_free_ports:
free_percpu(pp->ports);
err_clk:
clk_disable_unprepare(pp->clk);
-err_put_phy_node:
- of_node_put(phy_node);
err_free_irq:
irq_dispose_mapping(dev->irq);
err_free_netdev:
@@ -4194,7 +4195,7 @@ static int mvneta_remove(struct platform
free_percpu(pp->ports);
free_percpu(pp->stats);
irq_dispose_mapping(dev->irq);
- of_node_put(pp->phy_node);
+ phylink_destroy(pp->phylink);
free_netdev(dev);
if (pp->bm_priv) {

@ -0,0 +1,80 @@
From 9be436bdb67c1f4aa9f33f2477f94e1f58a0ff02 Mon Sep 17 00:00:00 2001
From: Russell King <rmk+kernel@arm.linux.org.uk>
Date: Fri, 2 Oct 2015 22:46:54 +0100
Subject: [PATCH 723/744] phy: fixed-phy: remove fixed_phy_update_state()
mvneta is the only user of fixed_phy_update_state(), which has been
converted to use phylink instead. Remove fixed_phy_update_state().
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
drivers/net/phy/fixed_phy.c | 31 -------------------------------
include/linux/phy_fixed.h | 9 ---------
2 files changed, 40 deletions(-)
--- a/drivers/net/phy/fixed_phy.c
+++ b/drivers/net/phy/fixed_phy.c
@@ -115,37 +115,6 @@ int fixed_phy_set_link_update(struct phy
}
EXPORT_SYMBOL_GPL(fixed_phy_set_link_update);
-int fixed_phy_update_state(struct phy_device *phydev,
- const struct fixed_phy_status *status,
- const struct fixed_phy_status *changed)
-{
- struct fixed_mdio_bus *fmb = &platform_fmb;
- struct fixed_phy *fp;
-
- if (!phydev || phydev->bus != fmb->mii_bus)
- return -EINVAL;
-
- list_for_each_entry(fp, &fmb->phys, node) {
- if (fp->addr == phydev->addr) {
- write_seqcount_begin(&fp->seqcount);
-#define _UPD(x) if (changed->x) \
- fp->status.x = status->x
- _UPD(link);
- _UPD(speed);
- _UPD(duplex);
- _UPD(pause);
- _UPD(asym_pause);
-#undef _UPD
- fixed_phy_update(fp);
- write_seqcount_end(&fp->seqcount);
- return 0;
- }
- }
-
- return -ENOENT;
-}
-EXPORT_SYMBOL(fixed_phy_update_state);
-
int fixed_phy_add(unsigned int irq, int phy_addr,
struct fixed_phy_status *status,
int link_gpio)
--- a/include/linux/phy_fixed.h
+++ b/include/linux/phy_fixed.h
@@ -23,9 +23,6 @@ extern void fixed_phy_del(int phy_addr);
extern int fixed_phy_set_link_update(struct phy_device *phydev,
int (*link_update)(struct net_device *,
struct fixed_phy_status *));
-extern int fixed_phy_update_state(struct phy_device *phydev,
- const struct fixed_phy_status *status,
- const struct fixed_phy_status *changed);
#else
static inline int fixed_phy_add(unsigned int irq, int phy_id,
struct fixed_phy_status *status,
@@ -50,12 +47,6 @@ static inline int fixed_phy_set_link_upd
{
return -ENODEV;
}
-static inline int fixed_phy_update_state(struct phy_device *phydev,
- const struct fixed_phy_status *status,
- const struct fixed_phy_status *changed)
-{
- return -ENODEV;
-}
#endif /* CONFIG_FIXED_PHY */
#endif /* __PHY_FIXED_H */

@ -0,0 +1,48 @@
From d6bd25b692378ec17bdb1023d398c03c45829947 Mon Sep 17 00:00:00 2001
From: Russell King <rmk+kernel@arm.linux.org.uk>
Date: Thu, 1 Oct 2015 20:27:19 +0100
Subject: [PATCH 724/744] phylink: add ethtool nway_reset support
Add ethtool nway_reset support to phylink, to allow userspace to
request a re-negotiation of the link.
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
drivers/net/phy/phylink.c | 14 ++++++++++++++
include/linux/phylink.h | 1 +
2 files changed, 15 insertions(+)
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -687,6 +687,20 @@ int phylink_ethtool_set_settings(struct
}
EXPORT_SYMBOL_GPL(phylink_ethtool_set_settings);
+int phylink_ethtool_nway_reset(struct phylink *pl)
+{
+ int ret = 0;
+
+ mutex_lock(&pl->config_mutex);
+ if (pl->phydev)
+ ret = genphy_restart_aneg(pl->phydev);
+ phylink_mac_an_restart(pl);
+ mutex_unlock(&pl->config_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phylink_ethtool_nway_reset);
+
/* This emulates MII registers for a fixed-mode phy operating as per the
* passed in state. "aneg" defines if we report negotiation is possible.
*
--- a/include/linux/phylink.h
+++ b/include/linux/phylink.h
@@ -65,6 +65,7 @@ void phylink_stop(struct phylink *);
int phylink_ethtool_get_settings(struct phylink *, struct ethtool_cmd *);
int phylink_ethtool_set_settings(struct phylink *, struct ethtool_cmd *);
+int phylink_ethtool_nway_reset(struct phylink *);
int phylink_mii_ioctl(struct phylink *, struct ifreq *, int);
void phylink_set_link_port(struct phylink *pl, u32 support, u8 port);

@ -0,0 +1,38 @@
From 244bee2889d08f876c64c335765a8ea6de0f5381 Mon Sep 17 00:00:00 2001
From: Russell King <rmk+kernel@arm.linux.org.uk>
Date: Thu, 1 Oct 2015 19:40:31 +0100
Subject: [PATCH 725/744] net: mvneta: add nway_reset support
Add ethtool nway_reset support to mvneta via phylink, so that userspace
can request the link in whatever mode to be renegotiated via
ethtool -r ethX.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
drivers/net/ethernet/marvell/mvneta.c | 8 ++++++++
1 file changed, 8 insertions(+)
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -3589,6 +3589,13 @@ int mvneta_ethtool_set_settings(struct n
return phylink_ethtool_set_settings(pp->phylink, cmd);
}
+static int mvneta_ethtool_nway_reset(struct net_device *dev)
+{
+ struct mvneta_port *pp = netdev_priv(dev);
+
+ return phylink_ethtool_nway_reset(pp->phylink);
+}
+
/* Set interrupt coalescing for ethtools */
static int mvneta_ethtool_set_coalesce(struct net_device *dev,
struct ethtool_coalesce *c)
@@ -3853,6 +3860,7 @@ const struct ethtool_ops mvneta_eth_tool
.get_link = ethtool_op_get_link,
.get_settings = mvneta_ethtool_get_settings,
.set_settings = mvneta_ethtool_set_settings,
+ .nway_reset = mvneta_ethtool_nway_reset,
.set_coalesce = mvneta_ethtool_set_coalesce,
.get_coalesce = mvneta_ethtool_get_coalesce,
.get_drvinfo = mvneta_ethtool_get_drvinfo,

@ -0,0 +1,262 @@
From f566177aa6661e646b83526f24391a568ffd1a75 Mon Sep 17 00:00:00 2001
From: Russell King <rmk+kernel@arm.linux.org.uk>
Date: Thu, 1 Oct 2015 20:32:07 +0100
Subject: [PATCH 726/744] phylink: add flow control support
Add flow control support, including ethtool support, to phylink. We
add support to allow ethtool to get and set the current flow control
settings, and the 802.3 specified resolution for the local and remote
link partner abilities.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
drivers/net/phy/phylink.c | 145 +++++++++++++++++++++++++++++++++++++++++-----
include/linux/phylink.h | 8 +++
2 files changed, 139 insertions(+), 14 deletions(-)
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -91,10 +91,12 @@ static int phylink_parse_fixedlink(struc
pl->link_config.an_complete = 1;
pl->link_config.speed = speed;
pl->link_config.duplex = DUPLEX_HALF;
- pl->link_config.pause = MLO_PAUSE_NONE;
if (of_property_read_bool(fixed_node, "full-duplex"))
pl->link_config.duplex = DUPLEX_FULL;
+
+ /* We treat the "pause" and "asym-pause" terminology as
+ * defining the link partner's ability. */
if (of_property_read_bool(fixed_node, "pause"))
pl->link_config.pause |= MLO_PAUSE_SYM;
if (of_property_read_bool(fixed_node, "asym-pause"))
@@ -118,7 +120,6 @@ static int phylink_parse_fixedlink(struc
pl->link_config.duplex = be32_to_cpu(fixed_prop[1]) ?
DUPLEX_FULL : DUPLEX_HALF;
pl->link_config.speed = be32_to_cpu(fixed_prop[2]);
- pl->link_config.pause = MLO_PAUSE_NONE;
if (be32_to_cpu(fixed_prop[3]))
pl->link_config.pause |= MLO_PAUSE_SYM;
if (be32_to_cpu(fixed_prop[4]))
@@ -130,16 +131,6 @@ static int phylink_parse_fixedlink(struc
}
if (pl->link_an_mode == MLO_AN_FIXED) {
- /* Generate the supported/advertising masks */
- if (pl->link_config.pause & MLO_PAUSE_SYM) {
- pl->link_config.supported |= SUPPORTED_Pause;
- pl->link_config.advertising |= ADVERTISED_Pause;
- }
- if (pl->link_config.pause & MLO_PAUSE_ASYM) {
- pl->link_config.supported |= SUPPORTED_Asym_Pause;
- pl->link_config.advertising |= ADVERTISED_Asym_Pause;
- }
-
if (pl->link_config.speed > SPEED_1000 &&
pl->link_config.duplex != DUPLEX_FULL)
netdev_warn(pl->netdev, "fixed link specifies half duplex for %dMbps link?\n",
@@ -242,6 +233,56 @@ static void phylink_get_fixed_state(stru
state->link = !!gpiod_get_value(pl->link_gpio);
}
+/* Flow control is resolved according to our and the link partners
+ * advertisments using the following drawn from the 802.3 specs:
+ * Local device Link partner
+ * Pause AsymDir Pause AsymDir Result
+ * 1 X 1 X TX+RX
+ * 0 1 1 1 RX
+ * 1 1 0 1 TX
+ */
+static void phylink_resolve_flow(struct phylink *pl,
+ struct phylink_link_state *state)
+{
+ int new_pause = 0;
+
+ if (pl->link_config.pause & MLO_PAUSE_AN) {
+ int pause = 0;
+
+ if (pl->link_config.advertising & ADVERTISED_Pause)
+ pause |= MLO_PAUSE_SYM;
+ if (pl->link_config.advertising & ADVERTISED_Asym_Pause)
+ pause |= MLO_PAUSE_ASYM;
+
+ pause &= state->pause;
+
+ if (pause & MLO_PAUSE_SYM)
+ new_pause = MLO_PAUSE_TX | MLO_PAUSE_RX;
+ else if (pause & MLO_PAUSE_ASYM)
+ new_pause = state->pause & MLO_PAUSE_SYM ?
+ MLO_PAUSE_RX : MLO_PAUSE_TX;
+ } else {
+ new_pause = pl->link_config.pause & MLO_PAUSE_TXRX_MASK;
+ }
+
+ state->pause &= ~MLO_PAUSE_TXRX_MASK;
+ state->pause |= new_pause;
+}
+
+static const char *phylink_pause_to_str(int pause)
+{
+ switch (pause & MLO_PAUSE_TXRX_MASK) {
+ case MLO_PAUSE_TX | MLO_PAUSE_RX:
+ return "rx/tx";
+ case MLO_PAUSE_TX:
+ return "tx";
+ case MLO_PAUSE_RX:
+ return "rx";
+ default:
+ return "off";
+ }
+}
+
extern const char *phy_speed_to_str(int speed);
static void phylink_resolve(struct work_struct *w)
@@ -257,6 +298,7 @@ static void phylink_resolve(struct work_
switch (pl->link_an_mode) {
case MLO_AN_PHY:
link_state = pl->phy_state;
+ phylink_resolve_flow(pl, &link_state);
break;
case MLO_AN_FIXED:
@@ -265,9 +307,12 @@ static void phylink_resolve(struct work_
case MLO_AN_SGMII:
phylink_get_mac_state(pl, &link_state);
- if (pl->phydev)
+ if (pl->phydev) {
link_state.link = link_state.link &&
pl->phy_state.link;
+ link_state.pause |= pl->phy_state.pause;
+ phylink_resolve_flow(pl, &link_state);
+ }
break;
case MLO_AN_8023Z:
@@ -297,7 +342,7 @@ static void phylink_resolve(struct work_
"Link is Up - %s/%s - flow control %s\n",
phy_speed_to_str(link_state.speed),
link_state.duplex ? "Full" : "Half",
- link_state.pause ? "rx/tx" : "off");
+ phylink_pause_to_str(link_state.pause));
}
}
mutex_unlock(&pl->state_mutex);
@@ -326,6 +371,7 @@ struct phylink *phylink_create(struct ne
pl->link_interface = iface;
pl->link_port_support = SUPPORTED_MII;
pl->link_port = PORT_MII;
+ pl->link_config.pause = MLO_PAUSE_AN;
pl->ops = ops;
__set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state);
@@ -507,6 +553,7 @@ void phylink_start(struct phylink *pl)
* a fixed-link to start with the correct parameters, and also
* ensures that we set the appropriate advertisment for Serdes links.
*/
+ phylink_resolve_flow(pl, &pl->link_config);
phylink_mac_config(pl, &pl->link_config);
clear_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state);
@@ -701,6 +748,76 @@ int phylink_ethtool_nway_reset(struct ph
}
EXPORT_SYMBOL_GPL(phylink_ethtool_nway_reset);
+void phylink_ethtool_get_pauseparam(struct phylink *pl,
+ struct ethtool_pauseparam *pause)
+{
+ mutex_lock(&pl->config_mutex);
+
+ pause->autoneg = !!(pl->link_config.pause & MLO_PAUSE_AN);
+ pause->rx_pause = !!(pl->link_config.pause & MLO_PAUSE_RX);
+ pause->tx_pause = !!(pl->link_config.pause & MLO_PAUSE_TX);
+
+ mutex_unlock(&pl->config_mutex);
+}
+EXPORT_SYMBOL_GPL(phylink_ethtool_get_pauseparam);
+
+static int __phylink_ethtool_set_pauseparam(struct phylink *pl,
+ struct ethtool_pauseparam *pause)
+{
+ struct phylink_link_state *config = &pl->link_config;
+
+ if (!(config->supported & (SUPPORTED_Pause | SUPPORTED_Asym_Pause)))
+ return -EOPNOTSUPP;
+
+ if (!(config->supported & SUPPORTED_Asym_Pause) &&
+ !pause->autoneg && pause->rx_pause != pause->tx_pause)
+ return -EINVAL;
+
+ config->pause &= ~(MLO_PAUSE_AN | MLO_PAUSE_TXRX_MASK);
+
+ if (pause->autoneg)
+ config->pause |= MLO_PAUSE_AN;
+ if (pause->rx_pause)
+ config->pause |= MLO_PAUSE_RX;
+ if (pause->tx_pause)
+ config->pause |= MLO_PAUSE_TX;
+
+ switch (pl->link_an_mode) {
+ case MLO_AN_PHY:
+ /* Silently mark the carrier down, and then trigger a resolve */
+ netif_carrier_off(pl->netdev);
+ phylink_run_resolve(pl);
+ break;
+
+ case MLO_AN_FIXED:
+ /* Should we allow fixed links to change against the config? */
+ phylink_resolve_flow(pl, config);
+ phylink_mac_config(pl, config);
+ break;
+
+ case MLO_AN_SGMII:
+ case MLO_AN_8023Z:
+ phylink_mac_config(pl, config);
+ phylink_mac_an_restart(pl);
+ break;
+ }
+
+ return 0;
+}
+
+int phylink_ethtool_set_pauseparam(struct phylink *pl,
+ struct ethtool_pauseparam *pause)
+{
+ int ret;
+
+ mutex_lock(&pl->config_mutex);
+ ret = __phylink_ethtool_set_pauseparam(pl, pause);
+ mutex_unlock(&pl->config_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phylink_ethtool_set_pauseparam);
+
/* This emulates MII registers for a fixed-mode phy operating as per the
* passed in state. "aneg" defines if we report negotiation is possible.
*
--- a/include/linux/phylink.h
+++ b/include/linux/phylink.h
@@ -13,6 +13,10 @@ 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,
MLO_AN_FIXED,
@@ -66,6 +70,10 @@ void phylink_stop(struct phylink *);
int phylink_ethtool_get_settings(struct phylink *, struct ethtool_cmd *);
int phylink_ethtool_set_settings(struct phylink *, struct ethtool_cmd *);
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_mii_ioctl(struct phylink *, struct ifreq *, int);
void phylink_set_link_port(struct phylink *pl, u32 support, u8 port);

@ -0,0 +1,66 @@
From 7bd34822b9922beb22a6384d9190646105d259d8 Mon Sep 17 00:00:00 2001
From: Russell King <rmk+kernel@arm.linux.org.uk>
Date: Thu, 1 Oct 2015 17:41:44 +0100
Subject: [PATCH 727/744] net: mvneta: add flow control support via phylink
Add flow control support to mvneta, including the ethtool hooks. This
uses the phylink code to calculate the result of autonegotiation where
a phy is attached, and to handle the ethtool settings.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
drivers/net/ethernet/marvell/mvneta.c | 22 +++++++++++++++++++++-
1 file changed, 21 insertions(+), 1 deletion(-)
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -3208,6 +3208,8 @@ static void mvneta_mac_config(struct net
if (state->advertising & ADVERTISED_Pause)
new_an |= MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL;
+ if (state->pause & MLO_PAUSE_TXRX_MASK)
+ new_an |= MVNETA_GMAC_CONFIG_FLOW_CTRL;
switch (mode) {
case MLO_AN_SGMII:
@@ -3232,7 +3234,7 @@ static void mvneta_mac_config(struct net
/* The MAC only supports FD mode */
MVNETA_GMAC_CONFIG_FULL_DUPLEX;
- if (state->an_enabled)
+ if (state->pause & MLO_PAUSE_AN && state->an_enabled)
new_an |= MVNETA_GMAC_AN_FLOW_CTRL_EN;
break;
@@ -3685,6 +3687,22 @@ static int mvneta_ethtool_set_ringparam(
return 0;
}
+static void mvneta_ethtool_get_pauseparam(struct net_device *dev,
+ struct ethtool_pauseparam *pause)
+{
+ struct mvneta_port *pp = netdev_priv(dev);
+
+ phylink_ethtool_get_pauseparam(pp->phylink, pause);
+}
+
+static int mvneta_ethtool_set_pauseparam(struct net_device *dev,
+ struct ethtool_pauseparam *pause)
+{
+ struct mvneta_port *pp = netdev_priv(dev);
+
+ return phylink_ethtool_set_pauseparam(pp->phylink, pause);
+}
+
static void mvneta_ethtool_get_strings(struct net_device *netdev, u32 sset,
u8 *data)
{
@@ -3866,6 +3884,8 @@ const struct ethtool_ops mvneta_eth_tool
.get_drvinfo = mvneta_ethtool_get_drvinfo,
.get_ringparam = mvneta_ethtool_get_ringparam,
.set_ringparam = mvneta_ethtool_set_ringparam,
+ .get_pauseparam = mvneta_ethtool_get_pauseparam,
+ .set_pauseparam = mvneta_ethtool_set_pauseparam,
.get_strings = mvneta_ethtool_get_strings,
.get_ethtool_stats = mvneta_ethtool_get_stats,
.get_sset_count = mvneta_ethtool_get_sset_count,

@ -0,0 +1,32 @@
From 62f8a12044265df11531750a240e516a5f1ff433 Mon Sep 17 00:00:00 2001
From: Russell King <rmk+kernel@arm.linux.org.uk>
Date: Thu, 1 Oct 2015 00:34:08 +0100
Subject: [PATCH 728/744] net: mvneta: enable flow control for PHY connections
Enable flow control support for PHY connections by indicating our
support via the ethtool capabilities. phylink takes care of the
appropriate handling.
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
drivers/net/ethernet/marvell/mvneta.c | 2 ++
1 file changed, 2 insertions(+)
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -3127,12 +3127,14 @@ static int mvneta_mac_support(struct net
state->supported = PHY_10BT_FEATURES |
PHY_100BT_FEATURES |
SUPPORTED_1000baseT_Full |
+ SUPPORTED_Pause |
SUPPORTED_Autoneg;
state->advertising = ADVERTISED_10baseT_Half |
ADVERTISED_10baseT_Full |
ADVERTISED_100baseT_Half |
ADVERTISED_100baseT_Full |
ADVERTISED_1000baseT_Full |
+ ADVERTISED_Pause |
ADVERTISED_Autoneg;
state->an_enabled = 1;
break;

@ -0,0 +1,53 @@
From 4c3e2dc08a11fb1273ca62467f1d06e59866bad3 Mon Sep 17 00:00:00 2001
From: Russell King <rmk+kernel@armlinux.org.uk>
Date: Tue, 12 Jul 2016 00:04:13 +0100
Subject: [PATCH 729/744] net: mvneta: enable flow control for fixed
connections
Allow symetric flow control to be enabled for fixed link connections as
well as other types of connections by setting the supported and
advertised capability bits.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
drivers/net/ethernet/marvell/mvneta.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -3114,9 +3114,9 @@ static int mvneta_mac_support(struct net
switch (mode) {
case MLO_AN_8023Z:
state->supported = SUPPORTED_1000baseT_Full |
- SUPPORTED_Autoneg | SUPPORTED_Pause;
+ SUPPORTED_Autoneg;
state->advertising = ADVERTISED_1000baseT_Full |
- ADVERTISED_Autoneg | ADVERTISED_Pause;
+ ADVERTISED_Autoneg;
state->an_enabled = 1;
break;
@@ -3127,18 +3127,21 @@ static int mvneta_mac_support(struct net
state->supported = PHY_10BT_FEATURES |
PHY_100BT_FEATURES |
SUPPORTED_1000baseT_Full |
- SUPPORTED_Pause |
SUPPORTED_Autoneg;
state->advertising = ADVERTISED_10baseT_Half |
ADVERTISED_10baseT_Full |
ADVERTISED_100baseT_Half |
ADVERTISED_100baseT_Full |
ADVERTISED_1000baseT_Full |
- ADVERTISED_Pause |
ADVERTISED_Autoneg;
state->an_enabled = 1;
break;
}
+
+ /* All modes support flow control */
+ state->supported |= SUPPORTED_Pause;
+ state->advertising |= ADVERTISED_Pause;
+
return 0;
}

@ -0,0 +1,111 @@
From ffba226d73a2be262fff12d30aecf76d107b2ace Mon Sep 17 00:00:00 2001
From: Russell King <rmk+kernel@arm.linux.org.uk>
Date: Thu, 1 Oct 2015 21:19:53 +0100
Subject: [PATCH 730/744] phylink: add EEE support
Add EEE hooks to phylink to allow the phylib EEE functions for the
connected phy to be safely accessed.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
drivers/net/phy/phylink.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++-
include/linux/phylink.h | 7 +++++-
2 files changed, 63 insertions(+), 2 deletions(-)
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -334,7 +334,8 @@ static void phylink_resolve(struct work_
if (pl->phydev)
phylink_mac_config(pl, &link_state);
- pl->ops->mac_link_up(ndev, pl->link_an_mode);
+ pl->ops->mac_link_up(ndev, pl->link_an_mode,
+ pl->phydev);
netif_carrier_on(ndev);
@@ -818,6 +819,61 @@ int phylink_ethtool_set_pauseparam(struc
}
EXPORT_SYMBOL_GPL(phylink_ethtool_set_pauseparam);
+int phylink_init_eee(struct phylink *pl, bool clk_stop_enable)
+{
+ int ret = -EPROTONOSUPPORT;
+
+ mutex_lock(&pl->config_mutex);
+ if (pl->phydev)
+ ret = phy_init_eee(pl->phydev, clk_stop_enable);
+ mutex_unlock(&pl->config_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phylink_init_eee);
+
+int phylink_get_eee_err(struct phylink *pl)
+{
+ int ret = 0;
+
+ mutex_lock(&pl->config_mutex);
+ if (pl->phydev)
+ ret = phy_get_eee_err(pl->phydev);
+ mutex_unlock(&pl->config_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phylink_get_eee_err);
+
+int phylink_ethtool_get_eee(struct phylink *pl, struct ethtool_eee *eee)
+{
+ int ret = -EOPNOTSUPP;
+
+ mutex_lock(&pl->config_mutex);
+ if (pl->phydev)
+ ret = phy_ethtool_get_eee(pl->phydev, eee);
+ mutex_unlock(&pl->config_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phylink_ethtool_get_eee);
+
+int phylink_ethtool_set_eee(struct phylink *pl, struct ethtool_eee *eee)
+{
+ int ret = -EOPNOTSUPP;
+
+ mutex_lock(&pl->config_mutex);
+ if (pl->phydev) {
+ ret = phy_ethtool_set_eee(pl->phydev, eee);
+ if (ret == 0 && eee->eee_enabled)
+ phy_start_aneg(pl->phydev);
+ }
+ mutex_unlock(&pl->config_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phylink_ethtool_set_eee);
+
/* This emulates MII registers for a fixed-mode phy operating as per the
* passed in state. "aneg" defines if we report negotiation is possible.
*
--- a/include/linux/phylink.h
+++ b/include/linux/phylink.h
@@ -51,7 +51,8 @@ struct phylink_mac_ops {
void (*mac_an_restart)(struct net_device *, unsigned int mode);
void (*mac_link_down)(struct net_device *, unsigned int mode);
- void (*mac_link_up)(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 *,
@@ -74,6 +75,10 @@ void phylink_ethtool_get_pauseparam(stru
struct ethtool_pauseparam *);
int phylink_ethtool_set_pauseparam(struct phylink *,
struct ethtool_pauseparam *);
+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);
void phylink_set_link_port(struct phylink *pl, u32 support, u8 port);

@ -0,0 +1,182 @@
From b7dacf514e41d6efff0ccc170f660cc6dc2aeae2 Mon Sep 17 00:00:00 2001
From: Russell King <rmk+kernel@arm.linux.org.uk>
Date: Tue, 29 Sep 2015 15:17:39 +0100
Subject: [PATCH 731/744] net: mvneta: add EEE support
Add EEE support to mvneta. This allows us to enable the low power idle
support at MAC level if there is a PHY attached through phylink which
supports LPI. The appropriate ethtool support is provided to allow the
feature to be controlled, including ethtool statistics for EEE wakeup
errors.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
drivers/net/ethernet/marvell/mvneta.c | 87 +++++++++++++++++++++++++++++++++++
1 file changed, 87 insertions(+)
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -243,6 +243,12 @@
#define MVNETA_TXQ_TOKEN_SIZE_REG(q) (0x3e40 + ((q) << 2))
#define MVNETA_TXQ_TOKEN_SIZE_MAX 0x7fffffff
+#define MVNETA_LPI_CTRL_0 0x2cc0
+#define MVNETA_LPI_CTRL_1 0x2cc4
+#define MVNETA_LPI_REQUEST_ENABLE BIT(0)
+#define MVNETA_LPI_CTRL_2 0x2cc8
+#define MVNETA_LPI_STATUS 0x2ccc
+
#define MVNETA_CAUSE_TXQ_SENT_DESC_ALL_MASK 0xff
/* Descriptor ring Macros */
@@ -316,6 +322,11 @@
#define MVNETA_RX_GET_BM_POOL_ID(rxd) \
(((rxd)->status & MVNETA_RXD_BM_POOL_MASK) >> MVNETA_RXD_BM_POOL_SHIFT)
+enum {
+ ETHTOOL_STAT_EEE_WAKEUP,
+ ETHTOOL_MAX_STATS,
+};
+
struct mvneta_statistic {
unsigned short offset;
unsigned short type;
@@ -324,6 +335,7 @@ struct mvneta_statistic {
#define T_REG_32 32
#define T_REG_64 64
+#define T_SW 1
static const struct mvneta_statistic mvneta_statistics[] = {
{ 0x3000, T_REG_64, "good_octets_received", },
@@ -358,6 +370,7 @@ static const struct mvneta_statistic mvn
{ 0x304c, T_REG_32, "broadcast_frames_sent", },
{ 0x3054, T_REG_32, "fc_sent", },
{ 0x300c, T_REG_32, "internal_mac_transmit_err", },
+ { ETHTOOL_STAT_EEE_WAKEUP, T_SW, "eee_wakeup_errors", },
};
struct mvneta_pcpu_stats {
@@ -413,6 +426,10 @@ struct mvneta_port {
struct mvneta_bm_pool *pool_short;
int bm_win_id;
+ bool eee_enabled;
+ bool eee_active;
+ bool tx_lpi_enabled;
+
u64 ethtool_stats[ARRAY_SIZE(mvneta_statistics)];
u32 indir[MVNETA_RSS_LU_TABLE_SIZE];
@@ -3276,6 +3293,18 @@ static void mvneta_mac_config(struct net
mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, new_an);
}
+static void mvneta_set_eee(struct mvneta_port *pp, bool enable)
+{
+ u32 lpi_ctl1;
+
+ lpi_ctl1 = mvreg_read(pp, MVNETA_LPI_CTRL_1);
+ if (enable)
+ lpi_ctl1 |= MVNETA_LPI_REQUEST_ENABLE;
+ else
+ lpi_ctl1 &= ~MVNETA_LPI_REQUEST_ENABLE;
+ mvreg_write(pp, MVNETA_LPI_CTRL_1, lpi_ctl1);
+}
+
static void mvneta_mac_link_down(struct net_device *ndev, unsigned int mode)
{
struct mvneta_port *pp = netdev_priv(ndev);
@@ -3289,6 +3318,9 @@ static void mvneta_mac_link_down(struct
val |= MVNETA_GMAC_FORCE_LINK_DOWN;
mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
}
+
+ pp->eee_active = false;
+ mvneta_set_eee(pp, false);
}
static void mvneta_mac_link_up(struct net_device *ndev, unsigned int mode,
@@ -3305,6 +3337,11 @@ static void mvneta_mac_link_up(struct ne
}
mvneta_port_up(pp);
+
+ if (phy && pp->eee_enabled) {
+ pp->eee_active = phy_init_eee(phy, 0) >= 0;
+ mvneta_set_eee(pp, pp->eee_active && pp->tx_lpi_enabled);
+ }
}
static const struct phylink_mac_ops mvneta_phylink_ops = {
@@ -3744,6 +3781,13 @@ static void mvneta_ethtool_update_stats(
val64 = (u64)high << 32 | low;
pp->ethtool_stats[i] += val64;
break;
+ case T_SW:
+ switch (s->offset) {
+ case ETHTOOL_STAT_EEE_WAKEUP:
+ val = phylink_get_eee_err(pp->phylink);
+ break;
+ }
+ break;
}
}
}
@@ -3867,6 +3911,47 @@ static int mvneta_ethtool_get_rxfh(struc
return 0;
}
+static int mvneta_ethtool_get_eee(struct net_device *dev,
+ struct ethtool_eee *eee)
+{
+ struct mvneta_port *pp = netdev_priv(dev);
+ u32 lpi_ctl0;
+
+ lpi_ctl0 = mvreg_read(pp, MVNETA_LPI_CTRL_0);
+
+ eee->eee_enabled = pp->eee_enabled;
+ eee->eee_active = pp->eee_active;
+ eee->tx_lpi_enabled = pp->tx_lpi_enabled;
+ eee->tx_lpi_timer = (lpi_ctl0) >> 8; // * scale;
+
+ return phylink_ethtool_get_eee(pp->phylink, eee);
+}
+
+static int mvneta_ethtool_set_eee(struct net_device *dev,
+ struct ethtool_eee *eee)
+{
+ struct mvneta_port *pp = netdev_priv(dev);
+ u32 lpi_ctl0;
+
+ /* The Armada 37x documents do not give limits for this other than
+ * it being an 8-bit register. */
+ if (eee->tx_lpi_enabled &&
+ (eee->tx_lpi_timer < 0 || eee->tx_lpi_timer > 255))
+ return -EINVAL;
+
+ lpi_ctl0 = mvreg_read(pp, MVNETA_LPI_CTRL_0);
+ lpi_ctl0 &= ~(0xff << 8);
+ lpi_ctl0 |= eee->tx_lpi_timer << 8;
+ mvreg_write(pp, MVNETA_LPI_CTRL_0, lpi_ctl0);
+
+ pp->eee_enabled = eee->eee_enabled;
+ pp->tx_lpi_enabled = eee->tx_lpi_enabled;
+
+ mvneta_set_eee(pp, eee->tx_lpi_enabled && eee->eee_enabled);
+
+ return phylink_ethtool_set_eee(pp->phylink, eee);
+}
+
static const struct net_device_ops mvneta_netdev_ops = {
.ndo_open = mvneta_open,
.ndo_stop = mvneta_stop,
@@ -3898,6 +3983,8 @@ const struct ethtool_ops mvneta_eth_tool
.get_rxnfc = mvneta_ethtool_get_rxnfc,
.get_rxfh = mvneta_ethtool_get_rxfh,
.set_rxfh = mvneta_ethtool_set_rxfh,
+ .get_eee = mvneta_ethtool_get_eee,
+ .set_eee = mvneta_ethtool_set_eee,
};
/* Initialize hw */

@ -0,0 +1,137 @@
From 5419ccb638aa5c353ea88815e98953d9fc02e6ca Mon Sep 17 00:00:00 2001
From: Russell King <rmk+kernel@arm.linux.org.uk>
Date: Thu, 1 Oct 2015 23:10:05 +0100
Subject: [PATCH 732/744] phylink: add module EEPROM support
Add support for reading module EEPROMs through phylink.
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
drivers/net/phy/phylink.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++
include/linux/phylink.h | 12 +++++++++
2 files changed, 78 insertions(+)
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -60,6 +60,9 @@ struct phylink {
struct work_struct resolve;
bool mac_link_up;
+
+ const struct phylink_module_ops *module_ops;
+ void *module_data;
};
static const char *phylink_an_mode_str(unsigned int mode)
@@ -819,6 +822,36 @@ int phylink_ethtool_set_pauseparam(struc
}
EXPORT_SYMBOL_GPL(phylink_ethtool_set_pauseparam);
+int phylink_ethtool_get_module_info(struct phylink *pl,
+ struct ethtool_modinfo *modinfo)
+{
+ int ret = -EOPNOTSUPP;
+
+ mutex_lock(&pl->config_mutex);
+ if (pl->module_ops)
+ ret = pl->module_ops->get_module_info(pl->module_data,
+ modinfo);
+ mutex_unlock(&pl->config_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phylink_ethtool_get_module_info);
+
+int phylink_ethtool_get_module_eeprom(struct phylink *pl,
+ struct ethtool_eeprom *ee, u8 *buf)
+{
+ int ret = -EOPNOTSUPP;
+
+ mutex_lock(&pl->config_mutex);
+ if (pl->module_ops)
+ ret = pl->module_ops->get_module_eeprom(pl->module_data, ee,
+ buf);
+ mutex_unlock(&pl->config_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phylink_ethtool_get_module_eeprom);
+
int phylink_init_eee(struct phylink *pl, bool clk_stop_enable)
{
int ret = -EPROTONOSUPPORT;
@@ -1016,6 +1049,39 @@ EXPORT_SYMBOL_GPL(phylink_mii_ioctl);
+int phylink_register_module(struct phylink *pl, void *data,
+ const struct phylink_module_ops *ops)
+{
+ int ret = -EBUSY;
+
+ mutex_lock(&pl->config_mutex);
+ if (!pl->module_ops) {
+ pl->module_ops = ops;
+ pl->module_data = data;
+ ret = 0;
+ }
+ mutex_unlock(&pl->config_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phylink_register_module);
+
+int phylink_unregister_module(struct phylink *pl, void *data)
+{
+ int ret = -EINVAL;
+
+ mutex_lock(&pl->config_mutex);
+ if (pl->module_data == data) {
+ pl->module_ops = NULL;
+ pl->module_data = NULL;
+ ret = 0;
+ }
+ mutex_unlock(&pl->config_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phylink_unregister_module);
+
void phylink_disable(struct phylink *pl)
{
set_bit(PHYLINK_DISABLE_LINK, &pl->phylink_disable_state);
--- a/include/linux/phylink.h
+++ b/include/linux/phylink.h
@@ -55,6 +55,11 @@ struct phylink_mac_ops {
struct phy_device *);
};
+struct phylink_module_ops {
+ int (*get_module_info)(void *, struct ethtool_modinfo *);
+ int (*get_module_eeprom)(void *, struct ethtool_eeprom *, u8 *);
+};
+
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 *);
@@ -75,12 +80,19 @@ void phylink_ethtool_get_pauseparam(stru
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);
+int phylink_register_module(struct phylink *, void *,
+ const struct phylink_module_ops *);
+int phylink_unregister_module(struct phylink *, void *);
+
void phylink_set_link_port(struct phylink *pl, u32 support, u8 port);
int phylink_set_link_an_mode(struct phylink *pl, unsigned int mode);
void phylink_disable(struct phylink *pl);

@ -0,0 +1,44 @@
From 665e1fe77dedcfc6b5669214ebfd252c803290d4 Mon Sep 17 00:00:00 2001
From: Russell King <rmk+kernel@arm.linux.org.uk>
Date: Thu, 1 Oct 2015 23:32:39 +0100
Subject: [PATCH 733/744] net: mvneta: add module EEPROM reading support
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
drivers/net/ethernet/marvell/mvneta.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -3911,6 +3911,22 @@ static int mvneta_ethtool_get_rxfh(struc
return 0;
}
+static int mvneta_ethtool_get_module_info(struct net_device *dev,
+ struct ethtool_modinfo *modinfo)
+{
+ struct mvneta_port *pp = netdev_priv(dev);
+
+ return phylink_ethtool_get_module_info(pp->phylink, modinfo);
+}
+
+static int mvneta_ethtool_get_module_eeprom(struct net_device *dev,
+ struct ethtool_eeprom *ee, u8 *buf)
+{
+ struct mvneta_port *pp = netdev_priv(dev);
+
+ return phylink_ethtool_get_module_eeprom(pp->phylink, ee, buf);
+}
+
static int mvneta_ethtool_get_eee(struct net_device *dev,
struct ethtool_eee *eee)
{
@@ -3983,6 +3999,8 @@ const struct ethtool_ops mvneta_eth_tool
.get_rxnfc = mvneta_ethtool_get_rxnfc,
.get_rxfh = mvneta_ethtool_get_rxfh,
.set_rxfh = mvneta_ethtool_set_rxfh,
+ .get_module_info = mvneta_ethtool_get_module_info,
+ .get_module_eeprom = mvneta_ethtool_get_module_eeprom,
.get_eee = mvneta_ethtool_get_eee,
.set_eee = mvneta_ethtool_set_eee,
};

@ -0,0 +1,68 @@
From a7091ef24223ed39b39c6b73b77c55c8a607f34a Mon Sep 17 00:00:00 2001
From: Russell King <rmk+kernel@arm.linux.org.uk>
Date: Thu, 8 Oct 2015 23:49:47 +0100
Subject: [PATCH 734/744] sfp/phylink: hook up eeprom functions
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
drivers/net/phy/sfp.c | 19 +++++++++++--------
1 file changed, 11 insertions(+), 8 deletions(-)
--- a/drivers/net/phy/sfp.c
+++ b/drivers/net/phy/sfp.c
@@ -901,11 +901,9 @@ static void sfp_sm_event(struct sfp *sfp
mutex_unlock(&sfp->sm_mutex);
}
-#if 0
-static int sfp_phy_module_info(struct phy_device *phy,
- struct ethtool_modinfo *modinfo)
+static int sfp_module_info(void *priv, struct ethtool_modinfo *modinfo)
{
- struct sfp *sfp = phy->priv;
+ struct sfp *sfp = priv;
/* locking... and check module is present */
@@ -919,10 +917,9 @@ static int sfp_phy_module_info(struct ph
return 0;
}
-static int sfp_phy_module_eeprom(struct phy_device *phy,
- struct ethtool_eeprom *ee, u8 *data)
+static int sfp_module_eeprom(void *priv, struct ethtool_eeprom *ee, u8 *data)
{
- struct sfp *sfp = phy->priv;
+ struct sfp *sfp = priv;
unsigned int first, last, len;
int ret;
@@ -953,7 +950,11 @@ static int sfp_phy_module_eeprom(struct
}
return 0;
}
-#endif
+
+static const struct phylink_module_ops sfp_module_ops = {
+ .get_module_info = sfp_module_info,
+ .get_module_eeprom = sfp_module_eeprom,
+};
static void sfp_timeout(struct work_struct *work)
{
@@ -1029,6 +1030,7 @@ static int sfp_netdev_notify(struct noti
case NETDEV_UNREGISTER:
if (sfp->mod_phy && sfp->phylink)
phylink_disconnect_phy(sfp->phylink);
+ phylink_unregister_module(sfp->phylink, sfp);
sfp->phylink = NULL;
dev_put(sfp->ndev);
sfp->ndev = NULL;
@@ -1145,6 +1147,7 @@ static int sfp_probe(struct platform_dev
}
phylink_disable(sfp->phylink);
+ phylink_register_module(sfp->phylink, sfp, &sfp_module_ops);
}
sfp->state = sfp_get_state(sfp);

@ -0,0 +1,84 @@
From 63ff73593c2f5d3fc1cba479321d192caaca48aa Mon Sep 17 00:00:00 2001
From: Russell King <rmk+kernel@arm.linux.org.uk>
Date: Sat, 12 Sep 2015 18:43:39 +0100
Subject: [PATCH 738/744] ARM: dts: armada388-clearfog: add SFP module support
Add SFP module support for Clearfog using the SFP phylink support.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
arch/arm/boot/dts/armada-388-clearfog.dts | 44 ++++++++-----------------------
1 file changed, 11 insertions(+), 33 deletions(-)
--- a/arch/arm/boot/dts/armada-388-clearfog.dts
+++ b/arch/arm/boot/dts/armada-388-clearfog.dts
@@ -90,16 +90,12 @@
};
ethernet@34000 {
+ managed = "in-band-status";
phy-mode = "sgmii";
buffer-manager = <&bm>;
bm,pool-long = <3>;
bm,pool-short = <1>;
status = "okay";
-
- fixed-link {
- speed = <1000>;
- full-duplex;
- };
};
i2c@11000 {
@@ -183,34 +179,6 @@
output-low;
line-name = "m.2 devslp";
};
- sfp_los {
- /* SFP loss of signal */
- gpio-hog;
- gpios = <12 GPIO_ACTIVE_HIGH>;
- input;
- line-name = "sfp-los";
- };
- sfp_tx_fault {
- /* SFP laser fault */
- gpio-hog;
- gpios = <13 GPIO_ACTIVE_HIGH>;
- input;
- line-name = "sfp-tx-fault";
- };
- sfp_tx_disable {
- /* SFP transmit disable */
- gpio-hog;
- gpios = <14 GPIO_ACTIVE_HIGH>;
- output-low;
- line-name = "sfp-tx-disable";
- };
- sfp_mod_def0 {
- /* SFP module present */
- gpio-hog;
- gpios = <15 GPIO_ACTIVE_LOW>;
- input;
- line-name = "sfp-mod-def0";
- };
};
/* The MCP3021 is 100kHz clock only */
@@ -374,6 +342,16 @@
};
};
+ sfp: sfp {
+ compatible = "sff,sfp";
+ i2c-bus = <&i2c1>;
+ los-gpio = <&expander0 12 GPIO_ACTIVE_HIGH>;
+ moddef0-gpio = <&expander0 15 GPIO_ACTIVE_LOW>;
+ sfp,ethernet = <&eth2>;
+ tx-disable-gpio = <&expander0 14 GPIO_ACTIVE_HIGH>;
+ tx-fault-gpio = <&expander0 13 GPIO_ACTIVE_HIGH>;
+ };
+
dsa@0 {
compatible = "marvell,dsa";
dsa,ethernet = <&eth1>;

@ -0,0 +1,96 @@
From 28baa5e2635285b178326b301f534ed95c65dd01 Mon Sep 17 00:00:00 2001
From: Jonas Gorski <jonas.gorski@gmail.com>
Date: Thu, 29 Sep 2016 11:44:39 +0200
Subject: [PATCH] sfp: retry phy probe if unsuccessful
Some phys seem to take longer than 50 ms to come out of reset, so retry
until we find a phy.
Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
---
drivers/net/phy/sfp.c | 38 +++++++++++++++++++++++++-------------
1 file changed, 25 insertions(+), 13 deletions(-)
--- a/drivers/net/phy/sfp.c
+++ b/drivers/net/phy/sfp.c
@@ -488,7 +488,7 @@ static void sfp_sm_phy_detach(struct sfp
sfp->mod_phy = NULL;
}
-static void sfp_sm_probe_phy(struct sfp *sfp)
+static int sfp_sm_probe_phy(struct sfp *sfp)
{
struct phy_device *phy;
int err;
@@ -498,11 +498,11 @@ static void sfp_sm_probe_phy(struct sfp
phy = mdiobus_scan(sfp->i2c_mii, SFP_PHY_ADDR);
if (IS_ERR(phy)) {
dev_err(sfp->dev, "mdiobus scan returned %ld\n", PTR_ERR(phy));
- return;
+ return PTR_ERR(phy);
}
if (!phy) {
- dev_info(sfp->dev, "no PHY detected\n");
- return;
+ dev_dbg(sfp->dev, "no PHY detected\n");
+ return -EAGAIN;
}
err = phylink_connect_phy(sfp->phylink, phy);
@@ -510,11 +510,13 @@ static void sfp_sm_probe_phy(struct sfp
phy_device_remove(phy);
phy_device_free(phy);
dev_err(sfp->dev, "phylink_connect_phy failed: %d\n", err);
- return;
+ return err;
}
sfp->mod_phy = phy;
phy_start(phy);
+
+ return 0;
}
static void sfp_sm_link_up(struct sfp *sfp)
@@ -565,13 +567,6 @@ static void sfp_sm_mod_init(struct sfp *
{
sfp_module_tx_enable(sfp);
- /* Wait t_init before indicating that the link is up, provided the
- * current state indicates no TX_FAULT. If TX_FAULT clears before
- * this time, that's fine too.
- */
- sfp_sm_next(sfp, SFP_S_INIT, T_INIT_JIFFIES);
- sfp->sm_retries = 5;
-
if (sfp->phylink) {
/* Setting the serdes link mode is guesswork: there's no
* field in the EEPROM which indicates what mode should
@@ -587,9 +582,26 @@ static void sfp_sm_mod_init(struct sfp *
!sfp->id.base.e100_base_fx) {
phylink_set_link_an_mode(sfp->phylink, MLO_AN_8023Z);
} else {
+ int ret;
+
phylink_set_link_an_mode(sfp->phylink, MLO_AN_SGMII);
- sfp_sm_probe_phy(sfp);
+
+ ret = sfp_sm_probe_phy(sfp);
+ if (ret) {
+ if (ret == -EAGAIN)
+ sfp_sm_set_timer(sfp, T_PROBE_RETRY);
+ else
+ sfp_sm_next(sfp, SFP_S_TX_DISABLE, 0);
+ return;
+ }
}
+
+ /* Wait t_init before indicating that the link is up, provided the
+ * current state indicates no TX_FAULT. If TX_FAULT clears before
+ * this time, that's fine too.
+ */
+ sfp_sm_next(sfp, SFP_S_INIT, T_INIT_JIFFIES);
+ sfp->sm_retries = 5;
}
}
Loading…
Cancel
Save