Merge branch 'enetc-PCIe-MDIO'

Claudiu Manoil says:

====================
enetc: Add mdio bus driver for the PCIe MDIO endpoint

First patch fixes a sparse issue and cleans up accessors to avoid
casting to __iomem.  The second one cleans up the Makefile, to make
it easier to add new entries.

Third patch just registers the PCIe endpoint device containing
the MDIO registers as a standalone MDIO bus driver, to provide
an alternative way to control the MDIO bus.  The same code used
by the ENETC ports (eth controllers) to manage MDIO via local
registers applies and is reused.

Bindings are provided for the new MDIO node, similarly to ENETC
port nodes bindings.

Last patch enables the ENETC port 1 and its RGMII PHY on the
LS1028A QDS board, where the MDIO muxing configuration relies
on the MDIO support provided in the first patch.

Changes since v0:
v1 - fixed mdio bus allocation
v2 - cleaned up accessors to avoid casting
v3 - fixed spelling (mostly commit message)
v4 - fixed err path check blunder
v5 - fixed loadble module build, provided separate kbuild module
     for the driver
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2019-08-02 18:22:18 -07:00
commit 4de97b0c86
9 changed files with 264 additions and 67 deletions

View file

@ -11,7 +11,9 @@ Required properties:
to parent node bindings.
- compatible : Should be "fsl,enetc".
1) The ENETC external port is connected to a MDIO configurable phy:
1. The ENETC external port is connected to a MDIO configurable phy
1.1. Using the local ENETC Port MDIO interface
In this case, the ENETC node should include a "mdio" sub-node
that in turn should contain the "ethernet-phy" node describing the
@ -47,8 +49,42 @@ Example:
};
};
2) The ENETC port is an internal port or has a fixed-link external
connection:
1.2. Using the central MDIO PCIe endpoint device
In this case, the mdio node should be defined as another PCIe
endpoint node, at the same level with the ENETC port nodes.
Required properties:
- reg : Specifies PCIe Device Number and Function
Number of the ENETC endpoint device, according
to parent node bindings.
- compatible : Should be "fsl,enetc-mdio".
The remaining required mdio bus properties are standard, their bindings
already defined in Documentation/devicetree/bindings/net/mdio.txt.
Example:
ethernet@0,0 {
compatible = "fsl,enetc";
reg = <0x000000 0 0 0 0>;
phy-handle = <&sgmii_phy0>;
phy-connection-type = "sgmii";
};
mdio@0,3 {
compatible = "fsl,enetc-mdio";
reg = <0x000300 0 0 0 0>;
#address-cells = <1>;
#size-cells = <0>;
sgmii_phy0: ethernet-phy@2 {
reg = <0x2>;
};
};
2. The ENETC port is an internal port or has a fixed-link external
connection
In this case, the ENETC port node defines a fixed link connection,
as specified by Documentation/devicetree/bindings/net/fixed-link.txt.

View file

