summaryrefslogtreecommitdiffstats
path: root/drivers/net/phy
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-07-05 12:31:59 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2017-07-05 12:31:59 -0700
commit5518b69b76680a4f2df96b1deca260059db0c2de (patch)
treef33cd1519c8efb4590500f2f9617400be233238c /drivers/net/phy
parent8ad06e56dcbc1984ef0ff8f6e3c19982c5809f73 (diff)
parent0e72582270c07850b92cac351c8b97d4f9c123b9 (diff)
downloadop-kernel-dev-5518b69b76680a4f2df96b1deca260059db0c2de.zip
op-kernel-dev-5518b69b76680a4f2df96b1deca260059db0c2de.tar.gz
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller: "Reasonably busy this cycle, but perhaps not as busy as in the 4.12 merge window: 1) Several optimizations for UDP processing under high load from Paolo Abeni. 2) Support pacing internally in TCP when using the sch_fq packet scheduler for this is not practical. From Eric Dumazet. 3) Support mutliple filter chains per qdisc, from Jiri Pirko. 4) Move to 1ms TCP timestamp clock, from Eric Dumazet. 5) Add batch dequeueing to vhost_net, from Jason Wang. 6) Flesh out more completely SCTP checksum offload support, from Davide Caratti. 7) More plumbing of extended netlink ACKs, from David Ahern, Pablo Neira Ayuso, and Matthias Schiffer. 8) Add devlink support to nfp driver, from Simon Horman. 9) Add RTM_F_FIB_MATCH flag to RTM_GETROUTE queries, from Roopa Prabhu. 10) Add stack depth tracking to BPF verifier and use this information in the various eBPF JITs. From Alexei Starovoitov. 11) Support XDP on qed device VFs, from Yuval Mintz. 12) Introduce BPF PROG ID for better introspection of installed BPF programs. From Martin KaFai Lau. 13) Add bpf_set_hash helper for TC bpf programs, from Daniel Borkmann. 14) For loads, allow narrower accesses in bpf verifier checking, from Yonghong Song. 15) Support MIPS in the BPF selftests and samples infrastructure, the MIPS eBPF JIT will be merged in via the MIPS GIT tree. From David Daney. 16) Support kernel based TLS, from Dave Watson and others. 17) Remove completely DST garbage collection, from Wei Wang. 18) Allow installing TCP MD5 rules using prefixes, from Ivan Delalande. 19) Add XDP support to Intel i40e driver, from Björn Töpel 20) Add support for TC flower offload in nfp driver, from Simon Horman, Pieter Jansen van Vuuren, Benjamin LaHaise, Jakub Kicinski, and Bert van Leeuwen. 21) IPSEC offloading support in mlx5, from Ilan Tayari. 22) Add HW PTP support to macb driver, from Rafal Ozieblo. 23) Networking refcount_t conversions, From Elena Reshetova. 24) Add sock_ops support to BPF, from Lawrence Brako. This is useful for tuning the TCP sockopt settings of a group of applications, currently via CGROUPs" * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1899 commits) net: phy: dp83867: add workaround for incorrect RX_CTRL pin strap dt-bindings: phy: dp83867: provide a workaround for incorrect RX_CTRL pin strap cxgb4: Support for get_ts_info ethtool method cxgb4: Add PTP Hardware Clock (PHC) support cxgb4: time stamping interface for PTP nfp: default to chained metadata prepend format nfp: remove legacy MAC address lookup nfp: improve order of interfaces in breakout mode net: macb: remove extraneous return when MACB_EXT_DESC is defined bpf: add missing break in for the TCP_BPF_SNDCWND_CLAMP case bpf: fix return in load_bpf_file mpls: fix rtm policy in mpls_getroute net, ax25: convert ax25_cb.refcount from atomic_t to refcount_t net, ax25: convert ax25_route.refcount from atomic_t to refcount_t net, ax25: convert ax25_uid_assoc.refcount from atomic_t to refcount_t net, sctp: convert sctp_ep_common.refcnt from atomic_t to refcount_t net, sctp: convert sctp_transport.refcnt from atomic_t to refcount_t net, sctp: convert sctp_chunk.refcnt from atomic_t to refcount_t net, sctp: convert sctp_datamsg.refcnt from atomic_t to refcount_t net, sctp: convert sctp_auth_bytes.refcnt from atomic_t to refcount_t ...
Diffstat (limited to 'drivers/net/phy')
-rw-r--r--drivers/net/phy/Kconfig10
-rw-r--r--drivers/net/phy/Makefile4
-rw-r--r--drivers/net/phy/broadcom.c30
-rw-r--r--drivers/net/phy/cortina.c118
-rw-r--r--drivers/net/phy/dp83867.c11
-rw-r--r--drivers/net/phy/lxt.c11
-rw-r--r--drivers/net/phy/marvell.c732
-rw-r--r--drivers/net/phy/marvell10g.c368
-rw-r--r--drivers/net/phy/mdio-mux.c26
-rw-r--r--drivers/net/phy/mdio-xgene.c74
-rw-r--r--drivers/net/phy/mdio-xgene.h3
-rw-r--r--drivers/net/phy/mdio_bus.c66
-rw-r--r--drivers/net/phy/micrel.c38
-rw-r--r--drivers/net/phy/microchip.c2
-rw-r--r--drivers/net/phy/phy-c45.c298
-rw-r--r--drivers/net/phy/phy.c63
-rw-r--r--drivers/net/phy/phy_device.c183
-rw-r--r--drivers/net/phy/smsc.c84
18 files changed, 1535 insertions, 586 deletions
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 3ab6c58..2dda720 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -235,6 +235,11 @@ config CICADA_PHY
---help---
Currently supports the cis8204
+config CORTINA_PHY
+ tristate "Cortina EDC CDR 10G Ethernet PHY"
+ ---help---
+ Currently supports the CS4340 phy.
+
config DAVICOM_PHY
tristate "Davicom PHYs"
---help---
@@ -288,6 +293,11 @@ config MARVELL_PHY
---help---
Currently has a driver for the 88E1011S
+config MARVELL_10G_PHY
+ tristate "Marvell Alaska 10Gbit PHYs"
+ ---help---
+ Support for the Marvell Alaska MV88X3310 and compatible PHYs.
+
config MESON_GXL_PHY
tristate "Amlogic Meson GXL Internal PHY"
depends on ARCH_MESON || COMPILE_TEST
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index e36db9a..8e9b9f3 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -1,6 +1,6 @@
# Makefile for Linux PHY drivers and MDIO bus drivers
-libphy-y := phy.o phy-core.o phy_device.o
+libphy-y := phy.o phy-c45.o phy-core.o phy_device.o
mdio-bus-y += mdio_bus.o mdio_device.o
ifdef CONFIG_MDIO_DEVICE
@@ -46,6 +46,7 @@ obj-$(CONFIG_BCM_CYGNUS_PHY) += bcm-cygnus.o
obj-$(CONFIG_BCM_NET_PHYLIB) += bcm-phy-lib.o
obj-$(CONFIG_BROADCOM_PHY) += broadcom.o
obj-$(CONFIG_CICADA_PHY) += cicada.o
+obj-$(CONFIG_CORTINA_PHY) += cortina.o
obj-$(CONFIG_DAVICOM_PHY) += davicom.o
obj-$(CONFIG_DP83640_PHY) += dp83640.o
obj-$(CONFIG_DP83848_PHY) += dp83848.o
@@ -56,6 +57,7 @@ obj-$(CONFIG_INTEL_XWAY_PHY) += intel-xway.o
obj-$(CONFIG_LSI_ET1011C_PHY) += et1011c.o
obj-$(CONFIG_LXT_PHY) += lxt.o
obj-$(CONFIG_MARVELL_PHY) += marvell.o
+obj-$(CONFIG_MARVELL_10G_PHY) += marvell10g.o
obj-$(CONFIG_MESON_GXL_PHY) += meson-gxl.o
obj-$(CONFIG_MICREL_KS8995MA) += spi_ks8995.o
obj-$(CONFIG_MICREL_PHY) += micrel.o
diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c
index a32dc5d..1e9ad30 100644
--- a/drivers/net/phy/broadcom.c
+++ b/drivers/net/phy/broadcom.c
@@ -540,7 +540,7 @@ static struct phy_driver broadcom_drivers[] = {
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM5411",
.features = PHY_GBIT_FEATURES,
- .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .flags = PHY_HAS_INTERRUPT,
.config_init = bcm54xx_config_init,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
@@ -551,7 +551,7 @@ static struct phy_driver broadcom_drivers[] = {
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM5421",
.features = PHY_GBIT_FEATURES,
- .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .flags = PHY_HAS_INTERRUPT,
.config_init = bcm54xx_config_init,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
@@ -562,7 +562,7 @@ static struct phy_driver broadcom_drivers[] = {
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM54210E",
.features = PHY_GBIT_FEATURES,
- .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .flags = PHY_HAS_INTERRUPT,
.config_init = bcm54xx_config_init,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
@@ -573,7 +573,7 @@ static struct phy_driver broadcom_drivers[] = {
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM5461",
.features = PHY_GBIT_FEATURES,
- .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .flags = PHY_HAS_INTERRUPT,
.config_init = bcm54xx_config_init,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
@@ -584,7 +584,7 @@ static struct phy_driver broadcom_drivers[] = {
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM54612E",
.features = PHY_GBIT_FEATURES,
- .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .flags = PHY_HAS_INTERRUPT,
.config_init = bcm54xx_config_init,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
@@ -595,7 +595,7 @@ static struct phy_driver broadcom_drivers[] = {
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM54616S",
.features = PHY_GBIT_FEATURES,
- .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .flags = PHY_HAS_INTERRUPT,
.config_init = bcm54xx_config_init,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
@@ -606,7 +606,7 @@ static struct phy_driver broadcom_drivers[] = {
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM5464",
.features = PHY_GBIT_FEATURES,
- .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .flags = PHY_HAS_INTERRUPT,
.config_init = bcm54xx_config_init,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
@@ -617,7 +617,7 @@ static struct phy_driver broadcom_drivers[] = {
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM5481",
.features = PHY_GBIT_FEATURES,
- .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .flags = PHY_HAS_INTERRUPT,
.config_init = bcm54xx_config_init,
.config_aneg = bcm5481_config_aneg,
.read_status = genphy_read_status,
@@ -628,7 +628,7 @@ static struct phy_driver broadcom_drivers[] = {
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM54810",
.features = PHY_GBIT_FEATURES,
- .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .flags = PHY_HAS_INTERRUPT,
.config_init = bcm54xx_config_init,
.config_aneg = bcm5481_config_aneg,
.read_status = genphy_read_status,
@@ -639,7 +639,7 @@ static struct phy_driver broadcom_drivers[] = {
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM5482",
.features = PHY_GBIT_FEATURES,
- .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .flags = PHY_HAS_INTERRUPT,
.config_init = bcm5482_config_init,
.config_aneg = genphy_config_aneg,
.read_status = bcm5482_read_status,
@@ -650,7 +650,7 @@ static struct phy_driver broadcom_drivers[] = {
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM50610",
.features = PHY_GBIT_FEATURES,
- .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .flags = PHY_HAS_INTERRUPT,
.config_init = bcm54xx_config_init,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
@@ -661,7 +661,7 @@ static struct phy_driver broadcom_drivers[] = {
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM50610M",
.features = PHY_GBIT_FEATURES,
- .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .flags = PHY_HAS_INTERRUPT,
.config_init = bcm54xx_config_init,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
@@ -672,7 +672,7 @@ static struct phy_driver broadcom_drivers[] = {
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM57780",
.features = PHY_GBIT_FEATURES,
- .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .flags = PHY_HAS_INTERRUPT,
.config_init = bcm54xx_config_init,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
@@ -683,7 +683,7 @@ static struct phy_driver broadcom_drivers[] = {
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCMAC131",
.features = PHY_BASIC_FEATURES,
- .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .flags = PHY_HAS_INTERRUPT,
.config_init = brcm_fet_config_init,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
@@ -694,7 +694,7 @@ static struct phy_driver broadcom_drivers[] = {
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM5241",
.features = PHY_BASIC_FEATURES,
- .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .flags = PHY_HAS_INTERRUPT,
.config_init = brcm_fet_config_init,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
diff --git a/drivers/net/phy/cortina.c b/drivers/net/phy/cortina.c
new file mode 100644
index 0000000..72f4228
--- /dev/null
+++ b/drivers/net/phy/cortina.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2017 NXP
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * CORTINA is a registered trademark of Cortina Systems, Inc.
+ *
+ */
+#include <linux/module.h>
+#include <linux/phy.h>
+
+#define PHY_ID_CS4340 0x13e51002
+
+#define VILLA_GLOBAL_CHIP_ID_LSB 0x0
+#define VILLA_GLOBAL_CHIP_ID_MSB 0x1
+
+#define VILLA_GLOBAL_GPIO_1_INTS 0x017
+
+static int cortina_read_reg(struct phy_device *phydev, u16 regnum)
+{
+ return mdiobus_read(phydev->mdio.bus, phydev->mdio.addr,
+ MII_ADDR_C45 | regnum);
+}
+
+static int cortina_config_aneg(struct phy_device *phydev)
+{
+ phydev->supported = SUPPORTED_10000baseT_Full;
+ phydev->advertising = SUPPORTED_10000baseT_Full;
+
+ return 0;
+}
+
+static int cortina_read_status(struct phy_device *phydev)
+{
+ int gpio_int_status, ret = 0;
+
+ gpio_int_status = cortina_read_reg(phydev, VILLA_GLOBAL_GPIO_1_INTS);
+ if (gpio_int_status < 0) {
+ ret = gpio_int_status;
+ goto err;
+ }
+
+ if (gpio_int_status & 0x8) {
+ /* up when edc_convergedS set */
+ phydev->speed = SPEED_10000;
+ phydev->duplex = DUPLEX_FULL;
+ phydev->link = 1;
+ } else {
+ phydev->link = 0;
+ }
+
+err:
+ return ret;
+}
+
+static int cortina_soft_reset(struct phy_device *phydev)
+{
+ return 0;
+}
+
+static int cortina_probe(struct phy_device *phydev)
+{
+ u32 phy_id = 0;
+ int id_lsb = 0, id_msb = 0;
+
+ /* Read device id from phy registers. */
+ id_lsb = cortina_read_reg(phydev, VILLA_GLOBAL_CHIP_ID_LSB);
+ if (id_lsb < 0)
+ return -ENXIO;
+
+ phy_id = id_lsb << 16;
+
+ id_msb = cortina_read_reg(phydev, VILLA_GLOBAL_CHIP_ID_MSB);
+ if (id_msb < 0)
+ return -ENXIO;
+
+ phy_id |= id_msb;
+
+ /* Make sure the device tree binding matched the driver with the
+ * right device.
+ */
+ if (phy_id != phydev->drv->phy_id) {
+ phydev_err(phydev, "Error matching phy with %s driver\n",
+ phydev->drv->name);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static struct phy_driver cortina_driver[] = {
+{
+ .phy_id = PHY_ID_CS4340,
+ .phy_id_mask = 0xffffffff,
+ .name = "Cortina CS4340",
+ .config_aneg = cortina_config_aneg,
+ .read_status = cortina_read_status,
+ .soft_reset = cortina_soft_reset,
+ .probe = cortina_probe,
+},
+};
+
+module_phy_driver(cortina_driver);
+
+static struct mdio_device_id __maybe_unused cortina_tbl[] = {
+ { PHY_ID_CS4340, 0xffffffff},
+ {},
+};
+
+MODULE_DEVICE_TABLE(mdio, cortina_tbl);
diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c
index b57f20e..c1ab976 100644
--- a/drivers/net/phy/dp83867.c
+++ b/drivers/net/phy/dp83867.c
@@ -91,6 +91,7 @@ struct dp83867_private {
int fifo_depth;
int io_impedance;
int port_mirroring;
+ bool rxctrl_strap_quirk;
};
static int dp83867_ack_interrupt(struct phy_device *phydev)
@@ -164,6 +165,9 @@ static int dp83867_of_init(struct phy_device *phydev)
else if (of_property_read_bool(of_node, "ti,min-output-impedance"))
dp83867->io_impedance = DP83867_IO_MUX_CFG_IO_IMPEDANCE_MIN;
+ dp83867->rxctrl_strap_quirk = of_property_read_bool(of_node,
+ "ti,dp83867-rxctrl-strap-quirk");
+
ret = of_property_read_u32(of_node, "ti,rx-internal-delay",
&dp83867->rx_id_delay);
if (ret &&
@@ -214,6 +218,13 @@ static int dp83867_config_init(struct phy_device *phydev)
dp83867 = (struct dp83867_private *)phydev->priv;
}
+ /* RX_DV/RX_CTRL strapped in mode 1 or mode 2 workaround */
+ if (dp83867->rxctrl_strap_quirk) {
+ val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4);
+ val &= ~BIT(7);
+ phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4, val);
+ }
+
if (phy_interface_is_rgmii(phydev)) {
val = phy_read(phydev, MII_DP83867_PHYCTRL);
if (val < 0)
diff --git a/drivers/net/phy/lxt.c b/drivers/net/phy/lxt.c
index 8d198a1f..09d2151 100644
--- a/drivers/net/phy/lxt.c
+++ b/drivers/net/phy/lxt.c
@@ -152,7 +152,6 @@ static int lxt973a2_read_status(struct phy_device *phydev)
int adv;
int err;
int lpa;
- int lpagb = 0;
/* Update the link, but return if there was an error */
err = lxt973a2_update_link(phydev);
@@ -178,18 +177,15 @@ static int lxt973a2_read_status(struct phy_device *phydev)
*/
} while (lpa == adv && retry--);
+ phydev->lp_advertising = mii_lpa_to_ethtool_lpa_t(lpa);
+
lpa &= adv;
phydev->speed = SPEED_10;
phydev->duplex = DUPLEX_HALF;
phydev->pause = phydev->asym_pause = 0;
- if (lpagb & (LPA_1000FULL | LPA_1000HALF)) {
- phydev->speed = SPEED_1000;
-
- if (lpagb & LPA_1000FULL)
- phydev->duplex = DUPLEX_FULL;
- } else if (lpa & (LPA_100FULL | LPA_100HALF)) {
+ if (lpa & (LPA_100FULL | LPA_100HALF)) {
phydev->speed = SPEED_100;
if (lpa & LPA_100FULL)
@@ -222,6 +218,7 @@ static int lxt973a2_read_status(struct phy_device *phydev)
phydev->speed = SPEED_10;
phydev->pause = phydev->asym_pause = 0;
+ phydev->lp_advertising = 0;
}
return 0;
diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
index 57297ba..5d314f1 100644
--- a/drivers/net/phy/marvell.c
+++ b/drivers/net/phy/marvell.c
@@ -41,6 +41,12 @@
#include <linux/uaccess.h>
#define MII_MARVELL_PHY_PAGE 22
+#define MII_MARVELL_COPPER_PAGE 0x00
+#define MII_MARVELL_FIBER_PAGE 0x01
+#define MII_MARVELL_MSCR_PAGE 0x02
+#define MII_MARVELL_LED_PAGE 0x03
+#define MII_MARVELL_MISC_TEST_PAGE 0x06
+#define MII_MARVELL_WOL_PAGE 0x11
#define MII_M1011_IEVENT 0x13
#define MII_M1011_IEVENT_CLEAR 0x0000
@@ -54,7 +60,6 @@
#define MII_M1011_PHY_SCR_MDI_X 0x0020
#define MII_M1011_PHY_SCR_AUTO_CROSS 0x0060
-#define MII_M1145_PHY_EXT_ADDR_PAGE 0x16
#define MII_M1145_PHY_EXT_SR 0x1b
#define MII_M1145_PHY_EXT_CR 0x14
#define MII_M1145_RGMII_RX_DELAY 0x0080
@@ -83,10 +88,6 @@
#define MII_M1111_HWCFG_FIBER_COPPER_AUTO 0x8000
#define MII_M1111_HWCFG_FIBER_COPPER_RES 0x2000
-#define MII_M1111_COPPER 0
-#define MII_M1111_FIBER 1
-
-#define MII_88E1121_PHY_MSCR_PAGE 2
#define MII_88E1121_PHY_MSCR_REG 21
#define MII_88E1121_PHY_MSCR_RX_DELAY BIT(5)
#define MII_88E1121_PHY_MSCR_TX_DELAY BIT(4)
@@ -112,7 +113,6 @@
#define MII_88E1318S_PHY_CSIER_WOL_EIE BIT(7)
/* LED Timer Control Register */
-#define MII_88E1318S_PHY_LED_PAGE 0x03
#define MII_88E1318S_PHY_LED_TCR 0x12
#define MII_88E1318S_PHY_LED_TCR_FORCE_INT BIT(15)
#define MII_88E1318S_PHY_LED_TCR_INTn_ENABLE BIT(7)
@@ -123,13 +123,11 @@
#define MII_88E1318S_PHY_MAGIC_PACKET_WORD1 0x18
#define MII_88E1318S_PHY_MAGIC_PACKET_WORD0 0x19
-#define MII_88E1318S_PHY_WOL_PAGE 0x11
#define MII_88E1318S_PHY_WOL_CTRL 0x10
#define MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS BIT(12)
#define MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE BIT(14)
#define MII_88E1121_PHY_LED_CTRL 16
-#define MII_88E1121_PHY_LED_PAGE 3
#define MII_88E1121_PHY_LED_DEF 0x0030
#define MII_M1011_PHY_STATUS 0x11
@@ -189,6 +187,29 @@ struct marvell_priv {
struct device *hwmon_dev;
};
+static int marvell_get_page(struct phy_device *phydev)
+{
+ return phy_read(phydev, MII_MARVELL_PHY_PAGE);
+}
+
+static int marvell_set_page(struct phy_device *phydev, int page)
+{
+ return phy_write(phydev, MII_MARVELL_PHY_PAGE, page);
+}
+
+static int marvell_get_set_page(struct phy_device *phydev, int page)
+{
+ int oldpage = marvell_get_page(phydev);
+
+ if (oldpage < 0)
+ return oldpage;
+
+ if (page != oldpage)
+ return marvell_set_page(phydev, page);
+
+ return 0;
+}
+
static int marvell_ack_interrupt(struct phy_device *phydev)
{
int err;
@@ -207,9 +228,11 @@ static int marvell_config_intr(struct phy_device *phydev)
int err;
if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
- err = phy_write(phydev, MII_M1011_IMASK, MII_M1011_IMASK_INIT);
+ err = phy_write(phydev, MII_M1011_IMASK,
+ MII_M1011_IMASK_INIT);
else
- err = phy_write(phydev, MII_M1011_IMASK, MII_M1011_IMASK_CLEAR);
+ err = phy_write(phydev, MII_M1011_IMASK,
+ MII_M1011_IMASK_CLEAR);
return err;
}
@@ -271,8 +294,7 @@ static int marvell_config_aneg(struct phy_device *phydev)
if (phydev->autoneg != AUTONEG_ENABLE) {
int bmcr;
- /*
- * A write to speed/duplex bits (that is performed by
+ /* A write to speed/duplex bits (that is performed by
* genphy_config_aneg() call above) must be followed by
* a software reset. Otherwise, the write has no effect.
*/
@@ -367,8 +389,7 @@ static int m88e1111_config_aneg(struct phy_device *phydev)
}
#ifdef CONFIG_OF_MDIO
-/*
- * Set and/or override some configuration registers based on the
+/* Set and/or override some configuration registers based on the
* marvell,reg-init property stored in the of_node for the phydev.
*
* marvell,reg-init = <reg-page reg mask value>,...;
@@ -394,7 +415,7 @@ static int marvell_of_reg_init(struct phy_device *phydev)
if (!paddr || len < (4 * sizeof(*paddr)))
return 0;
- saved_page = phy_read(phydev, MII_MARVELL_PHY_PAGE);
+ saved_page = marvell_get_page(phydev);
if (saved_page < 0)
return saved_page;
current_page = saved_page;
@@ -402,15 +423,15 @@ static int marvell_of_reg_init(struct phy_device *phydev)
ret = 0;
len /= sizeof(*paddr);
for (i = 0; i < len - 3; i += 4) {
- u16 reg_page = be32_to_cpup(paddr + i);
+ u16 page = be32_to_cpup(paddr + i);
u16 reg = be32_to_cpup(paddr + i + 1);
u16 mask = be32_to_cpup(paddr + i + 2);
u16 val_bits = be32_to_cpup(paddr + i + 3);
int val;
- if (reg_page != current_page) {
- current_page = reg_page;
- ret = phy_write(phydev, MII_MARVELL_PHY_PAGE, reg_page);
+ if (page != current_page) {
+ current_page = page;
+ ret = marvell_set_page(phydev, page);
if (ret < 0)
goto err;
}
@@ -429,11 +450,10 @@ static int marvell_of_reg_init(struct phy_device *phydev)
ret = phy_write(phydev, reg, val);
if (ret < 0)
goto err;
-
}
err:
if (current_page != saved_page) {
- i = phy_write(phydev, MII_MARVELL_PHY_PAGE, saved_page);
+ i = marvell_set_page(phydev, saved_page);
if (ret == 0)
ret = i;
}
@@ -450,15 +470,11 @@ static int m88e1121_config_aneg(struct phy_device *phydev)
{
int err, oldpage, mscr;
- oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE);
-
- err = phy_write(phydev, MII_MARVELL_PHY_PAGE,
- MII_88E1121_PHY_MSCR_PAGE);
- if (err < 0)
- return err;
+ oldpage = marvell_get_set_page(phydev, MII_MARVELL_MSCR_PAGE);
+ if (oldpage < 0)
+ return oldpage;
if (phy_interface_is_rgmii(phydev)) {
-
mscr = phy_read(phydev, MII_88E1121_PHY_MSCR_REG) &
MII_88E1121_PHY_MSCR_DELAY_MASK;
@@ -475,7 +491,7 @@ static int m88e1121_config_aneg(struct phy_device *phydev)
return err;
}
- phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage);
+ marvell_set_page(phydev, oldpage);
err = phy_write(phydev, MII_BMCR, BMCR_RESET);
if (err < 0)
@@ -493,12 +509,9 @@ static int m88e1318_config_aneg(struct phy_device *phydev)
{
int err, oldpage, mscr;
- oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE);
-
- err = phy_write(phydev, MII_MARVELL_PHY_PAGE,
- MII_88E1121_PHY_MSCR_PAGE);
- if (err < 0)
- return err;
+ oldpage = marvell_get_set_page(phydev, MII_MARVELL_MSCR_PAGE);
+ if (oldpage < 0)
+ return oldpage;
mscr = phy_read(phydev, MII_88E1318S_PHY_MSCR1_REG);
mscr |= MII_88E1318S_PHY_MSCR1_PAD_ODD;
@@ -507,7 +520,7 @@ static int m88e1318_config_aneg(struct phy_device *phydev)
if (err < 0)
return err;
- err = phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage);
+ err = marvell_set_page(phydev, oldpage);
if (err < 0)
return err;
@@ -607,7 +620,7 @@ static int m88e1510_config_aneg(struct phy_device *phydev)
{
int err;
- err = phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_M1111_COPPER);
+ err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
if (err < 0)
goto error;
@@ -617,7 +630,7 @@ static int m88e1510_config_aneg(struct phy_device *phydev)
goto error;
/* Then the fiber link */
- err = phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_M1111_FIBER);
+ err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
if (err < 0)
goto error;
@@ -625,10 +638,10 @@ static int m88e1510_config_aneg(struct phy_device *phydev)
if (err < 0)
goto error;
- return phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_M1111_COPPER);
+ return marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
error:
- phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_M1111_COPPER);
+ marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
return err;
}
@@ -651,7 +664,7 @@ static int m88e1116r_config_init(struct phy_device *phydev)
mdelay(500);
- err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0);
+ err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
if (err < 0)
return err;
@@ -663,7 +676,7 @@ static int m88e1116r_config_init(struct phy_device *phydev)
if (err < 0)
return err;
- err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 2);
+ err = marvell_set_page(phydev, MII_MARVELL_MSCR_PAGE);
if (err < 0)
return err;
temp = phy_read(phydev, MII_M1116R_CONTROL_REG_MAC);
@@ -672,7 +685,7 @@ static int m88e1116r_config_init(struct phy_device *phydev)
err = phy_write(phydev, MII_M1116R_CONTROL_REG_MAC, temp);
if (err < 0)
return err;
- err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0);
+ err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
if (err < 0)
return err;
@@ -706,103 +719,129 @@ static int m88e3016_config_init(struct phy_device *phydev)
return marvell_config_init(phydev);
}
-static int m88e1111_config_init(struct phy_device *phydev)
+static int m88e1111_config_init_rgmii(struct phy_device *phydev)
{
int err;
int temp;
- if (phy_interface_is_rgmii(phydev)) {
+ temp = phy_read(phydev, MII_M1111_PHY_EXT_CR);
+ if (temp < 0)
+ return temp;
- temp = phy_read(phydev, MII_M1111_PHY_EXT_CR);
- if (temp < 0)
- return temp;
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
+ temp |= (MII_M1111_RX_DELAY | MII_M1111_TX_DELAY);
+ } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
+ temp &= ~MII_M1111_TX_DELAY;
+ temp |= MII_M1111_RX_DELAY;
+ } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
+ temp &= ~MII_M1111_RX_DELAY;
+ temp |= MII_M1111_TX_DELAY;
+ }
- if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
- temp |= (MII_M1111_RX_DELAY | MII_M1111_TX_DELAY);
- } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
- temp &= ~MII_M1111_TX_DELAY;
- temp |= MII_M1111_RX_DELAY;
- } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
- temp &= ~MII_M1111_RX_DELAY;
- temp |= MII_M1111_TX_DELAY;
- }
+ err = phy_write(phydev, MII_M1111_PHY_EXT_CR, temp);
+ if (err < 0)
+ return err;
- err = phy_write(phydev, MII_M1111_PHY_EXT_CR, temp);
- if (err < 0)
- return err;
+ temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
+ if (temp < 0)
+ return temp;
- temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
- if (temp < 0)
- return temp;
+ temp &= ~(MII_M1111_HWCFG_MODE_MASK);
- temp &= ~(MII_M1111_HWCFG_MODE_MASK);
+ if (temp & MII_M1111_HWCFG_FIBER_COPPER_RES)
+ temp |= MII_M1111_HWCFG_MODE_FIBER_RGMII;
+ else
+ temp |= MII_M1111_HWCFG_MODE_COPPER_RGMII;
- if (temp & MII_M1111_HWCFG_FIBER_COPPER_RES)
- temp |= MII_M1111_HWCFG_MODE_FIBER_RGMII;
- else
- temp |= MII_M1111_HWCFG_MODE_COPPER_RGMII;
+ return phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
+}
- err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
- if (err < 0)
- return err;
- }
+static int m88e1111_config_init_sgmii(struct phy_device *phydev)
+{
+ int err;
+ int temp;
- if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
- temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
- if (temp < 0)
- return temp;
+ temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
+ if (temp < 0)
+ return temp;
- temp &= ~(MII_M1111_HWCFG_MODE_MASK);
- temp |= MII_M1111_HWCFG_MODE_SGMII_NO_CLK;
- temp |= MII_M1111_HWCFG_FIBER_COPPER_AUTO;
+ temp &= ~(MII_M1111_HWCFG_MODE_MASK);
+ temp |= MII_M1111_HWCFG_MODE_SGMII_NO_CLK;
+ temp |= MII_M1111_HWCFG_FIBER_COPPER_AUTO;
- err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
- if (err < 0)
- return err;
+ err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
+ if (err < 0)
+ return err;
- /* make sure copper is selected */
- err = phy_read(phydev, MII_M1145_PHY_EXT_ADDR_PAGE);
- if (err < 0)
- return err;
+ /* make sure copper is selected */
+ return marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
+}
- err = phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE,
- err & (~0xff));
- if (err < 0)
- return err;
- }
+static int m88e1111_config_init_rtbi(struct phy_device *phydev)
+{
+ int err;
+ int temp;
- if (phydev->interface == PHY_INTERFACE_MODE_RTBI) {
- temp = phy_read(phydev, MII_M1111_PHY_EXT_CR);
- if (temp < 0)
- return temp;
- temp |= (MII_M1111_RX_DELAY | MII_M1111_TX_DELAY);
- err = phy_write(phydev, MII_M1111_PHY_EXT_CR, temp);
- if (err < 0)
- return err;
+ temp = phy_read(phydev, MII_M1111_PHY_EXT_CR);
+ if (temp < 0)
+ return temp;
- temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
- if (temp < 0)
- return temp;
- temp &= ~(MII_M1111_HWCFG_MODE_MASK | MII_M1111_HWCFG_FIBER_COPPER_RES);
- temp |= 0x7 | MII_M1111_HWCFG_FIBER_COPPER_AUTO;
- err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
- if (err < 0)
+ temp |= (MII_M1111_RX_DELAY | MII_M1111_TX_DELAY);
+ err = phy_write(phydev, MII_M1111_PHY_EXT_CR, temp);
+ if (err < 0)
+ return err;
+
+ temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
+ if (temp < 0)
+ return temp;
+
+ temp &= ~(MII_M1111_HWCFG_MODE_MASK |
+ MII_M1111_HWCFG_FIBER_COPPER_RES);
+ temp |= 0x7 | MII_M1111_HWCFG_FIBER_COPPER_AUTO;
+
+ err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
+ if (err < 0)
+ return err;
+
+ /* soft reset */
+ err = phy_write(phydev, MII_BMCR, BMCR_RESET);
+ if (err < 0)
+ return err;
+
+ do
+ temp = phy_read(phydev, MII_BMCR);
+ while (temp & BMCR_RESET);
+
+ temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
+ if (temp < 0)
+ return temp;
+
+ temp &= ~(MII_M1111_HWCFG_MODE_MASK |
+ MII_M1111_HWCFG_FIBER_COPPER_RES);
+ temp |= MII_M1111_HWCFG_MODE_COPPER_RTBI |
+ MII_M1111_HWCFG_FIBER_COPPER_AUTO;
+
+ return phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
+}
+
+static int m88e1111_config_init(struct phy_device *phydev)
+{
+ int err;
+
+ if (phy_interface_is_rgmii(phydev)) {
+ err = m88e1111_config_init_rgmii(phydev);
+ if (err)
return err;
+ }
- /* soft reset */
- err = phy_write(phydev, MII_BMCR, BMCR_RESET);
+ if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
+ err = m88e1111_config_init_sgmii(phydev);
if (err < 0)
return err;
- do
- temp = phy_read(phydev, MII_BMCR);
- while (temp & BMCR_RESET);
+ }
- temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
- if (temp < 0)
- return temp;
- temp &= ~(MII_M1111_HWCFG_MODE_MASK | MII_M1111_HWCFG_FIBER_COPPER_RES);
- temp |= MII_M1111_HWCFG_MODE_COPPER_RTBI | MII_M1111_HWCFG_FIBER_COPPER_AUTO;
- err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
+ if (phydev->interface == PHY_INTERFACE_MODE_RTBI) {
+ err = m88e1111_config_init_rtbi(phydev);
if (err < 0)
return err;
}
@@ -818,11 +857,9 @@ static int m88e1121_config_init(struct phy_device *phydev)
{
int err, oldpage;
- oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE);
-
- err = phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_88E1121_PHY_LED_PAGE);
- if (err < 0)
- return err;
+ oldpage = marvell_get_set_page(phydev, MII_MARVELL_LED_PAGE);
+ if (oldpage < 0)
+ return oldpage;
/* Default PHY LED config: LED[0] .. Link, LED[1] .. Activity */
err = phy_write(phydev, MII_88E1121_PHY_LED_CTRL,
@@ -830,7 +867,7 @@ static int m88e1121_config_init(struct phy_device *phydev)
if (err < 0)
return err;
- phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage);
+ marvell_set_page(phydev, oldpage);
/* Set marvell,reg-init configuration from device tree */
return marvell_config_init(phydev);
@@ -844,7 +881,7 @@ static int m88e1510_config_init(struct phy_device *phydev)
/* SGMII-to-Copper mode initialization */
if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
/* Select page 18 */
- err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 18);
+ err = marvell_set_page(phydev, 18);
if (err < 0)
return err;
@@ -863,7 +900,7 @@ static int m88e1510_config_init(struct phy_device *phydev)
return err;
/* Reset page selection */
- err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0);
+ err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
if (err < 0)
return err;
}
@@ -893,7 +930,7 @@ static int m88e1118_config_init(struct phy_device *phydev)
int err;
/* Change address */
- err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0002);
+ err = marvell_set_page(phydev, MII_MARVELL_MSCR_PAGE);
if (err < 0)
return err;
@@ -903,7 +940,7 @@ static int m88e1118_config_init(struct phy_device *phydev)
return err;
/* Change address */
- err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0003);
+ err = marvell_set_page(phydev, MII_MARVELL_LED_PAGE);
if (err < 0)
return err;
@@ -920,7 +957,7 @@ static int m88e1118_config_init(struct phy_device *phydev)
return err;
/* Reset address */
- err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0);
+ err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
if (err < 0)
return err;
@@ -932,7 +969,7 @@ static int m88e1149_config_init(struct phy_device *phydev)
int err;
/* Change address */
- err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0002);
+ err = marvell_set_page(phydev, MII_MARVELL_MSCR_PAGE);
if (err < 0)
return err;
@@ -946,17 +983,70 @@ static int m88e1149_config_init(struct phy_device *phydev)
return err;
/* Reset address */
- err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0);
+ err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
if (err < 0)
return err;
return phy_write(phydev, MII_BMCR, BMCR_RESET);
}
+static int m88e1145_config_init_rgmii(struct phy_device *phydev)
+{
+ int err;
+ int temp = phy_read(phydev, MII_M1145_PHY_EXT_CR);
+
+ if (temp < 0)
+ return temp;
+
+ temp |= (MII_M1145_RGMII_RX_DELAY | MII_M1145_RGMII_TX_DELAY);
+
+ err = phy_write(phydev, MII_M1145_PHY_EXT_CR, temp);
+ if (err < 0)
+ return err;
+
+ if (phydev->dev_flags & MARVELL_PHY_M1145_FLAGS_RESISTANCE) {
+ err = phy_write(phydev, 0x1d, 0x0012);
+ if (err < 0)
+ return err;
+
+ temp = phy_read(phydev, 0x1e);
+ if (temp < 0)
+ return temp;
+
+ temp &= 0xf03f;
+ temp |= 2 << 9; /* 36 ohm */
+ temp |= 2 << 6; /* 39 ohm */
+
+ err = phy_write(phydev, 0x1e, temp);
+ if (err < 0)
+ return err;
+
+ err = phy_write(phydev, 0x1d, 0x3);
+ if (err < 0)
+ return err;
+
+ err = phy_write(phydev, 0x1e, 0x8000);
+ }
+ return err;
+}
+
+static int m88e1145_config_init_sgmii(struct phy_device *phydev)
+{
+ int temp = phy_read(phydev, MII_M1145_PHY_EXT_SR);
+
+ if (temp < 0)
+ return temp;
+
+ temp &= ~MII_M1145_HWCFG_MODE_MASK;
+ temp |= MII_M1145_HWCFG_MODE_SGMII_NO_CLK;
+ temp |= MII_M1145_HWCFG_FIBER_COPPER_AUTO;
+
+ return phy_write(phydev, MII_M1145_PHY_EXT_SR, temp);
+}
+
static int m88e1145_config_init(struct phy_device *phydev)
{
int err;
- int temp;
/* Take care of errata E0 & E1 */
err = phy_write(phydev, 0x1d, 0x001b);
@@ -976,53 +1066,13 @@ static int m88e1145_config_init(struct phy_device *phydev)
return err;
if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
- int temp = phy_read(phydev, MII_M1145_PHY_EXT_CR);
- if (temp < 0)
- return temp;
-
- temp |= (MII_M1145_RGMII_RX_DELAY | MII_M1145_RGMII_TX_DELAY);
-
- err = phy_write(phydev, MII_M1145_PHY_EXT_CR, temp);
+ err = m88e1145_config_init_rgmii(phydev);
if (err < 0)
return err;
-
- if (phydev->dev_flags & MARVELL_PHY_M1145_FLAGS_RESISTANCE) {
- err = phy_write(phydev, 0x1d, 0x0012);
- if (err < 0)
- return err;
-
- temp = phy_read(phydev, 0x1e);
- if (temp < 0)
- return temp;
-
- temp &= 0xf03f;
- temp |= 2 << 9; /* 36 ohm */
- temp |= 2 << 6; /* 39 ohm */
-
- err = phy_write(phydev, 0x1e, temp);
- if (err < 0)
- return err;
-
- err = phy_write(phydev, 0x1d, 0x3);
- if (err < 0)
- return err;
-
- err = phy_write(phydev, 0x1e, 0x8000);
- if (err < 0)
- return err;
- }
}
if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
- temp = phy_read(phydev, MII_M1145_PHY_EXT_SR);
- if (temp < 0)
- return temp;
-
- temp &= ~MII_M1145_HWCFG_MODE_MASK;
- temp |= MII_M1145_HWCFG_MODE_SGMII_NO_CLK;
- temp |= MII_M1145_HWCFG_FIBER_COPPER_AUTO;
-
- err = phy_write(phydev, MII_M1145_PHY_EXT_SR, temp);
+ err = m88e1145_config_init_sgmii(phydev);
if (err < 0)
return err;
}
@@ -1065,7 +1115,8 @@ static int marvell_update_link(struct phy_device *phydev, int fiber)
int status;
/* Use the generic register for copper link, or specific
- * register for fiber case */
+ * register for fiber case
+ */
if (fiber) {
status = phy_read(phydev, MII_M1011_PHY_STATUS);
if (status < 0)
@@ -1082,123 +1133,136 @@ static int marvell_update_link(struct phy_device *phydev, int fiber)
return 0;
}
-/* marvell_read_status_page
- *
- * Description:
- * Check the link, then figure out the current state
- * by comparing what we advertise with what the link partner
- * advertises. Start by checking the gigabit possibilities,
- * then move on to 10/100.
- */
-static int marvell_read_status_page(struct phy_device *phydev, int page)
+static int marvell_read_status_page_an(struct phy_device *phydev,
+ int fiber)
{
- int adv;
- int err;
+ int status;
int lpa;
int lpagb;
- int status = 0;
- int fiber;
-
- /* Detect and update the link, but return if there
- * was an error */
- if (page == MII_M1111_FIBER)
- fiber = 1;
- else
- fiber = 0;
- err = marvell_update_link(phydev, fiber);
- if (err)
- return err;
+ status = phy_read(phydev, MII_M1011_PHY_STATUS);
+ if (status < 0)
+ return status;
- if (AUTONEG_ENABLE == phydev->autoneg) {
- status = phy_read(phydev, MII_M1011_PHY_STATUS);
- if (status < 0)
- return status;
+ lpa = phy_read(phydev, MII_LPA);
+ if (lpa < 0)
+ return lpa;
- lpa = phy_read(phydev, MII_LPA);
- if (lpa < 0)
- return lpa;
+ lpagb = phy_read(phydev, MII_STAT1000);
+ if (lpagb < 0)
+ return lpagb;
- lpagb = phy_read(phydev, MII_STAT1000);
- if (lpagb < 0)
- return lpagb;
+ if (status & MII_M1011_PHY_STATUS_FULLDUPLEX)
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
- adv = phy_read(phydev, MII_ADVERTISE);
- if (adv < 0)
- return adv;
+ status = status & MII_M1011_PHY_STATUS_SPD_MASK;
+ phydev->pause = 0;
+ phydev->asym_pause = 0;
- if (status & MII_M1011_PHY_STATUS_FULLDUPLEX)
- phydev->duplex = DUPLEX_FULL;
- else
- phydev->duplex = DUPLEX_HALF;
+ switch (status) {
+ case MII_M1011_PHY_STATUS_1000:
+ phydev->speed = SPEED_1000;
+ break;
- status = status & MII_M1011_PHY_STATUS_SPD_MASK;
- phydev->pause = phydev->asym_pause = 0;
+ case MII_M1011_PHY_STATUS_100:
+ phydev->speed = SPEED_100;
+ break;
- switch (status) {
- case MII_M1011_PHY_STATUS_1000:
- phydev->speed = SPEED_1000;
- break;
+ default:
+ phydev->speed = SPEED_10;
+ break;
+ }
- case MII_M1011_PHY_STATUS_100:
- phydev->speed = SPEED_100;
- break;
+ if (!fiber) {
+ phydev->lp_advertising =
+ mii_stat1000_to_ethtool_lpa_t(lpagb) |
+ mii_lpa_to_ethtool_lpa_t(lpa);
- default:
- phydev->speed = SPEED_10;
- break;
+ if (phydev->duplex == DUPLEX_FULL) {
+ phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0;
+ phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0;
}
-
- if (!fiber) {
- phydev->lp_advertising = mii_stat1000_to_ethtool_lpa_t(lpagb) |
- mii_lpa_to_ethtool_lpa_t(lpa);
-
- if (phydev->duplex == DUPLEX_FULL) {
- phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0;
- phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0;
- }
- } else {
- /* The fiber link is only 1000M capable */
- phydev->lp_advertising = fiber_lpa_to_ethtool_lpa_t(lpa);
-
- if (phydev->duplex == DUPLEX_FULL) {
- if (!(lpa & LPA_PAUSE_FIBER)) {
- phydev->pause = 0;
- phydev->asym_pause = 0;
- } else if ((lpa & LPA_PAUSE_ASYM_FIBER)) {
- phydev->pause = 1;
- phydev->asym_pause = 1;
- } else {
- phydev->pause = 1;
- phydev->asym_pause = 0;
- }
+ } else {
+ /* The fiber link is only 1000M capable */
+ phydev->lp_advertising = fiber_lpa_to_ethtool_lpa_t(lpa);
+
+ if (phydev->duplex == DUPLEX_FULL) {
+ if (!(lpa & LPA_PAUSE_FIBER)) {
+ phydev->pause = 0;
+ phydev->asym_pause = 0;
+ } else if ((lpa & LPA_PAUSE_ASYM_FIBER)) {
+ phydev->pause = 1;
+ phydev->asym_pause = 1;
+ } else {
+ phydev->pause = 1;
+ phydev->asym_pause = 0;
}
}
- } else {
- int bmcr = phy_read(phydev, MII_BMCR);
+ }
+ return 0;
+}
- if (bmcr < 0)
- return bmcr;
+static int marvell_read_status_page_fixed(struct phy_device *phydev)
+{
+ int bmcr = phy_read(phydev, MII_BMCR);
- if (bmcr & BMCR_FULLDPLX)
- phydev->duplex = DUPLEX_FULL;
- else
- phydev->duplex = DUPLEX_HALF;
+ if (bmcr < 0)
+ return bmcr;
- if (bmcr & BMCR_SPEED1000)
- phydev->speed = SPEED_1000;
- else if (bmcr & BMCR_SPEED100)
- phydev->speed = SPEED_100;
- else
- phydev->speed = SPEED_10;
+ if (bmcr & BMCR_FULLDPLX)
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
- phydev->pause = phydev->asym_pause = 0;
- phydev->lp_advertising = 0;
- }
+ if (bmcr & BMCR_SPEED1000)
+ phydev->speed = SPEED_1000;
+ else if (bmcr & BMCR_SPEED100)
+ phydev->speed = SPEED_100;
+ else
+ phydev->speed = SPEED_10;
+
+ phydev->pause = 0;
+ phydev->asym_pause = 0;
+ phydev->lp_advertising = 0;
return 0;
}
+/* marvell_read_status_page
+ *
+ * Description:
+ * Check the link, then figure out the current state
+ * by comparing what we advertise with what the link partner
+ * advertises. Start by checking the gigabit possibilities,
+ * then move on to 10/100.
+ */
+static int marvell_read_status_page(struct phy_device *phydev, int page)
+{
+ int fiber;
+ int err;
+
+ /* Detect and update the link, but return if there
+ * was an error
+ */
+ if (page == MII_MARVELL_FIBER_PAGE)
+ fiber = 1;
+ else
+ fiber = 0;
+
+ err = marvell_update_link(phydev, fiber);
+ if (err)
+ return err;
+
+ if (phydev->autoneg == AUTONEG_ENABLE)
+ err = marvell_read_status_page_an(phydev, fiber);
+ else
+ err = marvell_read_status_page_fixed(phydev);
+
+ return err;
+}
+
/* marvell_read_status
*
* Some Marvell's phys have two modes: fiber and copper.
@@ -1215,33 +1279,34 @@ static int marvell_read_status(struct phy_device *phydev)
/* Check the fiber mode first */
if (phydev->supported & SUPPORTED_FIBRE &&
phydev->interface != PHY_INTERFACE_MODE_SGMII) {
- err = phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_M1111_FIBER);
+ err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
if (err < 0)
goto error;
- err = marvell_read_status_page(phydev, MII_M1111_FIBER);
+ err = marvell_read_status_page(phydev, MII_MARVELL_FIBER_PAGE);
if (err < 0)
goto error;
- /* If the fiber link is up, it is the selected and used link.
- * In this case, we need to stay in the fiber page.
- * Please to be careful about that, avoid to restore Copper page
- * in other functions which could break the behaviour
- * for some fiber phy like 88E1512.
- * */
+ /* If the fiber link is up, it is the selected and
+ * used link. In this case, we need to stay in the
+ * fiber page. Please to be careful about that, avoid
+ * to restore Copper page in other functions which
+ * could break the behaviour for some fiber phy like
+ * 88E1512.
+ */
if (phydev->link)
return 0;
/* If fiber link is down, check and save copper mode state */
- err = phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_M1111_COPPER);
+ err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
if (err < 0)
goto error;
}
- return marvell_read_status_page(phydev, MII_M1111_COPPER);
+ return marvell_read_status_page(phydev, MII_MARVELL_COPPER_PAGE);
error:
- phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_M1111_COPPER);
+ marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
return err;
}
@@ -1256,7 +1321,7 @@ static int marvell_suspend(struct phy_device *phydev)
/* Suspend the fiber mode first */
if (!(phydev->supported & SUPPORTED_FIBRE)) {
- err = phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_M1111_FIBER);
+ err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
if (err < 0)
goto error;
@@ -1266,7 +1331,7 @@ static int marvell_suspend(struct phy_device *phydev)
goto error;
/* Then, the copper link */
- err = phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_M1111_COPPER);
+ err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
if (err < 0)
goto error;
}
@@ -1275,7 +1340,7 @@ static int marvell_suspend(struct phy_device *phydev)
return genphy_suspend(phydev);
error:
- phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_M1111_COPPER);
+ marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
return err;
}
@@ -1290,7 +1355,7 @@ static int marvell_resume(struct phy_device *phydev)
/* Resume the fiber mode first */
if (!(phydev->supported & SUPPORTED_FIBRE)) {
- err = phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_M1111_FIBER);
+ err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
if (err < 0)
goto error;
@@ -1300,7 +1365,7 @@ static int marvell_resume(struct phy_device *phydev)
goto error;
/* Then, the copper link */
- err = phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_M1111_COPPER);
+ err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
if (err < 0)
goto error;
}
@@ -1309,13 +1374,14 @@ static int marvell_resume(struct phy_device *phydev)
return genphy_resume(phydev);
error:
- phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_M1111_COPPER);
+ marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
return err;
}
static int marvell_aneg_done(struct phy_device *phydev)
{
int retval = phy_read(phydev, MII_M1011_PHY_STATUS);
+
return (retval < 0) ? retval : (retval & MII_M1011_PHY_STATUS_RESOLVED);
}
@@ -1331,32 +1397,33 @@ static int m88e1121_did_interrupt(struct phy_device *phydev)
return 0;
}
-static void m88e1318_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
+static void m88e1318_get_wol(struct phy_device *phydev,
+ struct ethtool_wolinfo *wol)
{
wol->supported = WAKE_MAGIC;
wol->wolopts = 0;
- if (phy_write(phydev, MII_MARVELL_PHY_PAGE,
- MII_88E1318S_PHY_WOL_PAGE) < 0)
+ if (marvell_set_page(phydev, MII_MARVELL_WOL_PAGE) < 0)
return;
if (phy_read(phydev, MII_88E1318S_PHY_WOL_CTRL) &
MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE)
wol->wolopts |= WAKE_MAGIC;
- if (phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x00) < 0)
+ if (marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE) < 0)
return;
}
-static int m88e1318_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
+static int m88e1318_set_wol(struct phy_device *phydev,
+ struct ethtool_wolinfo *wol)
{
int err, oldpage, temp;
- oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE);
+ oldpage = marvell_get_page(phydev);
if (wol->wolopts & WAKE_MAGIC) {
/* Explicitly switch to page 0x00, just to be sure */
- err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x00);
+ err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
if (err < 0)
return err;
@@ -1367,8 +1434,7 @@ static int m88e1318_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *w
if (err < 0)
return err;
- err = phy_write(phydev, MII_MARVELL_PHY_PAGE,
- MII_88E1318S_PHY_LED_PAGE);
+ err = marvell_set_page(phydev, MII_MARVELL_LED_PAGE);
if (err < 0)
return err;
@@ -1381,8 +1447,7 @@ static int m88e1318_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *w
if (err < 0)
return err;
- err = phy_write(phydev, MII_MARVELL_PHY_PAGE,
- MII_88E1318S_PHY_WOL_PAGE);
+ err = marvell_set_page(phydev, MII_MARVELL_WOL_PAGE);
if (err < 0)
return err;
@@ -1411,8 +1476,7 @@ static int m88e1318_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *w
if (err < 0)
return err;
} else {
- err = phy_write(phydev, MII_MARVELL_PHY_PAGE,
- MII_88E1318S_PHY_WOL_PAGE);
+ err = marvell_set_page(phydev, MII_MARVELL_WOL_PAGE);
if (err < 0)
return err;
@@ -1425,7 +1489,7 @@ static int m88e1318_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *w
return err;
}
- err = phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage);
+ err = marvell_set_page(phydev, oldpage);
if (err < 0)
return err;
@@ -1457,13 +1521,11 @@ static u64 marvell_get_stat(struct phy_device *phydev, int i)
{
struct marvell_hw_stat stat = marvell_hw_stats[i];
struct marvell_priv *priv = phydev->priv;
- int err, oldpage, val;
+ int oldpage, val;
u64 ret;
- oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE);
- err = phy_write(phydev, MII_MARVELL_PHY_PAGE,
- stat.page);
- if (err < 0)
+ oldpage = marvell_get_set_page(phydev, stat.page);
+ if (oldpage < 0)
return UINT64_MAX;
val = phy_read(phydev, stat.reg);
@@ -1475,7 +1537,7 @@ static u64 marvell_get_stat(struct phy_device *phydev, int i)
ret = priv->stats[i];
}
- phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage);
+ marvell_set_page(phydev, oldpage);
return ret;
}
@@ -1492,6 +1554,7 @@ static void marvell_get_stats(struct phy_device *phydev,
#ifdef CONFIG_HWMON
static int m88e1121_get_temp(struct phy_device *phydev, long *temp)
{
+ int oldpage;
int ret;
int val;
@@ -1499,9 +1562,11 @@ static int m88e1121_get_temp(struct phy_device *phydev, long *temp)
mutex_lock(&phydev->lock);
- ret = phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE, 0x6);
- if (ret < 0)
- goto error;
+ oldpage = marvell_get_set_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
+ if (oldpage < 0) {
+ mutex_unlock(&phydev->lock);
+ return oldpage;
+ }
/* Enable temperature sensor */
ret = phy_read(phydev, MII_88E1121_MISC_TEST);
@@ -1531,7 +1596,7 @@ static int m88e1121_get_temp(struct phy_device *phydev, long *temp)
*temp = ((val & MII_88E1121_MISC_TEST_TEMP_MASK) - 5) * 5000;
error:
- phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE, 0x0);
+ marvell_set_page(phydev, oldpage);
mutex_unlock(&phydev->lock);
return ret;
@@ -1608,15 +1673,18 @@ static const struct hwmon_chip_info m88e1121_hwmon_chip_info = {
static int m88e1510_get_temp(struct phy_device *phydev, long *temp)
{
+ int oldpage;
int ret;
*temp = 0;
mutex_lock(&phydev->lock);
- ret = phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE, 0x6);
- if (ret < 0)
- goto error;
+ oldpage = marvell_get_set_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
+ if (oldpage < 0) {
+ mutex_unlock(&phydev->lock);
+ return oldpage;
+ }
ret = phy_read(phydev, MII_88E1510_TEMP_SENSOR);
if (ret < 0)
@@ -1625,23 +1693,26 @@ static int m88e1510_get_temp(struct phy_device *phydev, long *temp)
*temp = ((ret & MII_88E1510_TEMP_SENSOR_MASK) - 25) * 1000;
error:
- phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE, 0x0);
+ marvell_set_page(phydev, oldpage);
mutex_unlock(&phydev->lock);
return ret;
}
-int m88e1510_get_temp_critical(struct phy_device *phydev, long *temp)
+static int m88e1510_get_temp_critical(struct phy_device *phydev, long *temp)
{
+ int oldpage;
int ret;
*temp = 0;
mutex_lock(&phydev->lock);
- ret = phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE, 0x6);
- if (ret < 0)
- goto error;
+ oldpage = marvell_get_set_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
+ if (oldpage < 0) {
+ mutex_unlock(&phydev->lock);
+ return oldpage;
+ }
ret = phy_read(phydev, MII_88E1121_MISC_TEST);
if (ret < 0)
@@ -1653,21 +1724,24 @@ int m88e1510_get_temp_critical(struct phy_device *phydev, long *temp)
*temp *= 1000;
error:
- phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE, 0x0);
+ marvell_set_page(phydev, oldpage);
mutex_unlock(&phydev->lock);
return ret;
}
-int m88e1510_set_temp_critical(struct phy_device *phydev, long temp)
+static int m88e1510_set_temp_critical(struct phy_device *phydev, long temp)
{
+ int oldpage;
int ret;
mutex_lock(&phydev->lock);
- ret = phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE, 0x6);
- if (ret < 0)
- goto error;
+ oldpage = marvell_get_set_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
+ if (oldpage < 0) {
+ mutex_unlock(&phydev->lock);
+ return oldpage;
+ }
ret = phy_read(phydev, MII_88E1121_MISC_TEST);
if (ret < 0)
@@ -1680,23 +1754,26 @@ int m88e1510_set_temp_critical(struct phy_device *phydev, long temp)
(temp << MII_88E1510_MISC_TEST_TEMP_THRESHOLD_SHIFT));
error:
- phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE, 0x0);
+ marvell_set_page(phydev, oldpage);
mutex_unlock(&phydev->lock);
return ret;
}
-int m88e1510_get_temp_alarm(struct phy_device *phydev, long *alarm)
+static int m88e1510_get_temp_alarm(struct phy_device *phydev, long *alarm)
{
+ int oldpage;
int ret;
*alarm = false;
mutex_lock(&phydev->lock);
- ret = phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE, 0x6);
- if (ret < 0)
- goto error;
+ oldpage = marvell_get_set_page(phydev, MII_MARVELL_MISC_TEST_PAGE);
+ if (oldpage < 0) {
+ mutex_unlock(&phydev->lock);
+ return oldpage;
+ }
ret = phy_read(phydev, MII_88E1121_MISC_TEST);
if (ret < 0)
@@ -1704,7 +1781,7 @@ int m88e1510_get_temp_alarm(struct phy_device *phydev, long *alarm)
*alarm = !!(ret & MII_88E1510_MISC_TEST_TEMP_IRQ);
error:
- phy_write(phydev, MII_M1145_PHY_EXT_ADDR_PAGE, 0x0);
+ marvell_set_page(phydev, oldpage);
mutex_unlock(&phydev->lock);
return ret;
@@ -2094,6 +2171,7 @@ static struct phy_driver marvell_drivers[] = {
.get_sset_count = marvell_get_sset_count,
.get_strings = marvell_get_strings,
.get_stats = marvell_get_stats,
+ .set_loopback = genphy_loopback,
},
{
.phy_id = MARVELL_PHY_ID_88E1540,
diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c
new file mode 100644
index 0000000..aebc08b
--- /dev/null
+++ b/drivers/net/phy/marvell10g.c
@@ -0,0 +1,368 @@
+/*
+ * Marvell 10G 88x3310 PHY driver
+ *
+ * Based upon the ID registers, this PHY appears to be a mixture of IPs
+ * from two different companies.
+ *
+ * There appears to be several different data paths through the PHY which
+ * are automatically managed by the PHY. The following has been determined
+ * via observation and experimentation:
+ *
+ * SGMII PHYXS -- BASE-T PCS -- 10G PMA -- AN -- Copper (for <= 1G)
+ * 10GBASE-KR PHYXS -- BASE-T PCS -- 10G PMA -- AN -- Copper (for 10G)
+ * 10GBASE-KR PHYXS -- BASE-R PCS -- Fiber
+ *
+ * If both the fiber and copper ports are connected, the first to gain
+ * link takes priority and the other port is completely locked out.
+ */
+#include <linux/phy.h>
+
+enum {
+ MV_PCS_BASE_T = 0x0000,
+ MV_PCS_BASE_R = 0x1000,
+ MV_PCS_1000BASEX = 0x2000,
+
+ /* These registers appear at 0x800X and 0xa00X - the 0xa00X control
+ * registers appear to set themselves to the 0x800X when AN is
+ * restarted, but status registers appear readable from either.
+ */
+ MV_AN_CTRL1000 = 0x8000, /* 1000base-T control register */
+ MV_AN_STAT1000 = 0x8001, /* 1000base-T status register */
+
+ /* This register appears to reflect the copper status */
+ MV_AN_RESULT = 0xa016,
+ MV_AN_RESULT_SPD_10 = BIT(12),
+ MV_AN_RESULT_SPD_100 = BIT(13),
+ MV_AN_RESULT_SPD_1000 = BIT(14),
+ MV_AN_RESULT_SPD_10000 = BIT(15),
+};
+
+static int mv3310_modify(struct phy_device *phydev, int devad, u16 reg,
+ u16 mask, u16 bits)
+{
+ int old, val, ret;
+
+ old = phy_read_mmd(phydev, devad, reg);
+ if (old < 0)
+ return old;
+
+ val = (old & ~mask) | (bits & mask);
+ if (val == old)
+ return 0;
+
+ ret = phy_write_mmd(phydev, devad, reg, val);
+
+ return ret < 0 ? ret : 1;
+}
+
+static int mv3310_probe(struct phy_device *phydev)
+{
+ u32 mmd_mask = MDIO_DEVS_PMAPMD | MDIO_DEVS_AN;
+
+ if (!phydev->is_c45 ||
+ (phydev->c45_ids.devices_in_package & mmd_mask) != mmd_mask)
+ return -ENODEV;
+
+ return 0;
+}
+
+/*
+ * Resetting the MV88X3310 causes it to become non-responsive. Avoid
+ * setting the reset bit(s).
+ */
+static int mv3310_soft_reset(struct phy_device *phydev)
+{
+ return 0;
+}
+
+static int mv3310_config_init(struct phy_device *phydev)
+{
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = { 0, };
+ u32 mask;
+ int val;
+
+ /* Check that the PHY interface type is compatible */
+ if (phydev->interface != PHY_INTERFACE_MODE_SGMII &&
+ phydev->interface != PHY_INTERFACE_MODE_XGMII &&
+ phydev->interface != PHY_INTERFACE_MODE_XAUI &&
+ phydev->interface != PHY_INTERFACE_MODE_RXAUI &&
+ phydev->interface != PHY_INTERFACE_MODE_10GKR)
+ return -ENODEV;
+
+ __set_bit(ETHTOOL_LINK_MODE_Pause_BIT, supported);
+ __set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, supported);
+
+ if (phydev->c45_ids.devices_in_package & MDIO_DEVS_AN) {
+ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
+ if (val < 0)
+ return val;
+
+ if (val & MDIO_AN_STAT1_ABLE)
+ __set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, supported);
+ }
+
+ val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_STAT2);
+ if (val < 0)
+ return val;
+
+ /* Ethtool does not support the WAN mode bits */
+ if (val & (MDIO_PMA_STAT2_10GBSR | MDIO_PMA_STAT2_10GBLR |
+ MDIO_PMA_STAT2_10GBER | MDIO_PMA_STAT2_10GBLX4 |
+ MDIO_PMA_STAT2_10GBSW | MDIO_PMA_STAT2_10GBLW |
+ MDIO_PMA_STAT2_10GBEW))
+ __set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, supported);
+ if (val & MDIO_PMA_STAT2_10GBSR)
+ __set_bit(ETHTOOL_LINK_MODE_10000baseSR_Full_BIT, supported);
+ if (val & MDIO_PMA_STAT2_10GBLR)
+ __set_bit(ETHTOOL_LINK_MODE_10000baseLR_Full_BIT, supported);
+ if (val & MDIO_PMA_STAT2_10GBER)
+ __set_bit(ETHTOOL_LINK_MODE_10000baseER_Full_BIT, supported);
+
+ if (val & MDIO_PMA_STAT2_EXTABLE) {
+ val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_EXTABLE);
+ if (val < 0)
+ return val;
+
+ if (val & (MDIO_PMA_EXTABLE_10GBT | MDIO_PMA_EXTABLE_1000BT |
+ MDIO_PMA_EXTABLE_100BTX | MDIO_PMA_EXTABLE_10BT))
+ __set_bit(ETHTOOL_LINK_MODE_TP_BIT, supported);
+ if (val & MDIO_PMA_EXTABLE_10GBLRM)
+ __set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, supported);
+ if (val & (MDIO_PMA_EXTABLE_10GBKX4 | MDIO_PMA_EXTABLE_10GBKR |
+ MDIO_PMA_EXTABLE_1000BKX))
+ __set_bit(ETHTOOL_LINK_MODE_Backplane_BIT, supported);
+ if (val & MDIO_PMA_EXTABLE_10GBLRM)
+ __set_bit(ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT,
+ supported);
+ if (val & MDIO_PMA_EXTABLE_10GBT)
+ __set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
+ supported);
+ if (val & MDIO_PMA_EXTABLE_10GBKX4)
+ __set_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
+ supported);
+ if (val & MDIO_PMA_EXTABLE_10GBKR)
+ __set_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
+ supported);
+ if (val & MDIO_PMA_EXTABLE_1000BT)
+ __set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+ supported);
+ if (val & MDIO_PMA_EXTABLE_1000BKX)
+ __set_bit(ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
+ supported);
+ if (val & MDIO_PMA_EXTABLE_100BTX)
+ __set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
+ supported);
+ if (val & MDIO_PMA_EXTABLE_10BT)
+ __set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
+ supported);
+ }
+
+ if (!ethtool_convert_link_mode_to_legacy_u32(&mask, supported))
+ dev_warn(&phydev->mdio.dev,
+ "PHY supports (%*pb) more modes than phylib supports, some modes not supported.\n",
+ __ETHTOOL_LINK_MODE_MASK_NBITS, supported);
+
+ phydev->supported &= mask;
+ phydev->advertising &= phydev->supported;
+
+ return 0;
+}
+
+static int mv3310_config_aneg(struct phy_device *phydev)
+{
+ bool changed = false;
+ u32 advertising;
+ int ret;
+
+ if (phydev->autoneg == AUTONEG_DISABLE) {
+ ret = genphy_c45_pma_setup_forced(phydev);
+ if (ret < 0)
+ return ret;
+
+ return genphy_c45_an_disable_aneg(phydev);
+ }
+
+ phydev->advertising &= phydev->supported;
+ advertising = phydev->advertising;
+
+ ret = mv3310_modify(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE,
+ ADVERTISE_ALL | ADVERTISE_100BASE4 |
+ ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM,
+ ethtool_adv_to_mii_adv_t(advertising));
+ if (ret < 0)
+ return ret;
+ if (ret > 0)
+ changed = true;
+
+ ret = mv3310_modify(phydev, MDIO_MMD_AN, MV_AN_CTRL1000,
+ ADVERTISE_1000FULL | ADVERTISE_1000HALF,
+ ethtool_adv_to_mii_ctrl1000_t(advertising));
+ if (ret < 0)
+ return ret;
+ if (ret > 0)
+ changed = true;
+
+ /* 10G control register */
+ ret = mv3310_modify(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
+ MDIO_AN_10GBT_CTRL_ADV10G,
+ advertising & ADVERTISED_10000baseT_Full ?
+ MDIO_AN_10GBT_CTRL_ADV10G : 0);
+ if (ret < 0)
+ return ret;
+ if (ret > 0)
+ changed = true;
+
+ if (changed)
+ ret = genphy_c45_restart_aneg(phydev);
+
+ return ret;
+}
+
+static int mv3310_aneg_done(struct phy_device *phydev)
+{
+ int val;
+
+ val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_BASE_R + MDIO_STAT1);
+ if (val < 0)
+ return val;
+
+ if (val & MDIO_STAT1_LSTATUS)
+ return 1;
+
+ return genphy_c45_aneg_done(phydev);
+}
+
+/* 10GBASE-ER,LR,LRM,SR do not support autonegotiation. */
+static int mv3310_read_10gbr_status(struct phy_device *phydev)
+{
+ phydev->link = 1;
+ phydev->speed = SPEED_10000;
+ phydev->duplex = DUPLEX_FULL;
+
+ if (phydev->interface == PHY_INTERFACE_MODE_SGMII)
+ phydev->interface = PHY_INTERFACE_MODE_10GKR;
+
+ return 0;
+}
+
+static int mv3310_read_status(struct phy_device *phydev)
+{
+ u32 mmd_mask = phydev->c45_ids.devices_in_package;
+ int val;
+
+ /* The vendor devads do not report link status. Avoid the PHYXS
+ * instance as there are three, and its status depends on the MAC
+ * being appropriately configured for the negotiated speed.
+ */
+ mmd_mask &= ~(BIT(MDIO_MMD_VEND1) | BIT(MDIO_MMD_VEND2) |
+ BIT(MDIO_MMD_PHYXS));
+
+ phydev->speed = SPEED_UNKNOWN;
+ phydev->duplex = DUPLEX_UNKNOWN;
+ phydev->lp_advertising = 0;
+ phydev->link = 0;
+ phydev->pause = 0;
+ phydev->asym_pause = 0;
+
+ val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_BASE_R + MDIO_STAT1);
+ if (val < 0)
+ return val;
+
+ if (val & MDIO_STAT1_LSTATUS)
+ return mv3310_read_10gbr_status(phydev);
+
+ val = genphy_c45_read_link(phydev, mmd_mask);
+ if (val < 0)
+ return val;
+
+ phydev->link = val > 0 ? 1 : 0;
+
+ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
+ if (val < 0)
+ return val;
+
+ if (val & MDIO_AN_STAT1_COMPLETE) {
+ val = genphy_c45_read_lpa(phydev);
+ if (val < 0)
+ return val;
+
+ /* Read the link partner's 1G advertisment */
+ val = phy_read_mmd(phydev, MDIO_MMD_AN, MV_AN_STAT1000);
+ if (val < 0)
+ return val;
+
+ phydev->lp_advertising |= mii_stat1000_to_ethtool_lpa_t(val);
+
+ if (phydev->autoneg == AUTONEG_ENABLE) {
+ val = phy_read_mmd(phydev, MDIO_MMD_AN, MV_AN_RESULT);
+ if (val < 0)
+ return val;
+
+ if (val & MV_AN_RESULT_SPD_10000)
+ phydev->speed = SPEED_10000;
+ else if (val & MV_AN_RESULT_SPD_1000)
+ phydev->speed = SPEED_1000;
+ else if (val & MV_AN_RESULT_SPD_100)
+ phydev->speed = SPEED_100;
+ else if (val & MV_AN_RESULT_SPD_10)
+ phydev->speed = SPEED_10;
+
+ phydev->duplex = DUPLEX_FULL;
+ }
+ }
+
+ if (phydev->autoneg != AUTONEG_ENABLE) {
+ val = genphy_c45_read_pma(phydev);
+ if (val < 0)
+ return val;
+ }
+
+ if ((phydev->interface == PHY_INTERFACE_MODE_SGMII ||
+ phydev->interface == PHY_INTERFACE_MODE_10GKR) && phydev->link) {
+ /* The PHY automatically switches its serdes interface (and
+ * active PHYXS instance) between Cisco SGMII and 10GBase-KR
+ * modes according to the speed. Florian suggests setting
+ * phydev->interface to communicate this to the MAC. Only do
+ * this if we are already in either SGMII or 10GBase-KR mode.
+ */
+ if (phydev->speed == SPEED_10000)
+ phydev->interface = PHY_INTERFACE_MODE_10GKR;
+ else if (phydev->speed >= SPEED_10 &&
+ phydev->speed < SPEED_10000)
+ phydev->interface = PHY_INTERFACE_MODE_SGMII;
+ }
+
+ return 0;
+}
+
+static struct phy_driver mv3310_drivers[] = {
+ {
+ .phy_id = 0x002b09aa,
+ .phy_id_mask = 0xffffffff,
+ .name = "mv88x3310",
+ .features = SUPPORTED_10baseT_Full |
+ SUPPORTED_100baseT_Full |
+ SUPPORTED_1000baseT_Full |
+ SUPPORTED_Autoneg |
+ SUPPORTED_TP |
+ SUPPORTED_FIBRE |
+ SUPPORTED_10000baseT_Full |
+ SUPPORTED_Backplane,
+ .probe = mv3310_probe,
+ .soft_reset = mv3310_soft_reset,
+ .config_init = mv3310_config_init,
+ .config_aneg = mv3310_config_aneg,
+ .aneg_done = mv3310_aneg_done,
+ .read_status = mv3310_read_status,
+ },
+};
+
+module_phy_driver(mv3310_drivers);
+
+static struct mdio_device_id __maybe_unused mv3310_tbl[] = {
+ { 0x002b09aa, 0xffffffff },
+ { },
+};
+MODULE_DEVICE_TABLE(mdio, mv3310_tbl);
+MODULE_DESCRIPTION("Marvell Alaska X 10Gigabit Ethernet PHY driver (MV88X3310)");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/phy/mdio-mux.c b/drivers/net/phy/mdio-mux.c
index 599ce24..00755b6 100644
--- a/drivers/net/phy/mdio-mux.c
+++ b/drivers/net/phy/mdio-mux.c
@@ -133,29 +133,35 @@ int mdio_mux_init(struct device *dev,
ret_val = -ENODEV;
for_each_available_child_of_node(dev->of_node, child_bus_node) {
- u32 v;
+ int v;
- r = of_property_read_u32(child_bus_node, "reg", &v);
- if (r)
+ v = of_mdio_parse_addr(dev, child_bus_node);
+ if (v < 0) {
+ dev_err(dev,
+ "Error: Failed to find reg for child %s\n",
+ of_node_full_name(child_bus_node));
continue;
+ }
cb = devm_kzalloc(dev, sizeof(*cb), GFP_KERNEL);
if (cb == NULL) {
dev_err(dev,
- "Error: Failed to allocate memory for child\n");
+ "Error: Failed to allocate memory for child %s\n",
+ of_node_full_name(child_bus_node));
ret_val = -ENOMEM;
- of_node_put(child_bus_node);
- break;
+ continue;
}
cb->bus_number = v;
cb->parent = pb;
cb->mii_bus = mdiobus_alloc();
if (!cb->mii_bus) {
+ dev_err(dev,
+ "Error: Failed to allocate MDIO bus for child %s\n",
+ of_node_full_name(child_bus_node));
ret_val = -ENOMEM;
devm_kfree(dev, cb);
- of_node_put(child_bus_node);
- break;
+ continue;
}
cb->mii_bus->priv = cb;
@@ -167,6 +173,9 @@ int mdio_mux_init(struct device *dev,
cb->mii_bus->write = mdio_mux_write;
r = of_mdiobus_register(cb->mii_bus, child_bus_node);
if (r) {
+ dev_err(dev,
+ "Error: Failed to register MDIO bus for child %s\n",
+ of_node_full_name(child_bus_node));
mdiobus_free(cb->mii_bus);
devm_kfree(dev, cb);
} else {
@@ -180,6 +189,7 @@ int mdio_mux_init(struct device *dev,
return 0;
}
+ dev_err(dev, "Error: No acceptable child buses found\n");
devm_kfree(dev, pb);
err_pb_kz:
/* balance the reference of_mdio_find_bus() took */
diff --git a/drivers/net/phy/mdio-xgene.c b/drivers/net/phy/mdio-xgene.c
index 3e2ac07..bfd3090 100644
--- a/drivers/net/phy/mdio-xgene.c
+++ b/drivers/net/phy/mdio-xgene.c
@@ -34,76 +34,73 @@
static bool xgene_mdio_status;
-static u32 xgene_enet_rd_mac(void __iomem *base_addr, u32 rd_addr)
+u32 xgene_mdio_rd_mac(struct xgene_mdio_pdata *pdata, u32 rd_addr)
{
void __iomem *addr, *rd, *cmd, *cmd_done;
u32 done, rd_data = BUSY_MASK;
u8 wait = 10;
- addr = base_addr + MAC_ADDR_REG_OFFSET;
- rd = base_addr + MAC_READ_REG_OFFSET;
- cmd = base_addr + MAC_COMMAND_REG_OFFSET;
- cmd_done = base_addr + MAC_COMMAND_DONE_REG_OFFSET;
+ addr = pdata->mac_csr_addr + MAC_ADDR_REG_OFFSET;
+ rd = pdata->mac_csr_addr + MAC_READ_REG_OFFSET;
+ cmd = pdata->mac_csr_addr + MAC_COMMAND_REG_OFFSET;
+ cmd_done = pdata->mac_csr_addr + MAC_COMMAND_DONE_REG_OFFSET;
+ spin_lock(&pdata->mac_lock);
iowrite32(rd_addr, addr);
iowrite32(XGENE_ENET_RD_CMD, cmd);
- while (wait--) {
- done = ioread32(cmd_done);
- if (done)
- break;
+ while (!(done = ioread32(cmd_done)) && wait--)
udelay(1);
- }
- if (!done)
- return rd_data;
+ if (done)
+ rd_data = ioread32(rd);
- rd_data = ioread32(rd);
iowrite32(0, cmd);
+ spin_unlock(&pdata->mac_lock);
return rd_data;
}
+EXPORT_SYMBOL(xgene_mdio_rd_mac);
-static void xgene_enet_wr_mac(void __iomem *base_addr, u32 wr_addr, u32 wr_data)
+void xgene_mdio_wr_mac(struct xgene_mdio_pdata *pdata, u32 wr_addr, u32 data)
{
void __iomem *addr, *wr, *cmd, *cmd_done;
u8 wait = 10;
u32 done;
- addr = base_addr + MAC_ADDR_REG_OFFSET;
- wr = base_addr + MAC_WRITE_REG_OFFSET;
- cmd = base_addr + MAC_COMMAND_REG_OFFSET;
- cmd_done = base_addr + MAC_COMMAND_DONE_REG_OFFSET;
+ addr = pdata->mac_csr_addr + MAC_ADDR_REG_OFFSET;
+ wr = pdata->mac_csr_addr + MAC_WRITE_REG_OFFSET;
+ cmd = pdata->mac_csr_addr + MAC_COMMAND_REG_OFFSET;
+ cmd_done = pdata->mac_csr_addr + MAC_COMMAND_DONE_REG_OFFSET;
+ spin_lock(&pdata->mac_lock);
iowrite32(wr_addr, addr);
- iowrite32(wr_data, wr);
+ iowrite32(data, wr);
iowrite32(XGENE_ENET_WR_CMD, cmd);
- while (wait--) {
- done = ioread32(cmd_done);
- if (done)
- break;
+ while (!(done = ioread32(cmd_done)) && wait--)
udelay(1);
- }
if (!done)
pr_err("MCX mac write failed, addr: 0x%04x\n", wr_addr);
iowrite32(0, cmd);
+ spin_unlock(&pdata->mac_lock);
}
+EXPORT_SYMBOL(xgene_mdio_wr_mac);
int xgene_mdio_rgmii_read(struct mii_bus *bus, int phy_id, int reg)
{
- void __iomem *addr = (void __iomem *)bus->priv;
+ struct xgene_mdio_pdata *pdata = (struct xgene_mdio_pdata *)bus->priv;
u32 data, done;
u8 wait = 10;
data = SET_VAL(PHY_ADDR, phy_id) | SET_VAL(REG_ADDR, reg);
- xgene_enet_wr_mac(addr, MII_MGMT_ADDRESS_ADDR, data);
- xgene_enet_wr_mac(addr, MII_MGMT_COMMAND_ADDR, READ_CYCLE_MASK);
+ xgene_mdio_wr_mac(pdata, MII_MGMT_ADDRESS_ADDR, data);
+ xgene_mdio_wr_mac(pdata, MII_MGMT_COMMAND_ADDR, READ_CYCLE_MASK);
do {
usleep_range(5, 10);
- done = xgene_enet_rd_mac(addr, MII_MGMT_INDICATORS_ADDR);
+ done = xgene_mdio_rd_mac(pdata, MII_MGMT_INDICATORS_ADDR);
} while ((done & BUSY_MASK) && wait--);
if (done & BUSY_MASK) {
@@ -111,8 +108,8 @@ int xgene_mdio_rgmii_read(struct mii_bus *bus, int phy_id, int reg)
return -EBUSY;
}
- data = xgene_enet_rd_mac(addr, MII_MGMT_STATUS_ADDR);
- xgene_enet_wr_mac(addr, MII_MGMT_COMMAND_ADDR, 0);
+ data = xgene_mdio_rd_mac(pdata, MII_MGMT_STATUS_ADDR);
+ xgene_mdio_wr_mac(pdata, MII_MGMT_COMMAND_ADDR, 0);
return data;
}
@@ -120,17 +117,17 @@ EXPORT_SYMBOL(xgene_mdio_rgmii_read);
int xgene_mdio_rgmii_write(struct mii_bus *bus, int phy_id, int reg, u16 data)
{
- void __iomem *addr = (void __iomem *)bus->priv;
+ struct xgene_mdio_pdata *pdata = (struct xgene_mdio_pdata *)bus->priv;
u32 val, done;
u8 wait = 10;
val = SET_VAL(PHY_ADDR, phy_id) | SET_VAL(REG_ADDR, reg);
- xgene_enet_wr_mac(addr, MII_MGMT_ADDRESS_ADDR, val);
+ xgene_mdio_wr_mac(pdata, MII_MGMT_ADDRESS_ADDR, val);
- xgene_enet_wr_mac(addr, MII_MGMT_CONTROL_ADDR, data);
+ xgene_mdio_wr_mac(pdata, MII_MGMT_CONTROL_ADDR, data);
do {
usleep_range(5, 10);
- done = xgene_enet_rd_mac(addr, MII_MGMT_INDICATORS_ADDR);
+ done = xgene_mdio_rd_mac(pdata, MII_MGMT_INDICATORS_ADDR);
} while ((done & BUSY_MASK) && wait--);
if (done & BUSY_MASK) {
@@ -174,8 +171,8 @@ static int xgene_enet_ecc_init(struct xgene_mdio_pdata *pdata)
static void xgene_gmac_reset(struct xgene_mdio_pdata *pdata)
{
- xgene_enet_wr_mac(pdata->mac_csr_addr, MAC_CONFIG_1_ADDR, SOFT_RESET);
- xgene_enet_wr_mac(pdata->mac_csr_addr, MAC_CONFIG_1_ADDR, 0);
+ xgene_mdio_wr_mac(pdata, MAC_CONFIG_1_ADDR, SOFT_RESET);
+ xgene_mdio_wr_mac(pdata, MAC_CONFIG_1_ADDR, 0);
}
static int xgene_mdio_reset(struct xgene_mdio_pdata *pdata)
@@ -375,6 +372,9 @@ static int xgene_mdio_probe(struct platform_device *pdev)
pdata->mdio_csr_addr = csr_base + BLOCK_XG_MDIO_CSR_OFFSET;
pdata->diag_csr_addr = csr_base + BLOCK_DIAG_CSR_OFFSET;
+ if (mdio_id == XGENE_MDIO_RGMII)
+ spin_lock_init(&pdata->mac_lock);
+
if (dev->of_node) {
pdata->clk = devm_clk_get(dev, NULL);
if (IS_ERR(pdata->clk)) {
@@ -396,7 +396,7 @@ static int xgene_mdio_probe(struct platform_device *pdev)
if (mdio_id == XGENE_MDIO_RGMII) {
mdio_bus->read = xgene_mdio_rgmii_read;
mdio_bus->write = xgene_mdio_rgmii_write;
- mdio_bus->priv = (void __force *)pdata->mac_csr_addr;
+ mdio_bus->priv = (void __force *)pdata;
snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%s",
"xgene-mii-rgmii");
} else {
diff --git a/drivers/net/phy/mdio-xgene.h b/drivers/net/phy/mdio-xgene.h
index 594a11d..3c85f3e 100644
--- a/drivers/net/phy/mdio-xgene.h
+++ b/drivers/net/phy/mdio-xgene.h
@@ -102,6 +102,7 @@ struct xgene_mdio_pdata {
void __iomem *mdio_csr_addr;
struct mii_bus *mdio_bus;
int mdio_id;
+ spinlock_t mac_lock; /* mac lock */
};
/* Set the specified value into a bit-field defined by its starting position
@@ -132,6 +133,8 @@ static inline u64 xgene_enet_get_field_value(int pos, int len, u64 src)
#define GET_BIT(field, src) \
xgene_enet_get_field_value(field ## _POS, 1, src)
+u32 xgene_mdio_rd_mac(struct xgene_mdio_pdata *pdata, u32 rd_addr);
+void xgene_mdio_wr_mac(struct xgene_mdio_pdata *pdata, u32 wr_addr, u32 data);
int xgene_mdio_rgmii_read(struct mii_bus *bus, int phy_id, int reg);
int xgene_mdio_rgmii_write(struct mii_bus *bus, int phy_id, int reg, u16 data);
struct phy_device *xgene_enet_phy_register(struct mii_bus *bus, int phy_addr);
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index f99c21f..2df7b62c 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -263,21 +263,10 @@ static void of_mdiobus_link_mdiodev(struct mii_bus *bus,
for_each_available_child_of_node(bus->dev.of_node, child) {
int addr;
- int ret;
- ret = of_property_read_u32(child, "reg", &addr);
- if (ret < 0) {
- dev_err(dev, "%s has invalid MDIO address\n",
- child->full_name);
+ addr = of_mdio_parse_addr(dev, child);
+ if (addr < 0)
continue;
- }
-
- /* A MDIO device must have a reg property in the range [0-31] */
- if (addr >= PHY_MAX_ADDR) {
- dev_err(dev, "%s MDIO address %i is too large\n",
- child->full_name, addr);
- continue;
- }
if (addr == mdiodev->addr) {
dev->of_node = child;
@@ -364,33 +353,18 @@ int __mdiobus_register(struct mii_bus *bus, struct module *owner)
mutex_init(&bus->mdio_lock);
- /* de-assert bus level PHY GPIO resets */
- if (bus->num_reset_gpios > 0) {
- bus->reset_gpiod = devm_kcalloc(&bus->dev,
- bus->num_reset_gpios,
- sizeof(struct gpio_desc *),
- GFP_KERNEL);
- if (!bus->reset_gpiod)
- return -ENOMEM;
- }
-
- for (i = 0; i < bus->num_reset_gpios; i++) {
- gpiod = devm_gpiod_get_index(&bus->dev, "reset", i,
- GPIOD_OUT_LOW);
- if (IS_ERR(gpiod)) {
- err = PTR_ERR(gpiod);
- if (err != -ENOENT) {
- dev_err(&bus->dev,
- "mii_bus %s couldn't get reset GPIO\n",
- bus->id);
- return err;
- }
- } else {
- bus->reset_gpiod[i] = gpiod;
- gpiod_set_value_cansleep(gpiod, 1);
- udelay(bus->reset_delay_us);
- gpiod_set_value_cansleep(gpiod, 0);
- }
+ /* de-assert bus level PHY GPIO reset */
+ gpiod = devm_gpiod_get_optional(&bus->dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(gpiod)) {
+ dev_err(&bus->dev, "mii_bus %s couldn't get reset GPIO\n",
+ bus->id);
+ return PTR_ERR(gpiod);
+ } else if (gpiod) {
+ bus->reset_gpiod = gpiod;
+
+ gpiod_set_value_cansleep(gpiod, 1);
+ udelay(bus->reset_delay_us);
+ gpiod_set_value_cansleep(gpiod, 0);
}
if (bus->reset)
@@ -425,10 +399,8 @@ error:
}
/* Put PHYs in RESET to save power */
- for (i = 0; i < bus->num_reset_gpios; i++) {
- if (bus->reset_gpiod[i])
- gpiod_set_value_cansleep(bus->reset_gpiod[i], 1);
- }
+ if (bus->reset_gpiod)
+ gpiod_set_value_cansleep(bus->reset_gpiod, 1);
device_del(&bus->dev);
return err;
@@ -453,10 +425,8 @@ void mdiobus_unregister(struct mii_bus *bus)
}
/* Put PHYs in RESET to save power */
- for (i = 0; i < bus->num_reset_gpios; i++) {
- if (bus->reset_gpiod[i])
- gpiod_set_value_cansleep(bus->reset_gpiod[i], 1);
- }
+ if (bus->reset_gpiod)
+ gpiod_set_value_cansleep(bus->reset_gpiod, 1);
device_del(&bus->dev);
}
diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c
index 8b20388..fdb43dd 100644
--- a/drivers/net/phy/micrel.c
+++ b/drivers/net/phy/micrel.c
@@ -20,6 +20,7 @@
* ksz8081, ksz8091,
* ksz8061,
* Switch : ksz8873, ksz886x
+ * ksz9477
*/
#include <linux/kernel.h>
@@ -795,7 +796,7 @@ static struct phy_driver ksphy_driver[] = {
.phy_id_mask = MICREL_PHY_ID_MASK,
.name = "Micrel KS8737",
.features = PHY_BASIC_FEATURES,
- .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .flags = PHY_HAS_INTERRUPT,
.driver_data = &ks8737_type,
.config_init = kszphy_config_init,
.config_aneg = genphy_config_aneg,
@@ -809,7 +810,7 @@ static struct phy_driver ksphy_driver[] = {
.phy_id_mask = 0x00ffffff,
.name = "Micrel KSZ8021 or KSZ8031",
.features = PHY_BASIC_FEATURES,
- .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .flags = PHY_HAS_INTERRUPT,
.driver_data = &ksz8021_type,
.probe = kszphy_probe,
.config_init = kszphy_config_init,
@@ -827,7 +828,7 @@ static struct phy_driver ksphy_driver[] = {
.phy_id_mask = 0x00ffffff,
.name = "Micrel KSZ8031",
.features = PHY_BASIC_FEATURES,
- .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .flags = PHY_HAS_INTERRUPT,
.driver_data = &ksz8021_type,
.probe = kszphy_probe,
.config_init = kszphy_config_init,
@@ -845,7 +846,7 @@ static struct phy_driver ksphy_driver[] = {
.phy_id_mask = MICREL_PHY_ID_MASK,
.name = "Micrel KSZ8041",
.features = PHY_BASIC_FEATURES,
- .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .flags = PHY_HAS_INTERRUPT,
.driver_data = &ksz8041_type,
.probe = kszphy_probe,
.config_init = ksz8041_config_init,
@@ -863,7 +864,7 @@ static struct phy_driver ksphy_driver[] = {
.phy_id_mask = MICREL_PHY_ID_MASK,
.name = "Micrel KSZ8041RNLI",
.features = PHY_BASIC_FEATURES,
- .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .flags = PHY_HAS_INTERRUPT,
.driver_data = &ksz8041_type,
.probe = kszphy_probe,
.config_init = kszphy_config_init,
@@ -881,7 +882,7 @@ static struct phy_driver ksphy_driver[] = {
.phy_id_mask = MICREL_PHY_ID_MASK,
.name = "Micrel KSZ8051",
.features = PHY_BASIC_FEATURES,
- .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .flags = PHY_HAS_INTERRUPT,
.driver_data = &ksz8051_type,
.probe = kszphy_probe,
.config_init = kszphy_config_init,
@@ -899,7 +900,7 @@ static struct phy_driver ksphy_driver[] = {
.name = "Micrel KSZ8001 or KS8721",
.phy_id_mask = 0x00fffffc,
.features = PHY_BASIC_FEATURES,
- .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .flags = PHY_HAS_INTERRUPT,
.driver_data = &ksz8041_type,
.probe = kszphy_probe,
.config_init = kszphy_config_init,
@@ -917,7 +918,7 @@ static struct phy_driver ksphy_driver[] = {
.name = "Micrel KSZ8081 or KSZ8091",
.phy_id_mask = MICREL_PHY_ID_MASK,
.features = PHY_BASIC_FEATURES,
- .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .flags = PHY_HAS_INTERRUPT,
.driver_data = &ksz8081_type,
.probe = kszphy_probe,
.config_init = kszphy_config_init,
@@ -935,7 +936,7 @@ static struct phy_driver ksphy_driver[] = {
.name = "Micrel KSZ8061",
.phy_id_mask = MICREL_PHY_ID_MASK,
.features = PHY_BASIC_FEATURES,
- .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .flags = PHY_HAS_INTERRUPT,
.config_init = kszphy_config_init,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
@@ -948,7 +949,7 @@ static struct phy_driver ksphy_driver[] = {
.phy_id_mask = 0x000ffffe,
.name = "Micrel KSZ9021 Gigabit PHY",
.features = PHY_GBIT_FEATURES,
- .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .flags = PHY_HAS_INTERRUPT,
.driver_data = &ksz9021_type,
.probe = kszphy_probe,
.config_init = ksz9021_config_init,
@@ -968,7 +969,7 @@ static struct phy_driver ksphy_driver[] = {
.phy_id_mask = MICREL_PHY_ID_MASK,
.name = "Micrel KSZ9031 Gigabit PHY",
.features = PHY_GBIT_FEATURES,
- .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .flags = PHY_HAS_INTERRUPT,
.driver_data = &ksz9021_type,
.probe = kszphy_probe,
.config_init = ksz9031_config_init,
@@ -985,7 +986,6 @@ static struct phy_driver ksphy_driver[] = {
.phy_id = PHY_ID_KSZ8873MLL,
.phy_id_mask = MICREL_PHY_ID_MASK,
.name = "Micrel KSZ8873MLL Switch",
- .flags = PHY_HAS_MAGICANEG,
.config_init = kszphy_config_init,
.config_aneg = ksz8873mll_config_aneg,
.read_status = ksz8873mll_read_status,
@@ -996,7 +996,7 @@ static struct phy_driver ksphy_driver[] = {
.phy_id_mask = MICREL_PHY_ID_MASK,
.name = "Micrel KSZ886X Switch",
.features = PHY_BASIC_FEATURES,
- .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .flags = PHY_HAS_INTERRUPT,
.config_init = kszphy_config_init,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
@@ -1007,12 +1007,22 @@ static struct phy_driver ksphy_driver[] = {
.phy_id_mask = MICREL_PHY_ID_MASK,
.name = "Micrel KSZ8795",
.features = PHY_BASIC_FEATURES,
- .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .flags = PHY_HAS_INTERRUPT,
.config_init = kszphy_config_init,
.config_aneg = ksz8873mll_config_aneg,
.read_status = ksz8873mll_read_status,
.suspend = genphy_suspend,
.resume = genphy_resume,
+}, {
+ .phy_id = PHY_ID_KSZ9477,
+ .phy_id_mask = MICREL_PHY_ID_MASK,
+ .name = "Microchip KSZ9477",
+ .features = PHY_GBIT_FEATURES,
+ .config_init = kszphy_config_init,
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
} };
module_phy_driver(ksphy_driver);
diff --git a/drivers/net/phy/microchip.c b/drivers/net/phy/microchip.c
index 2b2f543..37ee856 100644
--- a/drivers/net/phy/microchip.c
+++ b/drivers/net/phy/microchip.c
@@ -146,7 +146,7 @@ static struct phy_driver microchip_phy_driver[] = {
.name = "Microchip LAN88xx",
.features = PHY_GBIT_FEATURES,
- .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
+ .flags = PHY_HAS_INTERRUPT,
.probe = lan88xx_probe,
.remove = lan88xx_remove,
diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c
new file mode 100644
index 0000000..dada819
--- /dev/null
+++ b/drivers/net/phy/phy-c45.c
@@ -0,0 +1,298 @@
+/*
+ * Clause 45 PHY support
+ */
+#include <linux/ethtool.h>
+#include <linux/export.h>
+#include <linux/mdio.h>
+#include <linux/mii.h>
+#include <linux/phy.h>
+
+/**
+ * genphy_c45_setup_forced - configures a forced speed
+ * @phydev: target phy_device struct
+ */
+int genphy_c45_pma_setup_forced(struct phy_device *phydev)
+{
+ int ctrl1, ctrl2, ret;
+
+ /* Half duplex is not supported */
+ if (phydev->duplex != DUPLEX_FULL)
+ return -EINVAL;
+
+ ctrl1 = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1);
+ if (ctrl1 < 0)
+ return ctrl1;
+
+ ctrl2 = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL2);
+ if (ctrl2 < 0)
+ return ctrl2;
+
+ ctrl1 &= ~MDIO_CTRL1_SPEEDSEL;
+ /*
+ * PMA/PMD type selection is 1.7.5:0 not 1.7.3:0. See 45.2.1.6.1
+ * in 802.3-2012 and 802.3-2015.
+ */
+ ctrl2 &= ~(MDIO_PMA_CTRL2_TYPE | 0x30);
+
+ switch (phydev->speed) {
+ case SPEED_10:
+ ctrl2 |= MDIO_PMA_CTRL2_10BT;
+ break;
+ case SPEED_100:
+ ctrl1 |= MDIO_PMA_CTRL1_SPEED100;
+ ctrl2 |= MDIO_PMA_CTRL2_100BTX;
+ break;
+ case SPEED_1000:
+ ctrl1 |= MDIO_PMA_CTRL1_SPEED1000;
+ /* Assume 1000base-T */
+ ctrl2 |= MDIO_PMA_CTRL2_1000BT;
+ break;
+ case SPEED_10000:
+ ctrl1 |= MDIO_CTRL1_SPEED10G;
+ /* Assume 10Gbase-T */
+ ctrl2 |= MDIO_PMA_CTRL2_10GBT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1, ctrl1);
+ if (ret < 0)
+ return ret;
+
+ return phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL2, ctrl2);
+}
+EXPORT_SYMBOL_GPL(genphy_c45_pma_setup_forced);
+
+/**
+ * genphy_c45_an_disable_aneg - disable auto-negotiation
+ * @phydev: target phy_device struct
+ *
+ * Disable auto-negotiation in the Clause 45 PHY. The link parameters
+ * parameters are controlled through the PMA/PMD MMD registers.
+ *
+ * Returns zero on success, negative errno code on failure.
+ */
+int genphy_c45_an_disable_aneg(struct phy_device *phydev)
+{
+ int val;
+
+ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1);
+ if (val < 0)
+ return val;
+
+ val &= ~(MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART);
+
+ return phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, val);
+}
+EXPORT_SYMBOL_GPL(genphy_c45_an_disable_aneg);
+
+/**
+ * genphy_c45_restart_aneg - Enable and restart auto-negotiation
+ * @phydev: target phy_device struct
+ *
+ * This assumes that the auto-negotiation MMD is present.
+ *
+ * Enable and restart auto-negotiation.
+ */
+int genphy_c45_restart_aneg(struct phy_device *phydev)
+{
+ int val;
+
+ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1);
+ if (val < 0)
+ return val;
+
+ val |= MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART;
+
+ return phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, val);
+}
+EXPORT_SYMBOL_GPL(genphy_c45_restart_aneg);
+
+/**
+ * genphy_c45_aneg_done - return auto-negotiation complete status
+ * @phydev: target phy_device struct
+ *
+ * This assumes that the auto-negotiation MMD is present.
+ *
+ * Reads the status register from the auto-negotiation MMD, returning:
+ * - positive if auto-negotiation is complete
+ * - negative errno code on error
+ * - zero otherwise
+ */
+int genphy_c45_aneg_done(struct phy_device *phydev)
+{
+ int val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
+
+ return val < 0 ? val : val & MDIO_AN_STAT1_COMPLETE ? 1 : 0;
+}
+EXPORT_SYMBOL_GPL(genphy_c45_aneg_done);
+
+/**
+ * genphy_c45_read_link - read the overall link status from the MMDs
+ * @phydev: target phy_device struct
+ * @mmd_mask: MMDs to read status from
+ *
+ * Read the link status from the specified MMDs, and if they all indicate
+ * that the link is up, return positive. If an error is encountered,
+ * a negative errno will be returned, otherwise zero.
+ */
+int genphy_c45_read_link(struct phy_device *phydev, u32 mmd_mask)
+{
+ int val, devad;
+ bool link = true;
+
+ while (mmd_mask) {
+ devad = __ffs(mmd_mask);
+ mmd_mask &= ~BIT(devad);
+
+ /* The link state is latched low so that momentary link
+ * drops can be detected. Do not double-read the status
+ * register if the link is down.
+ */
+ val = phy_read_mmd(phydev, devad, MDIO_STAT1);
+ if (val < 0)
+ return val;
+
+ if (!(val & MDIO_STAT1_LSTATUS))
+ link = false;
+ }
+
+ return link;
+}
+EXPORT_SYMBOL_GPL(genphy_c45_read_link);
+
+/**
+ * genphy_c45_read_lpa - read the link partner advertisment and pause
+ * @phydev: target phy_device struct
+ *
+ * Read the Clause 45 defined base (7.19) and 10G (7.33) status registers,
+ * filling in the link partner advertisment, pause and asym_pause members
+ * in @phydev. This assumes that the auto-negotiation MMD is present, and
+ * the backplane bit (7.48.0) is clear. Clause 45 PHY drivers are expected
+ * to fill in the remainder of the link partner advert from vendor registers.
+ */
+int genphy_c45_read_lpa(struct phy_device *phydev)
+{
+ int val;
+
+ /* Read the link partner's base page advertisment */
+ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPA);
+ if (val < 0)
+ return val;
+
+ phydev->lp_advertising = mii_lpa_to_ethtool_lpa_t(val);
+ phydev->pause = val & LPA_PAUSE_CAP ? 1 : 0;
+ phydev->asym_pause = val & LPA_PAUSE_ASYM ? 1 : 0;
+
+ /* Read the link partner's 10G advertisment */
+ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT);
+ if (val < 0)
+ return val;
+
+ if (val & MDIO_AN_10GBT_STAT_LP10G)
+ phydev->lp_advertising |= ADVERTISED_10000baseT_Full;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(genphy_c45_read_lpa);
+
+/**
+ * genphy_c45_read_pma - read link speed etc from PMA
+ * @phydev: target phy_device struct
+ */
+int genphy_c45_read_pma(struct phy_device *phydev)
+{
+ int val;
+
+ val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1);
+ if (val < 0)
+ return val;
+
+ switch (val & MDIO_CTRL1_SPEEDSEL) {
+ case 0:
+ phydev->speed = SPEED_10;
+ break;
+ case MDIO_PMA_CTRL1_SPEED100:
+ phydev->speed = SPEED_100;
+ break;
+ case MDIO_PMA_CTRL1_SPEED1000:
+ phydev->speed = SPEED_1000;
+ break;
+ case MDIO_CTRL1_SPEED10G:
+ phydev->speed = SPEED_10000;
+ break;
+ default:
+ phydev->speed = SPEED_UNKNOWN;
+ break;
+ }
+
+ phydev->duplex = DUPLEX_FULL;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(genphy_c45_read_pma);
+
+/* The gen10g_* functions are the old Clause 45 stub */
+
+static int gen10g_config_aneg(struct phy_device *phydev)
+{
+ return 0;
+}
+
+static int gen10g_read_status(struct phy_device *phydev)
+{
+ u32 mmd_mask = phydev->c45_ids.devices_in_package;
+ int ret;
+
+ /* For now just lie and say it's 10G all the time */
+ phydev->speed = SPEED_10000;
+ phydev->duplex = DUPLEX_FULL;
+
+ /* Avoid reading the vendor MMDs */
+ mmd_mask &= ~(BIT(MDIO_MMD_VEND1) | BIT(MDIO_MMD_VEND2));
+
+ ret = genphy_c45_read_link(phydev, mmd_mask);
+
+ phydev->link = ret > 0 ? 1 : 0;
+
+ return 0;
+}
+
+static int gen10g_soft_reset(struct phy_device *phydev)
+{
+ /* Do nothing for now */
+ return 0;
+}
+
+static int gen10g_config_init(struct phy_device *phydev)
+{
+ /* Temporarily just say we support everything */
+ phydev->supported = SUPPORTED_10000baseT_Full;
+ phydev->advertising = SUPPORTED_10000baseT_Full;
+
+ return 0;
+}
+
+static int gen10g_suspend(struct phy_device *phydev)
+{
+ return 0;
+}
+
+static int gen10g_resume(struct phy_device *phydev)
+{
+ return 0;
+}
+
+struct phy_driver genphy_10g_driver = {
+ .phy_id = 0xffffffff,
+ .phy_id_mask = 0xffffffff,
+ .name = "Generic 10G PHY",
+ .soft_reset = gen10g_soft_reset,
+ .config_init = gen10g_config_init,
+ .features = 0,
+ .config_aneg = gen10g_config_aneg,
+ .read_status = gen10g_read_status,
+ .suspend = gen10g_suspend,
+ .resume = gen10g_resume,
+};
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 3e231a5..d0626bf 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -151,6 +151,25 @@ static int phy_config_interrupt(struct phy_device *phydev, u32 interrupts)
return 0;
}
+/**
+ * phy_restart_aneg - restart auto-negotiation
+ * @phydev: target phy_device struct
+ *
+ * Restart the autonegotiation on @phydev. Returns >= 0 on success or
+ * negative errno on error.
+ */
+int phy_restart_aneg(struct phy_device *phydev)
+{
+ int ret;
+
+ if (phydev->is_c45 && !(phydev->c45_ids.devices_in_package & BIT(0)))
+ ret = genphy_c45_restart_aneg(phydev);
+ else
+ ret = genphy_restart_aneg(phydev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phy_restart_aneg);
/**
* phy_aneg_done - return auto-negotiation status
@@ -165,6 +184,12 @@ int phy_aneg_done(struct phy_device *phydev)
if (phydev->drv && phydev->drv->aneg_done)
return phydev->drv->aneg_done(phydev);
+ /* Avoid genphy_aneg_done() if the Clause 45 PHY does not
+ * implement Clause 22 registers
+ */
+ if (phydev->is_c45 && !(phydev->c45_ids.devices_in_package & BIT(0)))
+ return -EINVAL;
+
return genphy_aneg_done(phydev);
}
EXPORT_SYMBOL(phy_aneg_done);
@@ -487,32 +512,8 @@ int phy_ethtool_ksettings_set(struct phy_device *phydev,
}
EXPORT_SYMBOL(phy_ethtool_ksettings_set);
-int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd)
-{
- cmd->supported = phydev->supported;
-
- cmd->advertising = phydev->advertising;
- cmd->lp_advertising = phydev->lp_advertising;
-
- ethtool_cmd_speed_set(cmd, phydev->speed);
- cmd->duplex = phydev->duplex;
- if (phydev->interface == PHY_INTERFACE_MODE_MOCA)
- cmd->port = PORT_BNC;
- else
- cmd->port = PORT_MII;
- cmd->phy_address = phydev->mdio.addr;
- cmd->transceiver = phy_is_internal(phydev) ?
- XCVR_INTERNAL : XCVR_EXTERNAL;
- cmd->autoneg = phydev->autoneg;
- cmd->eth_tp_mdix_ctrl = phydev->mdix_ctrl;
- cmd->eth_tp_mdix = phydev->mdix;
-
- return 0;
-}
-EXPORT_SYMBOL(phy_ethtool_gset);
-
-int phy_ethtool_ksettings_get(struct phy_device *phydev,
- struct ethtool_link_ksettings *cmd)
+void phy_ethtool_ksettings_get(struct phy_device *phydev,
+ struct ethtool_link_ksettings *cmd)
{
ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
phydev->supported);
@@ -534,8 +535,6 @@ int phy_ethtool_ksettings_get(struct phy_device *phydev,
cmd->base.autoneg = phydev->autoneg;
cmd->base.eth_tp_mdix_ctrl = phydev->mdix_ctrl;
cmd->base.eth_tp_mdix = phydev->mdix;
-
- return 0;
}
EXPORT_SYMBOL(phy_ethtool_ksettings_get);
@@ -1418,7 +1417,7 @@ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data)
/* Restart autonegotiation so the new modes get sent to the
* link partner.
*/
- ret = genphy_restart_aneg(phydev);
+ ret = phy_restart_aneg(phydev);
if (ret < 0)
return ret;
}
@@ -1451,7 +1450,9 @@ int phy_ethtool_get_link_ksettings(struct net_device *ndev,
if (!phydev)
return -ENODEV;
- return phy_ethtool_ksettings_get(phydev, cmd);
+ phy_ethtool_ksettings_get(phydev, cmd);
+
+ return 0;
}
EXPORT_SYMBOL(phy_ethtool_get_link_ksettings);
@@ -1477,6 +1478,6 @@ int phy_ethtool_nway_reset(struct net_device *ndev)
if (!phydev->drv)
return -EIO;
- return genphy_restart_aneg(phydev);
+ return phy_restart_aneg(phydev);
}
EXPORT_SYMBOL(phy_ethtool_nway_reset);
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 1219eea..1790f7f 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -69,13 +69,8 @@ static void phy_mdio_device_remove(struct mdio_device *mdiodev)
phy_device_remove(phydev);
}
-enum genphy_driver {
- GENPHY_DRV_1G,
- GENPHY_DRV_10G,
- GENPHY_DRV_MAX
-};
-
-static struct phy_driver genphy_driver[GENPHY_DRV_MAX];
+static struct phy_driver genphy_driver;
+extern struct phy_driver genphy_10g_driver;
static LIST_HEAD(phy_fixup_list);
static DEFINE_MUTEX(phy_fixup_lock);
@@ -928,11 +923,9 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
*/
if (!d->driver) {
if (phydev->is_c45)
- d->driver =
- &genphy_driver[GENPHY_DRV_10G].mdiodrv.driver;
+ d->driver = &genphy_10g_driver.mdiodrv.driver;
else
- d->driver =
- &genphy_driver[GENPHY_DRV_1G].mdiodrv.driver;
+ d->driver = &genphy_driver.mdiodrv.driver;
using_genphy = true;
}
@@ -961,6 +954,27 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
phydev->attached_dev = dev;
dev->phydev = phydev;
+ /* Some Ethernet drivers try to connect to a PHY device before
+ * calling register_netdevice() -> netdev_register_kobject() and
+ * does the dev->dev.kobj initialization. Here we only check for
+ * success which indicates that the network device kobject is
+ * ready. Once we do that we still need to keep track of whether
+ * links were successfully set up or not for phy_detach() to
+ * remove them accordingly.
+ */
+ phydev->sysfs_links = false;
+
+ err = sysfs_create_link(&phydev->mdio.dev.kobj, &dev->dev.kobj,
+ "attached_dev");
+ if (!err) {
+ err = sysfs_create_link(&dev->dev.kobj, &phydev->mdio.dev.kobj,
+ "phydev");
+ if (err)
+ goto error;
+
+ phydev->sysfs_links = true;
+ }
+
phydev->dev_flags = flags;
phydev->interface = interface;
@@ -1048,8 +1062,11 @@ void phy_detach(struct phy_device *phydev)
struct net_device *dev = phydev->attached_dev;
struct module *ndev_owner = dev->dev.parent->driver->owner;
struct mii_bus *bus;
- int i;
+ if (phydev->sysfs_links) {
+ sysfs_remove_link(&dev->dev.kobj, "phydev");
+ sysfs_remove_link(&phydev->mdio.dev.kobj, "attached_dev");
+ }
phydev->attached_dev->phydev = NULL;
phydev->attached_dev = NULL;
phy_suspend(phydev);
@@ -1063,13 +1080,9 @@ void phy_detach(struct phy_device *phydev)
* from the generic driver so that there's a chance a
* real driver could be loaded
*/
- for (i = 0; i < ARRAY_SIZE(genphy_driver); i++) {
- if (phydev->mdio.dev.driver ==
- &genphy_driver[i].mdiodrv.driver) {
- device_release_driver(&phydev->mdio.dev);
- break;
- }
- }
+ if (phydev->mdio.dev.driver == &genphy_10g_driver.mdiodrv.driver ||
+ phydev->mdio.dev.driver == &genphy_driver.mdiodrv.driver)
+ device_release_driver(&phydev->mdio.dev);
/*
* The phydev might go away on the put_device() below, so avoid
@@ -1123,6 +1136,39 @@ int phy_resume(struct phy_device *phydev)
}
EXPORT_SYMBOL(phy_resume);
+int phy_loopback(struct phy_device *phydev, bool enable)
+{
+ struct phy_driver *phydrv = to_phy_driver(phydev->mdio.dev.driver);
+ int ret = 0;
+
+ mutex_lock(&phydev->lock);
+
+ if (enable && phydev->loopback_enabled) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ if (!enable && !phydev->loopback_enabled) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (phydev->drv && phydrv->set_loopback)
+ ret = phydrv->set_loopback(phydev, enable);
+ else
+ ret = -EOPNOTSUPP;
+
+ if (ret)
+ goto out;
+
+ phydev->loopback_enabled = enable;
+
+out:
+ mutex_unlock(&phydev->lock);
+ return ret;
+}
+EXPORT_SYMBOL(phy_loopback);
+
/* Generic PHY support and helper functions */
/**
@@ -1343,11 +1389,6 @@ int genphy_aneg_done(struct phy_device *phydev)
}
EXPORT_SYMBOL(genphy_aneg_done);
-static int gen10g_config_aneg(struct phy_device *phydev)
-{
- return 0;
-}
-
/**
* genphy_update_link - update link status in @phydev
* @phydev: target phy_device struct
@@ -1481,33 +1522,6 @@ int genphy_read_status(struct phy_device *phydev)
}
EXPORT_SYMBOL(genphy_read_status);
-static int gen10g_read_status(struct phy_device *phydev)
-{
- int devad, reg;
- u32 mmd_mask = phydev->c45_ids.devices_in_package;
-
- phydev->link = 1;
-
- /* For now just lie and say it's 10G all the time */
- phydev->speed = SPEED_10000;
- phydev->duplex = DUPLEX_FULL;
-
- for (devad = 0; mmd_mask; devad++, mmd_mask = mmd_mask >> 1) {
- if (!(mmd_mask & 1))
- continue;
-
- /* Read twice because link state is latched and a
- * read moves the current state into the register
- */
- phy_read_mmd(phydev, devad, MDIO_STAT1);
- reg = phy_read_mmd(phydev, devad, MDIO_STAT1);
- if (reg < 0 || !(reg & MDIO_STAT1_LSTATUS))
- phydev->link = 0;
- }
-
- return 0;
-}
-
/**
* genphy_soft_reset - software reset the PHY via BMCR_RESET bit
* @phydev: target phy_device struct
@@ -1571,23 +1585,8 @@ int genphy_config_init(struct phy_device *phydev)
return 0;
}
-
-static int gen10g_soft_reset(struct phy_device *phydev)
-{
- /* Do nothing for now */
- return 0;
-}
EXPORT_SYMBOL(genphy_config_init);
-static int gen10g_config_init(struct phy_device *phydev)
-{
- /* Temporarily just say we support everything */
- phydev->supported = SUPPORTED_10000baseT_Full;
- phydev->advertising = SUPPORTED_10000baseT_Full;
-
- return 0;
-}
-
int genphy_suspend(struct phy_device *phydev)
{
int value;
@@ -1603,11 +1602,6 @@ int genphy_suspend(struct phy_device *phydev)
}
EXPORT_SYMBOL(genphy_suspend);
-static int gen10g_suspend(struct phy_device *phydev)
-{
- return 0;
-}
-
int genphy_resume(struct phy_device *phydev)
{
int value;
@@ -1623,10 +1617,22 @@ int genphy_resume(struct phy_device *phydev)
}
EXPORT_SYMBOL(genphy_resume);
-static int gen10g_resume(struct phy_device *phydev)
+int genphy_loopback(struct phy_device *phydev, bool enable)
{
- return 0;
+ int value;
+
+ value = phy_read(phydev, MII_BMCR);
+ if (value < 0)
+ return value;
+
+ if (enable)
+ value |= BMCR_LOOPBACK;
+ else
+ value &= ~BMCR_LOOPBACK;
+
+ return phy_write(phydev, MII_BMCR, value);
}
+EXPORT_SYMBOL(genphy_loopback);
static int __set_phy_supported(struct phy_device *phydev, u32 max_speed)
{
@@ -1859,8 +1865,7 @@ void phy_drivers_unregister(struct phy_driver *drv, int n)
}
EXPORT_SYMBOL(phy_drivers_unregister);
-static struct phy_driver genphy_driver[] = {
-{
+static struct phy_driver genphy_driver = {
.phy_id = 0xffffffff,
.phy_id_mask = 0xffffffff,
.name = "Generic PHY",
@@ -1874,18 +1879,8 @@ static struct phy_driver genphy_driver[] = {
.read_status = genphy_read_status,
.suspend = genphy_suspend,
.resume = genphy_resume,
-}, {
- .phy_id = 0xffffffff,
- .phy_id_mask = 0xffffffff,
- .name = "Generic 10G PHY",
- .soft_reset = gen10g_soft_reset,
- .config_init = gen10g_config_init,
- .features = 0,
- .config_aneg = gen10g_config_aneg,
- .read_status = gen10g_read_status,
- .suspend = gen10g_suspend,
- .resume = gen10g_resume,
-} };
+ .set_loopback = genphy_loopback,
+};
static int __init phy_init(void)
{
@@ -1895,18 +1890,24 @@ static int __init phy_init(void)
if (rc)
return rc;
- rc = phy_drivers_register(genphy_driver,
- ARRAY_SIZE(genphy_driver), THIS_MODULE);
+ rc = phy_driver_register(&genphy_10g_driver, THIS_MODULE);
if (rc)
+ goto err_10g;
+
+ rc = phy_driver_register(&genphy_driver, THIS_MODULE);
+ if (rc) {
+ phy_driver_unregister(&genphy_10g_driver);
+err_10g:
mdio_bus_exit();
+ }
return rc;
}
static void __exit phy_exit(void)
{
- phy_drivers_unregister(genphy_driver,
- ARRAY_SIZE(genphy_driver));
+ phy_driver_unregister(&genphy_10g_driver);
+ phy_driver_unregister(&genphy_driver);
mdio_bus_exit();
}
diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c
index cef6967..2306bfa 100644
--- a/drivers/net/phy/smsc.c
+++ b/drivers/net/phy/smsc.c
@@ -25,6 +25,16 @@
#include <linux/netdevice.h>
#include <linux/smscphy.h>
+struct smsc_hw_stat {
+ const char *string;
+ u8 reg;
+ u8 bits;
+};
+
+static struct smsc_hw_stat smsc_hw_stats[] = {
+ { "phy_symbol_errors", 26, 16},
+};
+
struct smsc_phy_priv {
bool energy_enable;
};
@@ -143,6 +153,48 @@ static int lan87xx_read_status(struct phy_device *phydev)
return err;
}
+static int smsc_get_sset_count(struct phy_device *phydev)
+{
+ return ARRAY_SIZE(smsc_hw_stats);
+}
+
+static void smsc_get_strings(struct phy_device *phydev, u8 *data)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(smsc_hw_stats); i++) {
+ strncpy(data + i * ETH_GSTRING_LEN,
+ smsc_hw_stats[i].string, ETH_GSTRING_LEN);
+ }
+}
+
+#ifndef UINT64_MAX
+#define UINT64_MAX (u64)(~((u64)0))
+#endif
+static u64 smsc_get_stat(struct phy_device *phydev, int i)
+{
+ struct smsc_hw_stat stat = smsc_hw_stats[i];
+ int val;
+ u64 ret;
+
+ val = phy_read(phydev, stat.reg);
+ if (val < 0)
+ ret = UINT64_MAX;
+ else
+ ret = val;
+
+ return ret;
+}
+
+static void smsc_get_stats(struct phy_device *phydev,
+ struct ethtool_stats *stats, u64 *data)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(smsc_hw_stats); i++)
+ data[i] = smsc_get_stat(phydev, i);
+}
+
static int smsc_phy_probe(struct phy_device *phydev)
{
struct device *dev = &phydev->mdio.dev;
@@ -170,7 +222,7 @@ static struct phy_driver smsc_phy_driver[] = {
.name = "SMSC LAN83C185",
.features = PHY_BASIC_FEATURES,
- .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
+ .flags = PHY_HAS_INTERRUPT,
.probe = smsc_phy_probe,
@@ -192,7 +244,7 @@ static struct phy_driver smsc_phy_driver[] = {
.name = "SMSC LAN8187",
.features = PHY_BASIC_FEATURES,
- .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
+ .flags = PHY_HAS_INTERRUPT,
.probe = smsc_phy_probe,
@@ -206,6 +258,11 @@ static struct phy_driver smsc_phy_driver[] = {
.ack_interrupt = smsc_phy_ack_interrupt,
.config_intr = smsc_phy_config_intr,
+ /* Statistics */
+ .get_sset_count = smsc_get_sset_count,
+ .get_strings = smsc_get_strings,
+ .get_stats = smsc_get_stats,
+
.suspend = genphy_suspend,
.resume = genphy_resume,
}, {
@@ -214,7 +271,7 @@ static struct phy_driver smsc_phy_driver[] = {
.name = "SMSC LAN8700",
.features = PHY_BASIC_FEATURES,
- .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
+ .flags = PHY_HAS_INTERRUPT,
.probe = smsc_phy_probe,
@@ -228,6 +285,11 @@ static struct phy_driver smsc_phy_driver[] = {
.ack_interrupt = smsc_phy_ack_interrupt,
.config_intr = smsc_phy_config_intr,
+ /* Statistics */
+ .get_sset_count = smsc_get_sset_count,
+ .get_strings = smsc_get_strings,
+ .get_stats = smsc_get_stats,
+
.suspend = genphy_suspend,
.resume = genphy_resume,
}, {
@@ -236,7 +298,7 @@ static struct phy_driver smsc_phy_driver[] = {
.name = "SMSC LAN911x Internal PHY",
.features = PHY_BASIC_FEATURES,
- .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
+ .flags = PHY_HAS_INTERRUPT,
.probe = smsc_phy_probe,
@@ -257,7 +319,7 @@ static struct phy_driver smsc_phy_driver[] = {
.name = "SMSC LAN8710/LAN8720",
.features = PHY_BASIC_FEATURES,
- .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
+ .flags = PHY_HAS_INTERRUPT,
.probe = smsc_phy_probe,
@@ -271,6 +333,11 @@ static struct phy_driver smsc_phy_driver[] = {
.ack_interrupt = smsc_phy_ack_interrupt,
.config_intr = smsc_phy_config_intr,
+ /* Statistics */
+ .get_sset_count = smsc_get_sset_count,
+ .get_strings = smsc_get_strings,
+ .get_stats = smsc_get_stats,
+
.suspend = genphy_suspend,
.resume = genphy_resume,
}, {
@@ -279,7 +346,7 @@ static struct phy_driver smsc_phy_driver[] = {
.name = "SMSC LAN8740",
.features = PHY_BASIC_FEATURES,
- .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
+ .flags = PHY_HAS_INTERRUPT,
.probe = smsc_phy_probe,
@@ -293,6 +360,11 @@ static struct phy_driver smsc_phy_driver[] = {
.ack_interrupt = smsc_phy_ack_interrupt,
.config_intr = smsc_phy_config_intr,
+ /* Statistics */
+ .get_sset_count = smsc_get_sset_count,
+ .get_strings = smsc_get_strings,
+ .get_stats = smsc_get_stats,
+
.suspend = genphy_suspend,
.resume = genphy_resume,
} };
OpenPOWER on IntegriCloud