lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Thu, 13 Sep 2018 17:04:53 +0800
From:   Wang Dongsheng <dongsheng.wang@...-semitech.com>
To:     <timur@...nel.org>
CC:     <davem@...emloft.net>, <yu.zheng@...-semitech.com>,
        Wang Dongsheng <dongsheng.wang@...-semitech.com>,
        <netdev@...r.kernel.org>
Subject: [PATCH 2/2] net: qcom/emac: add shared mdio bus support

Share the mii_bus for others MAC device because QDF2400 emac
include MDIO, and the motherboard has more than one PHY connected
to an MDIO bus.

Tested: QDF2400 (ACPI), buildin/insmod/rmmod

Signed-off-by: Wang Dongsheng <dongsheng.wang@...-semitech.com>
---
 drivers/net/ethernet/qualcomm/emac/emac-phy.c | 211 ++++++++++++++----
 drivers/net/ethernet/qualcomm/emac/emac.c     |   7 +-
 2 files changed, 174 insertions(+), 44 deletions(-)

diff --git a/drivers/net/ethernet/qualcomm/emac/emac-phy.c b/drivers/net/ethernet/qualcomm/emac/emac-phy.c
index 53dbf1e163a8..327362f6b673 100644
--- a/drivers/net/ethernet/qualcomm/emac/emac-phy.c
+++ b/drivers/net/ethernet/qualcomm/emac/emac-phy.c
@@ -13,6 +13,7 @@
 /* Qualcomm Technologies, Inc. EMAC PHY Controller driver.
  */
 
+#include <linux/of_platform.h>
 #include <linux/of_mdio.h>
 #include <linux/phy.h>
 #include <linux/iopoll.h>
@@ -96,15 +97,14 @@ static int emac_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val)
 	return 0;
 }
 
