Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

Merge branch 'dt_fixed_phy'

Thomas Petazzoni says:

====================
Add DT support for fixed PHYs

Here is a fourth version of the patch set that adds a Device Tree
binding and the related code to support fixed PHYs. I'm hoping to get
this merged in 3.16.

Changes since v3:

 * Rebased on top of v3.15-rc5

 * In patch "net: phy: decouple PHY id and PHY address in fixed PHY
   driver", changed the PHY ID of fixed PHYs from 0xdeadbeef to 0x0,
   as suggested by Grant Likely.

 * Fixed the !CONFIG_PHY_FIXED case in patch "net: phy: extend fixed
   driver with fixed_phy_register()". Noticed by Florian Fainelli.

 * Added Acked-by from Grant Likely and Florian Fainelli on patch
   "net: phy: extend fixed driver with fixed_phy_register()".

 * Reworked the new fixed-link DT binding to be just a sub-node of the
   Ethernet MAC node, and not a node referenced by the 'phy'
   property. This was requested by Grant Likely.

 * Reworked the code implementing the new DT binding to also make it
   accept the old, single property based, DT binding.

 * Added a patch that actually uses the new fixed link DT binding for
   the Armada XP Matrix board.

Changes since v2:

 * Rebased on top of v3.14-rc1, and re-tested on hardware.

 * Removed the RFC tag, since there seems to be some real interest in
   this feature, and the code has gone through several iterations
   already.

 * The error handling in fixed_phy_register() has been fixed.

Changes since v1:

 * Instead of using a 'fixed-link' property inside the Ethernet device
   DT node, with a fairly cryptic succession of integer values, we now
   use a PHY subnode under the Ethernet device DT node, with explicit
   properties to configure the duplex, speed, pause and other PHY
   properties.

 * The PHY address is automatically allocated by the kernel and no
   longer visible in the Device Tree binding.

 * The PHY device is created directly when the network driver calls
   of_phy_connect_fixed_link(), and associated to the PHY DT node,
   which allows the existing of_phy_connect() function to work,
   without the need to use the deprecated of_phy_connect_fixed_link().

Posts of previous versions:

  RFCv1:   http://www.spinics.net/lists/netdev/msg243253.html
  RFCv2:   http://lists.infradead.org/pipermail/linux-arm-kernel/2013-September/196919.html
  PATCHv3: http://www.spinics.net/lists/netdev/msg273117.html


====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 2770abcc 84f6e11f
Loading
Loading
Loading
Loading
+30 −0
Original line number Diff line number Diff line
Fixed link Device Tree binding
------------------------------

Some Ethernet MACs have a "fixed link", and are not connected to a
normal MDIO-managed PHY device. For those situations, a Device Tree
binding allows to describe a "fixed link".

Such a fixed link situation is described by creating a 'fixed-link'
sub-node of the Ethernet MAC device node, with the following
properties:

* 'speed' (integer, mandatory), to indicate the link speed. Accepted
  values are 10, 100 and 1000
* 'full-duplex' (boolean, optional), to indicate that full duplex is
  used. When absent, half duplex is assumed.
* 'pause' (boolean, optional), to indicate that pause should be
  enabled.
* 'asym-pause' (boolean, optional), to indicate that asym_pause should
  be enabled.

Example:

