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

Merge branch 'Convert-Felix-DSA-switch-to-PHYLINK'

Vladimir Oltean says:

====================
Convert Felix DSA switch to PHYLINK

Unlike most other conversions, this one is not by far a trivial one, and should
be seen as "Layerscape PCS meets PHYLINK". Actually, the PCS doesn't
need a lot of hand-holding and most of our other devices 'just work'
(this one included) without any sort of operating system awareness, just
an initialization procedure done typically in the bootloader.
Our issues start when the PCS stops from "just working", and that is
where PHYLINK comes in handy.

The PCS is not specific to the Vitesse / Microsemi / Microchip switching core
at all. Variations of this SerDes/PCS design can also be found on DPAA1 and
DPAA2 hardware.

The main idea of the abstraction provided is that the PCS looks so much like a
PHY device, that we model it as an actual PHY device and run the generic PHY
functions on it, where appropriate.

The 4xSGMII, QSGMII and QSXGMII modes are fairly straightforward.

The SerDes protocol which the driver calls 2500Base-X mode (a misnomer) is more
interesting. There is a description of how it works and what can be done with
it in patch 9/9 (in a comment above vsc9959_pcs_init_2500basex).
In short, it is a fixed speed protocol with no auto-negotiation whatsoever.
From my research of the SGMII-2500 patent [1], it has nothing to do with
SGMII-2500. That one:
* does not define any change to the AN base page compared to plain 10/100/1000
  SGMII. This implies that the 2500 speed is not negotiable, but the other
  speeds are. In our case, when the SerDes is configured for this protocol it's
  configured for good, there's no going back to SGMII.
* runs at a higher base frequency than regular SGMII. So SGMII-2500 operating
  at 1000 Mbps wouldn't interoperate with plain SGMII at 1000 Mbps. Strange,
  but ok..
* Emulates lower link speeds than 2500 by duplicating the codewords twice, then
  thrice, then twice again etc (2.5/25/250 times on average). The Layerscape
  PCS doesn't do that (it is fixed at 2500 Mbaud).

But on the other hand it isn't completely compatible with Base-X either,
since it doesn't do 802.3z / clause 37 auto negotiation (flow control,
local/remote fault etc). It is compatible with 2500Base-X without
in-band AN, and that is exactly how we decided to expose it (this is
actually similar to what others do).

For SGMII and USXGMII, the driver is using the PHYLINK 'managed =
"in-band-status"' DTS binding to figure out whether in-band AN is
expected to be enabled in the PCS or not. It is expected that the
attached PHY follows suite, but there is a gap here: the PHY driver does
not react to this setting, so only one of "AN on" and "AN off" works on
any particular PHY, even though that PHY might support bypassing the
SGMII AN process, as is the case on the VSC8514 PHY present on the
LS1028A-RDB board. A separate series will be sent to propose a way to
deal with that.

I dropped the Ocelot PHYLINK conversion because:
* I don't have VSC7514 hardware anyway
* The hardware is so different in this regard that there's almost nothing to
  share anyway.

Changes in v5:

- Added the register write to DEV_CLOCK_CFG back in
  felix_phylink_mac_config in patch 9/9.

Changes in v4:

- This is mostly a resend of v3, with the only notable change that I've
  dropped the PHY core patches for in_band_autoneg and I'll propose them
  independently.

v1 series:
https://www.spinics.net/lists/netdev/msg613869.html

RFC v2 series:
https://www.spinics.net/lists/netdev/msg620128.html

v3 series:
https://www.spinics.net/lists/netdev/msg622060.html

v4 series:
https://www.spinics.net/lists/netdev/msg622606.html

