From 9860118b58241169f67ba77dfeb935fcf53ce4cd Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 21 Mar 2017 16:36:37 +0000 Subject: net: phy: move phy MMD accessors to phy-core.c Move the phy_(read|write)__mmd() helpers out of line, they will become our main MMD accessor functions, and so will be a little more complex. This complexity doesn't belong in an inline function. Also move the _indirect variants as well to keep like functionality together. Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: Russell King Signed-off-by: David S. Miller --- include/linux/phy.h | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) (limited to 'include/linux/phy.h') diff --git a/include/linux/phy.h b/include/linux/phy.h index 43a774873aa9..bcb4549b41d6 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -651,14 +651,7 @@ struct phy_fixup { * * Same rules as for phy_read(); */ -static inline int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum) -{ - if (!phydev->is_c45) - return -EOPNOTSUPP; - - return mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, - MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff)); -} +int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum); /** * phy_read_mmd_indirect - reads data from the MMD registers @@ -752,16 +745,7 @@ static inline bool phy_is_pseudo_fixed_link(struct phy_device *phydev) * * Same rules as for phy_write(); */ -static inline int phy_write_mmd(struct phy_device *phydev, int devad, - u32 regnum, u16 val) -{ - if (!phydev->is_c45) - return -EOPNOTSUPP; - - regnum = MII_ADDR_C45 | ((devad & 0x1f) << 16) | (regnum & 0xffff); - - return mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, regnum, val); -} +int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val); /** * phy_write_mmd_indirect - writes data to the MMD registers -- cgit v1.3-14-g43fede From 1ee6b9bc6206cd0837bc16e46f580e40fe663384 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 21 Mar 2017 16:36:43 +0000 Subject: net: phy: make phy_(read|write)_mmd() generic MMD accessors Make phy_(read|write)_mmd() generic 802.3 clause 45 register accessors for both Clause 22 and Clause 45 PHYs, using either the direct register reading for Clause 45, or the indirect method for Clause 22 PHYs. Allow this behaviour to be overriden by PHY drivers where necessary. Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/phy-core.c | 33 +++++++++++++++++++++++++-------- include/linux/phy.h | 24 ++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 8 deletions(-) (limited to 'include/linux/phy.h') diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c index b8d8276a3099..d791100afab2 100644 --- a/drivers/net/phy/phy-core.c +++ b/drivers/net/phy/phy-core.c @@ -69,11 +69,18 @@ EXPORT_SYMBOL(phy_read_mmd_indirect); */ int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum) { - if (!phydev->is_c45) - return -EOPNOTSUPP; + if (regnum > (u16)~0 || devad > 32) + return -EINVAL; - return mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, - MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff)); + if (phydev->drv->read_mmd) + return phydev->drv->read_mmd(phydev, devad, regnum); + + if (phydev->is_c45) { + u32 addr = MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff); + return mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, addr); + } + + return phy_read_mmd_indirect(phydev, regnum, devad); } EXPORT_SYMBOL(phy_read_mmd); @@ -125,11 +132,21 @@ EXPORT_SYMBOL(phy_write_mmd_indirect); */ int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val) { - if (!phydev->is_c45) - return -EOPNOTSUPP; + if (regnum > (u16)~0 || devad > 32) + return -EINVAL; + + if (phydev->drv->read_mmd) + return phydev->drv->write_mmd(phydev, devad, regnum, val); + + if (phydev->is_c45) { + u32 addr = MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff); + + return mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, + addr, val); + } - regnum = MII_ADDR_C45 | ((devad & 0x1f) << 16) | (regnum & 0xffff); + phy_write_mmd_indirect(phydev, regnum, devad, val); - return mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, regnum, val); + return 0; } EXPORT_SYMBOL(phy_write_mmd); diff --git a/include/linux/phy.h b/include/linux/phy.h index bcb4549b41d6..b8feeffeb64c 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -587,6 +587,30 @@ struct phy_driver { */ void (*link_change_notify)(struct phy_device *dev); + /* + * Phy specific driver override for reading a MMD register. + * This function is optional for PHY specific drivers. When + * not provided, the default MMD read function will be used + * by phy_read_mmd(), which will use either a direct read for + * Clause 45 PHYs or an indirect read for Clause 22 PHYs. + * devnum is the MMD device number within the PHY device, + * regnum is the register within the selected MMD device. + */ + int (*read_mmd)(struct phy_device *dev, int devnum, u16 regnum); + + /* + * Phy specific driver override for writing a MMD register. + * This function is optional for PHY specific drivers. When + * not provided, the default MMD write function will be used + * by phy_write_mmd(), which will use either a direct write for + * Clause 45 PHYs, or an indirect write for Clause 22 PHYs. + * devnum is the MMD device number within the PHY device, + * regnum is the register within the selected MMD device. + * val is the value to be written. + */ + int (*write_mmd)(struct phy_device *dev, int devnum, u16 regnum, + u16 val); + /* A function provided by a phy specific driver to override the * the PHY driver framework support for reading a MMD register * from the PHY. If not supported, return -1. This function is -- cgit v1.3-14-g43fede From 3b85d8df2655a4a5831ee8233108b53e69efa1ed Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 21 Mar 2017 16:37:03 +0000 Subject: net: phy: remove the indirect MMD read/write methods Remove the indirect MMD read/write methods which are now no longer necessary. Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/phy-core.c | 119 +++++++++++++-------------------------------- include/linux/phy.h | 42 ---------------- 2 files changed, 34 insertions(+), 127 deletions(-) (limited to 'include/linux/phy.h') diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c index d791100afab2..80795ccd3fab 100644 --- a/drivers/net/phy/phy-core.c +++ b/drivers/net/phy/phy-core.c @@ -22,103 +22,42 @@ static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad, bus->write(bus, addr, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR)); } -/** - * phy_read_mmd_indirect - reads data from the MMD registers - * @phydev: The PHY device bus - * @prtad: MMD Address - * @devad: MMD DEVAD - * - * Description: it reads data from the MMD registers (clause 22 to access to - * clause 45) of the specified phy address. - * To read these register we have: - * 1) Write reg 13 // DEVAD - * 2) Write reg 14 // MMD Address - * 3) Write reg 13 // MMD Data Command for MMD DEVAD - * 3) Read reg 14 // Read MMD data - */ -int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, int devad) -{ - struct phy_driver *phydrv = phydev->drv; - int addr = phydev->mdio.addr; - int value = -1; - - if (!phydrv->read_mmd_indirect) { - struct mii_bus *bus = phydev->mdio.bus; - - mutex_lock(&bus->mdio_lock); - mmd_phy_indirect(bus, prtad, devad, addr); - - /* Read the content of the MMD's selected register */ - value = bus->read(bus, addr, MII_MMD_DATA); - mutex_unlock(&bus->mdio_lock); - } else { - value = phydrv->read_mmd_indirect(phydev, prtad, devad, addr); - } - return value; -} -EXPORT_SYMBOL(phy_read_mmd_indirect); - /** * phy_read_mmd - Convenience function for reading a register * from an MMD on a given PHY. * @phydev: The phy_device struct - * @devad: The MMD to read from - * @regnum: The register on the MMD to read + * @devad: The MMD to read from (0..31) + * @regnum: The register on the MMD to read (0..65535) * * Same rules as for phy_read(); */ int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum) { + int val; + if (regnum > (u16)~0 || devad > 32) return -EINVAL; - if (phydev->drv->read_mmd) - return phydev->drv->read_mmd(phydev, devad, regnum); - - if (phydev->is_c45) { + if (phydev->drv->read_mmd) { + val = phydev->drv->read_mmd(phydev, devad, regnum); + } else if (phydev->is_c45) { u32 addr = MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff); - return mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, addr); - } - return phy_read_mmd_indirect(phydev, regnum, devad); -} -EXPORT_SYMBOL(phy_read_mmd); - -/** - * phy_write_mmd_indirect - writes data to the MMD registers - * @phydev: The PHY device - * @prtad: MMD Address - * @devad: MMD DEVAD - * @data: data to write in the MMD register - * - * Description: Write data from the MMD registers of the specified - * phy address. - * To write these register we have: - * 1) Write reg 13 // DEVAD - * 2) Write reg 14 // MMD Address - * 3) Write reg 13 // MMD Data Command for MMD DEVAD - * 3) Write reg 14 // Write MMD data - */ -void phy_write_mmd_indirect(struct phy_device *phydev, int prtad, - int devad, u32 data) -{ - struct phy_driver *phydrv = phydev->drv; - int addr = phydev->mdio.addr; - - if (!phydrv->write_mmd_indirect) { + val = mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, addr); + } else { struct mii_bus *bus = phydev->mdio.bus; + int phy_addr = phydev->mdio.addr; mutex_lock(&bus->mdio_lock); - mmd_phy_indirect(bus, prtad, devad, addr); + mmd_phy_indirect(bus, regnum, devad, phy_addr); - /* Write the data into MMD's selected register */ - bus->write(bus, addr, MII_MMD_DATA, data); + /* Read the content of the MMD's selected register */ + val = bus->read(bus, phy_addr, MII_MMD_DATA); mutex_unlock(&bus->mdio_lock); - } else { - phydrv->write_mmd_indirect(phydev, prtad, devad, addr, data); } + return val; } -EXPORT_SYMBOL(phy_write_mmd_indirect); +EXPORT_SYMBOL(phy_read_mmd); /** * phy_write_mmd - Convenience function for writing a register @@ -132,21 +71,31 @@ EXPORT_SYMBOL(phy_write_mmd_indirect); */ int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val) { + int ret; + if (regnum > (u16)~0 || devad > 32) return -EINVAL; - if (phydev->drv->read_mmd) - return phydev->drv->write_mmd(phydev, devad, regnum, val); - - if (phydev->is_c45) { + if (phydev->drv->read_mmd) { + ret = phydev->drv->write_mmd(phydev, devad, regnum, val); + } else if (phydev->is_c45) { u32 addr = MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff); - return mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, - addr, val); - } + ret = mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, + addr, val); + } else { + struct mii_bus *bus = phydev->mdio.bus; + int phy_addr = phydev->mdio.addr; - phy_write_mmd_indirect(phydev, regnum, devad, val); + mutex_lock(&bus->mdio_lock); + mmd_phy_indirect(bus, regnum, devad, phy_addr); - return 0; + /* Write the data into MMD's selected register */ + bus->write(bus, phy_addr, MII_MMD_DATA, val); + mutex_unlock(&bus->mdio_lock); + + ret = 0; + } + return ret; } EXPORT_SYMBOL(phy_write_mmd); diff --git a/include/linux/phy.h b/include/linux/phy.h index b8feeffeb64c..2efca6b39fba 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -611,24 +611,6 @@ struct phy_driver { int (*write_mmd)(struct phy_device *dev, int devnum, u16 regnum, u16 val); - /* A function provided by a phy specific driver to override the - * the PHY driver framework support for reading a MMD register - * from the PHY. If not supported, return -1. This function is - * optional for PHY specific drivers, if not provided then the - * default MMD read function is used by the PHY framework. - */ - int (*read_mmd_indirect)(struct phy_device *dev, int ptrad, - int devnum, int regnum); - - /* A function provided by a phy specific driver to override the - * the PHY driver framework support for writing a MMD register - * from the PHY. This function is optional for PHY specific drivers, - * if not provided then the default MMD read function is used by - * the PHY framework. - */ - void (*write_mmd_indirect)(struct phy_device *dev, int ptrad, - int devnum, int regnum, u32 val); - /* Get the size and type of the eeprom contained within a plug-in * module */ int (*module_info)(struct phy_device *dev, @@ -677,17 +659,6 @@ struct phy_fixup { */ int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum); -/** - * phy_read_mmd_indirect - reads data from the MMD registers - * @phydev: The PHY device bus - * @prtad: MMD Address - * @addr: PHY address on the MII bus - * - * Description: it reads data from the MMD registers (clause 22 to access to - * clause 45) of the specified phy address. - */ -int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, int devad); - /** * phy_read - Convenience function for reading a given PHY register * @phydev: the phy_device struct @@ -771,19 +742,6 @@ static inline bool phy_is_pseudo_fixed_link(struct phy_device *phydev) */ int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val); -/** - * phy_write_mmd_indirect - writes data to the MMD registers - * @phydev: The PHY device - * @prtad: MMD Address - * @devad: MMD DEVAD - * @data: data to write in the MMD register - * - * Description: Write data from the MMD registers of the specified - * phy address. - */ -void phy_write_mmd_indirect(struct phy_device *phydev, int prtad, - int devad, u32 data); - struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, bool is_c45, struct phy_c45_device_ids *c45_ids); -- cgit v1.3-14-g43fede From 90eff9096c01ba90cdae504a6b95ee87fe2556a3 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 23 Mar 2017 10:01:19 -0700 Subject: net: phy: Allow splitting MDIO bus/device support from PHYs Introduce a new configuration symbol: MDIO_DEVICE which allows building the MDIO devices and bus code, without pulling in the entire Ethernet PHY library and devices code. PHYLIB nows select MDIO_DEVICE and the relevant Makefile files are updated to reflect that. When MDIO_DEVICE (MDIO bus/device only) is selected, but not PHYLIB, we have mdio-bus.ko as a loadable module, and it does not have a module_exit() function because the safety of removing a bus class is unclear. When both MDIO_DEVICE and PHYLIB are enabled, we need to assemble everything into a common loadable module: libphy.ko because of nasty circular dependencies between phy.c, phy_device.c and mdio_bus.c which are really tough to untangle. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/Makefile | 2 +- drivers/net/phy/Kconfig | 60 +++++++++++++++++++++++----------------- drivers/net/phy/Makefile | 13 +++++++-- drivers/net/phy/mdio-boardinfo.c | 1 + drivers/net/phy/mdio_bus.c | 9 ++++++ include/linux/phy.h | 21 ++++++++++++-- 6 files changed, 76 insertions(+), 30 deletions(-) (limited to 'include/linux/phy.h') diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 98ed4d96987c..55f75aea283c 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -18,7 +18,7 @@ obj-$(CONFIG_MII) += mii.o obj-$(CONFIG_MDIO) += mdio.o obj-$(CONFIG_NET) += Space.o loopback.o obj-$(CONFIG_NETCONSOLE) += netconsole.o -obj-$(CONFIG_PHYLIB) += phy/ +obj-$(CONFIG_MDIO_DEVICE) += phy/ obj-$(CONFIG_RIONET) += rionet.o obj-$(CONFIG_NET_TEAM) += team/ obj-$(CONFIG_TUN) += tun.o diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 7ab4b14a43b7..60ffc9da6a28 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -2,33 +2,12 @@ # PHY Layer Configuration # -menuconfig PHYLIB - tristate "PHY Device support and infrastructure" - depends on NETDEVICES +menuconfig MDIO_DEVICE + tristate "MDIO bus device drivers" help - Ethernet controllers are usually attached to PHY - devices. This option provides infrastructure for - managing PHY devices. - -if PHYLIB - -config SWPHY - bool - -config LED_TRIGGER_PHY - bool "Support LED triggers for tracking link state" - depends on LEDS_TRIGGERS - ---help--- - Adds support for a set of LED trigger events per-PHY. Link - state change will trigger the events, for consumption by an - LED class driver. There are triggers for each link speed currently - supported by the phy, and are of the form: - :: - - Where speed is in the form: - Mbps or Gbps + MDIO devices and driver infrastructure code. -comment "MDIO bus device drivers" +if MDIO_DEVICE config MDIO_BCM_IPROC tristate "Broadcom iProc MDIO bus controller" @@ -49,6 +28,7 @@ config MDIO_BCM_UNIMAC config MDIO_BITBANG tristate "Bitbanged MDIO buses" + depends on !(MDIO_DEVICE=y && PHYLIB=m) help This module implements the MDIO bus protocol in software, for use by low level drivers that export the ability to @@ -160,6 +140,36 @@ config MDIO_XGENE This module provides a driver for the MDIO busses found in the APM X-Gene SoC's. +endif + +menuconfig PHYLIB + tristate "PHY Device support and infrastructure" + depends on NETDEVICES + select MDIO_DEVICE + help + Ethernet controllers are usually attached to PHY + devices. This option provides infrastructure for + managing PHY devices. + +if PHYLIB + +config SWPHY + bool + +config LED_TRIGGER_PHY + bool "Support LED triggers for tracking link state" + depends on LEDS_TRIGGERS + ---help--- + Adds support for a set of LED trigger events per-PHY. Link + state change will trigger the events, for consumption by an + LED class driver. There are triggers for each link speed currently + supported by the phy, and are of the form: + :: + + Where speed is in the form: + Mbps or Gbps + + comment "MII PHY device drivers" config AMD_PHY diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 82d915614646..0e1ec0438c23 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -1,7 +1,16 @@ # Makefile for Linux PHY drivers and MDIO bus drivers -libphy-y := phy.o phy_device.o mdio_bus.o mdio_device.o \ - mdio-boardinfo.o phy-core.o +libphy-y := phy.o phy-core.o phy_device.o +mdio-bus-y += mdio_bus.o mdio_device.o mdio-boardinfo.o + +# PHYLIB implies MDIO_DEVICE, in that case, we have a bunch of circular +# dependencies that does not make it possible to split mdio-bus objects into a +# dedicated loadable module, so we bundle them all together into libphy.ko +ifdef CONFIG_PHYLIB +libphy-y += $(mdio-bus-y) +else +obj-$(CONFIG_MDIO_DEVICE) += mdio-bus.o +endif libphy-$(CONFIG_SWPHY) += swphy.o libphy-$(CONFIG_LED_TRIGGER_PHY) += phy_led_triggers.o diff --git a/drivers/net/phy/mdio-boardinfo.c b/drivers/net/phy/mdio-boardinfo.c index 6b988f77da08..61941e29daae 100644 --- a/drivers/net/phy/mdio-boardinfo.c +++ b/drivers/net/phy/mdio-boardinfo.c @@ -84,3 +84,4 @@ int mdiobus_register_board_info(const struct mdio_board_info *info, return 0; } +EXPORT_SYMBOL(mdiobus_register_board_info); diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index fa7d51f14869..46b468eb6e12 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -648,9 +648,18 @@ int __init mdio_bus_init(void) return ret; } +EXPORT_SYMBOL_GPL(mdio_bus_init); +#if IS_ENABLED(CONFIG_PHYLIB) void mdio_bus_exit(void) { class_unregister(&mdio_bus_class); bus_unregister(&mdio_bus_type); } +EXPORT_SYMBOL_GPL(mdio_bus_exit); +#else +module_init(mdio_bus_init); +/* no module_exit, intentional */ +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MDIO bus/device layer"); +#endif diff --git a/include/linux/phy.h b/include/linux/phy.h index 2efca6b39fba..624cecf69c28 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -745,8 +745,24 @@ int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val); struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, bool is_c45, struct phy_c45_device_ids *c45_ids); +#if IS_ENABLED(CONFIG_PHYLIB) struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45); int phy_device_register(struct phy_device *phy); +void phy_device_free(struct phy_device *phydev); +#else +static inline +struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45) +{ + return NULL; +} + +static inline int phy_device_register(struct phy_device *phy) +{ + return 0; +} + +static inline void phy_device_free(struct phy_device *phydev) { } +#endif /* CONFIG_PHYLIB */ void phy_device_remove(struct phy_device *phydev); int phy_init_hw(struct phy_device *phydev); int phy_suspend(struct phy_device *phydev); @@ -827,7 +843,6 @@ int phy_ethtool_ksettings_set(struct phy_device *phydev, int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd); int phy_start_interrupts(struct phy_device *phydev); void phy_print_status(struct phy_device *phydev); -void phy_device_free(struct phy_device *phydev); int phy_set_max_speed(struct phy_device *phydev, u32 max_speed); int phy_register_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask, @@ -854,8 +869,10 @@ int phy_ethtool_set_link_ksettings(struct net_device *ndev, const struct ethtool_link_ksettings *cmd); int phy_ethtool_nway_reset(struct net_device *ndev); +#if IS_ENABLED(CONFIG_PHYLIB) int __init mdio_bus_init(void); void mdio_bus_exit(void); +#endif extern struct bus_type mdio_bus_type; @@ -866,7 +883,7 @@ struct mdio_board_info { const void *platform_data; }; -#if IS_ENABLED(CONFIG_PHYLIB) +#if IS_ENABLED(CONFIG_MDIO_DEVICE) int mdiobus_register_board_info(const struct mdio_board_info *info, unsigned int n); #else -- cgit v1.3-14-g43fede From 69226896ad636b94f6d2e55d75ff21a29c4de83b Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 21 Apr 2017 16:15:38 +0300 Subject: mdio_bus: Issue GPIO RESET to PHYs. Some boards [1] leave the PHYs at an invalid state during system power-up or reset thus causing unreliability issues with the PHY which manifests as PHY not being detected or link not functional. To fix this, these PHYs need to be RESET via a GPIO connected to the PHY's RESET pin. Some boards have a single GPIO controlling the PHY RESET pin of all PHYs on the bus whereas some others have separate GPIOs controlling individual PHY RESETs. In both cases, the RESET de-assertion cannot be done in the PHY driver as the PHY will not probe till its reset is de-asserted. So do the RESET de-assertion in the MDIO bus driver. [1] - am572x-idk, am571x-idk, a437x-idk Signed-off-by: Roger Quadros Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/mdio.txt | 33 ++++++++++++++++++ drivers/net/phy/mdio_bus.c | 47 ++++++++++++++++++++++++++ drivers/of/of_mdio.c | 7 ++++ include/linux/phy.h | 7 ++++ 4 files changed, 94 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/mdio.txt (limited to 'include/linux/phy.h') diff --git a/Documentation/devicetree/bindings/net/mdio.txt b/Documentation/devicetree/bindings/net/mdio.txt new file mode 100644 index 000000000000..4ffbbacebda1 --- /dev/null +++ b/Documentation/devicetree/bindings/net/mdio.txt @@ -0,0 +1,33 @@ +Common MDIO bus properties. + +These are generic properties that can apply to any MDIO bus. + +Optional properties: +- reset-gpios: List of one or more GPIOs that control the RESET lines + of the PHYs on that MDIO bus. +- reset-delay-us: RESET pulse width in microseconds as per PHY datasheet. + +A list of child nodes, one per device on the bus is expected. These +should follow the generic phy.txt, or a device specific binding document. + +Example : +This example shows these optional properties, plus other properties +required for the TI Davinci MDIO driver. + + davinci_mdio: ethernet@0x5c030000 { + compatible = "ti,davinci_mdio"; + reg = <0x5c030000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + + reset-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>; + reset-delay-us = <2>; /* PHY datasheet states 1us min */ + + ethphy0: ethernet-phy@1 { + reg = <1>; + }; + + ethphy1: ethernet-phy@3 { + reg = <3>; + }; + }; diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 5a214f3b8671..a898e5c4ef1b 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -22,8 +22,11 @@ #include #include #include +#include +#include #include #include +#include #include #include #include @@ -337,6 +340,7 @@ int __mdiobus_register(struct mii_bus *bus, struct module *owner) { struct mdio_device *mdiodev; int i, err; + struct gpio_desc *gpiod; if (NULL == bus || NULL == bus->name || NULL == bus->read || NULL == bus->write) @@ -363,6 +367,35 @@ int __mdiobus_register(struct mii_bus *bus, struct module *owner) if (bus->reset) bus->reset(bus); + /* de-assert bus level PHY GPIO resets */ + if (bus->num_reset_gpios > 0) { + bus->reset_gpiod = devm_kcalloc(&bus->dev, + bus->num_reset_gpios, + sizeof(struct gpio_desc *), + GFP_KERNEL); + if (!bus->reset_gpiod) + return -ENOMEM; + } + + for (i = 0; i < bus->num_reset_gpios; i++) { + gpiod = devm_gpiod_get_index(&bus->dev, "reset", i, + GPIOD_OUT_LOW); + if (IS_ERR(gpiod)) { + err = PTR_ERR(gpiod); + if (err != -ENOENT) { + dev_err(&bus->dev, + "mii_bus %s couldn't get reset GPIO\n", + bus->id); + return err; + } + } else { + bus->reset_gpiod[i] = gpiod; + gpiod_set_value_cansleep(gpiod, 1); + udelay(bus->reset_delay_us); + gpiod_set_value_cansleep(gpiod, 0); + } + } + for (i = 0; i < PHY_MAX_ADDR; i++) { if ((bus->phy_mask & (1 << i)) == 0) { struct phy_device *phydev; @@ -390,6 +423,13 @@ error: mdiodev->device_remove(mdiodev); mdiodev->device_free(mdiodev); } + + /* Put PHYs in RESET to save power */ + for (i = 0; i < bus->num_reset_gpios; i++) { + if (bus->reset_gpiod[i]) + gpiod_set_value_cansleep(bus->reset_gpiod[i], 1); + } + device_del(&bus->dev); return err; } @@ -411,6 +451,13 @@ void mdiobus_unregister(struct mii_bus *bus) mdiodev->device_remove(mdiodev); mdiodev->device_free(mdiodev); } + + /* Put PHYs in RESET to save power */ + for (i = 0; i < bus->num_reset_gpios; i++) { + if (bus->reset_gpiod[i]) + gpiod_set_value_cansleep(bus->reset_gpiod[i], 1); + } + device_del(&bus->dev); } EXPORT_SYMBOL(mdiobus_unregister); diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c index 0b2979816dbf..7e4c80f9b6cd 100644 --- a/drivers/of/of_mdio.c +++ b/drivers/of/of_mdio.c @@ -22,6 +22,8 @@ #include #include +#define DEFAULT_GPIO_RESET_DELAY 10 /* in microseconds */ + MODULE_AUTHOR("Grant Likely "); MODULE_LICENSE("GPL"); @@ -221,6 +223,11 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) mdio->dev.of_node = np; + /* Get bus level PHY reset GPIO details */ + mdio->reset_delay_us = DEFAULT_GPIO_RESET_DELAY; + of_property_read_u32(np, "reset-delay-us", &mdio->reset_delay_us); + mdio->num_reset_gpios = of_gpio_named_count(np, "reset-gpios"); + /* Register the MDIO bus */ rc = mdiobus_register(mdio); if (rc) diff --git a/include/linux/phy.h b/include/linux/phy.h index 624cecf69c28..37ca77d86983 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -217,6 +217,13 @@ struct mii_bus { * matching its address */ int irq[PHY_MAX_ADDR]; + + /* GPIO reset pulse width in microseconds */ + int reset_delay_us; + /* Number of reset GPIOs */ + int num_reset_gpios; + /* Array of RESET GPIO descriptors */ + struct gpio_desc **reset_gpiod; }; #define to_mii_bus(d) container_of(d, struct mii_bus, dev) -- cgit v1.3-14-g43fede