Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1758003AbbLCBji (ORCPT ); Wed, 2 Dec 2015 20:39:38 -0500 Received: from szxga01-in.huawei.com ([58.251.152.64]:56350 "EHLO szxga01-in.huawei.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1757932AbbLCBje (ORCPT ); Wed, 2 Dec 2015 20:39:34 -0500 From: yankejian To: , , , , , , , CC: , , Subject: [PATCH] ACPI: Add phylib support code for mdio Date: Thu, 3 Dec 2015 09:54:43 +0800 Message-ID: <1449107683-69717-1-git-send-email-yankejian@huawei.com> X-Mailer: git-send-email 1.9.1 MIME-Version: 1.0 Content-Type: text/plain X-Originating-IP: [10.67.212.75] X-CFilter-Loop: Reflected X-Mirapoint-Virus-RAPID-Raw: score=unknown(0), refid=str=0001.0A020203.565F9D51.0109,ss=1,re=0.000,recu=0.000,reip=0.000,cl=1,cld=1,fgs=0, ip=0.0.0.0, so=2013-06-18 04:22:30, dmn=2013-03-21 17:37:32 X-Mirapoint-Loop-Id: d05d6c037c62e4df0cb4b05a4cfd06b4 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 11102 Lines: 393 Add support for getting the PHY devices on an MDIO bus by ACPI. Currently many of the ethernet drivers are open coding a solution for reading data out of ACPI to find the correct PHY device. This patch implements a set of common routines are similar to of_mdio.c Signed-off-by: yankejian --- drivers/acpi/Makefile | 3 + drivers/acpi/acpi_mdio.c | 263 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/acpi_mdio.h | 68 ++++++++++++ 3 files changed, 334 insertions(+) create mode 100644 drivers/acpi/acpi_mdio.c create mode 100644 include/linux/acpi_mdio.h diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 675eaf3..832e7d6 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -35,6 +35,7 @@ acpi-y += bus.o glue.o acpi-y += scan.o acpi-y += resource.o acpi-y += acpi_processor.o +acpi-y += acpi_mdio.o acpi-y += processor_core.o acpi-$(CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC) += processor_pdc.o acpi-y += ec.o @@ -65,6 +66,7 @@ obj-$(CONFIG_ACPI_BUTTON) += button.o obj-$(CONFIG_ACPI_FAN) += fan.o obj-$(CONFIG_ACPI_VIDEO) += video.o obj-$(CONFIG_ACPI_PCI_SLOT) += pci_slot.o +obj-$(CONFIG_PCI_MMCONFIG) += mcfg.o obj-$(CONFIG_ACPI_PROCESSOR) += processor.o obj-y += container.o obj-$(CONFIG_ACPI_THERMAL) += thermal.o @@ -79,6 +81,7 @@ obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o obj-$(CONFIG_ACPI_BGRT) += bgrt.o obj-$(CONFIG_ACPI_CPPC_LIB) += cppc_acpi.o +obj-$(CONFIG_IORT_TABLE) += iort.o # processor has its own "processor." module_param namespace processor-y := processor_driver.o diff --git a/drivers/acpi/acpi_mdio.c b/drivers/acpi/acpi_mdio.c new file mode 100644 index 0000000..3a5871d --- /dev/null +++ b/drivers/acpi/acpi_mdio.c @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static +int acpi_mdiobus_register_phy(struct mii_bus *mdio, struct fwnode_handle *child, + u32 addr) +{ + struct phy_device *phy; + const char *phy_type; + bool is_c45; + int rc; + + rc = fwnode_property_read_string(child, "ethernet-phy", &phy_type); + if (rc < 0) + return rc; + + if (!strncmp(phy_type, "ethernet-phy-ieee802.3-c45", + sizeof("ethernet-phy-ieee802.3-c45"))) + is_c45 = 1; + else if (!strncmp(phy_type, "ethernet-phy-ieee802.3-c22", + sizeof("ethernet-phy-ieee802.3-c22"))) + is_c45 = 0; + else + return -ENODATA; + + phy = get_phy_device(mdio, addr, is_c45); + if (!phy || IS_ERR(phy)) + return 1; + + /* Associate the fw node with the device structure so it + * can be looked up later + */ + phy->dev.fwnode = child; + + if (mdio->irq) + phy->irq = mdio->irq[addr]; + + if (fwnode_property_read_bool(child, "broken-turn-around")) + mdio->phy_ignore_ta_mask |= 1 << addr; + + /* All data is now stored in the phy struct; + * register it + */ + rc = phy_device_register(phy); + if (rc) { + phy_device_free(phy); + return 1; + } + + dev_dbg(&mdio->dev, "registered phy at address %i\n", addr); + + return 0; +} + +int acpi_mdio_parse_addr(struct device *dev, struct fwnode_handle *fwnode) +{ + u32 addr; + int ret; + + ret = fwnode_property_read_u32(fwnode, "phy-addr", &addr); + if (ret < 0) { + dev_err(dev, "has invalid PHY address ret:%d\n", ret); + return ret; + } + + if (addr >= PHY_MAX_ADDR) { + dev_err(dev, "PHY address %i is too large\n", addr); + return -EINVAL; + } + + return addr; +} +EXPORT_SYMBOL(acpi_mdio_parse_addr); + +/** + * acpi_mdiobus_register - Register mii_bus and create PHYs + * @mdio: pointer to mii_bus structure + * @fwnode: pointer to framework node of MDIO bus. + * + * This function registers the mii_bus structure and registers a phy_device + * for each child node of mdio device. + */ +int acpi_mdiobus_register(struct mii_bus *mdio, struct fwnode_handle *fwnode) +{ + struct fwnode_handle *child; + struct acpi_device *adev; + bool scanphys = false; + int addr, rc, i; + + /* Mask out all PHYs from auto probing. Instead the PHYs listed in + * the framework node are populated after the bus has been registered + */ + mdio->phy_mask = ~0; + + /* Clear all the IRQ properties */ + if (mdio->irq) + for (i = 0; i < PHY_MAX_ADDR; i++) + mdio->irq[i] = PHY_POLL; + + mdio->dev.fwnode = fwnode; + + /* Register the MDIO bus */ + rc = mdiobus_register(mdio); + if (rc) + return rc; + + /* Loop over the child nodes and register a phy_device for each one */ + device_for_each_child_node(&mdio->dev, child) { + adev = to_acpi_device_node(child); + if (!adev) + continue; + + addr = acpi_mdio_parse_addr(&adev->dev, child); + if (addr < 0) { + scanphys = true; + continue; + } + + rc = acpi_mdiobus_register_phy(mdio, child, addr); + dev_dbg(&mdio->dev, "acpi reg phy rc:%#x addr:%#x\n", rc, addr); + if (rc) + continue; + } + + if (!scanphys) + return 0; + + /* auto scan for PHYs with empty reg property */ + device_for_each_child_node(&mdio->dev, child) { + /* Skip PHYs with reg property set */ + if (!fwnode_property_present(child, "reg")) + continue; + + for (addr = 0; addr < PHY_MAX_ADDR; addr++) { + /* skip already registered PHYs */ + if (mdio->phy_map[addr]) + continue; + + /* be noisy to encourage people to set reg property */ + dev_info(&mdio->dev, "scan phy %s at address %i\n", + acpi_dev_name(to_acpi_device_node(child)), + addr); + + rc = acpi_mdiobus_register_phy(mdio, child, addr); + if (rc) + continue; + } + } + + return 0; +} +EXPORT_SYMBOL(acpi_mdiobus_register); + +/* Helper function for acpi_phy_find_device */ +static int acpi_phy_match(struct device *dev, void *phy_fwnode) +{ + return dev->fwnode == phy_fwnode; +} + +/** + * acpi_phy_find_device - Give a PHY node, find the phy_device + * @phy_fwnode: Pointer to the phy's framework node + * + * If successful, returns a pointer to the phy_device with the embedded + * struct device refcount incremented by one, or NULL on failure. + */ +struct phy_device *acpi_phy_find_device(struct fwnode_handle *phy_fwnode) +{ + struct device *d; + + if (!phy_fwnode) + return NULL; + + d = bus_find_device(&mdio_bus_type, NULL, phy_fwnode, acpi_phy_match); + + return d ? to_phy_device(d) : NULL; +} +EXPORT_SYMBOL(acpi_phy_find_device); + +/** + * acpi_phy_attach - Attach to a PHY without starting the state machine + * @dev: pointer to net_device claiming the phy + * @phy_fwnode: framework Node pointer for the PHY + * @flags: flags to pass to the PHY + * @iface: PHY data interface type + * + * If successful, returns a pointer to the phy_device with the embedded + * struct device refcount incremented by one, or NULL on failure. The + * refcount must be dropped by calling phy_disconnect() or phy_detach(). + */ +struct phy_device *acpi_phy_attach(struct net_device *dev, + struct fwnode_handle *phy_fwnode, u32 flags, + phy_interface_t iface) +{ + struct phy_device *phy = acpi_phy_find_device(phy_fwnode); + int ret; + + if (!phy) + return NULL; + + ret = phy_attach_direct(dev, phy, flags, iface); + + /* refcount is held by phy_attach_direct() on success */ + put_device(&phy->dev); + + return ret ? NULL : phy; +} +EXPORT_SYMBOL(acpi_phy_attach); + +/** + * acpi_phy_connect - Connect to the phy described + * @dev: pointer to net_device claiming the phy + * @phy_fwnode: Pointer to framework node for the PHY + * @hndlr: Link state callback for the network device + * @iface: PHY data interface type + * + * If successful, returns a pointer to the phy_device with the embedded + * struct device refcount incremented by one, or NULL on failure. The + * refcount must be dropped by calling phy_disconnect() or phy_detach(). + */ +struct phy_device *acpi_phy_connect(struct net_device *dev, + struct fwnode_handle *phy_fwnode, + void (*hndlr)(struct net_device *), + u32 flags, + phy_interface_t iface) +{ + struct phy_device *phy = acpi_phy_find_device(phy_fwnode); + int ret; + + if (!phy) + return NULL; + + phy->dev_flags = flags; + + ret = phy_connect_direct(dev, phy, hndlr, iface); + + /* refcount is held by phy_connect_direct() on success */ + put_device(&phy->dev); + + return ret ? NULL : phy; +} +EXPORT_SYMBOL(acpi_phy_connect); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Huawei Tech. Co., Ltd."); diff --git a/include/linux/acpi_mdio.h b/include/linux/acpi_mdio.h new file mode 100644 index 0000000..82b5be5 --- /dev/null +++ b/include/linux/acpi_mdio.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __LINUX_ACPI_MDIO_H +#define __LINUX_ACPI_MDIO_H + +#include + +#ifdef CONFIG_ACPI + +int acpi_mdio_parse_addr(struct device *dev, struct fwnode_handle *fwnode); +int acpi_mdiobus_register(struct mii_bus *mdio, struct fwnode_handle *fwnode); +struct phy_device *acpi_phy_find_device(struct fwnode_handle *phy_fwnode); +struct phy_device *acpi_phy_attach(struct net_device *dev, + struct fwnode_handle *phy_fwnode, u32 flags, + phy_interface_t iface); +struct phy_device *acpi_phy_connect(struct net_device *dev, + struct fwnode_handle *phy_fwnode, + void (*hndlr)(struct net_device *), + u32 flags, + phy_interface_t iface); + +#else +static inline int acpi_mdio_parse_addr(struct device *dev, + struct fwnode_handle *fwnode) +{ + return -ENXIO; +} + +static inline int acpi_mdiobus_register(struct mii_bus *mdio, + struct fwnode_handle *fwnode) +{ + return -ENXIO; +} + +static inline +struct phy_device *acpi_phy_find_device(struct fwnode_handle *phy_fwnode) +{ + return NULL; +} + +static inline +struct phy_device *acpi_phy_attach(struct net_device *dev, + struct fwnode_handle *phy_fwnode, u32 flags, + phy_interface_t iface) +{ + return NULL; +} + +static inline +struct phy_device *acpi_phy_connect(struct net_device *dev, + struct fwnode_handle *phy_fwnode, + void (*hndlr)(struct net_device *), + u32 flags, + phy_interface_t iface) +{ + return NULL; +} + +#endif + +#endif /* __LINUX_ACPI_MDIO_H */ -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/