[0]: https://www.spinics.net/lists/netdev/msg613869.html
[1]: https://patents.google.com/patent/US7356047B1/en
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents de1b23b9 bdeced75
...@@ -251,7 +251,8 @@ this documentation. ...@@ -251,7 +251,8 @@ this documentation.
phylink_mac_change(priv->phylink, link_is_up); phylink_mac_change(priv->phylink, link_is_up);
where ``link_is_up`` is true if the link is currently up or false where ``link_is_up`` is true if the link is currently up or false
otherwise. otherwise. If a MAC is unable to provide these interrupts, then
it should set ``priv->phylink_config.pcs_poll = true;`` in step 9.
11. Verify that the driver does not call:: 11. Verify that the driver does not call::
......
...@@ -3,8 +3,10 @@ config NET_DSA_MSCC_FELIX ...@@ -3,8 +3,10 @@ config NET_DSA_MSCC_FELIX
tristate "Ocelot / Felix Ethernet switch support" tristate "Ocelot / Felix Ethernet switch support"
depends on NET_DSA && PCI depends on NET_DSA && PCI
depends on NET_VENDOR_MICROSEMI depends on NET_VENDOR_MICROSEMI
depends on NET_VENDOR_FREESCALE
select MSCC_OCELOT_SWITCH select MSCC_OCELOT_SWITCH
select NET_DSA_TAG_OCELOT select NET_DSA_TAG_OCELOT
select FSL_ENETC_MDIO
help help
This driver supports the VSC9959 network switch, which is a member of This driver supports the VSC9959 network switch, which is a member of
the Vitesse / Microsemi / Microchip Ocelot family of switching cores. the Vitesse / Microsemi / Microchip Ocelot family of switching cores.
......
This diff is collapsed.
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
struct felix_info { struct felix_info {
struct resource *target_io_res; struct resource *target_io_res;
struct resource *port_io_res; struct resource *port_io_res;
struct resource *imdio_res;
const struct reg_field *regfields; const struct reg_field *regfields;
const u32 *const *map; const u32 *const *map;
const struct ocelot_ops *ops; const struct ocelot_ops *ops;
...@@ -17,7 +18,18 @@ struct felix_info { ...@@ -17,7 +18,18 @@ struct felix_info {
const struct ocelot_stat_layout *stats_layout; const struct ocelot_stat_layout *stats_layout;
unsigned int num_stats; unsigned int num_stats;
int num_ports; int num_ports;
int pci_bar; int switch_pci_bar;
int imdio_pci_bar;
int (*mdio_bus_alloc)(struct ocelot *ocelot);
void (*mdio_bus_free)(struct ocelot *ocelot);
void (*pcs_init)(struct ocelot *ocelot, int port,
unsigned int link_an_mode,
const struct phylink_link_state *state);
void (*pcs_an_restart)(struct ocelot *ocelot, int port);
void (*pcs_link_state)(struct ocelot *ocelot, int port,
struct phylink_link_state *state);
int (*prevalidate_phy_mode)(struct ocelot *ocelot, int port,
phy_interface_t phy_mode);
}; };
extern struct felix_info felix_info_vsc9959; extern struct felix_info felix_info_vsc9959;
...@@ -32,6 +44,8 @@ struct felix { ...@@ -32,6 +44,8 @@ struct felix {
struct pci_dev *pdev; struct pci_dev *pdev;
struct felix_info *info; struct felix_info *info;
struct ocelot ocelot; struct ocelot ocelot;
struct mii_bus *imdio;
struct phy_device **pcs;
}; };
#endif #endif
This diff is collapsed.
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
config FSL_ENETC config FSL_ENETC
tristate "ENETC PF driver" tristate "ENETC PF driver"
depends on PCI && PCI_MSI && (ARCH_LAYERSCAPE || COMPILE_TEST) depends on PCI && PCI_MSI && (ARCH_LAYERSCAPE || COMPILE_TEST)
select FSL_ENETC_MDIO
select PHYLIB select PHYLIB
help help
This driver supports NXP ENETC gigabit ethernet controller PCIe This driver supports NXP ENETC gigabit ethernet controller PCIe
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
common-objs := enetc.o enetc_cbdr.o enetc_ethtool.o common-objs := enetc.o enetc_cbdr.o enetc_ethtool.o
obj-$(CONFIG_FSL_ENETC) += fsl-enetc.o obj-$(CONFIG_FSL_ENETC) += fsl-enetc.o
fsl-enetc-y := enetc_pf.o enetc_mdio.o $(common-objs) fsl-enetc-y := enetc_pf.o $(common-objs)
fsl-enetc-$(CONFIG_PCI_IOV) += enetc_msg.o fsl-enetc-$(CONFIG_PCI_IOV) += enetc_msg.o
fsl-enetc-$(CONFIG_FSL_ENETC_QOS) += enetc_qos.o fsl-enetc-$(CONFIG_FSL_ENETC_QOS) += enetc_qos.o
......
...@@ -200,6 +200,7 @@ enum enetc_bdr_type {TX, RX}; ...@@ -200,6 +200,7 @@ enum enetc_bdr_type {TX, RX};
#define ENETC_PFPMR 0x1900 #define ENETC_PFPMR 0x1900
#define ENETC_PFPMR_PMACE BIT(1) #define ENETC_PFPMR_PMACE BIT(1)
#define ENETC_PFPMR_MWLM BIT(0) #define ENETC_PFPMR_MWLM BIT(0)
#define ENETC_EMDIO_BASE 0x1c00
#define ENETC_PSIUMHFR0(n, err) (((err) ? 0x1d08 : 0x1d00) + (n) * 0x10) #define ENETC_PSIUMHFR0(n, err) (((err) ? 0x1d08 : 0x1d00) + (n) * 0x10)
#define ENETC_PSIUMHFR1(n) (0x1d04 + (n) * 0x10) #define ENETC_PSIUMHFR1(n) (0x1d04 + (n) * 0x10)
#define ENETC_PSIMMHFR0(n, err) (((err) ? 0x1d00 : 0x1d08) + (n) * 0x10) #define ENETC_PSIMMHFR0(n, err) (((err) ? 0x1d00 : 0x1d08) + (n) * 0x10)
......
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/* Copyright 2019 NXP */ /* Copyright 2019 NXP */
#include <linux/fsl/enetc_mdio.h>
#include <linux/mdio.h> #include <linux/mdio.h>
#include <linux/of_mdio.h> #include <linux/of_mdio.h>
#include <linux/iopoll.h> #include <linux/iopoll.h>
#include <linux/of.h> #include <linux/of.h>
#include "enetc_mdio.h" #include "enetc_pf.h"
#define ENETC_MDIO_REG_OFFSET 0x1c00
#define ENETC_MDIO_CFG 0x0 /* MDIO configuration and status */ #define ENETC_MDIO_CFG 0x0 /* MDIO configuration and status */
#define ENETC_MDIO_CTL 0x4 /* MDIO control */ #define ENETC_MDIO_CTL 0x4 /* MDIO control */
#define ENETC_MDIO_DATA 0x8 /* MDIO data */ #define ENETC_MDIO_DATA 0x8 /* MDIO data */
#define ENETC_MDIO_ADDR 0xc /* MDIO address */ #define ENETC_MDIO_ADDR 0xc /* MDIO address */
#define enetc_mdio_rd(hw, off) \ static inline u32 _enetc_mdio_rd(struct enetc_mdio_priv *mdio_priv, int off)
enetc_port_rd(hw, ENETC_##off + ENETC_MDIO_REG_OFFSET) {
#define enetc_mdio_wr(hw, off, val) \ return enetc_port_rd(mdio_priv->hw, mdio_priv->mdio_base + off);
enetc_port_wr(hw, ENETC_##off + ENETC_MDIO_REG_OFFSET, val) }
#define enetc_mdio_rd_reg(off) enetc_mdio_rd(hw, off)
static inline void _enetc_mdio_wr(struct enetc_mdio_priv *mdio_priv, int off,
u32 val)
{
enetc_port_wr(mdio_priv->hw, mdio_priv->mdio_base + off, val);
}
#define ENETC_MDC_DIV 258 #define enetc_mdio_rd(mdio_priv, off) \
_enetc_mdio_rd(mdio_priv, ENETC_##off)
#define enetc_mdio_wr(mdio_priv, off, val) \
_enetc_mdio_wr(mdio_priv, ENETC_##off, val)
#define enetc_mdio_rd_reg(off) enetc_mdio_rd(mdio_priv, off)
#define MDIO_CFG_CLKDIV(x) ((((x) >> 1) & 0xff) << 8) #define MDIO_CFG_CLKDIV(x) ((((x) >> 1) & 0xff) << 8)
#define MDIO_CFG_BSY BIT(0) #define MDIO_CFG_BSY BIT(0)
#define MDIO_CFG_RD_ER BIT(1) #define MDIO_CFG_RD_ER BIT(1)
#define MDIO_CFG_HOLD(x) (((x) << 2) & GENMASK(4, 2))
#define MDIO_CFG_ENC45 BIT(6) #define MDIO_CFG_ENC45 BIT(6)
/* external MDIO only - driven on neg MDC edge */ /* external MDIO only - driven on neg MDC edge */
#define MDIO_CFG_NEG BIT(23) #define MDIO_CFG_NEG BIT(23)
#define ENETC_EMDIO_CFG \
(MDIO_CFG_HOLD(2) | \
MDIO_CFG_CLKDIV(258) | \
MDIO_CFG_NEG)
#define MDIO_CTL_DEV_ADDR(x) ((x) & 0x1f) #define MDIO_CTL_DEV_ADDR(x) ((x) & 0x1f)
#define MDIO_CTL_PORT_ADDR(x) (((x) & 0x1f) << 5) #define MDIO_CTL_PORT_ADDR(x) (((x) & 0x1f) << 5)
#define MDIO_CTL_READ BIT(15) #define MDIO_CTL_READ BIT(15)
#define MDIO_DATA(x) ((x) & 0xffff) #define MDIO_DATA(x) ((x) & 0xffff)
#define TIMEOUT 1000 #define TIMEOUT 1000
static int enetc_mdio_wait_complete(struct enetc_hw *hw) static int enetc_mdio_wait_complete(struct enetc_mdio_priv *mdio_priv)
{ {
u32 val; u32 val;
...@@ -46,12 +61,11 @@ static int enetc_mdio_wait_complete(struct enetc_hw *hw) ...@@ -46,12 +61,11 @@ static int enetc_mdio_wait_complete(struct enetc_hw *hw)
int enetc_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value) int enetc_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value)
{ {
struct enetc_mdio_priv *mdio_priv = bus->priv; struct enetc_mdio_priv *mdio_priv = bus->priv;
struct enetc_hw *hw = mdio_priv->hw;
u32 mdio_ctl, mdio_cfg; u32 mdio_ctl, mdio_cfg;
u16 dev_addr; u16 dev_addr;
int ret; int ret;
mdio_cfg = MDIO_CFG_CLKDIV(ENETC_MDC_DIV) | MDIO_CFG_NEG; mdio_cfg = ENETC_EMDIO_CFG;
if (regnum & MII_ADDR_C45) { if (regnum & MII_ADDR_C45) {
dev_addr = (regnum >> 16) & 0x1f; dev_addr = (regnum >> 16) & 0x1f;
mdio_cfg |= MDIO_CFG_ENC45; mdio_cfg |= MDIO_CFG_ENC45;
...@@ -61,44 +75,44 @@ int enetc_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value) ...@@ -61,44 +75,44 @@ int enetc_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value)
mdio_cfg &= ~MDIO_CFG_ENC45; mdio_cfg &= ~MDIO_CFG_ENC45;
} }
enetc_mdio_wr(hw, MDIO_CFG, mdio_cfg); enetc_mdio_wr(mdio_priv, MDIO_CFG, mdio_cfg);
ret = enetc_mdio_wait_complete(hw); ret = enetc_mdio_wait_complete(mdio_priv);
if (ret) if (ret)
return ret; return ret;
/* set port and dev addr */ /* set port and dev addr */
mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr); mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr);
enetc_mdio_wr(hw, MDIO_CTL, mdio_ctl); enetc_mdio_wr(mdio_priv, MDIO_CTL, mdio_ctl);
/* set the register address */ /* set the register address */
if (regnum & MII_ADDR_C45) { if (regnum & MII_ADDR_C45) {
enetc_mdio_wr(hw, MDIO_ADDR, regnum & 0xffff); enetc_mdio_wr(mdio_priv, MDIO_ADDR, regnum & 0xffff);
ret = enetc_mdio_wait_complete(hw); ret = enetc_mdio_wait_complete(mdio_priv);
if (ret) if (ret)
return ret; return ret;
} }
/* write the value */ /* write the value */
enetc_mdio_wr(hw, MDIO_DATA, MDIO_DATA(value)); enetc_mdio_wr(mdio_priv, MDIO_DATA, MDIO_DATA(value));
ret = enetc_mdio_wait_complete(hw); ret = enetc_mdio_wait_complete(mdio_priv);
if (ret) if (ret)
return ret; return ret;
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(enetc_mdio_write);
int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum) int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
{ {
struct enetc_mdio_priv *mdio_priv = bus->priv; struct enetc_mdio_priv *mdio_priv = bus->priv;
struct enetc_hw *hw = mdio_priv->hw;
u32 mdio_ctl, mdio_cfg; u32 mdio_ctl, mdio_cfg;
u16 dev_addr, value; u16 dev_addr, value;
int ret; int ret;
mdio_cfg = MDIO_CFG_CLKDIV(ENETC_MDC_DIV) | MDIO_CFG_NEG; mdio_cfg = ENETC_EMDIO_CFG;
if (regnum & MII_ADDR_C45) { if (regnum & MII_ADDR_C45) {
dev_addr = (regnum >> 16) & 0x1f; dev_addr = (regnum >> 16) & 0x1f;
mdio_cfg |= MDIO_CFG_ENC45; mdio_cfg |= MDIO_CFG_ENC45;
...@@ -107,86 +121,56 @@ int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum) ...@@ -107,86 +121,56 @@ int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
mdio_cfg &= ~MDIO_CFG_ENC45; mdio_cfg &= ~MDIO_CFG_ENC45;
} }
enetc_mdio_wr(hw, MDIO_CFG, mdio_cfg); enetc_mdio_wr(mdio_priv, MDIO_CFG, mdio_cfg);
ret = enetc_mdio_wait_complete(hw); ret = enetc_mdio_wait_complete(mdio_priv);
if (ret) if (ret)
return ret; return ret;
/* set port and device addr */ /* set port and device addr */
mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr); mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr);
enetc_mdio_wr(hw, MDIO_CTL, mdio_ctl); enetc_mdio_wr(mdio_priv, MDIO_CTL, mdio_ctl);
/* set the register address */ /* set the register address */
if (regnum & MII_ADDR_C45) { if (regnum & MII_ADDR_C45) {
enetc_mdio_wr(hw, MDIO_ADDR, regnum & 0xffff); enetc_mdio_wr(mdio_priv, MDIO_ADDR, regnum & 0xffff);
ret = enetc_mdio_wait_complete(hw); ret = enetc_mdio_wait_complete(mdio_priv);
if (ret) if (ret)
return ret; return ret;
} }
/* initiate the read */ /* initiate the read */
enetc_mdio_wr(hw, MDIO_CTL, mdio_ctl | MDIO_CTL_READ); enetc_mdio_wr(mdio_priv, MDIO_CTL, mdio_ctl | MDIO_CTL_READ);
ret = enetc_mdio_wait_complete(hw); ret = enetc_mdio_wait_complete(mdio_priv);
if (ret) if (ret)
return ret; return ret;
/* return all Fs if nothing was there */ /* return all Fs if nothing was there */
if (enetc_mdio_rd(hw, MDIO_CFG) & MDIO_CFG_RD_ER) { if (enetc_mdio_rd(mdio_priv, MDIO_CFG) & MDIO_CFG_RD_ER) {
dev_dbg(&bus->dev, dev_dbg(&bus->dev,
"Error while reading PHY%d reg at %d.%hhu\n", "Error while reading PHY%d reg at %d.%hhu\n",
phy_id, dev_addr, regnum); phy_id, dev_addr, regnum);
return 0xffff; return 0xffff;
} }
value = enetc_mdio_rd(hw, MDIO_DATA) & 0xffff; value = enetc_mdio_rd(mdio_priv, MDIO_DATA) & 0xffff;
return value; return value;
} }
EXPORT_SYMBOL_GPL(enetc_mdio_read);
int enetc_mdio_probe(struct enetc_pf *pf) struct enetc_hw *enetc_hw_alloc(struct device *dev, void __iomem *port_regs)
{ {
struct device *dev = &pf->si->pdev->dev; struct enetc_hw *hw;
struct enetc_mdio_priv *mdio_priv;
struct device_node *np;
struct mii_bus *bus;
int err;
bus = devm_mdiobus_alloc_size(dev, sizeof(*mdio_priv));
if (!bus)
return -ENOMEM;
bus->name = "Freescale ENETC MDIO Bus";
bus->read = enetc_mdio_read;
bus->write = enetc_mdio_write;
bus->parent = dev;
mdio_priv = bus->priv;
mdio_priv->hw = &pf->si->hw;
snprintf(bus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev));
np = of_get_child_by_name(dev->of_node, "mdio");
if (!np) {
dev_err(dev, "MDIO node missing\n");
return -EINVAL;
}
err = of_mdiobus_register(bus, np);
if (err) {
of_node_put(np);
dev_err(dev, "cannot register MDIO bus\n");
return err;
}
of_node_put(np); hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
pf->mdio = bus; if (!hw)
return ERR_PTR(-ENOMEM);
return 0; hw->port = port_regs;
}
void enetc_mdio_remove(struct enetc_pf *pf) return hw;
{
if (pf->mdio)
mdiobus_unregister(pf->mdio);
} }
EXPORT_SYMBOL_GPL(enetc_hw_alloc);
/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
/* Copyright 2019 NXP */
#include <linux/phy.h>
#include "enetc_pf.h"
struct enetc_mdio_priv {
struct enetc_hw *hw;
};
int enetc_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value);
int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum);
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/* Copyright 2019 NXP */ /* Copyright 2019 NXP */
#include <linux/fsl/enetc_mdio.h>
#include <linux/of_mdio.h> #include <linux/of_mdio.h>
#include "enetc_mdio.h" #include "enetc_pf.h"
#define ENETC_MDIO_DEV_ID 0xee01 #define ENETC_MDIO_DEV_ID 0xee01
#define ENETC_MDIO_DEV_NAME "FSL PCIe IE Central MDIO" #define ENETC_MDIO_DEV_NAME "FSL PCIe IE Central MDIO"
...@@ -13,17 +14,29 @@ static int enetc_pci_mdio_probe(struct pci_dev *pdev, ...@@ -13,17 +14,29 @@ static int enetc_pci_mdio_probe(struct pci_dev *pdev,
{ {
struct enetc_mdio_priv *mdio_priv; struct enetc_mdio_priv *mdio_priv;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
void __iomem *port_regs;
struct enetc_hw *hw; struct enetc_hw *hw;
struct mii_bus *bus; struct mii_bus *bus;
int err; int err;
hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL); port_regs = pci_iomap(pdev, 0, 0);
if (!hw) if (!port_regs) {
return -ENOMEM; dev_err(dev, "iomap failed\n");
err = -ENXIO;
goto err_ioremap;
}
hw = enetc_hw_alloc(dev, port_regs);
if (IS_ERR(enetc_hw_alloc)) {
err = PTR_ERR(hw);
goto err_hw_alloc;
}
bus = devm_mdiobus_alloc_size(dev, sizeof(*mdio_priv)); bus = devm_mdiobus_alloc_size(dev, sizeof(*mdio_priv));
if (!bus) if (!bus) {
return -ENOMEM; err = -ENOMEM;
goto err_mdiobus_alloc;
}
bus->name = ENETC_MDIO_BUS_NAME; bus->name = ENETC_MDIO_BUS_NAME;
bus->read = enetc_mdio_read; bus->read = enetc_mdio_read;
...@@ -31,13 +44,14 @@ static int enetc_pci_mdio_probe(struct pci_dev *pdev, ...@@ -31,13 +44,14 @@ static int enetc_pci_mdio_probe(struct pci_dev *pdev,
bus->parent = dev; bus->parent = dev;
mdio_priv = bus->priv; mdio_priv = bus->priv;
mdio_priv->hw = hw; mdio_priv->hw = hw;
mdio_priv->mdio_base = ENETC_EMDIO_BASE;
snprintf(bus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev)); snprintf(bus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev));
pcie_flr(pdev); pcie_flr(pdev);
err = pci_enable_device_mem(pdev); err = pci_enable_device_mem(pdev);
if (err) { if (err) {
dev_err(dev, "device enable failed\n"); dev_err(dev, "device enable failed\n");
return err; goto err_pci_enable;
} }
err = pci_request_region(pdev, 0, KBUILD_MODNAME); err = pci_request_region(pdev, 0, KBUILD_MODNAME);
...@@ -46,13 +60,6 @@ static int enetc_pci_mdio_probe(struct pci_dev *pdev, ...@@ -46,13 +60,6 @@ static int enetc_pci_mdio_probe(struct pci_dev *pdev,
goto err_pci_mem_reg; goto err_pci_mem_reg;
} }
hw->port = pci_iomap(pdev, 0, 0);
if (!hw->port) {
err = -ENXIO;
dev_err(dev, "iomap failed\n");
goto err_ioremap;
}
err = of_mdiobus_register(bus, dev->of_node); err = of_mdiobus_register(bus, dev->of_node);
if (err) if (err)
goto err_mdiobus_reg; goto err_mdiobus_reg;
...@@ -62,12 +69,14 @@ static int enetc_pci_mdio_probe(struct pci_dev *pdev, ...@@ -62,12 +69,14 @@ static int enetc_pci_mdio_probe(struct pci_dev *pdev,
return 0; return 0;
err_mdiobus_reg: err_mdiobus_reg:
iounmap(mdio_priv->hw->port);
err_ioremap:
pci_release_mem_regions(pdev); pci_release_mem_regions(pdev);
err_pci_mem_reg: err_pci_mem_reg:
pci_disable_device(pdev); pci_disable_device(pdev);
err_pci_enable:
err_mdiobus_alloc:
iounmap(port_regs);
err_hw_alloc:
err_ioremap:
return err; return err;
} }
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
/* Copyright 2017-2019 NXP */ /* Copyright 2017-2019 NXP */
#include <linux/module.h> #include <linux/module.h>
#include <linux/fsl/enetc_mdio.h>
#include <linux/of_mdio.h> #include <linux/of_mdio.h>
#include <linux/of_net.h> #include <linux/of_net.h>
#include "enetc_pf.h" #include "enetc_pf.h"
...@@ -749,6 +750,52 @@ static void enetc_pf_netdev_setup(struct enetc_si *si, struct net_device *ndev, ...@@ -749,6 +750,52 @@ static void enetc_pf_netdev_setup(struct enetc_si *si, struct net_device *ndev,
enetc_get_primary_mac_addr(&si->hw, ndev->dev_addr); enetc_get_primary_mac_addr(&si->hw, ndev->dev_addr);
} }
static int enetc_mdio_probe(struct enetc_pf *pf)
{
struct device *dev = &pf->si->pdev->dev;
struct enetc_mdio_priv *mdio_priv;
struct device_node *np;
struct mii_bus *bus;
int err;
bus = devm_mdiobus_alloc_size(dev, sizeof(*mdio_priv));
if (!bus)
return -ENOMEM;
bus->name = "Freescale ENETC MDIO Bus";
bus->read = enetc_mdio_read;
bus->write = enetc_mdio_write;
bus->parent = dev;
mdio_priv = bus->priv;
mdio_priv->hw = &pf->si->hw;
mdio_priv->mdio_base = ENETC_EMDIO_BASE;
snprintf(bus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev));
np = of_get_child_by_name(dev->of_node, "mdio");
if (!np) {
dev_err(dev, "MDIO node missing\n");
return -EINVAL;
}
err = of_mdiobus_register(bus, np);
if (err) {
of_node_put(np);
dev_err(dev, "cannot register MDIO bus\n");
return err;
}
of_node_put(np);
pf->mdio = bus;
return 0;
}
static void enetc_mdio_remove(struct enetc_pf *pf)
{
if (pf->mdio)
mdiobus_unregister(pf->mdio);
}
static int enetc_of_get_phy(struct enetc_ndev_priv *priv) static int enetc_of_get_phy(struct enetc_ndev_priv *priv)
{ {
struct enetc_pf *pf = enetc_si_priv(priv->si); struct enetc_pf *pf = enetc_si_priv(priv->si);
......
...@@ -49,7 +49,3 @@ struct enetc_pf { ...@@ -49,7 +49,3 @@ struct enetc_pf {
int enetc_msg_psi_init(struct enetc_pf *pf); int enetc_msg_psi_init(struct enetc_pf *pf);
void enetc_msg_psi_free(struct enetc_pf *pf); void enetc_msg_psi_free(struct enetc_pf *pf);
void enetc_msg_handle_rxmsg(struct enetc_pf *pf, int mbox_id, u16 *status); void enetc_msg_handle_rxmsg(struct enetc_pf *pf, int mbox_id, u16 *status);
/* MDIO */
int enetc_mdio_probe(struct enetc_pf *pf);
void enetc_mdio_remove(struct enetc_pf *pf);
...@@ -500,13 +500,14 @@ EXPORT_SYMBOL(ocelot_port_enable); ...@@ -500,13 +500,14 @@ EXPORT_SYMBOL(ocelot_port_enable);
static int ocelot_port_open(struct net_device *dev) static int ocelot_port_open(struct net_device *dev)
{ {
struct ocelot_port_private *priv = netdev_priv(dev); struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot; struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot;
int port = priv->chip_port; int port = priv->chip_port;
int err; int err;
if (priv->serdes) { if (priv->serdes) {
err = phy_set_mode_ext(priv->serdes, PHY_MODE_ETHERNET, err = phy_set_mode_ext(priv->serdes, PHY_MODE_ETHERNET,
priv->phy_mode); ocelot_port->phy_mode);
if (err) { if (err) {
netdev_err(dev, "Could not set mode of SerDes\n"); netdev_err(dev, "Could not set mode of SerDes\n");
return err; return err;
...@@ -514,7 +515,7 @@ static int ocelot_port_open(struct net_device *dev) ...@@ -514,7 +515,7 @@ static int ocelot_port_open(struct net_device *dev)
} }
err = phy_connect_direct(dev, priv->phy, &ocelot_port_adjust_link, err = phy_connect_direct(dev, priv->phy, &ocelot_port_adjust_link,
priv->phy_mode); ocelot_port->phy_mode);
if (err) { if (err) {
netdev_err(dev, "Could not attach to PHY\n"); netdev_err(dev, "Could not attach to PHY\n");
return err; return err;
......
...@@ -18,11 +18,11 @@ ...@@ -18,11 +18,11 @@
#include <linux/ptp_clock_kernel.h> #include <linux/ptp_clock_kernel.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <soc/mscc/ocelot_qsys.h>
#include <soc/mscc/ocelot_sys.h> #include <soc/mscc/ocelot_sys.h>
#include <soc/mscc/ocelot_dev.h>
#include <soc/mscc/ocelot_ana.h>
#include <soc/mscc/ocelot.h> #include <soc/mscc/ocelot.h>
#include "ocelot_ana.h"
#include "ocelot_dev.h"
#include "ocelot_qsys.h"
#include "ocelot_rew.h" #include "ocelot_rew.h"
#include "ocelot_qs.h" #include "ocelot_qs.h"
#include "ocelot_tc.h" #include "ocelot_tc.h"
...@@ -68,7 +68,6 @@ struct ocelot_port_private { ...@@ -68,7 +68,6 @@ struct ocelot_port_private {
u8 vlan_aware; u8 vlan_aware;
phy_interface_t phy_mode;
struct phy *serdes; struct phy *serdes;
struct ocelot_port_tc tc; struct ocelot_port_tc tc;
......
...@@ -402,9 +402,9 @@ static int mscc_ocelot_probe(struct platform_device *pdev) ...@@ -402,9 +402,9 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
of_get_phy_mode(portnp, &phy_mode); of_get_phy_mode(portnp, &phy_mode);
priv->phy_mode = phy_mode; ocelot_port->phy_mode = phy_mode;
switch (priv->phy_mode) { switch (ocelot_port->phy_mode) {
case PHY_INTERFACE_MODE_NA: case PHY_INTERFACE_MODE_NA:
continue; continue;
case PHY_INTERFACE_MODE_SGMII: case PHY_INTERFACE_MODE_SGMII:
......
...@@ -281,6 +281,7 @@ static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode) ...@@ -281,6 +281,7 @@ static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode)
switch (pl->link_config.interface) { switch (pl->link_config.interface) {
case PHY_INTERFACE_MODE_SGMII: case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_QSGMII:
phylink_set(pl->supported, 10baseT_Half); phylink_set(pl->supported, 10baseT_Half);
phylink_set(pl->supported, 10baseT_Full); phylink_set(pl->supported, 10baseT_Full);
phylink_set(pl->supported, 100baseT_Half); phylink_set(pl->supported, 100baseT_Half);
...@@ -1021,7 +1022,8 @@ void phylink_start(struct phylink *pl) ...@@ -1021,7 +1022,8 @@ void phylink_start(struct phylink *pl)
if (irq <= 0) if (irq <= 0)
mod_timer(&pl->link_poll, jiffies + HZ); mod_timer(&pl->link_poll, jiffies + HZ);
} }
if (pl->cfg_link_an_mode == MLO_AN_FIXED && pl->get_fixed_state) if ((pl->cfg_link_an_mode == MLO_AN_FIXED && pl->get_fixed_state) ||
pl->config->pcs_poll)
mod_timer(&pl->link_poll, jiffies + HZ); mod_timer(&pl->link_poll, jiffies + HZ);
if (pl->phydev) if (pl->phydev)
phy_start(pl->phydev); phy_start(pl->phydev);
......
/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
/* Copyright 2019 NXP */
#ifndef _FSL_ENETC_MDIO_H_
#define _FSL_ENETC_MDIO_H_
#include <linux/phy.h>
/* PCS registers */
#define ENETC_PCS_LINK_TIMER1 0x12
#define ENETC_PCS_LINK_TIMER1_VAL 0x06a0
#define ENETC_PCS_LINK_TIMER2 0x13
#define ENETC_PCS_LINK_TIMER2_VAL 0x0003
#define ENETC_PCS_IF_MODE 0x14
#define ENETC_PCS_IF_MODE_SGMII_EN BIT(0)
#define ENETC_PCS_IF_MODE_USE_SGMII_AN BIT(1)
#define ENETC_PCS_IF_MODE_SGMII_SPEED(x) (((x) << 2) & GENMASK(3, 2))
/* Not a mistake, the SerDes PLL needs to be set at 3.125 GHz by Reset
* Configuration Word (RCW, outside Linux control) for 2.5G SGMII mode. The PCS
* still thinks it's at gigabit.
*/
enum enetc_pcs_speed {
ENETC_PCS_SPEED_10 = 0,
ENETC_PCS_SPEED_100 = 1,
ENETC_PCS_SPEED_1000 = 2,
ENETC_PCS_SPEED_2500 = 2,
};
struct enetc_hw;
struct enetc_mdio_priv {
struct enetc_hw *hw;
int mdio_base;
};
#if IS_REACHABLE(CONFIG_FSL_ENETC_MDIO)
int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum);
int enetc_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value);
struct enetc_hw *enetc_hw_alloc(struct device *dev, void __iomem *port_regs);
#else
static inline int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
{ return -EINVAL; }
static inline int enetc_mdio_write(struct mii_bus *bus, int phy_id, int regnum,
u16 value)
{ return -EINVAL; }
struct enetc_hw *enetc_hw_alloc(struct device *dev, void __iomem *port_regs)
{ return ERR_PTR(-EINVAL); }
#endif
#endif
...@@ -372,6 +372,56 @@ static inline u32 mii_lpa_to_ethtool_lpa_x(u32 lpa) ...@@ -372,6 +372,56 @@ static inline u32 mii_lpa_to_ethtool_lpa_x(u32 lpa)
return result | mii_adv_to_ethtool_adv_x(lpa); return result | mii_adv_to_ethtool_adv_x(lpa);
} }
/**
* mii_lpa_mod_linkmode_adv_sgmii
* @lp_advertising: pointer to destination link mode.
* @lpa: value of the MII_LPA register
*
* A small helper function that translates MII_LPA bits to
* linkmode advertisement settings for SGMII.
* Leaves other bits unchanged.
*/
static inline void
mii_lpa_mod_linkmode_lpa_sgmii(unsigned long *lp_advertising, u32 lpa)
{
u32 speed_duplex = lpa & LPA_SGMII_DPX_SPD_MASK;
linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, lp_advertising,
speed_duplex == LPA_SGMII_1000HALF);
linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, lp_advertising,
speed_duplex == LPA_SGMII_1000FULL);
linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, lp_advertising,
speed_duplex == LPA_SGMII_100HALF);
linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, lp_advertising,
speed_duplex == LPA_SGMII_100FULL);
linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, lp_advertising,
speed_duplex == LPA_SGMII_10HALF);
linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, lp_advertising,
speed_duplex == LPA_SGMII_10FULL);
}
/**
* mii_lpa_to_linkmode_adv_sgmii
* @advertising: pointer to destination link mode.
* @lpa: value of the MII_LPA register
*
* A small helper function that translates MII_ADVERTISE bits
* to linkmode advertisement settings when in SGMII mode.
* Clears the old value of advertising.
*/
static inline void mii_lpa_to_linkmode_lpa_sgmii(unsigned long *lp_advertising,
u32 lpa)
{
linkmode_zero(lp_advertising);
mii_lpa_mod_linkmode_lpa_sgmii(lp_advertising, lpa);
}
/** /**
* mii_adv_mod_linkmode_adv_t * mii_adv_mod_linkmode_adv_t
* @advertising:pointer to destination link mode. * @advertising:pointer to destination link mode.
......
...@@ -63,10 +63,12 @@ enum phylink_op_type { ...@@ -63,10 +63,12 @@ enum phylink_op_type {
* struct phylink_config - PHYLINK configuration structure * struct phylink_config - PHYLINK configuration structure
* @dev: a pointer to a struct device associated with the MAC * @dev: a pointer to a struct device associated with the MAC
* @type: operation type of PHYLINK instance * @type: operation type of PHYLINK instance
* @pcs_poll: MAC PCS cannot provide link change interrupt
*/ */
struct phylink_config { struct phylink_config {
struct device *dev; struct device *dev;
enum phylink_op_type type; enum phylink_op_type type;
bool pcs_poll;
}; };
/** /**
......
...@@ -279,6 +279,11 @@ struct dsa_switch { ...@@ -279,6 +279,11 @@ struct dsa_switch {
*/ */
bool vlan_filtering; bool vlan_filtering;
/* MAC PCS does not provide link state change interrupt, and requires
* polling. Flag passed on to PHYLINK.
*/
bool pcs_poll;
size_t num_ports; size_t num_ports;
}; };
......
...@@ -420,6 +420,8 @@ struct ocelot_port { ...@@ -420,6 +420,8 @@ struct ocelot_port {
u8 ptp_cmd; u8 ptp_cmd;
struct sk_buff_head tx_skbs; struct sk_buff_head tx_skbs;
u8 ts_id; u8 ts_id;
phy_interface_t phy_mode;
}; };
struct ocelot { struct ocelot {
......
...@@ -131,6 +131,18 @@ ...@@ -131,6 +131,18 @@
#define NWAYTEST_LOOPBACK 0x0100 /* Enable loopback for N-way */ #define NWAYTEST_LOOPBACK 0x0100 /* Enable loopback for N-way */
#define NWAYTEST_RESV2 0xfe00 /* Unused... */ #define NWAYTEST_RESV2 0xfe00 /* Unused... */
/* MAC and PHY tx_config_Reg[15:0] for SGMII in-band auto-negotiation.*/
#define ADVERTISE_SGMII 0x0001 /* MAC can do SGMII */
#define LPA_SGMII 0x0001 /* PHY can do SGMII */
#define LPA_SGMII_DPX_SPD_MASK 0x1C00 /* SGMII duplex and speed bits */
#define LPA_SGMII_10HALF 0x0000 /* Can do 10mbps half-duplex */
#define LPA_SGMII_10FULL 0x1000 /* Can do 10mbps full-duplex */
#define LPA_SGMII_100HALF 0x0400 /* Can do 100mbps half-duplex */
#define LPA_SGMII_100FULL 0x1400 /* Can do 100mbps full-duplex */
#define LPA_SGMII_1000HALF 0x0800 /* Can do 1000mbps half-duplex */
#define LPA_SGMII_1000FULL 0x1800 /* Can do 1000mbps full-duplex */
#define LPA_SGMII_LINK 0x8000 /* PHY link with copper-side partner */
/* 1000BASE-T Control register */ /* 1000BASE-T Control register */
#define ADVERTISE_1000FULL 0x0200 /* Advertise 1000BASE-T full duplex */ #define ADVERTISE_1000FULL 0x0200 /* Advertise 1000BASE-T full duplex */
#define ADVERTISE_1000HALF 0x0100 /* Advertise 1000BASE-T half duplex */ #define ADVERTISE_1000HALF 0x0100 /* Advertise 1000BASE-T half duplex */
......
...@@ -599,6 +599,7 @@ static int dsa_port_phylink_register(struct dsa_port *dp) ...@@ -599,6 +599,7 @@ static int dsa_port_phylink_register(struct dsa_port *dp)
dp->pl_config.dev = ds->dev; dp->pl_config.dev = ds->dev;
dp->pl_config.type = PHYLINK_DEV; dp->pl_config.type = PHYLINK_DEV;
dp->pl_config.pcs_poll = ds->pcs_poll;
dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(port_dn), dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(port_dn),
mode, &dsa_port_phylink_mac_ops); mode, &dsa_port_phylink_mac_ops);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment