All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 1/3] dt-bindings: net: dsa: qca8k: fix example
@ 2019-03-19 19:54 Christian Lamparter
  2019-03-19 19:54 ` [PATCH v3 2/3] dt-bindings: net: dsa: qca8k: support internal mdio-bus Christian Lamparter
                   ` (2 more replies)
  0 siblings, 3 replies; 10+ messages in thread
From: Christian Lamparter @ 2019-03-19 19:54 UTC (permalink / raw)
  To: netdev, devicetree
  Cc: Florian Fainelli, Vivien Didelot, Andrew Lunn, Rob Herring, Mark Rutland

In the example, the phy at phy@0 is clashing with
the switch0@0 at the same address. Usually, the switches
are accessible through pseudo PHYs which in case of the
qca8k are located at 0x10 - 0x18.

Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
---
 Documentation/devicetree/bindings/net/dsa/qca8k.txt | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/net/dsa/qca8k.txt b/Documentation/devicetree/bindings/net/dsa/qca8k.txt
index bbcb255c3150..5eda99e6c86e 100644
--- a/Documentation/devicetree/bindings/net/dsa/qca8k.txt
+++ b/Documentation/devicetree/bindings/net/dsa/qca8k.txt
@@ -55,12 +55,12 @@ Example:
 			reg = <4>;
 		};
 
-		switch0@0 {
+		switch@10 {
 			compatible = "qca,qca8337";
 			#address-cells = <1>;
 			#size-cells = <0>;
 
-			reg = <0>;
+			reg = <0x10>;
 
 			ports {
 				#address-cells = <1>;
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v3 2/3] dt-bindings: net: dsa: qca8k: support internal mdio-bus
  2019-03-19 19:54 [PATCH v3 1/3] dt-bindings: net: dsa: qca8k: fix example Christian Lamparter
@ 2019-03-19 19:54 ` Christian Lamparter
  2019-03-20 18:04   ` Florian Fainelli
  2019-03-19 19:54 ` [PATCH v3 3/3] net: dsa: qca8k: extend slave-bus implementations Christian Lamparter
  2019-03-20 18:03 ` [PATCH v3 1/3] dt-bindings: net: dsa: qca8k: fix example Florian Fainelli
  2 siblings, 1 reply; 10+ messages in thread
From: Christian Lamparter @ 2019-03-19 19:54 UTC (permalink / raw)
  To: netdev, devicetree
  Cc: Florian Fainelli, Vivien Didelot, Andrew Lunn, Rob Herring, Mark Rutland

This patch updates the qca8k's binding to document to the
approach for using the internal mdio-bus of the supported
qca8k switches.

Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
---
 .../devicetree/bindings/net/dsa/qca8k.txt     | 69 +++++++++++++++++--
 1 file changed, 64 insertions(+), 5 deletions(-)

diff --git a/Documentation/devicetree/bindings/net/dsa/qca8k.txt b/Documentation/devicetree/bindings/net/dsa/qca8k.txt
index 5eda99e6c86e..93a7469e70d4 100644
--- a/Documentation/devicetree/bindings/net/dsa/qca8k.txt
+++ b/Documentation/devicetree/bindings/net/dsa/qca8k.txt
@@ -12,10 +12,15 @@ Required properties:
 Subnodes:
 
 The integrated switch subnode should be specified according to the binding
-described in dsa/dsa.txt. As the QCA8K switches do not have a N:N mapping of
-port and PHY id, each subnode describing a port needs to have a valid phandle
-referencing the internal PHY connected to it. The CPU port of this switch is
-always port 0.
+described in dsa/dsa.txt. If the QCA8K switch is connect to a SoC's external
+mdio-bus each subnode describing a port needs to have a valid phandle
+referencing the internal PHY it is connected to. This is because there's no
+N:N mapping of port and PHY id.
+
+Don't use mixed external and internal mdio-bus configurations, as this is
+not supported by the hardware.
+
+The CPU port of this switch is always port 0.
 
 A CPU port node has the following optional node:
 
@@ -31,8 +36,9 @@ For QCA8K the 'fixed-link' sub-node supports only the following properties:
 - 'full-duplex' (boolean, optional), to indicate that full duplex is
   used. When absent, half duplex is assumed.
 
-Example:
+Examples:
 
+for the external mdio-bus configuration:
 
 	&mdio0 {
 		phy_port1: phy@0 {
@@ -108,3 +114,56 @@ Example:
 			};
 		};
 	};
+
+for the internal master mdio-bus configuration:
+
+	&mdio0 {
+		switch@10 {
+			compatible = "qca,qca8337";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			reg = <0x10>;
+
+			ports {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				port@0 {
+					reg = <0>;
+					label = "cpu";
+					ethernet = <&gmac1>;
+					phy-mode = "rgmii";
+					fixed-link {
+						speed = 1000;
+						full-duplex;
+					};
+				};
+
+				port@1 {
+					reg = <1>;
+					label = "lan1";
+				};
+
+				port@2 {
+					reg = <2>;
+					label = "lan2";
+				};
+
+				port@3 {
+					reg = <3>;
+					label = "lan3";
+				};
+
+				port@4 {
+					reg = <4>;
+					label = "lan4";
+				};
+
+				port@5 {
+					reg = <5>;
+					label = "wan";
+				};
+			};
+		};
+	};
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v3 3/3] net: dsa: qca8k: extend slave-bus implementations
  2019-03-19 19:54 [PATCH v3 1/3] dt-bindings: net: dsa: qca8k: fix example Christian Lamparter
  2019-03-19 19:54 ` [PATCH v3 2/3] dt-bindings: net: dsa: qca8k: support internal mdio-bus Christian Lamparter
@ 2019-03-19 19:54 ` Christian Lamparter
  2019-03-20 17:55   ` David Miller
  2019-03-20 18:27   ` Florian Fainelli
  2019-03-20 18:03 ` [PATCH v3 1/3] dt-bindings: net: dsa: qca8k: fix example Florian Fainelli
  2 siblings, 2 replies; 10+ messages in thread
From: Christian Lamparter @ 2019-03-19 19:54 UTC (permalink / raw)
  To: netdev, devicetree
  Cc: Florian Fainelli, Vivien Didelot, Andrew Lunn, Rob Herring, Mark Rutland

This patch implements accessors for the QCA8337 MDIO access
through the MDIO_MASTER register, which makes it possible to
access the PHYs on slave-bus through the switch. In cases
where the switch ports are already mapped via external
"phy-phandles", the internal mdio-bus is disabled in order to
prevent a duplicated discovery and enumeration of the same
PHYs. Don't use mixed external and internal mdio-bus
configurations, as this is not supported by the hardware.

Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
---
Changes from v2:
 - Make it compatible with existing configurations
 - make it clear that's sadly a either external or
   internal mdio bus access.

Changes from v1:
 - drop DT port <-> phy mapping
 - added register definitions for the MDIO control register
 - implemented new slave-mdio bus accessors
 - DT-binding: fix switch's PSEUDO_PHY address. It's 0x10 not 0.

Old patch (+ discussion) for reference:
<https://patchwork.ozlabs.org/patch/1036309/>

Tested on a Compex WPQ864 (IPQ8064 + QCA8337N)
internal bus:
qca8k 37000000.mdio-mii:10 lan1 (uninitialized): PHY [!mdio@37000000!switch@10:01] driver [Generic PHY]
qca8k 37000000.mdio-mii:10 lan2 (uninitialized): PHY [!mdio@37000000!switch@10:02] driver [Generic PHY]
qca8k 37000000.mdio-mii:10 lan3 (uninitialized): PHY [!mdio@37000000!switch@10:03] driver [Generic PHY]
qca8k 37000000.mdio-mii:10 lan4 (uninitialized): PHY [!mdio@37000000!switch@10:04] driver [Generic PHY]
qca8k 37000000.mdio-mii:10 wan (uninitialized): PHY [!mdio@37000000!switch@10:05] driver [Generic PHY]

external bus:
qca8k 37000000.mdio-mii:10 lan1 (uninitialized): PHY [37000000.mdio-mii:00] driver [Generic PHY]
qca8k 37000000.mdio-mii:10 lan2 (uninitialized): PHY [37000000.mdio-mii:01] driver [Generic PHY]
qca8k 37000000.mdio-mii:10 lan3 (uninitialized): PHY [37000000.mdio-mii:02] driver [Generic PHY]
qca8k 37000000.mdio-mii:10 lan4 (uninitialized): PHY [37000000.mdio-mii:03] driver [Generic PHY]
qca8k 37000000.mdio-mii:10 wan (uninitialized): PHY [37000000.mdio-mii:04] driver [Generic PHY]
---
 drivers/net/dsa/qca8k.c | 203 ++++++++++++++++++++++++++++++++++++----
 drivers/net/dsa/qca8k.h |  13 +++
 2 files changed, 197 insertions(+), 19 deletions(-)

diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
index 576b37d12a63..26af1036a4ec 100644
--- a/drivers/net/dsa/qca8k.c
+++ b/drivers/net/dsa/qca8k.c
@@ -481,6 +481,184 @@ qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable)
 		qca8k_reg_clear(priv, QCA8K_REG_PORT_STATUS(port), mask);
 }
 
+static int
+qca8k_port_to_phy(int port)
+{
+	if (port < 1 || port > QCA8K_MDIO_MASTER_MAX_PORTS)
+		return -EINVAL;
+
+	return port - 1;
+}
+
+static int
+qca8k_mdio_write(struct qca8k_priv *priv, int port, int regnum, u16 data)
+{
+	u32 val;
+	int phy;
+
+	phy = qca8k_port_to_phy(port);
+	if (phy < 0 || (regnum < 0 || regnum >= QCA8K_MDIO_MASTER_MAX_REG))
+		return -EINVAL;
+
+	val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN |
+	      QCA8K_MDIO_MASTER_WRITE | QCA8K_MDIO_MASTER_PHY_ADDR(phy) |
+	      QCA8K_MDIO_MASTER_REG_ADDR(regnum) |
+	      QCA8K_MDIO_MASTER_DATA(data);
+
+	qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val);
+
+	return qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
+		QCA8K_MDIO_MASTER_BUSY);
+}
+
+static int
+qca8k_mdio_read(struct qca8k_priv *priv, int port, int regnum)
+{
+	u32 val;
+	int phy;
+
+	phy = qca8k_port_to_phy(port);
+	if (phy < 0 || (regnum < 0 || regnum >= QCA8K_MDIO_MASTER_MAX_REG))
+		return -EINVAL;
+
+	val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN |
+	      QCA8K_MDIO_MASTER_READ | QCA8K_MDIO_MASTER_PHY_ADDR(phy) |
+	      QCA8K_MDIO_MASTER_REG_ADDR(regnum);
+
+	qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val);
+
+	if (qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
+			    QCA8K_MDIO_MASTER_BUSY)) {
+		return -ETIMEDOUT;
+	}
+
+	val = (qca8k_read(priv, QCA8K_MDIO_MASTER_CTRL) &
+		QCA8K_MDIO_MASTER_DATA_MASK);
+
+	return val;
+}
+
+static int
+qca8k_slave_phy_read(struct mii_bus *bus, int addr, int reg)
+{
+	struct qca8k_priv *priv = bus->priv;
+
+	if (priv->ds->phys_mii_mask & BIT(addr)) {
+		int ret = qca8k_mdio_read(priv, addr, reg);
+
+		if (ret >= 0)
+			return ret;
+	}
+
+	return 0xffff;
+}
+
+static int
+qca8k_slave_phy_write(struct mii_bus *bus, int addr, int reg, u16 val)
+{
+	struct qca8k_priv *priv = bus->priv;
+
+	if (priv->ds->phys_mii_mask & BIT(addr))
+		qca8k_mdio_write(priv, addr, reg, val);
+
+	return 0;
+}
+
+static int
+qca8k_phy_write(struct dsa_switch *ds, int port, int regnum, u16 data)
+{
+	struct qca8k_priv *priv = ds->priv;
+	int ret = -EIO;
+
+	if (ds->slave_mii_bus->phy_mask & BIT(port))
+		ret = qca8k_mdio_write(priv, port, regnum, data);
+
+	return ret;
+}
+
+static int
+qca8k_phy_read(struct dsa_switch *ds, int port, int regnum)
+{
+	struct qca8k_priv *priv = ds->priv;
+	int ret = -EIO;
+
+	if (ds->slave_mii_bus->phy_mask & BIT(port))
+		ret = qca8k_mdio_read(priv, port, regnum);
+
+	return ret;
+}
+
+static int
+qca8k_setup_mdio_bus(struct qca8k_priv *priv)
+{
+	struct device_node *ports, *port;
+	struct mii_bus *bus;
+	u32 internal_mdio_mask = 0;
+	u32 external_mdio_mask = 0;
+	u32 reg;
+	int err;
+
+	ports = of_get_child_by_name(priv->dev->of_node, "ports");
+	if (!ports)
+		return -EINVAL;
+
+	for_each_available_child_of_node(ports, port) {
+		err = of_property_read_u32(port, "reg", &reg);
+		if (err)
+			return err;
+
+		if (dsa_is_user_port(priv->ds, reg)) {
+			if (of_property_read_bool(port, "phy-handle"))
+				external_mdio_mask |= BIT(reg);
+			else
+				internal_mdio_mask |= BIT(reg);
+		}
+	}
+
+	if (!external_mdio_mask && !internal_mdio_mask) {
+		dev_err(priv->dev, "no PHYs are defined.\n");
+		return -EINVAL;
+	}
+
+	/* The QCA8K_MDIO_MASTER_EN Bit, which grants access to PHYs through
+	 * the MDIO_MASTER register also _disconnects_ the external MDC
+	 * passthrough to the internal PHYs. It's not possible to use both
+	 * configurations at the same time!
+	 */
+	if (external_mdio_mask && internal_mdio_mask) {
+		dev_err(priv->dev, "either internal or external mdio bus configuration is supported.\n");
+		return -EINVAL;
+	}
+
+	if (external_mdio_mask) {
+		/* Make sure to disable the internal mdio bus in cases
+		 * a dt-overlay and driver reload changed the configuration
+		 */
+
+		qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL,
+				QCA8K_MDIO_MASTER_EN);
+		return 0;
+	}
+
+	bus = devm_mdiobus_alloc_size(priv->dev, sizeof(priv));
+	if (!bus)
+		return -ENOMEM;
+	bus->priv = (void *)priv;
+
+	bus->name = priv->dev->of_node->full_name;
+	snprintf(bus->id, MII_BUS_ID_SIZE, "%pOF", priv->dev->of_node);
+
+	bus->read = qca8k_slave_phy_read;
+	bus->write = qca8k_slave_phy_write;
+	bus->parent = priv->dev;
+	bus->phy_mask = ~internal_mdio_mask;
+	priv->ds->slave_mii_bus = bus;
+	priv->ops.phy_read = qca8k_phy_read;
+	priv->ops.phy_write = qca8k_phy_write;
+
+	return mdiobus_register(bus);
+}
+
 static int
 qca8k_setup(struct dsa_switch *ds)
 {
@@ -502,6 +680,10 @@ qca8k_setup(struct dsa_switch *ds)
 	if (IS_ERR(priv->regmap))
 		pr_warn("regmap initialization failed");
 
+	ret = qca8k_setup_mdio_bus(priv);
+	if (ret)
+		return ret;
+
 	/* Initialize CPU port pad mode (xMII type, delays...) */
 	phy_mode = of_get_phy_mode(ds->ports[QCA8K_CPU_PORT].dn);
 	if (phy_mode < 0) {
@@ -624,22 +806,6 @@ qca8k_adjust_link(struct dsa_switch *ds, int port, struct phy_device *phy)
 	qca8k_port_set_status(priv, port, 1);
 }
 
-static int
-qca8k_phy_read(struct dsa_switch *ds, int phy, int regnum)
-{
-	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
-
-	return mdiobus_read(priv->bus, phy, regnum);
-}
-
-static int
-qca8k_phy_write(struct dsa_switch *ds, int phy, int regnum, u16 val)
-{
-	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
-
-	return mdiobus_write(priv->bus, phy, regnum, val);
-}
-
 static void
 qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data)
 {
@@ -879,8 +1045,6 @@ static const struct dsa_switch_ops qca8k_switch_ops = {
 	.setup			= qca8k_setup,
 	.adjust_link            = qca8k_adjust_link,
 	.get_strings		= qca8k_get_strings,
-	.phy_read		= qca8k_phy_read,
-	.phy_write		= qca8k_phy_write,
 	.get_ethtool_stats	= qca8k_get_ethtool_stats,
 	.get_sset_count		= qca8k_get_sset_count,
 	.get_mac_eee		= qca8k_get_mac_eee,
@@ -923,7 +1087,8 @@ qca8k_sw_probe(struct mdio_device *mdiodev)
 		return -ENOMEM;
 
 	priv->ds->priv = priv;
-	priv->ds->ops = &qca8k_switch_ops;
+	priv->ops = qca8k_switch_ops;
+	priv->ds->ops = &priv->ops;
 	mutex_init(&priv->reg_mutex);
 	dev_set_drvdata(&mdiodev->dev, priv);
 
diff --git a/drivers/net/dsa/qca8k.h b/drivers/net/dsa/qca8k.h
index d146e54c8a6c..249fd62268e5 100644
--- a/drivers/net/dsa/qca8k.h
+++ b/drivers/net/dsa/qca8k.h
@@ -49,6 +49,18 @@
 #define   QCA8K_MIB_FLUSH				BIT(24)
 #define   QCA8K_MIB_CPU_KEEP				BIT(20)
 #define   QCA8K_MIB_BUSY				BIT(17)
+#define QCA8K_MDIO_MASTER_CTRL				0x3c
+#define   QCA8K_MDIO_MASTER_BUSY			BIT(31)
+#define   QCA8K_MDIO_MASTER_EN				BIT(30)
+#define   QCA8K_MDIO_MASTER_READ			BIT(27)
+#define   QCA8K_MDIO_MASTER_WRITE			0
+#define   QCA8K_MDIO_MASTER_SUP_PRE			BIT(26)
+#define   QCA8K_MDIO_MASTER_PHY_ADDR(x)			((x) << 21)
+#define   QCA8K_MDIO_MASTER_REG_ADDR(x)			((x) << 16)
+#define   QCA8K_MDIO_MASTER_DATA(x)			(x)
+#define   QCA8K_MDIO_MASTER_DATA_MASK			GENMASK(15, 0)
+#define   QCA8K_MDIO_MASTER_MAX_PORTS			5
+#define   QCA8K_MDIO_MASTER_MAX_REG			32
 #define QCA8K_GOL_MAC_ADDR0				0x60
 #define QCA8K_GOL_MAC_ADDR1				0x64
 #define QCA8K_REG_PORT_STATUS(_i)			(0x07c + (_i) * 4)
@@ -169,6 +181,7 @@ struct qca8k_priv {
 	struct dsa_switch *ds;
 	struct mutex reg_mutex;
 	struct device *dev;
+	struct dsa_switch_ops ops;
 };
 
 struct qca8k_mib_desc {
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH v3 3/3] net: dsa: qca8k: extend slave-bus implementations
  2019-03-19 19:54 ` [PATCH v3 3/3] net: dsa: qca8k: extend slave-bus implementations Christian Lamparter
