diff mbox

net/dt: Add support for overriding phy configuration from device tree

Message ID 1389821938-3126-1-git-send-email-matthew.garrett@nebula.com
State Changes Requested, archived
Delegated to: David Miller
Headers show

Commit Message

Matthew Garrett Jan. 15, 2014, 9:38 p.m. UTC
Some hardware may be broken in interesting and board-specific ways, such
that various bits of functionality don't work. This patch provides a
mechanism for overriding mii registers during init based on the contents of
the device tree data, allowing board-specific fixups without having to
pollute generic code.

Signed-off-by: Matthew Garrett <matthew.garrett@nebula.com>
---
 Documentation/devicetree/bindings/net/phy.txt |  13 +++
 drivers/net/phy/phy_device.c                  |  29 +++++-
 drivers/of/of_net.c                           | 124 ++++++++++++++++++++++++++
 include/linux/of_net.h                        |  12 +++
 4 files changed, 177 insertions(+), 1 deletion(-)

Comments

Gerhard Sittig Jan. 16, 2014, 1:59 p.m. UTC | #1
On Wed, Jan 15, 2014 at 16:38 -0500, Matthew Garrett wrote:
> 
> Some hardware may be broken in interesting and board-specific ways, such
> that various bits of functionality don't work. This patch provides a
> mechanism for overriding mii registers during init based on the contents of
> the device tree data, allowing board-specific fixups without having to
> pollute generic code.
> 
> Signed-off-by: Matthew Garrett <matthew.garrett@nebula.com>
> ---
>  Documentation/devicetree/bindings/net/phy.txt |  13 +++
>  drivers/net/phy/phy_device.c                  |  29 +++++-
>  drivers/of/of_net.c                           | 124 ++++++++++++++++++++++++++
>  include/linux/of_net.h                        |  12 +++
>  4 files changed, 177 insertions(+), 1 deletion(-)
> 
> diff --git a/Documentation/devicetree/bindings/net/phy.txt b/Documentation/devicetree/bindings/net/phy.txt
> index 7cd18fb..552a5e0 100644
> --- a/Documentation/devicetree/bindings/net/phy.txt
> +++ b/Documentation/devicetree/bindings/net/phy.txt
> @@ -23,6 +23,19 @@ Optional Properties:
>    assume clause 22. The compatible list may also contain other
>    elements.
>  
> +The following properties may be added to either the phy node or the parent
> +ethernet device:
> +
> +- phy-mii-advertise-10half: Whether to advertise half-duplex 10MBit
> +- phy-mii-advertise-10full: Whether to advertise full-duplex 10MBit
> +- phy-mii-advertise-100half: Whether to advertise half-duplex 100MBit
> +- phy-mii-advertise-100full: Whether to advertise full-duplex 100MBit
> +- phy-mii-advertise-100base4: Whether to advertise 100base4
> +- phy-mii-advertise-1000half: Whether to advertise half-duplex 1000MBit
> +- phy-mii-advertise-1000full: Whether to advertise full-duplex 1000MBit
> +- phy-mii-as-master: Configure phy to act as master/slave
> +- phy-mii-manual-master: Enable/disable manual master/slave configuration
> +
>  Example:
>  
>  ethernet-phy@0 {

These properties are booleans, and optional?  Does it mean that
you cannot _disable_ broken features?  Or does it mean that you
_must_ specify the non-broken features and thus break backwards
compatibility?  Or are these properties not boolean (they are not
used in the example either, unfortunately), and the binding text
would need an update for clarity?  What am I missing?


virtually yours
Gerhard Sittig
Matthew Garrett Jan. 16, 2014, 2:40 p.m. UTC | #2
On Thu, 2014-01-16 at 14:59 +0100, Gerhard Sittig wrote:

> These properties are booleans, and optional?  Does it mean that

> you cannot _disable_ broken features?  Or does it mean that you

> _must_ specify the non-broken features and thus break backwards

> compatibility?  Or are these properties not boolean (they are not

> used in the example either, unfortunately), and the binding text

> would need an update for clarity?  What am I missing?


They're not booleans. I'll update the text to make that clear.

-- 
Matthew Garrett <matthew.garrett@nebula.com>
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/net/phy.txt b/Documentation/devicetree/bindings/net/phy.txt
index 7cd18fb..552a5e0 100644
--- a/Documentation/devicetree/bindings/net/phy.txt
+++ b/Documentation/devicetree/bindings/net/phy.txt
@@ -23,6 +23,19 @@  Optional Properties:
   assume clause 22. The compatible list may also contain other
   elements.
 
+The following properties may be added to either the phy node or the parent
+ethernet device:
+
+- phy-mii-advertise-10half: Whether to advertise half-duplex 10MBit
+- phy-mii-advertise-10full: Whether to advertise full-duplex 10MBit
+- phy-mii-advertise-100half: Whether to advertise half-duplex 100MBit
+- phy-mii-advertise-100full: Whether to advertise full-duplex 100MBit
+- phy-mii-advertise-100base4: Whether to advertise 100base4
+- phy-mii-advertise-1000half: Whether to advertise half-duplex 1000MBit
+- phy-mii-advertise-1000full: Whether to advertise full-duplex 1000MBit
+- phy-mii-as-master: Configure phy to act as master/slave
+- phy-mii-manual-master: Enable/disable manual master/slave configuration
+
 Example:
 
 ethernet-phy@0 {
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index d6447b3..91793bc 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -33,6 +33,7 @@ 
 #include <linux/mii.h>
 #include <linux/ethtool.h>
 #include <linux/phy.h>
+#include <linux/of_net.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
@@ -497,6 +498,28 @@  void phy_disconnect(struct phy_device *phydev)
 }
 EXPORT_SYMBOL(phy_disconnect);
 
+int phy_override_from_of(struct phy_device *phydev)
+{
+	int reg, regval;
+	u16 val, mask;
+
+	/* Check for phy register overrides from OF */
+	for (reg = 0; reg < 16; reg++) {
+		if (!of_get_mii_register(phydev, reg, &val, &mask)) {
+			if (!mask)
+				continue;
+			regval = phy_read(phydev, reg);
+			if (regval < 0)
+				continue;
+			regval &= ~mask;
+			regval |= val;
+			phy_write(phydev, reg, regval);
+		}
+	}
+
+	return 0;
+}
+
 int phy_init_hw(struct phy_device *phydev)
 {
 	int ret;
@@ -508,7 +531,11 @@  int phy_init_hw(struct phy_device *phydev)
 	if (ret < 0)
 		return ret;
 
-	return phydev->drv->config_init(phydev);
+	ret = phydev->drv->config_init(phydev);
+	if (ret < 0)
+		return ret;
+
+	return phy_override_from_of(phydev);
 }
 
 /**
diff --git a/drivers/of/of_net.c b/drivers/of/of_net.c
index 8f9be2e..4545608 100644
--- a/drivers/of/of_net.c
+++ b/drivers/of/of_net.c
@@ -93,3 +93,127 @@  const void *of_get_mac_address(struct device_node *np)
 	return NULL;
 }
 EXPORT_SYMBOL(of_get_mac_address);
+
+/**
+ * Provide phy register overrides from the device tree. Some hardware may
+ * be broken in interesting and board-specific ways, so we want a mechanism
+ * for the board data to provide overrides for default values. This should be
+ * called during phy init.
+ */
+int of_get_mii_register(struct phy_device *phydev, int reg, u16 *val,
+			u16 *mask)
+{
+	u32 tmp;
+	struct device *dev = &phydev->dev;
+	struct device_node *np = dev->of_node;
+
+	*val = 0;
+	*mask = 0;
+
+	if (!np && dev->parent->of_node)
+		np = dev->parent->of_node;
+
+	if (!np)
+		return 0;
+
+	switch (reg) {
+	case MII_ADVERTISE:
+		if (!of_property_read_u32(np, "phy-mii-advertise-10half",
+					   &tmp)) {
+			if (tmp) {
+				*val |= ADVERTISE_10HALF;
+				phydev->advertising |= SUPPORTED_10baseT_Half;
+			} else {
+				phydev->advertising &=
+					~(SUPPORTED_10baseT_Half);
+			}
+
+			*mask |= ADVERTISE_10HALF;
+		}
+		if (!of_property_read_u32(np, "phy-mii-advertise-10full",
+					   &tmp)) {
+			if (tmp) {
+				*val |= ADVERTISE_10FULL;
+				phydev->advertising |= SUPPORTED_10baseT_Full;
+			} else {
+				phydev->advertising &=
+					~(SUPPORTED_10baseT_Full);
+			}
+
+			*mask |= ADVERTISE_10FULL;
+		}
+		if (!of_property_read_u32(np, "phy-mii-advertise-100half",
+					   &tmp)) {
+			if (tmp) {
+				*val |= ADVERTISE_100HALF;
+				phydev->advertising |= SUPPORTED_100baseT_Half;
+			} else {
+				phydev->advertising &=
+					~(SUPPORTED_100baseT_Half);
+			}
+
+			*mask |= ADVERTISE_100HALF;
+		}
+		if (!of_property_read_u32(np, "phy-mii-advertise-100full",
+					   &tmp)) {
+			if (tmp) {
+				*val |= ADVERTISE_100FULL;
+				phydev->advertising |= SUPPORTED_100baseT_Full;
+			} else {
+				phydev->advertising &=
+					~(SUPPORTED_100baseT_Full);
+			}
+
+			*mask |= ADVERTISE_100FULL;
+		}
+		if (!of_property_read_u32(np, "phy-mii-advertise-100base4",
+					   &tmp)) {
+			if (tmp)
+				*val |= ADVERTISE_100BASE4;
+			*mask |= ADVERTISE_100BASE4;
+		}
+		break;
+	case MII_CTRL1000:
+		if (!of_property_read_u32(np, "phy-mii-advertise-1000full",
+					   &tmp)) {
+			if (tmp) {
+				*val |= ADVERTISE_1000FULL;
+				phydev->advertising |= SUPPORTED_1000baseT_Full;
+			} else {
+				phydev->advertising &=
+					~(SUPPORTED_1000baseT_Full);
+			}
+
+			*mask |= ADVERTISE_1000FULL;
+		}
+		if (!of_property_read_u32(np, "phy-mii-advertise-1000half",
+					   &tmp)) {
+			if (tmp) {
+				*val |= ADVERTISE_1000HALF;
+				phydev->advertising |= SUPPORTED_1000baseT_Half;
+			} else {
+				phydev->advertising &=
+					~(SUPPORTED_1000baseT_Half);
+			}
+
+			*mask |= ADVERTISE_1000HALF;
+		}
+		if (!of_property_read_u32(np, "phy-mii-as-master",
+					   &tmp)) {
+			if (tmp)
+				*val |= CTL1000_AS_MASTER;
+			*mask |= CTL1000_AS_MASTER;
+		}
+		if (!of_property_read_u32(np, "phy-mii-manual-master",
+					   &tmp)) {
+			if (tmp)
+				*val |= CTL1000_ENABLE_MASTER;
+			*mask |= CTL1000_ENABLE_MASTER;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+EXPORT_SYMBOL(of_get_mii_register);
diff --git a/include/linux/of_net.h b/include/linux/of_net.h
index 34597c8..2e478bc 100644
--- a/include/linux/of_net.h
+++ b/include/linux/of_net.h
@@ -7,10 +7,14 @@ 
 #ifndef __LINUX_OF_NET_H
 #define __LINUX_OF_NET_H
 
+#include <linux/phy.h>
+
 #ifdef CONFIG_OF_NET
 #include <linux/of.h>
 extern int of_get_phy_mode(struct device_node *np);
 extern const void *of_get_mac_address(struct device_node *np);
+extern int of_get_mii_register(struct phy_device *np, int reg, u16 *val,
+			       u16 *mask);
 #else
 static inline int of_get_phy_mode(struct device_node *np)
 {
@@ -21,6 +25,14 @@  static inline const void *of_get_mac_address(struct device_node *np)
 {
 	return NULL;
 }
+static inline int of_get_mii_register(struct phy_device *np, int reg, u16 *val,
+				      u16 *mask)
+{
+	*val = 0;
+	*mask = 0;
+
+	return -EINVAL;
+}
 #endif
 
 #endif /* __LINUX_OF_NET_H */