Commit 73f3d9df authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'enetc-Add-mdio-support-and-device-tree-nodes'



Claudiu Manoil says:

====================
enetc: Add mdio support and device tree nodes

This is the missing part to enable PCI probing of the ENETC ethernet
ports on the LS1028A SoC and external traffic on the LS1028A RDB board.
It's one of the first items on the TODO list for the recently merged
ENETC ethernet driver.

v3: Add DT bindings doc for ENETC connections
v4: none
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents be9cefe7 20cc5dde
Loading
Loading
Loading
Loading
+69 −0
Original line number Diff line number Diff line
* ENETC ethernet device tree bindings

Depending on board design and ENETC port type (internal or
external) there are two supported link modes specified by
below device tree bindings.

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".

1) The ENETC external port is connected to a MDIO configurable phy:

In this case, the ENETC node should include a "mdio" sub-node
that in turn should contain the "ethernet-phy" node describing the
external phy.  Below properties are required, their bindings
already defined in ethernet.txt or phy.txt, under
Documentation/devicetree/bindings/net/*.

Required:

- phy-handle		: Phandle to a PHY on the MDIO bus.
			  Defined in ethernet.txt.

- phy-connection-type	: Defined in ethernet.txt.

- mdio			: "mdio" node, defined in mdio.txt.

- ethernet-phy		: "ethernet-phy" node, defined in phy.txt.

Example:

	ethernet@0,0 {
		compatible = "fsl,enetc";
		reg = <0x000000 0 0 0 0>;
		phy-handle = <&sgmii_phy0>;
		phy-connection-type = "sgmii";

		mdio {
			#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 "fixed-link.txt", under
Documentation/devicetree/bindings/net/*.

Required:

- fixed-link	: "fixed-link" node, defined in "fixed-link.txt".

Example:
	ethernet@0,2 {
		compatible = "fsl,enetc";
		reg = <0x000200 0 0 0 0>;
		fixed-link {
			speed = <1000>;
			full-duplex;
		};
	};
+17 −0
Original line number Diff line number Diff line
@@ -71,3 +71,20 @@
&duart1 {
	status = "okay";
};

&enetc_port0 {
	phy-handle = <&sgmii_phy0>;
	phy-connection-type = "sgmii";

	mdio {
		#address-cells = <1>;
		#size-cells = <0>;
		sgmii_phy0: ethernet-phy@2 {
			reg = <0x2>;
		};
	};
};

&enetc_port1 {
	status = "disabled";
};
+35 −0
Original line number Diff line number Diff line
@@ -335,5 +335,40 @@
				     <GIC_SPI 206 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 207 IRQ_TYPE_LEVEL_HIGH>,
				     <GIC_SPI 208 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 209 IRQ_TYPE_LEVEL_HIGH>;
		};

		pcie@1f0000000 { /* Integrated Endpoint Root Complex */
			compatible = "pci-host-ecam-generic";
			reg = <0x01 0xf0000000 0x0 0x100000>;
			#address-cells = <3>;
			#size-cells = <2>;
			#interrupt-cells = <1>;
			msi-parent = <&its>;
			device_type = "pci";
			bus-range = <0x0 0x0>;
			dma-coherent;
			msi-map = <0 &its 0x17 0xe>;
			iommu-map = <0 &smmu 0x17 0xe>;
				  /* PF0-6 BAR0 - non-prefetchable memory */
			ranges = <0x82000000 0x0 0x00000000  0x1 0xf8000000  0x0 0x160000
				  /* PF0-6 BAR2 - prefetchable memory */
				  0xc2000000 0x0 0x00000000  0x1 0xf8160000  0x0 0x070000
				  /* PF0: VF0-1 BAR0 - non-prefetchable memory */
				  0x82000000 0x0 0x00000000  0x1 0xf81d0000  0x0 0x020000
				  /* PF0: VF0-1 BAR2 - prefetchable memory */
				  0xc2000000 0x0 0x00000000  0x1 0xf81f0000  0x0 0x020000
				  /* PF1: VF0-1 BAR0 - non-prefetchable memory */
				  0x82000000 0x0 0x00000000  0x1 0xf8210000  0x0 0x020000
				  /* PF1: VF0-1 BAR2 - prefetchable memory */
				  0xc2000000 0x0 0x00000000  0x1 0xf8230000  0x0 0x020000>;

			enetc_port0: ethernet@0,0 {
				compatible = "fsl,enetc";
				reg = <0x000000 0 0 0 0>;
			};
			enetc_port1: ethernet@0,1 {
				compatible = "fsl,enetc";
				reg = <0x000100 0 0 0 0>;
			};
		};
	};
};
+2 −1
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_FSL_ENETC) += fsl-enetc.o
fsl-enetc-$(CONFIG_FSL_ENETC) += enetc.o enetc_cbdr.o enetc_ethtool.o
fsl-enetc-$(CONFIG_FSL_ENETC) += enetc.o enetc_cbdr.o enetc_ethtool.o \
				 enetc_mdio.o