@ 2019-03-20 17:55   ` David Miller
  2019-03-20 18:27   ` Florian Fainelli
  1 sibling, 0 replies; 10+ messages in thread
From: David Miller @ 2019-03-20 17:55 UTC (permalink / raw)
  To: chunkeey
  Cc: netdev, devicetree, f.fainelli, vivien.didelot, andrew, robh+dt,
	mark.rutland

From: Christian Lamparter <chunkeey@gmail.com>
Date: Tue, 19 Mar 2019 20:54:19 +0100

> This patch implements accessors for the QCA8337 MDIO access
> through the MDIO_MASTER register, which makes it possible to
> access the PHYs on slave-bus through the switch. In cases
> where the switch ports are already mapped via external
> "phy-phandles", the internal mdio-bus is disabled in order to
> prevent a duplicated discovery and enumeration of the same
> PHYs. Don't use mixed external and internal mdio-bus
> configurations, as this is not supported by the hardware.
> 
> Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
> ---
> Changes from v2:
>  - Make it compatible with existing configurations
>  - make it clear that's sadly a either external or
>    internal mdio bus access.
> 
> Changes from v1:
>  - drop DT port <-> phy mapping
>  - added register definitions for the MDIO control register
>  - implemented new slave-mdio bus accessors
>  - DT-binding: fix switch's PSEUDO_PHY address. It's 0x10 not 0.
> 
> Old patch (+ discussion) for reference:
> <https://patchwork.ozlabs.org/patch/1036309/>

Would be great to get some review on this.

But I have a coding style change request:

> +static int
> +qca8k_setup_mdio_bus(struct qca8k_priv *priv)
> +{
> +	struct device_node *ports, *port;
> +	struct mii_bus *bus;
> +	u32 internal_mdio_mask = 0;
> +	u32 external_mdio_mask = 0;
> +	u32 reg;
> +	int err;

Reverse christmas tree ordering here please.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3 1/3] dt-bindings: net: dsa: qca8k: fix example
  2019-03-19 19:54 [PATCH v3 1/3] dt-bindings: net: dsa: qca8k: fix example Christian Lamparter
  2019-03-19 19:54 ` [PATCH v3 2/3] dt-bindings: net: dsa: qca8k: support internal mdio-bus Christian Lamparter
  2019-03-19 19:54 ` [PATCH v3 3/3] net: dsa: qca8k: extend slave-bus implementations Christian Lamparter
@ 2019-03-20 18:03 ` Florian Fainelli
  2 siblings, 0 replies; 10+ messages in thread
From: Florian Fainelli @ 2019-03-20 18:03 UTC (permalink / raw)
  To: Christian Lamparter, netdev, devicetree
  Cc: Vivien Didelot, Andrew Lunn, Rob Herring, Mark Rutland

On 3/19/19 12:54 PM, Christian Lamparter wrote:
> In the example, the phy at phy@0 is clashing with
> the switch0@0 at the same address. Usually, the switches
> are accessible through pseudo PHYs which in case of the
> qca8k are located at 0x10 - 0x18.
> 
> Signed-off-by: Christian Lamparter <chunkeey@gmail.com>

Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
-- 
Florian

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3 2/3] dt-bindings: net: dsa: qca8k: support internal mdio-bus
  2019-03-19 19:54 ` [PATCH v3 2/3] dt-bindings: net: dsa: qca8k: support internal mdio-bus Christian Lamparter
@ 2019-03-20 18:04   ` Florian Fainelli
  0 siblings, 0 replies; 10+ messages in thread
From: Florian Fainelli @ 2019-03-20 18:04 UTC (permalink / raw)
  To: Christian Lamparter, netdev, devicetree
  Cc: Vivien Didelot, Andrew Lunn, Rob Herring, Mark Rutland

On 3/19/19 12:54 PM, Christian Lamparter wrote:
> This patch updates the qca8k's binding to document to the
> approach for using the internal mdio-bus of the supported
> qca8k switches.
> 
> Signed-off-by: Christian Lamparter <chunkeey@gmail.com>

Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
-- 
Florian

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3 3/3] net: dsa: qca8k: extend slave-bus implementations
  2019-03-19 19:54 ` [PATCH v3 3/3] net: dsa: qca8k: extend slave-bus implementations Christian Lamparter
  2019-03-20 17:55   ` David Miller
@ 2019-03-20 18:27   ` Florian Fainelli
  2019-03-20 18:41     ` Christian Lamparter
  2019-03-20 22:02     ` Christian Lamparter
  1 sibling, 2 replies; 10+ messages in thread
From: Florian Fainelli @ 2019-03-20 18:27 UTC (permalink / raw)
  To: Christian Lamparter, netdev, devicetree
  Cc: Vivien Didelot, Andrew Lunn, Rob Herring, Mark Rutland

