diff options
Diffstat (limited to 'drivers/net/phy')
-rw-r--r-- | drivers/net/phy/fixed.c | 33 | ||||
-rw-r--r-- | drivers/net/phy/mdio-bitbang.c | 4 | ||||
-rw-r--r-- | drivers/net/phy/mdio-ofgpio.c | 11 | ||||
-rw-r--r-- | drivers/net/phy/mdio_bus.c | 231 | ||||
-rw-r--r-- | drivers/net/phy/phy.c | 60 | ||||
-rw-r--r-- | drivers/net/phy/phy_device.c | 49 |
6 files changed, 256 insertions, 132 deletions
diff --git a/drivers/net/phy/fixed.c b/drivers/net/phy/fixed.c index 4e07956a483..cf24cc34deb 100644 --- a/drivers/net/phy/fixed.c +++ b/drivers/net/phy/fixed.c @@ -24,7 +24,7 @@ struct fixed_mdio_bus { int irqs[PHY_MAX_ADDR]; - struct mii_bus mii_bus; + struct mii_bus *mii_bus; struct list_head phys; }; @@ -115,8 +115,7 @@ static int fixed_phy_update_regs(struct fixed_phy *fp) static int fixed_mdio_read(struct mii_bus *bus, int phy_id, int reg_num) { - struct fixed_mdio_bus *fmb = container_of(bus, struct fixed_mdio_bus, - mii_bus); + struct fixed_mdio_bus *fmb = bus->priv; struct fixed_phy *fp; if (reg_num >= MII_REGS_NUM) @@ -213,19 +212,28 @@ static int __init fixed_mdio_bus_init(void) goto err_pdev; } - snprintf(fmb->mii_bus.id, MII_BUS_ID_SIZE, "0"); - fmb->mii_bus.name = "Fixed MDIO Bus"; - fmb->mii_bus.dev = &pdev->dev; - fmb->mii_bus.read = &fixed_mdio_read; - fmb->mii_bus.write = &fixed_mdio_write; - fmb->mii_bus.irq = fmb->irqs; + fmb->mii_bus = mdiobus_alloc(); + if (fmb->mii_bus == NULL) { + ret = -ENOMEM; + goto err_mdiobus_reg; + } - ret = mdiobus_register(&fmb->mii_bus); + snprintf(fmb->mii_bus->id, MII_BUS_ID_SIZE, "0"); + fmb->mii_bus->name = "Fixed MDIO Bus"; + fmb->mii_bus->priv = fmb; + fmb->mii_bus->parent = &pdev->dev; + fmb->mii_bus->read = &fixed_mdio_read; + fmb->mii_bus->write = &fixed_mdio_write; + fmb->mii_bus->irq = fmb->irqs; + + ret = mdiobus_register(fmb->mii_bus); if (ret) - goto err_mdiobus_reg; + goto err_mdiobus_alloc; return 0; +err_mdiobus_alloc: + mdiobus_free(fmb->mii_bus); err_mdiobus_reg: platform_device_unregister(pdev); err_pdev: @@ -238,7 +246,8 @@ static void __exit fixed_mdio_bus_exit(void) struct fixed_mdio_bus *fmb = &platform_fmb; struct fixed_phy *fp, *tmp; - mdiobus_unregister(&fmb->mii_bus); + mdiobus_unregister(fmb->mii_bus); + mdiobus_free(fmb->mii_bus); platform_device_unregister(pdev); list_for_each_entry_safe(fp, tmp, &fmb->phys, node) { diff --git a/drivers/net/phy/mdio-bitbang.c b/drivers/net/phy/mdio-bitbang.c index c01b78013dd..2576055b350 100644 --- a/drivers/net/phy/mdio-bitbang.c +++ b/drivers/net/phy/mdio-bitbang.c @@ -165,7 +165,7 @@ struct mii_bus *alloc_mdio_bitbang(struct mdiobb_ctrl *ctrl) { struct mii_bus *bus; - bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL); + bus = mdiobus_alloc(); if (!bus) return NULL; @@ -184,7 +184,7 @@ void free_mdio_bitbang(struct mii_bus *bus) struct mdiobb_ctrl *ctrl = bus->priv; module_put(ctrl->ops->owner); - kfree(bus); + mdiobus_free(bus); } EXPORT_SYMBOL(free_mdio_bitbang); diff --git a/drivers/net/phy/mdio-ofgpio.c b/drivers/net/phy/mdio-ofgpio.c index 7edfc0c3483..2ff97754e57 100644 --- a/drivers/net/phy/mdio-ofgpio.c +++ b/drivers/net/phy/mdio-ofgpio.c @@ -122,7 +122,7 @@ static int __devinit mdio_ofgpio_probe(struct of_device *ofdev, new_bus = alloc_mdio_bitbang(&bitbang->ctrl); if (!new_bus) - goto out_free_priv; + goto out_free_bitbang; new_bus->name = "GPIO Bitbanged MII", @@ -142,7 +142,7 @@ static int __devinit mdio_ofgpio_probe(struct of_device *ofdev, if (!strcmp(np->type, "ethernet-phy")) add_phy(new_bus, np); - new_bus->dev = &ofdev->dev; + new_bus->parent = &ofdev->dev; dev_set_drvdata(&ofdev->dev, new_bus); ret = mdiobus_register(new_bus); @@ -155,9 +155,9 @@ out_free_irqs: dev_set_drvdata(&ofdev->dev, NULL); kfree(new_bus->irq); out_free_bus: - kfree(new_bus); -out_free_priv: free_mdio_bitbang(new_bus); +out_free_bitbang: + kfree(bitbang); out: return ret; } @@ -168,11 +168,10 @@ static int mdio_ofgpio_remove(struct of_device *ofdev) struct mdio_gpio_info *bitbang = bus->priv; mdiobus_unregister(bus); + kfree(bus->irq); free_mdio_bitbang(bus); dev_set_drvdata(&ofdev->dev, NULL); - kfree(bus->irq); kfree(bitbang); - kfree(bus); return 0; } diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 94e0b7ed76f..6671e2da0d5 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -36,6 +36,42 @@ #include <asm/uaccess.h> /** + * mdiobus_alloc - allocate a mii_bus structure + * + * Description: called by a bus driver to allocate an mii_bus + * structure to fill in. + */ +struct mii_bus *mdiobus_alloc(void) +{ + struct mii_bus *bus; + + bus = kzalloc(sizeof(*bus), GFP_KERNEL); + if (bus != NULL) + bus->state = MDIOBUS_ALLOCATED; + + return bus; +} +EXPORT_SYMBOL(mdiobus_alloc); + +/** + * mdiobus_release - mii_bus device release callback + * + * Description: called when the last reference to an mii_bus is + * dropped, to free the underlying memory. + */ +static void mdiobus_release(struct device *d) +{ + struct mii_bus *bus = to_mii_bus(d); + BUG_ON(bus->state != MDIOBUS_RELEASED); + kfree(bus); +} + +static struct class mdio_bus_class = { + .name = "mdio_bus", + .dev_release = mdiobus_release, +}; + +/** * mdiobus_register - bring up all the PHYs on a given bus and attach them to bus * @bus: target mii_bus * @@ -54,55 +90,36 @@ int mdiobus_register(struct mii_bus *bus) NULL == bus->write) return -EINVAL; - mutex_init(&bus->mdio_lock); - - if (bus->reset) - bus->reset(bus); - - for (i = 0; i < PHY_MAX_ADDR; i++) { - struct phy_device *phydev; - - if (bus->phy_mask & (1 << i)) { - bus->phy_map[i] = NULL; - continue; - } - - phydev = get_phy_device(bus, i); + BUG_ON(bus->state != MDIOBUS_ALLOCATED && + bus->state != MDIOBUS_UNREGISTERED); - if (IS_ERR(phydev)) - return PTR_ERR(phydev); + bus->dev.parent = bus->parent; + bus->dev.class = &mdio_bus_class; + bus->dev.groups = NULL; + memcpy(bus->dev.bus_id, bus->id, MII_BUS_ID_SIZE); - /* There's a PHY at this address - * We need to set: - * 1) IRQ - * 2) bus_id - * 3) parent - * 4) bus - * 5) mii_bus - * And, we need to register it */ - if (phydev) { - phydev->irq = bus->irq[i]; + err = device_register(&bus->dev); + if (err) { + printk(KERN_ERR "mii_bus %s failed to register\n", bus->id); + return -EINVAL; + } - phydev->dev.parent = bus->dev; - phydev->dev.bus = &mdio_bus_type; - snprintf(phydev->dev.bus_id, BUS_ID_SIZE, PHY_ID_FMT, bus->id, i); + bus->state = MDIOBUS_REGISTERED; - phydev->bus = bus; + mutex_init(&bus->mdio_lock); - /* Run all of the fixups for this PHY */ - phy_scan_fixups(phydev); + if (bus->reset) + bus->reset(bus); - err = device_register(&phydev->dev); + for (i = 0; i < PHY_MAX_ADDR; i++) { + bus->phy_map[i] = NULL; + if ((bus->phy_mask & (1 << i)) == 0) { + struct phy_device *phydev; - if (err) { - printk(KERN_ERR "phy %d failed to register\n", - i); - phy_device_free(phydev); - phydev = NULL; - } + phydev = mdiobus_scan(bus, i); + if (IS_ERR(phydev)) + err = PTR_ERR(phydev); } - - bus->phy_map[i] = phydev; } pr_info("%s: probed\n", bus->name); @@ -115,6 +132,10 @@ void mdiobus_unregister(struct mii_bus *bus) { int i; + BUG_ON(bus->state != MDIOBUS_REGISTERED); + bus->state = MDIOBUS_UNREGISTERED; + + device_unregister(&bus->dev); for (i = 0; i < PHY_MAX_ADDR; i++) { if (bus->phy_map[i]) device_unregister(&bus->phy_map[i]->dev); @@ -123,6 +144,122 @@ void mdiobus_unregister(struct mii_bus *bus) EXPORT_SYMBOL(mdiobus_unregister); /** + * mdiobus_free - free a struct mii_bus + * @bus: mii_bus to free + * + * This function releases the reference to the underlying device + * object in the mii_bus. If this is the last reference, the mii_bus + * will be freed. + */ +void mdiobus_free(struct mii_bus *bus) +{ + /* + * For compatibility with error handling in drivers. + */ + if (bus->state == MDIOBUS_ALLOCATED) { + kfree(bus); + return; + } + + BUG_ON(bus->state != MDIOBUS_UNREGISTERED); + bus->state = MDIOBUS_RELEASED; + + put_device(&bus->dev); +} +EXPORT_SYMBOL(mdiobus_free); + +struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr) +{ + struct phy_device *phydev; + int err; + + phydev = get_phy_device(bus, addr); + if (IS_ERR(phydev) || phydev == NULL) + return phydev; + + /* There's a PHY at this address + * We need to set: + * 1) IRQ + * 2) bus_id + * 3) parent + * 4) bus + * 5) mii_bus + * And, we need to register it */ + + phydev->irq = bus->irq != NULL ? bus->irq[addr] : PHY_POLL; + + phydev->dev.parent = bus->parent; + phydev->dev.bus = &mdio_bus_type; + snprintf(phydev->dev.bus_id, BUS_ID_SIZE, PHY_ID_FMT, bus->id, addr); + + phydev->bus = bus; + + /* Run all of the fixups for this PHY */ + phy_scan_fixups(phydev); + + err = device_register(&phydev->dev); + if (err) { + printk(KERN_ERR "phy %d failed to register\n", addr); + phy_device_free(phydev); + phydev = NULL; + } + + bus->phy_map[addr] = phydev; + + return phydev; +} +EXPORT_SYMBOL(mdiobus_scan); + +/** + * mdiobus_read - Convenience function for reading a given MII mgmt register + * @bus: the mii_bus struct + * @addr: the phy address + * @regnum: register number to read + * + * NOTE: MUST NOT be called from interrupt context, + * because the bus read/write functions may wait for an interrupt + * to conclude the operation. + */ +int mdiobus_read(struct mii_bus *bus, int addr, u16 regnum) +{ + int retval; + + BUG_ON(in_interrupt()); + + mutex_lock(&bus->mdio_lock); + retval = bus->read(bus, addr, regnum); + mutex_unlock(&bus->mdio_lock); + + return retval; +} +EXPORT_SYMBOL(mdiobus_read); + +/** + * mdiobus_write - Convenience function for writing a given MII mgmt register + * @bus: the mii_bus struct + * @addr: the phy address + * @regnum: register number to write + * @val: value to write to @regnum + * + * NOTE: MUST NOT be called from interrupt context, + * because the bus read/write functions may wait for an interrupt + * to conclude the operation. + */ +int mdiobus_write(struct mii_bus *bus, int addr, u16 regnum, u16 val) +{ + int err; + + BUG_ON(in_interrupt()); + + mutex_lock(&bus->mdio_lock); + err = bus->write(bus, addr, regnum, val); + mutex_unlock(&bus->mdio_lock); + + return err; +} +EXPORT_SYMBOL(mdiobus_write); + +/** * mdio_bus_match - determine if given PHY driver supports the given PHY device * @dev: target PHY device * @drv: given PHY driver @@ -174,10 +311,20 @@ EXPORT_SYMBOL(mdio_bus_type); int __init mdio_bus_init(void) { - return bus_register(&mdio_bus_type); + int ret; + + ret = class_register(&mdio_bus_class); + if (!ret) { + ret = bus_register(&mdio_bus_type); + if (ret) + class_unregister(&mdio_bus_class); + } + + return ret; } void mdio_bus_exit(void) { + class_unregister(&mdio_bus_class); bus_unregister(&mdio_bus_type); } diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 45cc2914d34..df4e6257d4a 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -58,55 +58,6 @@ EXPORT_SYMBOL(phy_print_status); /** - * phy_read - Convenience function for reading a given PHY register - * @phydev: the phy_device struct - * @regnum: register number to read - * - * NOTE: MUST NOT be called from interrupt context, - * because the bus read/write functions may wait for an interrupt - * to conclude the operation. - */ -int phy_read(struct phy_device *phydev, u16 regnum) -{ - int retval; - struct mii_bus *bus = phydev->bus; - - BUG_ON(in_interrupt()); - - mutex_lock(&bus->mdio_lock); - retval = bus->read(bus, phydev->addr, regnum); - mutex_unlock(&bus->mdio_lock); - - return retval; -} -EXPORT_SYMBOL(phy_read); - -/** - * phy_write - Convenience function for writing a given PHY register - * @phydev: the phy_device struct - * @regnum: register number to write - * @val: value to write to @regnum - * - * NOTE: MUST NOT be called from interrupt context, - * because the bus read/write functions may wait for an interrupt - * to conclude the operation. - */ -int phy_write(struct phy_device *phydev, u16 regnum, u16 val) -{ - int err; - struct mii_bus *bus = phydev->bus; - - BUG_ON(in_interrupt()); - - mutex_lock(&bus->mdio_lock); - err = bus->write(bus, phydev->addr, regnum, val); - mutex_unlock(&bus->mdio_lock); - - return err; -} -EXPORT_SYMBOL(phy_write); - -/** * phy_clear_interrupt - Ack the phy device's interrupt * @phydev: the phy_device struct * @@ -366,7 +317,8 @@ int phy_mii_ioctl(struct phy_device *phydev, switch (cmd) { case SIOCGMIIPHY: mii_data->phy_id = phydev->addr; - break; + /* fall through */ + case SIOCGMIIREG: mii_data->val_out = phy_read(phydev, mii_data->reg_num); break; @@ -413,7 +365,7 @@ int phy_mii_ioctl(struct phy_device *phydev, break; default: - return -ENOTTY; + return -EOPNOTSUPP; } return 0; @@ -728,6 +680,12 @@ static void phy_change(struct work_struct *work) if (err) goto irq_enable_err; + /* Stop timer and run the state queue now. The work function for + * state_queue will start the timer up again. + */ + del_timer(&phydev->phy_timer); + schedule_work(&phydev->state_queue); + return; irq_enable_err: diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 16a0e7de588..17162748005 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -419,13 +419,14 @@ EXPORT_SYMBOL(phy_detach); * * Description: Writes MII_ADVERTISE with the appropriate values, * after sanitizing the values to make sure we only advertise - * what is supported. + * what is supported. Returns < 0 on error, 0 if the PHY's advertisement + * hasn't changed, and > 0 if it has changed. */ int genphy_config_advert(struct phy_device *phydev) { u32 advertise; - int adv; - int err; + int oldadv, adv; + int err, changed = 0; /* Only allow advertising what * this PHY supports */ @@ -433,7 +434,7 @@ int genphy_config_advert(struct phy_device *phydev) advertise = phydev->advertising; /* Setup standard advertisement */ - adv = phy_read(phydev, MII_ADVERTISE); + oldadv = adv = phy_read(phydev, MII_ADVERTISE); if (adv < 0) return adv; @@ -453,15 +454,18 @@ int genphy_config_advert(struct phy_device *phydev) if (advertise & ADVERTISED_Asym_Pause) adv |= ADVERTISE_PAUSE_ASYM; - err = phy_write(phydev, MII_ADVERTISE, adv); + if (adv != oldadv) { + err = phy_write(phydev, MII_ADVERTISE, adv); - if (err < 0) - return err; + if (err < 0) + return err; + changed = 1; + } /* Configure gigabit if it's supported */ if (phydev->supported & (SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full)) { - adv = phy_read(phydev, MII_CTRL1000); + oldadv = adv = phy_read(phydev, MII_CTRL1000); if (adv < 0) return adv; @@ -471,13 +475,17 @@ int genphy_config_advert(struct phy_device *phydev) adv |= ADVERTISE_1000HALF; if (advertise & SUPPORTED_1000baseT_Full) adv |= ADVERTISE_1000FULL; - err = phy_write(phydev, MII_CTRL1000, adv); - if (err < 0) - return err; + if (adv != oldadv) { + err = phy_write(phydev, MII_CTRL1000, adv); + + if (err < 0) + return err; + changed = 1; + } } - return adv; + return changed; } EXPORT_SYMBOL(genphy_config_advert); @@ -561,19 +569,22 @@ int genphy_restart_aneg(struct phy_device *phydev) */ int genphy_config_aneg(struct phy_device *phydev) { - int err = 0; + int result = 0; if (AUTONEG_ENABLE == phydev->autoneg) { - err = genphy_config_advert(phydev); + int result = genphy_config_advert(phydev); - if (err < 0) - return err; + if (result < 0) /* error */ + return result; - err = genphy_restart_aneg(phydev); + /* Only restart aneg if we are advertising something different + * than we were before. */ + if (result > 0) + result = genphy_restart_aneg(phydev); } else - err = genphy_setup_forced(phydev); + result = genphy_setup_forced(phydev); - return err; + return result; } EXPORT_SYMBOL(genphy_config_aneg); |