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>
This commit is contained in:
parent
a454756c91
commit
4aa5d3e60d
30 changed files with 6212 additions and 0 deletions
|
@ -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);
|
163
target/linux/mvebu/patches-4.4/129-phy-add-I2C-mdio-bus.patch
Normal file
163
target/linux/mvebu/patches-4.4/129-phy-add-I2C-mdio-bus.patch
Normal file
|
@ -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
|
File diff suppressed because it is too large
Load diff
|
@ -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
|
File diff suppressed because it is too large
Load diff
|
@ -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;
|
||||
}
|
||||
|
111
target/linux/mvebu/patches-4.4/142-phylink-add-EEE-support.patch
Normal file
111
target/linux/mvebu/patches-4.4/142-phylink-add-EEE-support.patch
Normal file
|
@ -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 = <ð2>;
|
||||
+ tx-disable-gpio = <&expander0 14 GPIO_ACTIVE_HIGH>;
|
||||
+ tx-fault-gpio = <&expander0 13 GPIO_ACTIVE_HIGH>;
|
||||
+ };
|
||||
+
|
||||
dsa@0 {
|
||||
compatible = "marvell,dsa";
|
||||
dsa,ethernet = <ð1>;
|
96
target/linux/mvebu/patches-4.4/300-reprobe_sfp_phy.patch
Normal file
96
target/linux/mvebu/patches-4.4/300-reprobe_sfp_phy.patch
Normal file
|
@ -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…
Reference in a new issue