On 3/19/19 12:54 PM, Christian Lamparter wrote:
> This patch implements accessors for the QCA8337 MDIO access
> through the MDIO_MASTER register, which makes it possible to
> access the PHYs on slave-bus through the switch. In cases
> where the switch ports are already mapped via external
> "phy-phandles", the internal mdio-bus is disabled in order to
> prevent a duplicated discovery and enumeration of the same
> PHYs. Don't use mixed external and internal mdio-bus
> configurations, as this is not supported by the hardware.
> 
> Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
> ---
> Changes from v2:
>  - Make it compatible with existing configurations
>  - make it clear that's sadly a either external or
>    internal mdio bus access.
> 
> Changes from v1:
>  - drop DT port <-> phy mapping
>  - added register definitions for the MDIO control register
>  - implemented new slave-mdio bus accessors
>  - DT-binding: fix switch's PSEUDO_PHY address. It's 0x10 not 0.
> 
> Old patch (+ discussion) for reference:
> <https://patchwork.ozlabs.org/patch/1036309/>

LGTM, just a few comments/nits below.

> 
> Tested on a Compex WPQ864 (IPQ8064 + QCA8337N)
> internal bus:
> qca8k 37000000.mdio-mii:10 lan1 (uninitialized): PHY [!mdio@37000000!switch@10:01] driver [Generic PHY]
> qca8k 37000000.mdio-mii:10 lan2 (uninitialized): PHY [!mdio@37000000!switch@10:02] driver [Generic PHY]
> qca8k 37000000.mdio-mii:10 lan3 (uninitialized): PHY [!mdio@37000000!switch@10:03] driver [Generic PHY]
> qca8k 37000000.mdio-mii:10 lan4 (uninitialized): PHY [!mdio@37000000!switch@10:04] driver [Generic PHY]
> qca8k 37000000.mdio-mii:10 wan (uninitialized): PHY [!mdio@37000000!switch@10:05] driver [Generic PHY]
> 
> external bus:
> qca8k 37000000.mdio-mii:10 lan1 (uninitialized): PHY [37000000.mdio-mii:00] driver [Generic PHY]
> qca8k 37000000.mdio-mii:10 lan2 (uninitialized): PHY [37000000.mdio-mii:01] driver [Generic PHY]
> qca8k 37000000.mdio-mii:10 lan3 (uninitialized): PHY [37000000.mdio-mii:02] driver [Generic PHY]
> qca8k 37000000.mdio-mii:10 lan4 (uninitialized): PHY [37000000.mdio-mii:03] driver [Generic PHY]
> qca8k 37000000.mdio-mii:10 wan (uninitialized): PHY [37000000.mdio-mii:04] driver [Generic PHY]
> ---

[snip]