@ -85,6 +85,26 @@ simple-audio-card,codec {
system-clock-frequency = <25000000>;
};
};
mdio-mux {
compatible = "mdio-mux-multiplexer";
mux-controls = <&mux 0>;
mdio-parent-bus = <&enetc_mdio_pf3>;
#address-cells=<1>;
#size-cells = <0>;
/* on-board RGMII PHY */
mdio@0 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
qds_phy1: ethernet-phy@5 {
/* Atheros 8035 */
reg = <5>;
};
};
};
};
&duart0 {
@ -164,6 +184,26 @@ sgtl5000: audio-codec@a {
};
};
};
fpga@66 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,ls1028aqds-fpga", "fsl,fpga-qixis-i2c",
"simple-mfd";
reg = <0x66>;
mux: mux-controller {
compatible = "reg-mux";
#mux-control-cells = <1>;
mux-reg-masks = <0x54 0xf0>; /* 0: reg 0x54, bits 7:4 */
};
};
};
&enetc_port1 {
phy-handle = <&qds_phy1>;
phy-connection-type = "rgmii-id";
};
&sai1 {

View file

@ -536,6 +536,12 @@ enetc_port1: ethernet@0,1 {
compatible = "fsl,enetc";
reg = <0x000100 0 0 0 0>;
};
enetc_mdio_pf3: mdio@0,3 {
compatible = "fsl,enetc-mdio";
reg = <0x000300 0 0 0 0>;
#address-cells = <1>;
#size-cells = <0>;
};
ethernet@0,4 {
compatible = "fsl,enetc-ptp";
reg = <0x000400 0 0 0 0>;

View file

@ -18,6 +18,15 @@ config FSL_ENETC_VF
If compiled as module (M), the module name is fsl-enetc-vf.
config FSL_ENETC_MDIO
tristate "ENETC MDIO driver"
depends on PCI && (ARCH_LAYERSCAPE || COMPILE_TEST)
help
This driver supports NXP ENETC Central MDIO controller as a PCIe
physical function (PF) device.
If compiled as module (M), the module name is fsl-enetc-mdio.
config FSL_ENETC_PTP_CLOCK
tristate "ENETC PTP clock driver"
depends on PTP_1588_CLOCK_QORIQ && (FSL_ENETC || FSL_ENETC_VF)

View file

@ -1,19 +1,16 @@
# SPDX-License-Identifier: GPL-2.0
common-objs := enetc.o enetc_cbdr.o enetc_ethtool.o
obj-$(CONFIG_FSL_ENETC) += fsl-enetc.o
fsl-enetc-$(CONFIG_FSL_ENETC) += enetc.o enetc_cbdr.o enetc_ethtool.o \
enetc_mdio.o
fsl-enetc-y := enetc_pf.o enetc_mdio.o $(common-objs)
fsl-enetc-$(CONFIG_PCI_IOV) += enetc_msg.o
fsl-enetc-objs := enetc_pf.o $(fsl-enetc-y)
obj-$(CONFIG_FSL_ENETC_VF) += fsl-enetc-vf.o
fsl-enetc-vf-y := enetc_vf.o $(common-objs)
ifeq ($(CONFIG_FSL_ENETC)$(CONFIG_FSL_ENETC_VF), yy)
fsl-enetc-vf-objs := enetc_vf.o
else
fsl-enetc-vf-$(CONFIG_FSL_ENETC_VF) += enetc.o enetc_cbdr.o \
enetc_ethtool.o
fsl-enetc-vf-objs := enetc_vf.o $(fsl-enetc-vf-y)
endif
obj-$(CONFIG_FSL_ENETC_MDIO) += fsl-enetc-mdio.o
fsl-enetc-mdio-y := enetc_pci_mdio.o enetc_mdio.o
obj-$(CONFIG_FSL_ENETC_PTP_CLOCK) += fsl-enetc-ptp.o
fsl-enetc-ptp-$(CONFIG_FSL_ENETC_PTP_CLOCK) += enetc_ptp.o
fsl-enetc-ptp-y := enetc_ptp.o

View file

@ -6,18 +6,20 @@
#include <linux/iopoll.h>
#include <linux/of.h>
#include "enetc_pf.h"
#include "enetc_mdio.h"
struct enetc_mdio_regs {
u32 mdio_cfg; /* MDIO configuration and status */
u32 mdio_ctl; /* MDIO control */
u32 mdio_data; /* MDIO data */
u32 mdio_addr; /* MDIO address */
};
#define ENETC_MDIO_REG_OFFSET 0x1c00
#define ENETC_MDIO_CFG 0x0 /* MDIO configuration and status */
#define ENETC_MDIO_CTL 0x4 /* MDIO control */
#define ENETC_MDIO_DATA 0x8 /* MDIO data */
#define ENETC_MDIO_ADDR 0xc /* MDIO address */
#define bus_to_enetc_regs(bus) (struct enetc_mdio_regs __iomem *)((bus)->priv)
#define enetc_mdio_rd(hw, off) \
enetc_port_rd(hw, ENETC_##off + ENETC_MDIO_REG_OFFSET)
#define enetc_mdio_wr(hw, off, val) \
enetc_port_wr(hw, ENETC_##off + ENETC_MDIO_REG_OFFSET, val)
#define enetc_mdio_rd_reg(off) enetc_mdio_rd(hw, off)
#define ENETC_MDIO_REG_OFFSET 0x1c00
#define ENETC_MDC_DIV 258
#define MDIO_CFG_CLKDIV(x) ((((x) >> 1) & 0xff) << 8)
@ -33,18 +35,18 @@ struct enetc_mdio_regs {
#define MDIO_DATA(x) ((x) & 0xffff)
#define TIMEOUT 1000
static int enetc_mdio_wait_complete(struct enetc_mdio_regs __iomem *regs)
static int enetc_mdio_wait_complete(struct enetc_hw *hw)
{
u32 val;
return readx_poll_timeout(enetc_rd_reg, &regs->mdio_cfg, val,
return readx_poll_timeout(enetc_mdio_rd_reg, MDIO_CFG, val,
!(val & MDIO_CFG_BSY), 10, 10 * TIMEOUT);
}
static 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_regs __iomem *regs = bus_to_enetc_regs(bus);
struct enetc_mdio_priv *mdio_priv = bus->priv;
struct enetc_hw *hw = mdio_priv->hw;
u32 mdio_ctl, mdio_cfg;
u16 dev_addr;
int ret;
@ -59,38 +61,39 @@ static int enetc_mdio_write(struct mii_bus *bus, int phy_id, int regnum,
mdio_cfg &= ~MDIO_CFG_ENC45;
}
enetc_wr_reg(&regs->mdio_cfg, mdio_cfg);
enetc_mdio_wr(hw, MDIO_CFG, mdio_cfg);
ret = enetc_mdio_wait_complete(regs);
ret = enetc_mdio_wait_complete(hw);
if (ret)
return ret;
/* set port and dev addr */
mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr);
enetc_wr_reg(&regs->mdio_ctl, mdio_ctl);
enetc_mdio_wr(hw, MDIO_CTL, mdio_ctl);
/* set the register address */
if (regnum & MII_ADDR_C45) {
enetc_wr_reg(&regs->mdio_addr, regnum & 0xffff);
enetc_mdio_wr(hw, MDIO_ADDR, regnum & 0xffff);
ret = enetc_mdio_wait_complete(regs);
ret = enetc_mdio_wait_complete(hw);
if (ret)
return ret;
}
/* write the value */
enetc_wr_reg(&regs->mdio_data, MDIO_DATA(value));
enetc_mdio_wr(hw, MDIO_DATA, MDIO_DATA(value));
ret = enetc_mdio_wait_complete(regs);
ret = enetc_mdio_wait_complete(hw);
if (ret)
return ret;
return 0;
}
static 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_regs __iomem *regs = bus_to_enetc_regs(bus);
struct enetc_mdio_priv *mdio_priv = bus->priv;
struct enetc_hw *hw = mdio_priv->hw;
u32 mdio_ctl, mdio_cfg;
u16 dev_addr, value;
int ret;
@ -104,41 +107,41 @@ static int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
mdio_cfg &= ~MDIO_CFG_ENC45;
}
enetc_wr_reg(&regs->mdio_cfg, mdio_cfg);
enetc_mdio_wr(hw, MDIO_CFG, mdio_cfg);
ret = enetc_mdio_wait_complete(regs);
ret = enetc_mdio_wait_complete(hw);
if (ret)
return ret;
/* set port and device addr */
mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr);
enetc_wr_reg(&regs->mdio_ctl, mdio_ctl);
enetc_mdio_wr(hw, MDIO_CTL, mdio_ctl);
/* set the register address */
if (regnum & MII_ADDR_C45) {
enetc_wr_reg(&regs->mdio_addr, regnum & 0xffff);
enetc_mdio_wr(hw, MDIO_ADDR, regnum & 0xffff);
ret = enetc_mdio_wait_complete(regs);
ret = enetc_mdio_wait_complete(hw);
if (ret)
return ret;
}
/* initiate the read */
enetc_wr_reg(&regs->mdio_ctl, mdio_ctl | MDIO_CTL_READ);
enetc_mdio_wr(hw, MDIO_CTL, mdio_ctl | MDIO_CTL_READ);
ret = enetc_mdio_wait_complete(regs);
ret = enetc_mdio_wait_complete(hw);
if (ret)
return ret;
/* return all Fs if nothing was there */
if (enetc_rd_reg(&regs->mdio_cfg) & MDIO_CFG_RD_ER) {
if (enetc_mdio_rd(hw, MDIO_CFG) & MDIO_CFG_RD_ER) {
dev_dbg(&bus->dev,
"Error while reading PHY%d reg at %d.%hhu\n",
phy_id, dev_addr, regnum);
return 0xffff;
}
value = enetc_rd_reg(&regs->mdio_data) & 0xffff;
value = enetc_mdio_rd(hw, MDIO_DATA) & 0xffff;
return value;
}
@ -146,12 +149,12 @@ static int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
int enetc_mdio_probe(struct enetc_pf *pf)
{
struct device *dev = &pf->si->pdev->dev;
struct enetc_mdio_regs __iomem *regs;
struct enetc_mdio_priv *mdio_priv;
struct device_node *np;
struct mii_bus *bus;
int ret;
int err;
bus = mdiobus_alloc_size(sizeof(regs));
bus = devm_mdiobus_alloc_size(dev, sizeof(*mdio_priv));
if (!bus)
return -ENOMEM;
@ -159,41 +162,31 @@ int enetc_mdio_probe(struct enetc_pf *pf)
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));
/* store the enetc mdio base address for this bus */
regs = pf->si->hw.port + ENETC_MDIO_REG_OFFSET;
bus->priv = regs;
np = of_get_child_by_name(dev->of_node, "mdio");
if (!np) {
dev_err(dev, "MDIO node missing\n");
ret = -EINVAL;
goto err_registration;
return -EINVAL;
}
ret = of_mdiobus_register(bus, np);
if (ret) {
err = of_mdiobus_register(bus, np);
if (err) {
of_node_put(np);
dev_err(dev, "cannot register MDIO bus\n");
goto err_registration;
return err;
}
of_node_put(np);
pf->mdio = bus;
return 0;
err_registration:
mdiobus_free(bus);
return ret;
}
void enetc_mdio_remove(struct enetc_pf *pf)
{
if (pf->mdio) {
if (pf->mdio)
mdiobus_unregister(pf->mdio);
mdiobus_free(pf->mdio);
}
}

View file

@ -0,0 +1,12 @@
/* 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);

View file

@ -0,0 +1,101 @@
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/* Copyright 2019 NXP */
#include <linux/of_mdio.h>
#include "enetc_mdio.h"
#define ENETC_MDIO_DEV_ID 0xee01
#define ENETC_MDIO_DEV_NAME "FSL PCIe IE Central MDIO"
#define ENETC_MDIO_BUS_NAME ENETC_MDIO_DEV_NAME " Bus"
#define ENETC_MDIO_DRV_NAME ENETC_MDIO_DEV_NAME " driver"
static int enetc_pci_mdio_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct enetc_mdio_priv *mdio_priv;
struct device *dev = &pdev->dev;
struct enetc_hw *hw;
struct mii_bus *bus;
int err;
hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
if (!hw)
return -ENOMEM;
bus = devm_mdiobus_alloc_size(dev, sizeof(*mdio_priv));
if (!bus)
return -ENOMEM;
bus->name = ENETC_MDIO_BUS_NAME;
bus->read = enetc_mdio_read;
bus->write = enetc_mdio_write;
bus->parent = dev;
mdio_priv = bus->priv;
mdio_priv->hw = hw;
snprintf(bus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev));
pcie_flr(pdev);
err = pci_enable_device_mem(pdev);
if (err) {
dev_err(dev, "device enable failed\n");
return err;
}
err = pci_request_region(pdev, 0, KBUILD_MODNAME);
if (err) {
dev_err(dev, "pci_request_region failed\n");
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);
if (err)
goto err_mdiobus_reg;
pci_set_drvdata(pdev, bus);
return 0;
err_mdiobus_reg:
iounmap(mdio_priv->hw->port);
err_ioremap:
pci_release_mem_regions(pdev);
err_pci_mem_reg:
pci_disable_device(pdev);
return err;
}
static void enetc_pci_mdio_remove(struct pci_dev *pdev)
{
struct mii_bus *bus = pci_get_drvdata(pdev);
struct enetc_mdio_priv *mdio_priv;
mdiobus_unregister(bus);
mdio_priv = bus->priv;
iounmap(mdio_priv->hw->port);
pci_release_mem_regions(pdev);
pci_disable_device(pdev);
}
static const struct pci_device_id enetc_pci_mdio_id_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, ENETC_MDIO_DEV_ID) },
{ 0, } /* End of table. */
};
MODULE_DEVICE_TABLE(pci, enetc_pci_mdio_id_table);
static struct pci_driver enetc_pci_mdio_driver = {
.name = KBUILD_MODNAME,
.id_table = enetc_pci_mdio_id_table,
.probe = enetc_pci_mdio_probe,
.remove = enetc_pci_mdio_remove,
};
module_pci_driver(enetc_pci_mdio_driver);
MODULE_DESCRIPTION(ENETC_MDIO_DRV_NAME);
MODULE_LICENSE("Dual BSD/GPL");

View file

@ -750,6 +750,7 @@ static int enetc_of_get_phy(struct enetc_ndev_priv *priv)
{
struct enetc_pf *pf = enetc_si_priv(priv->si);
struct device_node *np = priv->dev->of_node;
struct device_node *mdio_np;
int err;
if (!np) {
@ -773,7 +774,9 @@ static int enetc_of_get_phy(struct enetc_ndev_priv *priv)
priv->phy_node = of_node_get(np);
}
if (!of_phy_is_fixed_link(np)) {
mdio_np = of_get_child_by_name(np, "mdio");
if (mdio_np) {
of_node_put(mdio_np);
err = enetc_mdio_probe(pf);
if (err) {
of_node_put(priv->phy_node);