From 3fcb734639b50f2b821585f2ab61bfc995f9feed Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 20 May 2014 15:01:37 +0200 Subject: [PATCH 01/12] net: phy: factor out phy_device_attach function phy_device_connect combines searching for a phy with actually attaching it to the ethernet device. Factor out a phy_device_attach function to have a function for each purpose. This makes the logic of phy_device_connect simpler. Signed-off-by: Sascha Hauer --- drivers/net/phy/phy.c | 99 ++++++++++++++++++++++++------------------- 1 file changed, 56 insertions(+), 43 deletions(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 879939d4a..a8a8a2ce5 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -260,12 +260,48 @@ int phy_register_device(struct phy_device* dev) return ret; } +static int phy_device_attach(struct phy_device *phy, struct eth_device *edev, + void (*adjust_link) (struct eth_device *edev), + u32 flags, phy_interface_t interface) +{ + int ret; + + if (phy->attached_dev) + return -EBUSY; + + phy->interface = interface; + phy->dev_flags = flags; + + if (!phy->registered) { + ret = phy_register_device(phy); + if (ret) + return ret; + } + + edev->phydev = phy; + phy->attached_dev = edev; + + ret = phy_init_hw(phy); + if (ret) + return ret; + + /* Sanitize settings based on PHY capabilities */ + if ((phy->supported & SUPPORTED_Autoneg) == 0) + phy->autoneg = AUTONEG_DISABLE; + + phy_config_aneg(edev->phydev); + + phy->adjust_link = adjust_link; + + return 0; +} + /* Automatically gets and returns the PHY device */ int phy_device_connect(struct eth_device *edev, struct mii_bus *bus, int addr, void (*adjust_link) (struct eth_device *edev), u32 flags, phy_interface_t interface) { - struct phy_device* dev = NULL; + struct phy_device *phy; unsigned int i; int ret = -EINVAL; @@ -275,59 +311,36 @@ int phy_device_connect(struct eth_device *edev, struct mii_bus *bus, int addr, } if (addr >= 0) { - dev = mdiobus_scan(bus, addr); - if (IS_ERR(dev)) { + phy = mdiobus_scan(bus, addr); + if (IS_ERR(phy)) { ret = -EIO; - goto fail; - } - } else { - for (i = 0; i < PHY_MAX_ADDR && !edev->phydev; i++) { - /* skip masked out PHY addresses */ - if (bus->phy_mask & (1 << i)) - continue; - - dev = mdiobus_scan(bus, i); - if (!IS_ERR(dev) && !dev->attached_dev) - break; + goto out; } - if (IS_ERR(dev)) { - ret = PTR_ERR(dev); - goto fail; - } + ret = phy_device_attach(phy, edev, adjust_link, flags, interface); + + goto out; } - if (dev->attached_dev) - return -EBUSY; + for (i = 0; i < PHY_MAX_ADDR && !edev->phydev; i++) { + /* skip masked out PHY addresses */ + if (bus->phy_mask & (1 << i)) + continue; - dev->interface = interface; - dev->dev_flags = flags; + phy = mdiobus_scan(bus, i); + if (IS_ERR(phy)) + continue; - if (!dev->registered) { - ret = phy_register_device(dev); - if (ret) - goto fail; + ret = phy_device_attach(phy, edev, adjust_link, flags, interface); + + goto out; } - edev->phydev = dev; - dev->attached_dev = edev; - - ret = phy_init_hw(dev); + ret = -ENODEV; +out: if (ret) - goto fail; + puts("Unable to find a PHY (unknown ID?)\n"); - /* Sanitize settings based on PHY capabilities */ - if ((dev->supported & SUPPORTED_Autoneg) == 0) - dev->autoneg = AUTONEG_DISABLE; - - phy_config_aneg(edev->phydev); - - dev->adjust_link = adjust_link; - - return 0; - -fail: - puts("Unable to find a PHY (unknown ID?)\n"); return ret; } From b90dc180542602cb7581cff56f21fc90eb66fa68 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 20 May 2014 15:20:02 +0200 Subject: [PATCH 02/12] net: phy: move setting of phy_map to phy_register_device The phy_map should be valid once a phy_device is registered. This allows registering phys outside of mdiobus_scan. Signed-off-by: Sascha Hauer --- drivers/net/phy/mdio_bus.c | 2 -- drivers/net/phy/phy.c | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 895ead0a5..5c4dea4e8 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -114,8 +114,6 @@ struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr) if (IS_ERR(phydev)) return phydev; - bus->phy_map[addr] = phydev; - return phydev; } EXPORT_SYMBOL(mdiobus_scan); diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index a8a8a2ce5..1e26e455f 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -245,6 +245,8 @@ int phy_register_device(struct phy_device* dev) if (ret) return ret; + dev->bus->phy_map[dev->addr] = dev; + dev->registered = 1; if (dev->dev.driver) From 9b259eb3fd9c025d8a94d8ef682f96eb90cfdec8 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 21 May 2014 11:38:14 +0200 Subject: [PATCH 03/12] net: phy: register phys specified in devicetree When a mdio bus has a device node attached then register the phys specified there. This makes it possible to lookup the phys using phandles later. Signed-off-by: Sascha Hauer --- drivers/net/phy/mdio_bus.c | 69 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 5c4dea4e8..ca016736d 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -49,6 +49,71 @@ int mdiobus_detect(struct device_d *dev) return 0; } +static int of_mdiobus_register_phy(struct mii_bus *mdio, struct device_node *child, + u32 addr) +{ + struct phy_device *phy; + int ret; + + phy = get_phy_device(mdio, addr); + if (IS_ERR(phy)) + return PTR_ERR(phy); + + /* + * Associate the OF node with the device structure so it + * can be looked up later + */ + phy->dev.device_node = child; + + /* + * All data is now stored in the phy struct; + * register it + */ + ret = phy_register_device(phy); + if (ret) + return ret; + + dev_dbg(&mdio->dev, "registered phy %s at address %i\n", + child->name, addr); + + return 0; +} + +/** + * of_mdiobus_register - Register mii_bus and create PHYs from the device tree + * @mdio: pointer to mii_bus structure + * @np: pointer to device_node of MDIO bus. + * + * This function registers the mii_bus structure and registers a phy_device + * for each child node of @np. + */ +static int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) +{ + struct device_node *child; + u32 addr; + int ret; + + /* Loop over the child nodes and register a phy_device for each one */ + for_each_available_child_of_node(np, child) { + ret = of_property_read_u32(child, "reg", &addr); + if (ret) { + dev_err(&mdio->dev, "%s has invalid PHY address\n", + child->full_name); + continue; + } + + if (addr >= PHY_MAX_ADDR) { + dev_err(&mdio->dev, "%s PHY address %i is too large\n", + child->full_name, addr); + continue; + } + + of_mdiobus_register_phy(mdio, child, addr); + } + + return 0; +} + /** * mdiobus_register - bring up all the PHYs on a given bus and attach them to bus * @bus: target mii_bus @@ -85,6 +150,10 @@ int mdiobus_register(struct mii_bus *bus) list_add_tail(&bus->list, &mii_bus_list); pr_info("%s: probed\n", dev_name(&bus->dev)); + + if (bus->dev.device_node) + of_mdiobus_register(bus, bus->dev.device_node); + return 0; } EXPORT_SYMBOL(mdiobus_register); From 9cbfe615f9a0784dc1186a9cc117bc40111d0db9 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 21 May 2014 11:53:28 +0200 Subject: [PATCH 04/12] net: phy: Support finding a phy in the devicetree When the ethernet device has a device_node then try finding the associated phy via the phy-handle property. This makes the phy handling via devicetree transparent to the ethernet drivers. Signed-off-by: Sascha Hauer --- drivers/net/phy/phy.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 1e26e455f..257df7437 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -262,6 +262,29 @@ int phy_register_device(struct phy_device* dev) return ret; } +static struct phy_device *of_mdio_find_phy(struct eth_device *edev) +{ + struct device_d *dev; + struct device_node *phy_node; + + if (!IS_ENABLED(CONFIG_OFDEVICE)) + return NULL; + + if (!edev->parent->device_node) + return NULL; + + phy_node = of_parse_phandle(edev->parent->device_node, "phy-handle", 0); + if (!phy_node) + return NULL; + + bus_for_each_device(&mdio_bus_type, dev) { + if (dev->device_node == phy_node) + return container_of(dev, struct phy_device, dev); + } + + return NULL; +} + static int phy_device_attach(struct phy_device *phy, struct eth_device *edev, void (*adjust_link) (struct eth_device *edev), u32 flags, phy_interface_t interface) @@ -312,6 +335,13 @@ int phy_device_connect(struct eth_device *edev, struct mii_bus *bus, int addr, return 0; } + phy = of_mdio_find_phy(edev); + if (phy) { + ret = phy_device_attach(phy, edev, adjust_link, flags, interface); + + goto out; + } + if (addr >= 0) { phy = mdiobus_scan(bus, addr); if (IS_ERR(phy)) { From 4778c7da3786cab7ee6c453fa19149e8b995bdf3 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 21 May 2014 11:56:12 +0200 Subject: [PATCH 05/12] net: phy: Support limiting phy speed in the devicetree Even when both the ethernet controller and the phy support certain features a board may have additional limitations. Allow specifying it in the devicetree. Signed-off-by: Sascha Hauer --- drivers/net/phy/mdio_bus.c | 36 +++++++++++++++++++++++++++++++++++- include/linux/phy.h | 22 +++++++++++++++------- 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index ca016736d..f526cfcd8 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -242,6 +242,39 @@ static struct file_operations phydev_ops = { .lseek = dev_lseek_default, }; +static void of_set_phy_supported(struct phy_device *phydev) +{ + struct device_node *node = phydev->dev.device_node; + u32 max_speed; + + if (!IS_ENABLED(CONFIG_OFDEVICE)) + return; + + if (!node) + return; + + if (!of_property_read_u32(node, "max-speed", &max_speed)) { + /* + * The default values for phydev->supported are provided by the PHY + * driver "features" member, we want to reset to sane defaults first + * before supporting higher speeds. + */ + phydev->supported &= PHY_DEFAULT_FEATURES; + + switch (max_speed) { + default: + return; + + case SPEED_1000: + phydev->supported |= PHY_1000BT_FEATURES; + case SPEED_100: + phydev->supported |= PHY_100BT_FEATURES; + case SPEED_10: + phydev->supported |= PHY_10BT_FEATURES; + } + } +} + static int mdio_bus_probe(struct device_d *_dev) { struct phy_device *dev = to_phy_device(_dev); @@ -275,7 +308,8 @@ static int mdio_bus_probe(struct device_d *_dev) * a controller will attach, and may modify one * or both of these values */ dev->supported = drv->features; - dev->advertising = drv->features; + of_set_phy_supported(dev); + dev->advertising = dev->supported; dev_add_param_int_ro(&dev->dev, "phy_addr", dev->addr, "%d"); dev_add_param_int_ro(&dev->dev, "phy_id", dev->phy_id, "0x%08x"); diff --git a/include/linux/phy.h b/include/linux/phy.h index 9567c43e3..3cdbc6ebf 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -20,18 +20,26 @@ #include #include -#define PHY_BASIC_FEATURES (SUPPORTED_10baseT_Half | \ - SUPPORTED_10baseT_Full | \ - SUPPORTED_100baseT_Half | \ - SUPPORTED_100baseT_Full | \ - SUPPORTED_Autoneg | \ +#define PHY_DEFAULT_FEATURES (SUPPORTED_Autoneg | \ SUPPORTED_TP | \ SUPPORTED_MII) -#define PHY_GBIT_FEATURES (PHY_BASIC_FEATURES | \ - SUPPORTED_1000baseT_Half | \ +#define PHY_10BT_FEATURES (SUPPORTED_10baseT_Half | \ + SUPPORTED_10baseT_Full) + +#define PHY_100BT_FEATURES (SUPPORTED_100baseT_Half | \ + SUPPORTED_100baseT_Full) + +#define PHY_1000BT_FEATURES (SUPPORTED_1000baseT_Half | \ SUPPORTED_1000baseT_Full) +#define PHY_BASIC_FEATURES (PHY_10BT_FEATURES | \ + PHY_100BT_FEATURES | \ + PHY_DEFAULT_FEATURES) + +#define PHY_GBIT_FEATURES (PHY_BASIC_FEATURES | \ + PHY_1000BT_FEATURES) + /* Interface Mode definitions */ typedef enum { PHY_INTERFACE_MODE_NA, From 7240f56d360b369f5e20fc260ede31043f193efa Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 21 May 2014 12:13:57 +0200 Subject: [PATCH 06/12] net: orion-gbe: use transparent-to-driver of mdio functions barebox can transparently handle phys specified in the devicetree. Use this functionality in the orion-gbe driver. This requires: - add a device to the orion-gbe ports. This has the port device_node attached and is set as the parent of the ethernet device so that the ethernet code finds the correct device_node - In the mdio-mvebu driver attach the device_node of the mdio device to the miibus device so that the phy code finds the correct node - call phy_device_connect instead of of_phy_device_connect. Signed-off-by: Sascha Hauer Tested-by: Sebastian Hesselbarth --- drivers/net/orion-gbe.c | 62 ++++++++++++++++++++---------------- drivers/net/phy/mdio-mvebu.c | 1 + 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/drivers/net/orion-gbe.c b/drivers/net/orion-gbe.c index 00f5e543c..85db17cf1 100644 --- a/drivers/net/orion-gbe.c +++ b/drivers/net/orion-gbe.c @@ -56,6 +56,7 @@ struct txdesc { }; struct port_priv { + struct device_d dev; struct eth_device edev; void __iomem *regs; struct device_node *np; @@ -64,6 +65,7 @@ struct port_priv { struct rxdesc *rxdesc; struct rxdesc *current_rxdesc; u8 *rxbuf; + phy_interface_t intf; }; struct orion_gbe { @@ -351,16 +353,6 @@ static int port_get_ethaddr(struct eth_device *edev, unsigned char *mac) return 0; } -static int port_open(struct eth_device *edev) -{ - struct port_priv *port = edev->priv; - - /* enable receive queue */ - writel(BIT(URXQ), port->regs + PORT_RQC); - - return 0; -} - static void port_adjust_link(struct eth_device *edev) { struct port_priv *port = edev->priv; @@ -389,10 +381,25 @@ static void port_adjust_link(struct eth_device *edev) writel(reg, port->regs + PORT_SC0); } +static int port_open(struct eth_device *edev) +{ + struct port_priv *port = edev->priv; + int ret; + + ret = phy_device_connect(&port->edev, NULL, -1, port_adjust_link, 0, + port->intf); + if (ret) + return ret; + + /* enable receive queue */ + writel(BIT(URXQ), port->regs + PORT_RQC); + + return 0; +} + static int port_probe(struct device_d *parent, struct port_priv *port) { - struct device_node *phynp; - phy_interface_t intf = PHY_INTERFACE_MODE_RGMII; + struct device_d *dev = &port->dev; u32 reg; int ret; @@ -400,12 +407,11 @@ static int port_probe(struct device_d *parent, struct port_priv *port) if (of_property_read_u32(port->np, "reg", &port->portno)) dev_warn(parent, "port node is missing reg property\n"); - phynp = of_parse_phandle(port->np, "phy-handle", 0); - if (phynp) { - ret = of_get_phy_mode(port->np); - if (ret > 0) - intf = ret; - } + ret = of_get_phy_mode(port->np); + if (ret > 0) + port->intf = ret; + else + port->intf = PHY_INTERFACE_MODE_RGMII; port->regs = dev_get_mem_region(parent, 0) + PORTn_REGS(port->portno); @@ -440,10 +446,18 @@ static int port_probe(struct device_d *parent, struct port_priv *port) reg = SC1_RESERVED; reg |= DEFAULT_COL_LIMIT | COL_ON_BACKPRESS | INBAND_ANEG_BYPASS; - if (intf == PHY_INTERFACE_MODE_RGMII) + if (port->intf == PHY_INTERFACE_MODE_RGMII) reg |= RGMII_ENABLE; writel(reg, port->regs + PORT_SC1); + sprintf(dev->name, "orion-gbe-port"); + dev->id = port->portno; + dev->parent = parent; + dev->device_node = port->np; + ret = register_device(dev); + if (ret) + return ret; + /* register eth device */ port->edev.priv = port; port->edev.open = port_open; @@ -452,20 +466,12 @@ static int port_probe(struct device_d *parent, struct port_priv *port) port->edev.halt = port_halt; port->edev.set_ethaddr = port_set_ethaddr; port->edev.get_ethaddr = port_get_ethaddr; - port->edev.parent = parent; + port->edev.parent = dev; ret = eth_register(&port->edev); if (ret) return ret; - /* attach phy device */ - if (phynp) { - ret = of_phy_device_connect(&port->edev, phynp, - port_adjust_link, 0, intf); - if (ret) - return ret; - } - return 0; } diff --git a/drivers/net/phy/mdio-mvebu.c b/drivers/net/phy/mdio-mvebu.c index f8b492a24..3dcf644ec 100644 --- a/drivers/net/phy/mdio-mvebu.c +++ b/drivers/net/phy/mdio-mvebu.c @@ -120,6 +120,7 @@ static int mvebu_mdio_probe(struct device_d *dev) if (!IS_ERR(priv->clk)) clk_enable(priv->clk); + priv->miibus.dev.device_node = dev->device_node; priv->miibus.priv = priv; priv->miibus.parent = dev; priv->miibus.read = mvebu_mdio_read; From 75635f6926d2a3dcadd9b9c026aa21a16e113ee3 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 21 May 2014 12:22:21 +0200 Subject: [PATCH 07/12] net: phy: remove now unused of_phy_device_connect Since barebox handles phys from devicetree transparently we no longer need of_phy_device_connect. Signed-off-by: Sascha Hauer --- drivers/net/phy/phy.c | 31 ------------------------------- include/linux/phy.h | 14 -------------- 2 files changed, 45 deletions(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 257df7437..b0e0c1e36 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -376,37 +376,6 @@ out: return ret; } -#if defined(CONFIG_OFTREE) -int of_phy_device_connect(struct eth_device *edev, struct device_node *phy_np, - void (*adjust_link) (struct eth_device *edev), - u32 flags, phy_interface_t interface) -{ - struct device_node *bus_np; - struct mii_bus *miibus; - int phy_addr = -ENODEV; - - if (!phy_np) - return -EINVAL; - - of_property_read_u32(phy_np, "reg", &phy_addr); - - bus_np = of_get_parent(phy_np); - if (!bus_np) - return -ENODEV; - - for_each_mii_bus(miibus) { - if (miibus->parent && miibus->parent->device_node == bus_np) - return phy_device_connect(edev, miibus, phy_addr, - adjust_link, flags, interface); - } - - dev_err(&edev->dev, "unable to mdio bus for phy %s\n", - phy_np->full_name); - - return -ENODEV; -} -#endif - /* Generic PHY support and helper functions */ /** diff --git a/include/linux/phy.h b/include/linux/phy.h index 3cdbc6ebf..3229aa630 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -288,20 +288,6 @@ int phy_device_connect(struct eth_device *dev, struct mii_bus *bus, int addr, void (*adjust_link) (struct eth_device *edev), u32 flags, phy_interface_t interface); -#if defined(CONFIG_OFTREE) -int of_phy_device_connect(struct eth_device *edev, struct device_node *phy_np, - void (*adjust_link) (struct eth_device *edev), - u32 flags, phy_interface_t interface); -#else -static inline int of_phy_device_connect(struct eth_device *edev, - struct device_node *phy_np, - void (*adjust_link) (struct eth_device *edev), - u32 flags, phy_interface_t interface) -{ - return -ENOSYS; -} -#endif - int phy_update_status(struct phy_device *phydev); int phy_wait_aneg_done(struct phy_device *phydev); From e2eb6e50f45ef078a6bda48acb3aea4b63e49237 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 21 May 2014 12:53:11 +0200 Subject: [PATCH 08/12] net: phy: genphy: always write MII_CTRL1000 when available the phydev->supported field does not necessarily match the hardware capabilities, it could be limited by the board to force the phy to a lower speed. In this case make sure the gigabit advertise bits are cleared on a gigabit phy. Signed-off-by: Sascha Hauer --- drivers/net/phy/phy.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index b0e0c1e36..e214c1321 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -390,7 +390,7 @@ out: int genphy_config_advert(struct phy_device *phydev) { u32 advertise; - int oldadv, adv; + int oldadv, adv, bmsr; int err, changed = 0; /* Only allow advertising what @@ -417,8 +417,11 @@ int genphy_config_advert(struct phy_device *phydev) } /* Configure gigabit if it's supported */ - if (phydev->supported & (SUPPORTED_1000baseT_Half | - SUPPORTED_1000baseT_Full)) { + bmsr = phy_read(phydev, MII_BMSR); + if (bmsr < 0) + return bmsr; + + if (bmsr & BMSR_ESTATEN) { oldadv = adv = phy_read(phydev, MII_CTRL1000); if (adv < 0) From 1607f679dab49bf5434f0e758247feaca92a50d5 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 21 May 2014 13:38:10 +0200 Subject: [PATCH 09/12] net: phy: genphy: Make it work with of_set_phy_supported phys start with features initialized from the phy driver and are eventually limited by of_set_phy_supported. This does not work with the genphy driver which starts with no features and overwrites phydev->supported with the values read from hardware in config_init. This overwrites the features adjusted by of_set_phy_supported. To fix this let the genphy driver start with full features which are then only limited in config_init, but never extended. Signed-off-by: Sascha Hauer --- drivers/net/phy/phy.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index e214c1321..517ed583b 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -805,8 +805,8 @@ static int genphy_config_init(struct phy_device *phydev) features |= SUPPORTED_1000baseT_Half; } - phydev->supported = features; - phydev->advertising = features; + phydev->supported &= features; + phydev->advertising &= features; return 0; } @@ -858,7 +858,9 @@ static struct phy_driver genphy_driver = { .drv.name = "Generic PHY", .phy_id = PHY_ANY_UID, .phy_id_mask = PHY_ANY_UID, - .features = 0, + .features = PHY_GBIT_FEATURES | SUPPORTED_MII | + SUPPORTED_AUI | SUPPORTED_FIBRE | + SUPPORTED_BNC, }; static int generic_phy_register(void) From 0955253ce1de3fd34c711cc351b96975c2fdc673 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 21 May 2014 11:58:50 +0200 Subject: [PATCH 10/12] net: fec_imx: Add devicetree support for mdio bus The fec has a mdio bus. This adds support for a subnode in which the phys on this bus can be specified. Signed-off-by: Sascha Hauer --- drivers/net/fec_imx.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/fec_imx.c b/drivers/net/fec_imx.c index 72e689dcb..cc2a33174 100644 --- a/drivers/net/fec_imx.c +++ b/drivers/net/fec_imx.c @@ -621,6 +621,7 @@ static int fec_alloc_receive_packets(struct fec_priv *fec, int count, int size) #ifdef CONFIG_OFDEVICE static int fec_probe_dt(struct device_d *dev, struct fec_priv *fec) { + struct device_node *mdiobus; int ret; ret = of_get_phy_mode(dev->device_node); @@ -629,6 +630,10 @@ static int fec_probe_dt(struct device_d *dev, struct fec_priv *fec) else fec->interface = ret; + mdiobus = of_get_child_by_name(dev->device_node, "mdio"); + if (mdiobus) + fec->miibus.dev.device_node = mdiobus; + return 0; } #else From 54bbaafcbd5b797c28b45a16cfeb319b86660c41 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 20 May 2014 10:16:46 +0200 Subject: [PATCH 11/12] net: phy: micrel: Add kwz9031 support Signed-off-by: Sascha Hauer --- drivers/net/phy/micrel.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 3cba2c405..36c82d26b 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -169,6 +169,15 @@ static struct phy_driver ksphy_driver[] = { .config_init = kszphy_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, +}, { + .phy_id = PHY_ID_KSZ9031, + .phy_id_mask = 0x00fffff0, + .drv.name = "Micrel KSZ9031 Gigabit PHY", + .features = (PHY_GBIT_FEATURES | SUPPORTED_Pause + | SUPPORTED_Asym_Pause), + .config_init = kszphy_config_init, + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, }, { .phy_id = PHY_ID_KSZ8873MLL, .phy_id_mask = 0x00fffff0, From c3f5ce73083a1160fd60f0e2c7ad2c781874a212 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 21 May 2014 11:40:37 +0200 Subject: [PATCH 12/12] net: phy: micrel: Add support for specifying pad skew values Signed-off-by: Sascha Hauer --- drivers/net/phy/micrel.c | 86 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 36c82d26b..8aea6534b 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -37,6 +37,34 @@ #define KS8737_CTRL_INT_ACTIVE_HIGH (1 << 14) #define KSZ8051_RMII_50MHZ_CLK (1 << 7) +/* Write/read to/from extended registers */ +#define MII_KSZPHY_EXTREG 0x0b +#define KSZPHY_EXTREG_WRITE 0x8000 + +#define MII_KSZPHY_EXTREG_WRITE 0x0c +#define MII_KSZPHY_EXTREG_READ 0x0d + +/* Extended registers */ +#define MII_KSZPHY_CLK_CONTROL_PAD_SKEW 0x104 +#define MII_KSZPHY_RX_DATA_PAD_SKEW 0x105 +#define MII_KSZPHY_TX_DATA_PAD_SKEW 0x106 + +#define PS_TO_REG 200 + +static int kszphy_extended_write(struct phy_device *phydev, + u32 regnum, u16 val) +{ + phy_write(phydev, MII_KSZPHY_EXTREG, KSZPHY_EXTREG_WRITE | regnum); + return phy_write(phydev, MII_KSZPHY_EXTREG_WRITE, val); +} + +static int kszphy_extended_read(struct phy_device *phydev, + u32 regnum) +{ + phy_write(phydev, MII_KSZPHY_EXTREG, regnum); + return phy_read(phydev, MII_KSZPHY_EXTREG_READ); +} + static int kszphy_config_init(struct phy_device *phydev) { return 0; @@ -62,6 +90,62 @@ static int ks8051_config_init(struct phy_device *phydev) return 0; } +static int ksz9021_load_values_from_of(struct phy_device *phydev, + struct device_node *of_node, u16 reg, + const char *field[]) +{ + int val, regval, i; + + regval = kszphy_extended_read(phydev, reg); + + for (i = 0; i < 4; i++) { + int shift = i * 4; + + if (of_property_read_u32(of_node, field[i], &val)) + continue; + + regval &= ~(0xf << shift); + regval |= ((val / PS_TO_REG) & 0xf) << shift; + } + + return kszphy_extended_write(phydev, reg, regval); +} + +static int ksz9021_config_init(struct phy_device *phydev) +{ + struct device_d *dev = &phydev->dev; + struct device_node *of_node = dev->device_node; + const char *clk_pad_skew_names[] = { + "txen-skew-ps", "txc-skew-ps", + "rxdv-skew-ps", "rxc-skew-ps" + }; + const char *rx_pad_skew_names[] = { + "rxd0-skew-ps", "rxd1-skew-ps", + "rxd2-skew-ps", "rxd3-skew-ps" + }; + const char *tx_pad_skew_names[] = { + "txd0-skew-ps", "txd1-skew-ps", + "txd2-skew-ps", "txd3-skew-ps" + }; + + if (!of_node && dev->parent->device_node) + of_node = dev->parent->device_node; + + if (of_node) { + ksz9021_load_values_from_of(phydev, of_node, + MII_KSZPHY_CLK_CONTROL_PAD_SKEW, + clk_pad_skew_names); + ksz9021_load_values_from_of(phydev, of_node, + MII_KSZPHY_RX_DATA_PAD_SKEW, + rx_pad_skew_names); + ksz9021_load_values_from_of(phydev, of_node, + MII_KSZPHY_TX_DATA_PAD_SKEW, + tx_pad_skew_names); + } + + return 0; +} + #define KSZ8873MLL_GLOBAL_CONTROL_4 0x06 #define KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX (1 << 6) #define KSZ8873MLL_GLOBAL_CONTROL_4_SPEED (1 << 4) @@ -166,7 +250,7 @@ static struct phy_driver ksphy_driver[] = { .phy_id_mask = 0x000ffffe, .drv.name = "Micrel KSZ9021 Gigabit PHY", .features = (PHY_GBIT_FEATURES | SUPPORTED_Pause), - .config_init = kszphy_config_init, + .config_init = ksz9021_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, }, {