-/* Configure the MDIO bus and connect the external PHY */
-int emac_phy_config(struct platform_device *pdev, struct emac_adapter *adpt)
+static int __do_emac_mido_bus_create(struct platform_device *pdev,
+				     struct emac_adapter *adpt)
 {
 	struct device_node *np = pdev->dev.of_node;
 	struct mii_bus *mii_bus;
 	int ret;
 
-	/* Create the mii_bus object for talking to the MDIO bus */
-	adpt->mii_bus = mii_bus = devm_mdiobus_alloc(&pdev->dev);
+	mii_bus = devm_mdiobus_alloc(&pdev->dev);
 	if (!mii_bus)
 		return -ENOMEM;
 
@@ -115,50 +115,177 @@ int emac_phy_config(struct platform_device *pdev, struct emac_adapter *adpt)
 	mii_bus->parent = &pdev->dev;
 	mii_bus->priv = adpt;
 
-	if (has_acpi_companion(&pdev->dev)) {
-		u32 phy_addr;
+	ret = of_mdiobus_register(mii_bus, has_acpi_companion(&pdev->dev) ?
+				  NULL : np);
+	if (!ret) {
+		adpt->mii_bus = mii_bus;
+		return 0;
+	}
 
-		ret = mdiobus_register(mii_bus);
-		if (ret) {
-			dev_err(&pdev->dev, "could not register mdio bus\n");
-			return ret;
-		}
-		ret = device_property_read_u32(&pdev->dev, "phy-channel",
-					       &phy_addr);
-		if (ret)
-			/* If we can't read a valid phy address, then assume
-			 * that there is only one phy on this mdio bus.
-			 */
-			adpt->phydev = phy_find_first(mii_bus);
-		else
-			adpt->phydev = mdiobus_get_phy(mii_bus, phy_addr);
-
-		/* of_phy_find_device() claims a reference to the phydev,
-		 * so we do that here manually as well. When the driver
-		 * later unloads, it can unilaterally drop the reference
-		 * without worrying about ACPI vs DT.
-		 */
-		if (adpt->phydev)
-			get_device(&adpt->phydev->mdio.dev);
-	} else {
-		struct device_node *phy_np;
-
-		ret = of_mdiobus_register(mii_bus, np);
-		if (ret) {
-			dev_err(&pdev->dev, "could not register mdio bus\n");
-			return ret;
-		}
+	dev_err(&pdev->dev, "Could not register mdio bus\n");
+	return ret;
+}
 
-		phy_np = of_parse_phandle(np, "phy-handle", 0);
-		adpt->phydev = of_phy_find_device(phy_np);
-		of_node_put(phy_np);
+static int acpi_device_match(struct device *dev, void *fwnode)
+{
+	return dev->fwnode == fwnode;
+}
+
+static int emac_acpi_get_shared_bus(struct platform_device *pdev,
+				    struct mii_bus **bus)
+{
+	acpi_handle shared_handle;
+	struct acpi_device *adev;
+	const union acpi_object *obj;
+	union acpi_object *obj_e;
+	struct device *shared_dev;
+	struct net_device *shared_netdev;
+	struct emac_adapter *shared_adpt;
+	int ret;
+
+	adev = ACPI_COMPANION(&pdev->dev);
+	if (!adev)
+		return -ENODEV;
+
+	ret = acpi_dev_get_property(adev, "mdio-device", ACPI_TYPE_ANY, &obj);
+	if (ret) {
+		dev_err(&pdev->dev, "Missing mdio-device property\n");
+		return -ENODEV;
 	}
 
-	if (!adpt->phydev) {
-		dev_err(&pdev->dev, "could not find external phy\n");
-		mdiobus_unregister(mii_bus);
+	if (obj->package.count != 1)
+		return -ENODEV;
+
+	obj_e = &obj->package.elements[0];
+	if (obj_e->type != ACPI_TYPE_LOCAL_REFERENCE)
+		return -ENODEV;
+
+	if (obj_e->reference.actual_type != ACPI_TYPE_DEVICE)
+		return -ENODEV;
+
+	shared_handle = obj_e->reference.handle;
+	if (!shared_handle || acpi_bus_get_device(shared_handle, &adev))
+		return -ENODEV;
+
+	shared_dev = bus_find_device(&platform_bus_type, NULL,
+				     acpi_fwnode_handle(adev),
+				     acpi_device_match);
+	if (!shared_dev)
+		return -EPROBE_DEFER;
+
+	shared_netdev = dev_get_drvdata(shared_dev);
+	if (!shared_netdev)
+		return -EPROBE_DEFER;
+
+	shared_adpt = netdev_priv(shared_netdev);
+	if (!shared_adpt->mii_bus)
+		return -EPROBE_DEFER;
+
+	*bus = shared_adpt->mii_bus;
+	return 0;
+}
+
+static int emac_of_get_shared_bus(struct platform_device *pdev,
+				  struct mii_bus **bus)
+{
+	struct device_node *shared_node;
+	struct platform_device *shared_pdev;
+	struct net_device *shared_netdev;
+	struct emac_adapter *shared_adpt;
+	struct device_node *np = pdev->dev.of_node;
+
+	const phandle *prop;
+
+	prop = of_get_property(np, "mdio-device", NULL);
+	if (!prop) {
+		dev_err(&pdev->dev, "Missing mdio-device property\n");
 		return -ENODEV;
 	}
 
+	shared_node = of_find_node_by_phandle(*prop);
+	if (!shared_node)
+		return -ENODEV;
+
+	shared_pdev = of_find_device_by_node(shared_node);
+	if (!shared_pdev)
+		return -ENODEV;
+
+	shared_netdev = dev_get_drvdata(&shared_pdev->dev);
+	if (!shared_netdev)
+		return -EPROBE_DEFER;
+
+	shared_adpt = netdev_priv(shared_netdev);
+	if (!shared_adpt->mii_bus)
+		return -EPROBE_DEFER;
+
+	*bus = shared_adpt->mii_bus;
 	return 0;
 }
+
+static int __do_get_emac_mido_shared_bus(struct platform_device *pdev,
+					 struct emac_adapter *adpt)
+{
+	int ret = -ENODEV;
+
+	if (IS_ENABLED(CONFIG_ACPI)) {
+		ret = emac_acpi_get_shared_bus(pdev, &adpt->mii_bus);
+		if (adpt->mii_bus || ret == -EPROBE_DEFER)
+			return ret;
+	}
+
+	if (IS_ENABLED(CONFIG_OF)) {
+		ret = emac_of_get_shared_bus(pdev, &adpt->mii_bus);
+		if (adpt->mii_bus || ret == -EPROBE_DEFER)
+			return ret;
+	}
+
+	return ret;
+}
+
+static int emac_mdio_bus_create(struct platform_device *pdev,
+				struct emac_adapter *adpt)
+{
+	bool shared_mdio;
+
+	shared_mdio = device_property_read_bool(&pdev->dev, "mdio-device");
+	if (shared_mdio)
+		return __do_get_emac_mido_shared_bus(pdev, adpt);
+
+	return __do_emac_mido_bus_create(pdev, adpt);
+}
+
+/* Configure the MDIO bus and connect the external PHY */
+int emac_phy_config(struct platform_device *pdev, struct emac_adapter *adpt)
+{
+	struct device *dev = &pdev->dev;
+	u32 phy_addr = PHY_MAX_ADDR;
+	int ret;
+
+	ret = emac_mdio_bus_create(pdev, adpt);
+	if (ret)
+		return ret;
+
+	ret = device_property_read_u32(dev,
+				       has_acpi_companion(dev) ?
+				       "phy-channel" : "phy-handle",
+				       &phy_addr);
+	if (ret || phy_addr == PHY_MAX_ADDR)
+		/* If we can't read a valid phy address, then assume
+		 * that there is only one phy on this mdio bus.
+		 */
+		adpt->phydev = phy_find_first(adpt->mii_bus);
+	else
+		adpt->phydev = mdiobus_get_phy(adpt->mii_bus, phy_addr);
+
+	if (adpt->phydev) {
+		get_device(&adpt->phydev->mdio.dev);
+		return 0;
+	}
+
+	dev_err(dev, "Could not find external phy\n");
+	/* Only the bus creator can unregister mdio bus */
+	if (dev == adpt->mii_bus->parent)
+		mdiobus_unregister(adpt->mii_bus);
+
+	return -ENODEV;
+}
diff --git a/drivers/net/ethernet/qualcomm/emac/emac.c b/drivers/net/ethernet/qualcomm/emac/emac.c
index 2a0cbc535a2e..11d0fe795616 100644
--- a/drivers/net/ethernet/qualcomm/emac/emac.c
+++ b/drivers/net/ethernet/qualcomm/emac/emac.c
@@ -738,7 +738,8 @@ static int emac_probe(struct platform_device *pdev)
 
 static int emac_remove(struct platform_device *pdev)
 {
-	struct net_device *netdev = dev_get_drvdata(&pdev->dev);
+	struct device *dev = &pdev->dev;
+	struct net_device *netdev = dev_get_drvdata(dev);
 	struct emac_adapter *adpt = netdev_priv(netdev);
 
 	unregister_netdev(netdev);
@@ -747,7 +748,9 @@ static int emac_remove(struct platform_device *pdev)
 	emac_clks_teardown(adpt);
 
 	put_device(&adpt->phydev->mdio.dev);
-	mdiobus_unregister(adpt->mii_bus);
+	/* Only the bus creator can unregister mdio bus */
+	if (dev == adpt->mii_bus->parent)
+		mdiobus_unregister(adpt->mii_bus);
 	free_netdev(netdev);
 
 	if (adpt->phy.digital)
-- 
2.18.0

Powered by blists - more mailing lists