Commit 6517798d authored by Claudiu Manoil's avatar Claudiu Manoil Committed by David S. Miller
Browse files

enetc: Make MDIO accessors more generic and export to include/linux/fsl



Within the LS1028A SoC, the register map for the ENETC MDIO controller
is instantiated a few times: for the central (external) MDIO controller,
for the internal bus of each standalone ENETC port, and for the internal
bus of the Felix switch.

Refactoring is needed to support multiple MDIO buses from multiple
drivers. The enetc_hw structure is made an opaque type and a smaller
enetc_mdio_priv is created.

'mdio_base' - MDIO registers base address - is being parameterized, to
be able to work with different MDIO register bases.

The ENETC MDIO bus operations are exported from the fsl-enetc-mdio
kernel object, the same that registers the central MDIO controller (the
dedicated PF). The ENETC main driver has been changed to select it, and
use its exported helpers to further register its private MDIO bus. The
DSA Felix driver will do the same.

Signed-off-by: default avatarClaudiu Manoil <claudiu.manoil@nxp.com>
Signed-off-by: default avatarVladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 787cac3f
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@
config FSL_ENETC
	tristate "ENETC PF driver"
	depends on PCI && PCI_MSI && (ARCH_LAYERSCAPE || COMPILE_TEST)
	select FSL_ENETC_MDIO
	select PHYLIB
	help
	  This driver supports NXP ENETC gigabit ethernet controller PCIe
+1 −1
Original line number Diff line number Diff line
@@ -3,7 +3,7 @@
common-objs := enetc.o enetc_cbdr.o enetc_ethtool.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_FSL_ENETC_QOS) += enetc_qos.o

+1 −0
Original line number Diff line number Diff line
@@ -200,6 +200,7 @@ enum enetc_bdr_type {TX, RX};
#define ENETC_PFPMR		0x1900
#define ENETC_PFPMR_PMACE	BIT(1)
#define ENETC_PFPMR_MWLM	BIT(0)
#define ENETC_EMDIO_BASE	0x1c00
#define ENETC_PSIUMHFR0(n, err)	(((err) ? 0x1d08 : 0x1d00) + (n) * 0x10)
#define ENETC_PSIUMHFR1(n)	(0x1d04 + (n) * 0x10)
#define ENETC_PSIMMHFR0(n, err)	(((err) ? 0x1d00 : 0x1d08) + (n) * 0x10)
+45 −65
Original line number Diff line number Diff line
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/* Copyright 2019 NXP */

#include <linux/fsl/enetc_mdio.h>
#include <linux/mdio.h>
#include <linux/of_mdio.h>
#include <linux/iopoll.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_CTL	0x4	/* MDIO control */
#define	ENETC_MDIO_DATA	0x8	/* MDIO data */
#define	ENETC_MDIO_ADDR	0xc	/* MDIO address */

#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)
static inline u32 _enetc_mdio_rd(struct enetc_mdio_priv *mdio_priv, int off)
{
	return enetc_port_rd(mdio_priv->hw, mdio_priv->mdio_base + 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_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 ENETC_MDC_DIV		258

@@ -35,7 +46,7 @@
#define MDIO_DATA(x)		((x) & 0xffff)

#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;

@@ -46,7 +57,6 @@ 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)
{
	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;
@@ -61,39 +71,39 @@ int enetc_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value)
		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)
		return ret;

	/* set port and 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 */
	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)
			return ret;
	}

	/* 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)
		return ret;

	return 0;
}
EXPORT_SYMBOL_GPL(enetc_mdio_write);

int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
{
	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;
@@ -107,86 +117,56 @@ int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
		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)
		return ret;

	/* set port and device 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 */
	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)
			return ret;
	}

	/* 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)
		return ret;

	/* 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,
			"Error while reading PHY%d reg at %d.%hhu\n",
			phy_id, dev_addr, regnum);
		return 0xffff;
	}

	value = enetc_mdio_rd(hw, MDIO_DATA) & 0xffff;
	value = enetc_mdio_rd(mdio_priv, MDIO_DATA) & 0xffff;

	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_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;
	}
	struct enetc_hw *hw;

	of_node_put(np);
	pf->mdio = bus;
	hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
	if (!hw)
		return ERR_PTR(-ENOMEM);

	return 0;
}
	hw->port = port_regs;

void enetc_mdio_remove(struct enetc_pf *pf)
{
	if (pf->mdio)
		mdiobus_unregister(pf->mdio);
	return hw;
}
EXPORT_SYMBOL_GPL(enetc_hw_alloc);
+0 −12
Original line number Diff line number Diff line
/* 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);
Loading