[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <1536829493-10088-3-git-send-email-dongsheng.wang@hxt-semitech.com>
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