fsl-enetc-$(CONFIG_PCI_IOV) += enetc_msg.o
fsl-enetc-objs := enetc_pf.o $(fsl-enetc-y)

+199 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/* Copyright 2019 NXP */

#include <linux/mdio.h>
#include <linux/of_mdio.h>
#include <linux/iopoll.h>
#include <linux/of.h>

#include "enetc_pf.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 bus_to_enetc_regs(bus)	(struct enetc_mdio_regs __iomem *)((bus)->priv)

#define ENETC_MDIO_REG_OFFSET	0x1c00
#define ENETC_MDC_DIV		258

#define MDIO_CFG_CLKDIV(x)	((((x) >> 1) & 0xff) << 8)
#define MDIO_CFG_BSY		BIT(0)
#define MDIO_CFG_RD_ER		BIT(1)
#define MDIO_CFG_ENC45		BIT(6)
 /* external MDIO only - driven on neg MDC edge */
#define MDIO_CFG_NEG		BIT(23)

#define MDIO_CTL_DEV_ADDR(x)	((x) & 0x1f)
#define MDIO_CTL_PORT_ADDR(x)	(((x) & 0x1f) << 5)
#define MDIO_CTL_READ		BIT(15)
#define MDIO_DATA(x)		((x) & 0xffff)

#define TIMEOUT	1000
static int enetc_mdio_wait_complete(struct enetc_mdio_regs __iomem *regs)
{
	u32 val;

	return readx_poll_timeout(enetc_rd_reg, &regs->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)
{
	struct enetc_mdio_regs __iomem *regs = bus_to_enetc_regs(bus);
	u32 mdio_ctl, mdio_cfg;
	u16 dev_addr;
	int ret;

	mdio_cfg = MDIO_CFG_CLKDIV(ENETC_MDC_DIV) | MDIO_CFG_NEG;
	if (regnum & MII_ADDR_C45) {
		dev_addr = (regnum >> 16) & 0x1f;
		mdio_cfg |= MDIO_CFG_ENC45;
	} else {
		/* clause 22 (ie 1G) */
		dev_addr = regnum & 0x1f;
		mdio_cfg &= ~MDIO_CFG_ENC45;
	}

	enetc_wr_reg(&regs->mdio_cfg, mdio_cfg);

	ret = enetc_mdio_wait_complete(regs);
	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);

	/* set the register address */
	if (regnum & MII_ADDR_C45) {
		enetc_wr_reg(&regs->mdio_addr, regnum & 0xffff);

		ret = enetc_mdio_wait_complete(regs);
		if (ret)
			return ret;
	}

	/* write the value */
	enetc_wr_reg(&regs->mdio_data, MDIO_DATA(value));

	ret = enetc_mdio_wait_complete(regs);
	if (ret)
		return ret;

	return 0;
}

static int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
{
	struct enetc_mdio_regs __iomem *regs = bus_to_enetc_regs(bus);
	u32 mdio_ctl, mdio_cfg;
	u16 dev_addr, value;
	int ret;

	mdio_cfg = MDIO_CFG_CLKDIV(ENETC_MDC_DIV) | MDIO_CFG_NEG;
	if (regnum & MII_ADDR_C45) {
		dev_addr = (regnum >> 16) & 0x1f;
		mdio_cfg |= MDIO_CFG_ENC45;
	} else {
		dev_addr = regnum & 0x1f;
		mdio_cfg &= ~MDIO_CFG_ENC45;
	}

	enetc_wr_reg(&regs->mdio_cfg, mdio_cfg);

	ret = enetc_mdio_wait_complete(regs);
	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);

	/* set the register address */
	if (regnum & MII_ADDR_C45) {
		enetc_wr_reg(&regs->mdio_addr, regnum & 0xffff);

		ret = enetc_mdio_wait_complete(regs);
		if (ret)
			return ret;
	}

	/* initiate the read */
	enetc_wr_reg(&regs->mdio_ctl, mdio_ctl | MDIO_CTL_READ);

	ret = enetc_mdio_wait_complete(regs);
	if (ret)
		return ret;

	/* return all Fs if nothing was there */
	if (enetc_rd_reg(&regs->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;

	return value;
}

int enetc_mdio_probe(struct enetc_pf *pf)
{
	struct device *dev = &pf->si->pdev->dev;
	struct enetc_mdio_regs __iomem *regs;
	struct device_node *np;
	struct mii_bus *bus;
	int ret;

	bus = mdiobus_alloc_size(sizeof(regs));
	if (!bus)
		return -ENOMEM;

	bus->name = "Freescale ENETC MDIO Bus";
	bus->read = enetc_mdio_read;
	bus->write = enetc_mdio_write;
	bus->parent = dev;
	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;
	}

	ret = of_mdiobus_register(bus, np);
	if (ret) {
		of_node_put(np);
		dev_err(dev, "cannot register MDIO bus\n");
		goto err_registration;
	}

	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) {
		mdiobus_unregister(pf->mdio);
		mdiobus_free(pf->mdio);
	}
}
Loading