> +static int
> +qca8k_mdio_write(struct qca8k_priv *priv, int port, int regnum, u16 data)
> +{
> +	u32 val;
> +	int phy;
> +
> +	phy = qca8k_port_to_phy(port);
> +	if (phy < 0 || (regnum < 0 || regnum >= QCA8K_MDIO_MASTER_MAX_REG))
> +		return -EINVAL;

Is not the first condition always true? We should fix the signature of
phy_{read,write} in dsa.h to match what mdiobus_{read,write} takes,
which is an u32 regnum.

> +
> +	val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN |
> +	      QCA8K_MDIO_MASTER_WRITE | QCA8K_MDIO_MASTER_PHY_ADDR(phy) |
> +	      QCA8K_MDIO_MASTER_REG_ADDR(regnum) |
> +	      QCA8K_MDIO_MASTER_DATA(data);
> +
> +	qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val);
> +
> +	return qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
> +		QCA8K_MDIO_MASTER_BUSY);
> +}
> +
> +static int
> +qca8k_mdio_read(struct qca8k_priv *priv, int port, int regnum)
> +{
> +	u32 val;
> +	int phy;
> +
> +	phy = qca8k_port_to_phy(port);
> +	if (phy < 0 || (regnum < 0 || regnum >= QCA8K_MDIO_MASTER_MAX_REG))
> +		return -EINVAL;

Likewise.

[snip]

> +static int
> +qca8k_phy_read(struct dsa_switch *ds, int port, int regnum)
> +{
> +	struct qca8k_priv *priv = ds->priv;
> +	int ret = -EIO;
> +
> +	if (ds->slave_mii_bus->phy_mask & BIT(port))
> +		ret = qca8k_mdio_read(priv, port, regnum);

I suppose in theory you could also look at the external_mdio_mask and do
something like this:

	if (ds->slave_mii_bus->phy_mask & BIT(port))
		ret = qca8k_mdio_read(priv, port, regnum);
	else
		ret = mdiobus_read_nested(priv->bus, port, regnum);

Not strictly necessary for now.

> +
> +	return ret;
> +}
> +
> +static int
> +qca8k_setup_mdio_bus(struct qca8k_priv *priv)
> +{
> +	struct device_node *ports, *port;
> +	struct mii_bus *bus;
> +	u32 internal_mdio_mask = 0;
> +	u32 external_mdio_mask = 0;
> +	u32 reg;
> +	int err;
> +
> +	ports = of_get_child_by_name(priv->dev->of_node, "ports");
> +	if (!ports)
> +		return -EINVAL;
> +
> +	for_each_available_child_of_node(ports, port) {
> +		err = of_property_read_u32(port, "reg", &reg);
> +		if (err)
> +			return err;
> +
> +		if (dsa_is_user_port(priv->ds, reg)) {

You could reduce indentation a bit with:

	if (!dsa_is_user_port(priv->ds, reg))
		continue;

> +			if (of_property_read_bool(port, "phy-handle"))
> +				external_mdio_mask |= BIT(reg);
> +			else
> +				internal_mdio_mask |= BIT(reg);
> +		}
> +	}
> +
> +	if (!external_mdio_mask && !internal_mdio_mask) {
> +		dev_err(priv->dev, "no PHYs are defined.\n");
> +		return -EINVAL;
> +	}
> +
> +	/* The QCA8K_MDIO_MASTER_EN Bit, which grants access to PHYs through
> +	 * the MDIO_MASTER register also _disconnects_ the external MDC
> +	 * passthrough to the internal PHYs. It's not possible to use both
> +	 * configurations at the same time!
> +	 */

Right, but presumably you can do this on a per-port basis and support
both types of configuration? bcm_sf2 is pretty much the same thing.

> +	if (external_mdio_mask && internal_mdio_mask) {
> +		dev_err(priv->dev, "either internal or external mdio bus configuration is supported.\n");
> +		return -EINVAL;
> +	}

Don't both conditions amount to:

	if (external_mdio_mask == internal_mdio_mask)
		error()?

> +
> +	if (external_mdio_mask) {
> +		/* Make sure to disable the internal mdio bus in cases
> +		 * a dt-overlay and driver reload changed the configuration
> +		 */
> +
> +		qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL,
> +				QCA8K_MDIO_MASTER_EN);
> +		return 0;
> +	}
> +
> +	bus = devm_mdiobus_alloc_size(priv->dev, sizeof(priv));
> +	if (!bus)
> +		return -ENOMEM;
> +	bus->priv = (void *)priv;

Cast is not necessary.
-- 
Florian

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3 3/3] net: dsa: qca8k: extend slave-bus implementations
  2019-03-20 18:27   ` Florian Fainelli
@ 2019-03-20 18:41     ` Christian Lamparter
  2019-03-20 22:02     ` Christian Lamparter
  1 sibling, 0 replies; 10+ messages in thread
From: Christian Lamparter @ 2019-03-20 18:41 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: netdev, devicetree, Vivien Didelot, Andrew Lunn, Rob Herring,
	Mark Rutland

On Wednesday, March 20, 2019 7:27:09 PM CET Florian Fainelli wrote:
> On 3/19/19 12:54 PM, Christian Lamparter wrote:
> > This patch implements accessors for the QCA8337 MDIO access
> > through the MDIO_MASTER register, which makes it possible to
> > access the PHYs on slave-bus through the switch. In cases
> > where the switch ports are already mapped via external
> > "phy-phandles", the internal mdio-bus is disabled in order to
> > prevent a duplicated discovery and enumeration of the same
> > PHYs. Don't use mixed external and internal mdio-bus
> > configurations, as this is not supported by the hardware.
> > 
> > Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
> > ---
> > Changes from v2:
> >  - Make it compatible with existing configurations
> >  - make it clear that's sadly a either external or
> >    internal mdio bus access.
> > 
> > Changes from v1:
> >  - drop DT port <-> phy mapping
> >  - added register definitions for the MDIO control register
> >  - implemented new slave-mdio bus accessors
> >  - DT-binding: fix switch's PSEUDO_PHY address. It's 0x10 not 0.
> > 
> > Old patch (+ discussion) for reference:
> > <https://patchwork.ozlabs.org/patch/1036309/>
> 
> LGTM, just a few comments/nits below.
> 
> > 
> > Tested on a Compex WPQ864 (IPQ8064 + QCA8337N)
> > internal bus:
> > qca8k 37000000.mdio-mii:10 lan1 (uninitialized): PHY [!mdio@37000000!switch@10:01] driver [Generic PHY]
> > qca8k 37000000.mdio-mii:10 lan2 (uninitialized): PHY [!mdio@37000000!switch@10:02] driver [Generic PHY]
> > qca8k 37000000.mdio-mii:10 lan3 (uninitialized): PHY [!mdio@37000000!switch@10:03] driver [Generic PHY]
> > qca8k 37000000.mdio-mii:10 lan4 (uninitialized): PHY [!mdio@37000000!switch@10:04] driver [Generic PHY]
> > qca8k 37000000.mdio-mii:10 wan (uninitialized): PHY [!mdio@37000000!switch@10:05] driver [Generic PHY]
> > 
> > external bus:
> > qca8k 37000000.mdio-mii:10 lan1 (uninitialized): PHY [37000000.mdio-mii:00] driver [Generic PHY]
> > qca8k 37000000.mdio-mii:10 lan2 (uninitialized): PHY [37000000.mdio-mii:01] driver [Generic PHY]
> > qca8k 37000000.mdio-mii:10 lan3 (uninitialized): PHY [37000000.mdio-mii:02] driver [Generic PHY]
> > qca8k 37000000.mdio-mii:10 lan4 (uninitialized): PHY [37000000.mdio-mii:03] driver [Generic PHY]
> > qca8k 37000000.mdio-mii:10 wan (uninitialized): PHY [37000000.mdio-mii:04] driver [Generic PHY]
> > ---
> 
> [snip]
> 
> > +static int
> > +qca8k_mdio_write(struct qca8k_priv *priv, int port, int regnum, u16 data)
> > +{
> > +	u32 val;
> > +	int phy;
> > +
> > +	phy = qca8k_port_to_phy(port);
> > +	if (phy < 0 || (regnum < 0 || regnum >= QCA8K_MDIO_MASTER_MAX_REG))
> > +		return -EINVAL;
> 
> Is not the first condition always true? We should fix the signature of
> phy_{read,write} in dsa.h to match what mdiobus_{read,write} takes,
> which is an u32 regnum.

I think you are right. As long as 

> 
> > +
> > +	val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN |
> > +	      QCA8K_MDIO_MASTER_WRITE | QCA8K_MDIO_MASTER_PHY_ADDR(phy) |
> > +	      QCA8K_MDIO_MASTER_REG_ADDR(regnum) |
> > +	      QCA8K_MDIO_MASTER_DATA(data);
> > +
> > +	qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val);
> > +
> > +	return qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
> > +		QCA8K_MDIO_MASTER_BUSY);
> > +}
> > +
> > +static int
> > +qca8k_mdio_read(struct qca8k_priv *priv, int port, int regnum)
> > +{
> > +	u32 val;
> > +	int phy;
> > +
> > +	phy = qca8k_port_to_phy(port);
> > +	if (phy < 0 || (regnum < 0 || regnum >= QCA8K_MDIO_MASTER_MAX_REG))
> > +		return -EINVAL;
> 
> Likewise.
> 
> [snip]
> 
> > +static int
> > +qca8k_phy_read(struct dsa_switch *ds, int port, int regnum)
> > +{
> > +	struct qca8k_priv *priv = ds->priv;
> > +	int ret = -EIO;
> > +
> > +	if (ds->slave_mii_bus->phy_mask & BIT(port))
> > +		ret = qca8k_mdio_read(priv, port, regnum);
> 
> I suppose in theory you could also look at the external_mdio_mask and do
> something like this:
> 
> 	if (ds->slave_mii_bus->phy_mask & BIT(port))
> 		ret = qca8k_mdio_read(priv, port, regnum);
> 	else
> 		ret = mdiobus_read_nested(priv->bus, port, regnum);
> 
> Not strictly necessary for now.
> 
> > +
> > +	return ret;
> > +}
> > +
> > +static int
> > +qca8k_setup_mdio_bus(struct qca8k_priv *priv)
> > +{
> > +	struct device_node *ports, *port;
> > +	struct mii_bus *bus;
> > +	u32 internal_mdio_mask = 0;
> > +	u32 external_mdio_mask = 0;
> > +	u32 reg;
> > +	int err;
> > +
> > +	ports = of_get_child_by_name(priv->dev->of_node, "ports");
> > +	if (!ports)
> > +		return -EINVAL;
> > +
> > +	for_each_available_child_of_node(ports, port) {
> > +		err = of_property_read_u32(port, "reg", &reg);
> > +		if (err)
> > +			return err;
> > +
> > +		if (dsa_is_user_port(priv->ds, reg)) {
> 
> You could reduce indentation a bit with:
> 
> 	if (!dsa_is_user_port(priv->ds, reg))
> 		continue;
> 
> > +			if (of_property_read_bool(port, "phy-handle"))
> > +				external_mdio_mask |= BIT(reg);
> > +			else
> > +				internal_mdio_mask |= BIT(reg);
> > +		}
> > +	}
> > +
> > +	if (!external_mdio_mask && !internal_mdio_mask) {
> > +		dev_err(priv->dev, "no PHYs are defined.\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	/* The QCA8K_MDIO_MASTER_EN Bit, which grants access to PHYs through
> > +	 * the MDIO_MASTER register also _disconnects_ the external MDC
> > +	 * passthrough to the internal PHYs. It's not possible to use both
> > +	 * configurations at the same time!
> > +	 */
> 
> Right, but presumably you can do this on a per-port basis and support
> both types of configuration? bcm_sf2 is pretty much the same thing.
> 
> > +	if (external_mdio_mask && internal_mdio_mask) {
> > +		dev_err(priv->dev, "either internal or external mdio bus configuration is supported.\n");
> > +		return -EINVAL;
> > +	}
> 
> Don't both conditions amount to:
> 
> 	if (external_mdio_mask == internal_mdio_mask)
> 		error()?
> 
> > +
> > +	if (external_mdio_mask) {
> > +		/* Make sure to disable the internal mdio bus in cases
> > +		 * a dt-overlay and driver reload changed the configuration
> > +		 */
> > +
> > +		qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL,
> > +				QCA8K_MDIO_MASTER_EN);
> > +		return 0;
> > +	}
> > +
> > +	bus = devm_mdiobus_alloc_size(priv->dev, sizeof(priv));
> > +	if (!bus)
> > +		return -ENOMEM;
> > +	bus->priv = (void *)priv;
> 
> Cast is not necessary.
> 





^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3 3/3] net: dsa: qca8k: extend slave-bus implementations
  2019-03-20 18:27   ` Florian Fainelli
  2019-03-20 18:41     ` Christian Lamparter
@ 2019-03-20 22:02     ` Christian Lamparter
  2019-03-20 22:17       ` Florian Fainelli
  1 sibling, 1 reply; 10+ messages in thread
From: Christian Lamparter @ 2019-03-20 22:02 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: netdev, devicetree, Vivien Didelot, Andrew Lunn, Rob Herring,
	Mark Rutland

Sorry. I hit Sent by accident and then I had to run...
This is the full response.

On Wednesday, March 20, 2019 7:27:09 PM CET Florian Fainelli wrote:
> On 3/19/19 12:54 PM, Christian Lamparter wrote:
> > This patch implements accessors for the QCA8337 MDIO access
> > through the MDIO_MASTER register, which makes it possible to
> > access the PHYs on slave-bus through the switch. In cases
> > where the switch ports are already mapped via external
> > "phy-phandles", the internal mdio-bus is disabled in order to
> > prevent a duplicated discovery and enumeration of the same
> > PHYs. Don't use mixed external and internal mdio-bus
> > configurations, as this is not supported by the hardware.
> > 
> > Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
> > ---
> > Changes from v2:
> >  - Make it compatible with existing configurations
> >  - make it clear that's sadly a either external or
> >    internal mdio bus access.
> > 
> > Changes from v1:
> >  - drop DT port <-> phy mapping
> >  - added register definitions for the MDIO control register
> >  - implemented new slave-mdio bus accessors
> >  - DT-binding: fix switch's PSEUDO_PHY address. It's 0x10 not 0.
> > 
> > Old patch (+ discussion) for reference:
> > <https://patchwork.ozlabs.org/patch/1036309/>
> 
> LGTM, just a few comments/nits below.
> 
> > 
> > Tested on a Compex WPQ864 (IPQ8064 + QCA8337N)
> > internal bus:
> > qca8k 37000000.mdio-mii:10 lan1 (uninitialized): PHY [!mdio@37000000!switch@10:01] driver [Generic PHY]
> > qca8k 37000000.mdio-mii:10 lan2 (uninitialized): PHY [!mdio@37000000!switch@10:02] driver [Generic PHY]
> > qca8k 37000000.mdio-mii:10 lan3 (uninitialized): PHY [!mdio@37000000!switch@10:03] driver [Generic PHY]
> > qca8k 37000000.mdio-mii:10 lan4 (uninitialized): PHY [!mdio@37000000!switch@10:04] driver [Generic PHY]
> > qca8k 37000000.mdio-mii:10 wan (uninitialized): PHY [!mdio@37000000!switch@10:05] driver [Generic PHY]
> > 
> > external bus:
> > qca8k 37000000.mdio-mii:10 lan1 (uninitialized): PHY [37000000.mdio-mii:00] driver [Generic PHY]
> > qca8k 37000000.mdio-mii:10 lan2 (uninitialized): PHY [37000000.mdio-mii:01] driver [Generic PHY]
> > qca8k 37000000.mdio-mii:10 lan3 (uninitialized): PHY [37000000.mdio-mii:02] driver [Generic PHY]
> > qca8k 37000000.mdio-mii:10 lan4 (uninitialized): PHY [37000000.mdio-mii:03] driver [Generic PHY]
> > qca8k 37000000.mdio-mii:10 wan (uninitialized): PHY [37000000.mdio-mii:04] driver [Generic PHY]
> > ---
> 
> [snip]
> 
> > +static int
> > +qca8k_mdio_write(struct qca8k_priv *priv, int port, int regnum, u16 data)
> > +{
> > +	u32 val;
> > +	int phy;
> > +
> > +	phy = qca8k_port_to_phy(port);
> > +	if (phy < 0 || (regnum < 0 || regnum >= QCA8K_MDIO_MASTER_MAX_REG))
> > +		return -EINVAL;
> 
> Is not the first condition always true? We should fix the signature of
> phy_{read,write} in dsa.h to match what mdiobus_{read,write} takes,
> which is an u32 regnum.

Yes in that case regnum will never be negative so this check can be
futher simplyfied as dsa_slave_phy_{read|write} checks ds->phy_mii_mask.
So port can't be invalid. In the next version this will be:

if (regnum >= QCA8K_MDIO_MASTER_MAX_REG)
	return -EINVAL;

Since regnum > 31 will spill into QCA8K_MDIO_MASTER_PHY_ADDR
and the higher control bits.

> > +
> > +	val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN |
> > +	      QCA8K_MDIO_MASTER_WRITE | QCA8K_MDIO_MASTER_PHY_ADDR(phy) |
> > +	      QCA8K_MDIO_MASTER_REG_ADDR(regnum) |
> > +	      QCA8K_MDIO_MASTER_DATA(data);
> > +
> > +	qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val);
> > +
> > +	return qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
> > +		QCA8K_MDIO_MASTER_BUSY);
> > +}
> > +
> > +static int
> > +qca8k_mdio_read(struct qca8k_priv *priv, int port, int regnum)
> > +{
> > +	u32 val;
> > +	int phy;
> > +
> > +	phy = qca8k_port_to_phy(port);
> > +	if (phy < 0 || (regnum < 0 || regnum >= QCA8K_MDIO_MASTER_MAX_REG))
> > +		return -EINVAL;
> 
> Likewise.
Yes.
 
> [snip]
> 
> > +static int
> > +qca8k_phy_read(struct dsa_switch *ds, int port, int regnum)
> > +{
> > +	struct qca8k_priv *priv = ds->priv;
> > +	int ret = -EIO;
> > +
> > +	if (ds->slave_mii_bus->phy_mask & BIT(port))
> > +		ret = qca8k_mdio_read(priv, port, regnum);
> 
> I suppose in theory you could also look at the external_mdio_mask and do
> something like this:
> 
> 	if (ds->slave_mii_bus->phy_mask & BIT(port))
> 		ret = qca8k_mdio_read(priv, port, regnum);
> 	else
> 		ret = mdiobus_read_nested(priv->bus, port, regnum);
> 
> Not strictly necessary for now.

Oh, in the external mdio bus scenario, I don't have qca8k_phy_read wired up.
So the else branch would be dead code for now. (but see below)

But there's some other room for improvement:
However since we are always called from dsa_slave_phy_read the 
if (ds->slave_mii_bus->phy_mask & BIT(port))
could be removed since dsa_slave_phy_read does that already.
 
> > +
> > +	return ret;
> > +}
> > +
> > +static int
> > +qca8k_setup_mdio_bus(struct qca8k_priv *priv)
> > +{
> > +	struct device_node *ports, *port;
> > +	struct mii_bus *bus;
> > +	u32 internal_mdio_mask = 0;
> > +	u32 external_mdio_mask = 0;
> > +	u32 reg;
> > +	int err;
> > +
> > +	ports = of_get_child_by_name(priv->dev->of_node, "ports");
> > +	if (!ports)
> > +		return -EINVAL;
> > +
> > +	for_each_available_child_of_node(ports, port) {
> > +		err = of_property_read_u32(port, "reg", &reg);
> > +		if (err)
> > +			return err;
> > +
> > +		if (dsa_is_user_port(priv->ds, reg)) {
> 
> You could reduce indentation a bit with:
> 
> 	if (!dsa_is_user_port(priv->ds, reg))
> 		continue;

Yes. This is nicer.
> 
> > +			if (of_property_read_bool(port, "phy-handle"))
> > +				external_mdio_mask |= BIT(reg);
> > +			else
> > +				internal_mdio_mask |= BIT(reg);
> > +		}
> > +	}
> > +
> > +	if (!external_mdio_mask && !internal_mdio_mask) {
> > +		dev_err(priv->dev, "no PHYs are defined.\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	/* The QCA8K_MDIO_MASTER_EN Bit, which grants access to PHYs through
> > +	 * the MDIO_MASTER register also _disconnects_ the external MDC
> > +	 * passthrough to the internal PHYs. It's not possible to use both
> > +	 * configurations at the same time!
> > +	 */
> 
> Right, but presumably you can do this on a per-port basis and support
> both types of configuration? bcm_sf2 is pretty much the same thing.
per-port? Sadly not that I know of, it's a per-chip (or per-switch) setting
from what I can tell by poking the chip.

I've looked at bcm_sf2 and the best hint seems to be in this commit:

|b8c6cd1d316f net: dsa: bcm_sf2: do not use indirect reads and writes for 7445E0
|
|7445E0 contains an ECO which disconnected the internal SF2 pseudo-PHY which was
|known to conflict with the external pseudo-PHY of BCM53125 switches. This
|motivated the need to utilize the internal SF2 MDIO controller via indirect
|register reads/writes to control external Broadcom switches due to this address
|conflict (both responded at address 30d). [...]

But this seems to involve the pseudo-PHYs (i.e. register access)? Which does not
sound like it what happens on the QCA8337.

For the QCA8337 the QCA8K_MDIO_MASTER_EN bit works as a changeover switch
for just the MDC line (datasheet 5.2.13 MDIO Master Control). So the User-Port
PHYs for LAN1-4 and WAN are moved (as in "mv" not "cp") between the external or
internal bus. This causes some very weird behavior from any PHY access of the
"disabled" bus:

[   17.036963] Generic PHY 37000000.mdio-mii:01: Master/Slave resolution failed, maybe conflicting manual settings?
[   17.116927] Generic PHY 37000000.mdio-mii:02: Master/Slave resolution failed, maybe conflicting manual settings?
[   17.196894] Generic PHY 37000000.mdio-mii:03: Master/Slave resolution failed, maybe conflicting manual settings?

I did investigate this in https://patchwork.ozlabs.org/comment/2086257/ but
I didn't find a viable solution to make this work as from my POV, this
requires synchronization between the qca8k code and the variety of external
mdio-bus driver to pull this off.

In my case: The WPQ864 (IPQ806x) can either use a virtual mdio-gpio driver
(which is what most boards are using) or a some dedicated hardware that's
living in GMAC0's core but has no upstream linux driver (yet).

Note3:
The QCA8337 can be interfaced through either MDIO or UART (share the
same pins, so only one option). Or alternatively through special/
properitary ethernet-frames sent/received on the cpu port (I guess
that's "in-band").

(There's also a SPI interface but it's soley for connecting a
SPI EEPROM that would store VLAN, 802.1p QoS, DiffServ/TOS
settings. So this is probably aimed at standalone switches)

> 
> > +	if (external_mdio_mask && internal_mdio_mask) {
> > +		dev_err(priv->dev, "either internal or external mdio bus configuration is supported.\n");
> > +		return -EINVAL;
> > +	}
> 
> Don't both conditions amount to:
> 
> 	if (external_mdio_mask == internal_mdio_mask)
> 		error()?
I think it is either (!!external_mdio_mask == !!internal_mdio_mask)
or (!external_mdio_mask == !internal_mdio_mask).

if (external_mdio_mask == internal_mdio_mask) will always be false,
because the external_mdio_mask and internal_mdio_mask are bitmasks
on whenever the (enumerated) port has a phy-handle or not. It's not
possible for external_mdio_mask and internal_mdio_mask to be the
same non-null value.

As for why I did it this way: I liked having different, somewhat
descriptive error messages for these cases. Because I have been on
so many -EINVAL wild-goose chases before. So please let me keep the
two distinct messages, OK?

> > +
> > +	if (external_mdio_mask) {
> > +		/* Make sure to disable the internal mdio bus in cases
> > +		 * a dt-overlay and driver reload changed the configuration
> > +		 */
> > +
> > +		qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL,
> > +				QCA8K_MDIO_MASTER_EN);
> > +		return 0;
> > +	}
> > +
> > +	bus = devm_mdiobus_alloc_size(priv->dev, sizeof(priv));
> > +	if (!bus)
> > +		return -ENOMEM;
> > +	bus->priv = (void *)priv;
> 
> Cast is not necessary.
ACK

Cheers,
Christian



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3 3/3] net: dsa: qca8k: extend slave-bus implementations
  2019-03-20 22:02     ` Christian Lamparter