ethernet@0 {
	...
	fixed-link {
	      speed = <1000>;
	      full-duplex;
	};
	...
};
+4 −0
Original line number Diff line number Diff line
@@ -61,6 +61,10 @@
			ethernet@30000 {
				status = "okay";
				phy-mode = "sgmii";
				fixed-link {
					speed = <1000>;
					full-duplex;
				};
			};

			pcie-controller {
+16 −3
Original line number Diff line number Diff line
@@ -2797,11 +2797,24 @@ static int mvneta_probe(struct platform_device *pdev)

	phy_node = of_parse_phandle(dn, "phy", 0);
	if (!phy_node) {
		dev_err(&pdev->dev, "no associated PHY\n");
		if (!of_phy_is_fixed_link(dn)) {
			dev_err(&pdev->dev, "no PHY specified\n");
			err = -ENODEV;
			goto err_free_irq;
		}

		err = of_phy_register_fixed_link(dn);
		if (err < 0) {
			dev_err(&pdev->dev, "cannot register fixed PHY\n");
			goto err_free_irq;
		}

		/* In the case of a fixed PHY, the DT node associated
		 * to the PHY is the Ethernet MAC DT node.
		 */
		phy_node = dn;
	}

	phy_mode = of_get_phy_mode(dn);
	if (phy_mode < 0) {
		dev_err(&pdev->dev, "incorrect phy-mode\n");
+71 −10
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
#include <linux/phy_fixed.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/of.h>

#define MII_REGS_NUM 29

@@ -31,7 +32,7 @@ struct fixed_mdio_bus {
};

struct fixed_phy {
	int id;
	int addr;
	u16 regs[MII_REGS_NUM];
	struct phy_device *phydev;
	struct fixed_phy_status status;
@@ -104,8 +105,8 @@ static int fixed_phy_update_regs(struct fixed_phy *fp)
	if (fp->status.asym_pause)
		lpa |= LPA_PAUSE_ASYM;

	fp->regs[MII_PHYSID1] = fp->id >> 16;
	fp->regs[MII_PHYSID2] = fp->id;
	fp->regs[MII_PHYSID1] = 0;
	fp->regs[MII_PHYSID2] = 0;

	fp->regs[MII_BMSR] = bmsr;
	fp->regs[MII_BMCR] = bmcr;
@@ -115,7 +116,7 @@ static int fixed_phy_update_regs(struct fixed_phy *fp)
	return 0;
}

static int fixed_mdio_read(struct mii_bus *bus, int phy_id, int reg_num)
static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num)
{
	struct fixed_mdio_bus *fmb = bus->priv;
	struct fixed_phy *fp;
@@ -124,7 +125,7 @@ static int fixed_mdio_read(struct mii_bus *bus, int phy_id, int reg_num)
		return -1;

	list_for_each_entry(fp, &fmb->phys, node) {
		if (fp->id == phy_id) {
		if (fp->addr == phy_addr) {
			/* Issue callback if user registered it. */
			if (fp->link_update) {
				fp->link_update(fp->phydev->attached_dev,
@@ -138,7 +139,7 @@ static int fixed_mdio_read(struct mii_bus *bus, int phy_id, int reg_num)
	return 0xFFFF;
}

static int fixed_mdio_write(struct mii_bus *bus, int phy_id, int reg_num,
static int fixed_mdio_write(struct mii_bus *bus, int phy_addr, int reg_num,
			    u16 val)
{
	return 0;
@@ -160,7 +161,7 @@ int fixed_phy_set_link_update(struct phy_device *phydev,
		return -EINVAL;

	list_for_each_entry(fp, &fmb->phys, node) {
		if (fp->id == phydev->phy_id) {
		if (fp->addr == phydev->addr) {
			fp->link_update = link_update;
			fp->phydev = phydev;
			return 0;
@@ -171,7 +172,7 @@ int fixed_phy_set_link_update(struct phy_device *phydev,
}
EXPORT_SYMBOL_GPL(fixed_phy_set_link_update);

int fixed_phy_add(unsigned int irq, int phy_id,
int fixed_phy_add(unsigned int irq, int phy_addr,
		  struct fixed_phy_status *status)
{
	int ret;
@@ -184,9 +185,9 @@ int fixed_phy_add(unsigned int irq, int phy_id,

	memset(fp->regs, 0xFF,  sizeof(fp->regs[0]) * MII_REGS_NUM);

	fmb->irqs[phy_id] = irq;
	fmb->irqs[phy_addr] = irq;

	fp->id = phy_id;
	fp->addr = phy_addr;
	fp->status = *status;

	ret = fixed_phy_update_regs(fp);
@@ -203,6 +204,66 @@ err_regs:
}
EXPORT_SYMBOL_GPL(fixed_phy_add);

void fixed_phy_del(int phy_addr)
{
	struct fixed_mdio_bus *fmb = &platform_fmb;
	struct fixed_phy *fp, *tmp;

	list_for_each_entry_safe(fp, tmp, &fmb->phys, node) {
		if (fp->addr == phy_addr) {
			list_del(&fp->node);
			kfree(fp);
			return;
		}
	}
}
EXPORT_SYMBOL_GPL(fixed_phy_del);

static int phy_fixed_addr;
static DEFINE_SPINLOCK(phy_fixed_addr_lock);

int fixed_phy_register(unsigned int irq,
		       struct fixed_phy_status *status,
		       struct device_node *np)
{
	struct fixed_mdio_bus *fmb = &platform_fmb;
	struct phy_device *phy;
	int phy_addr;
	int ret;

	/* Get the next available PHY address, up to PHY_MAX_ADDR */
	spin_lock(&phy_fixed_addr_lock);
	if (phy_fixed_addr == PHY_MAX_ADDR) {
		spin_unlock(&phy_fixed_addr_lock);
		return -ENOSPC;
	}
	phy_addr = phy_fixed_addr++;
	spin_unlock(&phy_fixed_addr_lock);

	ret = fixed_phy_add(PHY_POLL, phy_addr, status);
	if (ret < 0)
		return ret;

	phy = get_phy_device(fmb->mii_bus, phy_addr, false);
	if (!phy || IS_ERR(phy)) {
		fixed_phy_del(phy_addr);
		return -EINVAL;
	}

	of_node_get(np);
	phy->dev.of_node = np;

	ret = phy_device_register(phy);
	if (ret) {
		phy_device_free(phy);
		of_node_put(np);
		fixed_phy_del(phy_addr);
		return ret;
	}

	return 0;
}

static int __init fixed_mdio_bus_init(void)
{
	struct fixed_mdio_bus *fmb = &platform_fmb;
+67 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
#include <linux/netdevice.h>
#include <linux/err.h>
#include <linux/phy.h>
#include <linux/phy_fixed.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_mdio.h>
@@ -301,3 +302,69 @@ struct phy_device *of_phy_attach(struct net_device *dev,
	return phy_attach_direct(dev, phy, flags, iface) ? NULL : phy;
}
EXPORT_SYMBOL(of_phy_attach);

#if defined(CONFIG_FIXED_PHY)
/*
 * of_phy_is_fixed_link() and of_phy_register_fixed_link() must
 * support two DT bindings:
 * - the old DT binding, where 'fixed-link' was a property with 5
 *   cells encoding various informations about the fixed PHY
 * - the new DT binding, where 'fixed-link' is a sub-node of the
 *   Ethernet device.
 */
bool of_phy_is_fixed_link(struct device_node *np)
{
	struct device_node *dn;
	int len;

	/* New binding */
	dn = of_get_child_by_name(np, "fixed-link");
	if (dn) {
		of_node_put(dn);
		return true;
	}

	/* Old binding */
	if (of_get_property(np, "fixed-link", &len) &&
	    len == (5 * sizeof(__be32)))
		return true;

	return false;
}
EXPORT_SYMBOL(of_phy_is_fixed_link);

int of_phy_register_fixed_link(struct device_node *np)
{
	struct fixed_phy_status status = {};
	struct device_node *fixed_link_node;
	const __be32 *fixed_link_prop;
	int len;

	/* New binding */
	fixed_link_node = of_get_child_by_name(np, "fixed-link");
	if (fixed_link_node) {
		status.link = 1;
		status.duplex = of_property_read_bool(np, "full-duplex");
		if (of_property_read_u32(fixed_link_node, "speed", &status.speed))
			return -EINVAL;
		status.pause = of_property_read_bool(np, "pause");
		status.asym_pause = of_property_read_bool(np, "asym-pause");
		of_node_put(fixed_link_node);
		return fixed_phy_register(PHY_POLL, &status, np);
	}

	/* Old binding */
	fixed_link_prop = of_get_property(np, "fixed-link", &len);
	if (fixed_link_prop && len == (5 * sizeof(__be32))) {
		status.link = 1;
		status.duplex = be32_to_cpu(fixed_link_prop[1]);
		status.speed = be32_to_cpu(fixed_link_prop[2]);
		status.pause = be32_to_cpu(fixed_link_prop[3]);
		status.asym_pause = be32_to_cpu(fixed_link_prop[4]);
		return fixed_phy_register(PHY_POLL, &status, np);
	}

	return -ENODEV;
}
EXPORT_SYMBOL(of_phy_register_fixed_link);
#endif
Loading