@ 2019-03-20 22:17       ` Florian Fainelli
  0 siblings, 0 replies; 10+ messages in thread
From: Florian Fainelli @ 2019-03-20 22:17 UTC (permalink / raw)
  To: Christian Lamparter
  Cc: netdev, devicetree, Vivien Didelot, Andrew Lunn, Rob Herring,
	Mark Rutland

On 3/20/19 3:02 PM, Christian Lamparter wrote:
> Sorry. I hit Sent by accident and then I had to run...
> This is the full response.
> 
> On Wednesday, March 20, 2019 7:27:09 PM CET Florian Fainelli wrote:
>> On 3/19/19 12:54 PM, Christian Lamparter wrote:
>>> This patch implements accessors for the QCA8337 MDIO access
>>> through the MDIO_MASTER register, which makes it possible to
>>> access the PHYs on slave-bus through the switch. In cases
>>> where the switch ports are already mapped via external
>>> "phy-phandles", the internal mdio-bus is disabled in order to
>>> prevent a duplicated discovery and enumeration of the same
>>> PHYs. Don't use mixed external and internal mdio-bus
>>> configurations, as this is not supported by the hardware.
>>>
>>> Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
>>> ---
>>> Changes from v2:
>>>  - Make it compatible with existing configurations
>>>  - make it clear that's sadly a either external or
>>>    internal mdio bus access.
>>>
>>> Changes from v1:
>>>  - drop DT port <-> phy mapping
>>>  - added register definitions for the MDIO control register
>>>  - implemented new slave-mdio bus accessors
>>>  - DT-binding: fix switch's PSEUDO_PHY address. It's 0x10 not 0.
>>>
>>> Old patch (+ discussion) for reference:
>>> <https://patchwork.ozlabs.org/patch/1036309/>
>>
>> LGTM, just a few comments/nits below.
>>
>>>
>>> Tested on a Compex WPQ864 (IPQ8064 + QCA8337N)
>>> internal bus:
>>> qca8k 37000000.mdio-mii:10 lan1 (uninitialized): PHY [!mdio@37000000!switch@10:01] driver [Generic PHY]
>>> qca8k 37000000.mdio-mii:10 lan2 (uninitialized): PHY [!mdio@37000000!switch@10:02] driver [Generic PHY]
>>> qca8k 37000000.mdio-mii:10 lan3 (uninitialized): PHY [!mdio@37000000!switch@10:03] driver [Generic PHY]
>>> qca8k 37000000.mdio-mii:10 lan4 (uninitialized): PHY [!mdio@37000000!switch@10:04] driver [Generic PHY]
>>> qca8k 37000000.mdio-mii:10 wan (uninitialized): PHY [!mdio@37000000!switch@10:05] driver [Generic PHY]
>>>
>>> external bus:
>>> qca8k 37000000.mdio-mii:10 lan1 (uninitialized): PHY [37000000.mdio-mii:00] driver [Generic PHY]
>>> qca8k 37000000.mdio-mii:10 lan2 (uninitialized): PHY [37000000.mdio-mii:01] driver [Generic PHY]
>>> qca8k 37000000.mdio-mii:10 lan3 (uninitialized): PHY [37000000.mdio-mii:02] driver [Generic PHY]
>>> qca8k 37000000.mdio-mii:10 lan4 (uninitialized): PHY [37000000.mdio-mii:03] driver [Generic PHY]
>>> qca8k 37000000.mdio-mii:10 wan (uninitialized): PHY [37000000.mdio-mii:04] driver [Generic PHY]
>>> ---
>>
>> [snip]
>>
>>> +static int
>>> +qca8k_mdio_write(struct qca8k_priv *priv, int port, int regnum, u16 data)
>>> +{
>>> +	u32 val;
>>> +	int phy;
>>> +
>>> +	phy = qca8k_port_to_phy(port);
>>> +	if (phy < 0 || (regnum < 0 || regnum >= QCA8K_MDIO_MASTER_MAX_REG))
>>> +		return -EINVAL;
>>
>> Is not the first condition always true? We should fix the signature of
>> phy_{read,write} in dsa.h to match what mdiobus_{read,write} takes,
>> which is an u32 regnum.
> 
> Yes in that case regnum will never be negative so this check can be
> futher simplyfied as dsa_slave_phy_{read|write} checks ds->phy_mii_mask.
> So port can't be invalid. In the next version this will be:
> 
> if (regnum >= QCA8K_MDIO_MASTER_MAX_REG)
> 	return -EINVAL;
> 
> Since regnum > 31 will spill into QCA8K_MDIO_MASTER_PHY_ADDR
> and the higher control bits.
> 
>>> +
>>> +	val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN |
>>> +	      QCA8K_MDIO_MASTER_WRITE | QCA8K_MDIO_MASTER_PHY_ADDR(phy) |
>>> +	      QCA8K_MDIO_MASTER_REG_ADDR(regnum) |
>>> +	      QCA8K_MDIO_MASTER_DATA(data);
>>> +
>>> +	qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val);
>>> +
>>> +	return qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
>>> +		QCA8K_MDIO_MASTER_BUSY);
>>> +}
>>> +
>>> +static int
>>> +qca8k_mdio_read(struct qca8k_priv *priv, int port, int regnum)
>>> +{
>>> +	u32 val;
>>> +	int phy;
>>> +
>>> +	phy = qca8k_port_to_phy(port);
>>> +	if (phy < 0 || (regnum < 0 || regnum >= QCA8K_MDIO_MASTER_MAX_REG))
>>> +		return -EINVAL;
>>
>> Likewise.
> Yes.
>  
>> [snip]
>>
>>> +static int
>>> +qca8k_phy_read(struct dsa_switch *ds, int port, int regnum)
>>> +{
>>> +	struct qca8k_priv *priv = ds->priv;
>>> +	int ret = -EIO;
>>> +
>>> +	if (ds->slave_mii_bus->phy_mask & BIT(port))
>>> +		ret = qca8k_mdio_read(priv, port, regnum);
>>
>> I suppose in theory you could also look at the external_mdio_mask and do
>> something like this:
>>
>> 	if (ds->slave_mii_bus->phy_mask & BIT(port))
>> 		ret = qca8k_mdio_read(priv, port, regnum);
>> 	else
>> 		ret = mdiobus_read_nested(priv->bus, port, regnum);
>>
>> Not strictly necessary for now.
> 
> Oh, in the external mdio bus scenario, I don't have qca8k_phy_read wired up.
> So the else branch would be dead code for now. (but see below)
> 
> But there's some other room for improvement:
> However since we are always called from dsa_slave_phy_read the 
> if (ds->slave_mii_bus->phy_mask & BIT(port))
> could be removed since dsa_slave_phy_read does that already.
>  
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static int
>>> +qca8k_setup_mdio_bus(struct qca8k_priv *priv)
>>> +{
>>> +	struct device_node *ports, *port;
>>> +	struct mii_bus *bus;
>>> +	u32 internal_mdio_mask = 0;
>>> +	u32 external_mdio_mask = 0;
>>> +	u32 reg;
>>> +	int err;
>>> +
>>> +	ports = of_get_child_by_name(priv->dev->of_node, "ports");
>>> +	if (!ports)
>>> +		return -EINVAL;
>>> +
>>> +	for_each_available_child_of_node(ports, port) {
>>> +		err = of_property_read_u32(port, "reg", &reg);
>>> +		if (err)
>>> +			return err;
>>> +
>>> +		if (dsa_is_user_port(priv->ds, reg)) {
>>
>> You could reduce indentation a bit with:
>>
>> 	if (!dsa_is_user_port(priv->ds, reg))
>> 		continue;
> 
> Yes. This is nicer.
>>
>>> +			if (of_property_read_bool(port, "phy-handle"))
>>> +				external_mdio_mask |= BIT(reg);
>>> +			else
>>> +				internal_mdio_mask |= BIT(reg);
>>> +		}
>>> +	}
>>> +
>>> +	if (!external_mdio_mask && !internal_mdio_mask) {
>>> +		dev_err(priv->dev, "no PHYs are defined.\n");
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	/* The QCA8K_MDIO_MASTER_EN Bit, which grants access to PHYs through
>>> +	 * the MDIO_MASTER register also _disconnects_ the external MDC
>>> +	 * passthrough to the internal PHYs. It's not possible to use both
>>> +	 * configurations at the same time!
>>> +	 */
>>
>> Right, but presumably you can do this on a per-port basis and support
>> both types of configuration? bcm_sf2 is pretty much the same thing.
> per-port? Sadly not that I know of, it's a per-chip (or per-switch) setting
> from what I can tell by poking the chip.

The MDIO_MASTER enable is also global to the switch with the bcm_sf2. It
is basically a mux to indicate whether the internal roboswitch MDIO
master should be used, or the glued MDIO controller should be used. Both
are integrated into the switch, just not at the same level, which makes
things confusing.

The issue in the D0 revision of the silicon was that when you used the
glued MDIO controller, any MDIO access would be snooped and interpreted
by the internal MDIO slave of the switch and so while programming a
seemingly ressembling external Broadcom switch, you would also happen to
program the internal Broadcom switch (where they are register
compatible, which is like 95%) but with possibly incompatible settings.

> 
> I've looked at bcm_sf2 and the best hint seems to be in this commit:
> 
> |b8c6cd1d316f net: dsa: bcm_sf2: do not use indirect reads and writes for 7445E0
> |
> |7445E0 contains an ECO which disconnected the internal SF2 pseudo-PHY which was
> |known to conflict with the external pseudo-PHY of BCM53125 switches. This
> |motivated the need to utilize the internal SF2 MDIO controller via indirect
> |register reads/writes to control external Broadcom switches due to this address
> |conflict (both responded at address 30d). [...]
> 
> But this seems to involve the pseudo-PHYs (i.e. register access)? Which does not
> sound like it what happens on the QCA8337.

It's largely problematic with the pseudo-PHY of the switch itself, but
can be generalized to any internal or external PHY really.

> 
> For the QCA8337 the QCA8K_MDIO_MASTER_EN bit works as a changeover switch
> for just the MDC line (datasheet 5.2.13 MDIO Master Control). So the User-Port
> PHYs for LAN1-4 and WAN are moved (as in "mv" not "cp") between the external or
> internal bus. This causes some very weird behavior from any PHY access of the
> "disabled" bus:
> 
> [   17.036963] Generic PHY 37000000.mdio-mii:01: Master/Slave resolution failed, maybe conflicting manual settings?
> [   17.116927] Generic PHY 37000000.mdio-mii:02: Master/Slave resolution failed, maybe conflicting manual settings?
> [   17.196894] Generic PHY 37000000.mdio-mii:03: Master/Slave resolution failed, maybe conflicting manual settings?
> 
> I did investigate this in https://patchwork.ozlabs.org/comment/2086257/ but
> I didn't find a viable solution to make this work as from my POV, this
> requires synchronization between the qca8k code and the variety of external
> mdio-bus driver to pull this off.

So I guess my point really is that on a per MDIO transaction basis, you
could theoretically flip the MDIO_MASTER_EN bit such that you always
read/write to the desired PHY address, whether it sits on the internal
QCA8K bus, or on an external MDIO bus. Not that I think you should do
it, but it sounds like it ought to be possible unless I completely
misunderstand something here.

> 
> In my case: The WPQ864 (IPQ806x) can either use a virtual mdio-gpio driver
> (which is what most boards are using) or a some dedicated hardware that's
> living in GMAC0's core but has no upstream linux driver (yet).
> 
> Note3:
> The QCA8337 can be interfaced through either MDIO or UART (share the
> same pins, so only one option). Or alternatively through special/
> properitary ethernet-frames sent/received on the cpu port (I guess
> that's "in-band").
> 
> (There's also a SPI interface but it's soley for connecting a
> SPI EEPROM that would store VLAN, 802.1p QoS, DiffServ/TOS
> settings. So this is probably aimed at standalone switches)
> 
>>
>>> +	if (external_mdio_mask && internal_mdio_mask) {
>>> +		dev_err(priv->dev, "either internal or external mdio bus configuration is supported.\n");
>>> +		return -EINVAL;
>>> +	}
>>
>> Don't both conditions amount to:
>>
>> 	if (external_mdio_mask == internal_mdio_mask)
>> 		error()?
> I think it is either (!!external_mdio_mask == !!internal_mdio_mask)
> or (!external_mdio_mask == !internal_mdio_mask).
> 
> if (external_mdio_mask == internal_mdio_mask) will always be false,
> because the external_mdio_mask and internal_mdio_mask are bitmasks
> on whenever the (enumerated) port has a phy-handle or not. It's not
> possible for external_mdio_mask and internal_mdio_mask to be the
> same non-null value.
> 
> As for why I did it this way: I liked having different, somewhat
> descriptive error messages for these cases. Because I have been on
> so many -EINVAL wild-goose chases before. So please let me keep the
> two distinct messages, OK?

Certainly, no problem with that.
-- 
Florian

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2019-03-20 22:17 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-03-19 19:54 [PATCH v3 1/3] dt-bindings: net: dsa: qca8k: fix example Christian Lamparter
2019-03-19 19:54 ` [PATCH v3 2/3] dt-bindings: net: dsa: qca8k: support internal mdio-bus Christian Lamparter
2019-03-20 18:04   ` Florian Fainelli
2019-03-19 19:54 ` [PATCH v3 3/3] net: dsa: qca8k: extend slave-bus implementations Christian Lamparter
2019-03-20 17:55   ` David Miller
2019-03-20 18:27   ` Florian Fainelli
2019-03-20 18:41     ` Christian Lamparter
2019-03-20 22:02     ` Christian Lamparter
2019-03-20 22:17       ` Florian Fainelli
2019-03-20 18:03 ` [PATCH v3 1/3] dt-bindings: net: dsa: qca8k: fix example Florian Fainelli

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.