--- zzzz-none-000/linux-3.10.107/drivers/net/phy/phy_device.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/drivers/net/phy/phy_device.c 2021-02-04 17:41:59.000000000 +0000 @@ -1,7 +1,4 @@ -/* - * drivers/net/phy/phy_device.c - * - * Framework for finding and configuring PHYs. +/* Framework for finding and configuring PHYs. * Also contains generic PHY driver * * Author: Andy Fleming @@ -33,10 +30,12 @@ #include #include #include +#include +#include +#include +#include -#include #include -#include MODULE_DESCRIPTION("PHY library"); MODULE_AUTHOR("Andy Fleming"); @@ -53,31 +52,31 @@ kfree(to_phy_device(dev)); } -static struct phy_driver genphy_driver; -extern int mdio_bus_init(void); -extern void mdio_bus_exit(void); +enum genphy_driver { + GENPHY_DRV_1G, + GENPHY_DRV_10G, + GENPHY_DRV_MAX +}; + +static struct phy_driver genphy_driver[GENPHY_DRV_MAX]; static LIST_HEAD(phy_fixup_list); static DEFINE_MUTEX(phy_fixup_lock); -static int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, - u32 flags, phy_interface_t interface); - -/* - * Creates a new phy_fixup and adds it to the list +/** + * phy_register_fixup - creates a new phy_fixup and adds it to the list * @bus_id: A string which matches phydev->dev.bus_id (or PHY_ANY_ID) * @phy_uid: Used to match against phydev->phy_id (the UID of the PHY) - * It can also be PHY_ANY_UID + * It can also be PHY_ANY_UID * @phy_uid_mask: Applied to phydev->phy_id and fixup->phy_uid before - * comparison + * comparison * @run: The actual code to be run when a matching PHY is found */ int phy_register_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask, - int (*run)(struct phy_device *)) + int (*run)(struct phy_device *)) { - struct phy_fixup *fixup; + struct phy_fixup *fixup = kzalloc(sizeof(*fixup), GFP_KERNEL); - fixup = kzalloc(sizeof(struct phy_fixup), GFP_KERNEL); if (!fixup) return -ENOMEM; @@ -96,7 +95,7 @@ /* Registers a fixup to be run on any PHY with the UID in phy_uid */ int phy_register_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask, - int (*run)(struct phy_device *)) + int (*run)(struct phy_device *)) { return phy_register_fixup(PHY_ANY_ID, phy_uid, phy_uid_mask, run); } @@ -104,14 +103,13 @@ /* Registers a fixup to be run on the PHY with id string bus_id */ int phy_register_fixup_for_id(const char *bus_id, - int (*run)(struct phy_device *)) + int (*run)(struct phy_device *)) { return phy_register_fixup(bus_id, PHY_ANY_UID, 0xffffffff, run); } EXPORT_SYMBOL(phy_register_fixup_for_id); -/* - * Returns 1 if fixup matches phydev in bus_id and phy_uid. +/* Returns 1 if fixup matches phydev in bus_id and phy_uid. * Fixups can be set to match any in one or more fields. */ static int phy_needs_fixup(struct phy_device *phydev, struct phy_fixup *fixup) @@ -121,7 +119,7 @@ return 0; if ((fixup->phy_uid & fixup->phy_uid_mask) != - (phydev->phy_id & fixup->phy_uid_mask)) + (phydev->phy_id & fixup->phy_uid_mask)) if (fixup->phy_uid != PHY_ANY_UID) return 0; @@ -129,46 +127,44 @@ } /* Runs any matching fixups for this phydev */ -int phy_scan_fixups(struct phy_device *phydev) +static int phy_scan_fixups(struct phy_device *phydev) { struct phy_fixup *fixup; mutex_lock(&phy_fixup_lock); list_for_each_entry(fixup, &phy_fixup_list, list) { if (phy_needs_fixup(phydev, fixup)) { - int err; - - err = fixup->run(phydev); + int err = fixup->run(phydev); if (err < 0) { mutex_unlock(&phy_fixup_lock); return err; } + phydev->has_fixups = true; } } mutex_unlock(&phy_fixup_lock); return 0; } -EXPORT_SYMBOL(phy_scan_fixups); 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) + bool is_c45, + struct phy_c45_device_ids *c45_ids) { struct phy_device *dev; - /* We allocate the device, and initialize the - * default values */ + /* We allocate the device, and initialize the default values */ dev = kzalloc(sizeof(*dev), GFP_KERNEL); - - if (NULL == dev) - return (struct phy_device*) PTR_ERR((void*)-ENOMEM); + if (!dev) + return ERR_PTR(-ENOMEM); dev->dev.release = phy_device_release; dev->speed = 0; dev->duplex = -1; - dev->pause = dev->asym_pause = 0; + dev->pause = 0; + dev->asym_pause = 0; dev->link = 1; dev->interface = PHY_INTERFACE_MODE_GMII; @@ -180,25 +176,27 @@ if (c45_ids) dev->c45_ids = *c45_ids; dev->bus = bus; - dev->dev.parent = bus->parent; + dev->dev.parent = &bus->dev; dev->dev.bus = &mdio_bus_type; - dev->irq = bus->irq != NULL ? bus->irq[addr] : PHY_POLL; + dev->irq = bus->irq ? bus->irq[addr] : PHY_POLL; dev_set_name(&dev->dev, PHY_ID_FMT, bus->id, addr); dev->state = PHY_DOWN; mutex_init(&dev->lock); INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine); + INIT_WORK(&dev->phy_queue, phy_change); /* Request the appropriate module unconditionally; don't - bother trying to do so only if it isn't already loaded, - because that gets complicated. A hotplug event would have - done an unconditional modprobe anyway. - We don't do normal hotplug because it won't work for MDIO - -- because it relies on the device staying around for long - enough for the driver to get loaded. With MDIO, the NIC - driver will get bored and give up as soon as it finds that - there's no driver _already_ loaded. */ + * bother trying to do so only if it isn't already loaded, + * because that gets complicated. A hotplug event would have + * done an unconditional modprobe anyway. + * We don't do normal hotplug because it won't work for MDIO + * -- because it relies on the device staying around for long + * enough for the driver to get loaded. With MDIO, the NIC + * driver will get bored and give up as soon as it finds that + * there's no driver _already_ loaded. + */ request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT, MDIO_ID_ARGS(phy_id)); device_initialize(&dev->dev); @@ -207,6 +205,37 @@ } EXPORT_SYMBOL(phy_device_create); +/* get_phy_c45_devs_in_pkg - reads a MMD's devices in package registers. + * @bus: the target MII bus + * @addr: PHY address on the MII bus + * @dev_addr: MMD address in the PHY. + * @devices_in_package: where to store the devices in package information. + * + * Description: reads devices in package registers of a MMD at @dev_addr + * from PHY at @addr on @bus. + * + * Returns: 0 on success, -EIO on failure. + */ +static int get_phy_c45_devs_in_pkg(struct mii_bus *bus, int addr, int dev_addr, + u32 *devices_in_package) +{ + int phy_reg, reg_addr; + + reg_addr = MII_ADDR_C45 | dev_addr << 16 | MDIO_DEVS2; + phy_reg = mdiobus_read(bus, addr, reg_addr); + if (phy_reg < 0) + return -EIO; + *devices_in_package = (phy_reg & 0xffff) << 16; + + reg_addr = MII_ADDR_C45 | dev_addr << 16 | MDIO_DEVS1; + phy_reg = mdiobus_read(bus, addr, reg_addr); + if (phy_reg < 0) + return -EIO; + *devices_in_package |= (phy_reg & 0xffff); + + return 0; +} + /** * get_phy_c45_ids - reads the specified addr for its 802.3-c45 IDs. * @bus: the target MII bus @@ -225,31 +254,32 @@ int phy_reg; int i, reg_addr; const int num_ids = ARRAY_SIZE(c45_ids->device_ids); + u32 *devs = &c45_ids->devices_in_package; - /* Find first non-zero Devices In package. Device - * zero is reserved, so don't probe it. + /* Find first non-zero Devices In package. Device zero is reserved + * for 802.3 c45 complied PHYs, so don't probe it at first. */ - for (i = 1; - i < num_ids && c45_ids->devices_in_package == 0; - i++) { - reg_addr = MII_ADDR_C45 | i << 16 | 6; - phy_reg = mdiobus_read(bus, addr, reg_addr); + for (i = 1; i < num_ids && *devs == 0; i++) { + phy_reg = get_phy_c45_devs_in_pkg(bus, addr, i, devs); if (phy_reg < 0) return -EIO; - c45_ids->devices_in_package = (phy_reg & 0xffff) << 16; - reg_addr = MII_ADDR_C45 | i << 16 | 5; - phy_reg = mdiobus_read(bus, addr, reg_addr); - if (phy_reg < 0) - return -EIO; - c45_ids->devices_in_package |= (phy_reg & 0xffff); - - /* If mostly Fs, there is no device there, - * let's get out of here. - */ - if ((c45_ids->devices_in_package & 0x1fffffff) == 0x1fffffff) { - *phy_id = 0xffffffff; - return 0; + if ((*devs & 0x1fffffff) == 0x1fffffff) { + /* If mostly Fs, there is no device there, + * then let's continue to probe more, as some + * 10G PHYs have zero Devices In package, + * e.g. Cortina CS4315/CS4340 PHY. + */ + phy_reg = get_phy_c45_devs_in_pkg(bus, addr, 0, devs); + if (phy_reg < 0) + return -EIO; + /* no device there, let's get out of here */ + if ((*devs & 0x1fffffff) == 0x1fffffff) { + *phy_id = 0xffffffff; + return 0; + } else { + break; + } } } @@ -298,10 +328,8 @@ if (is_c45) return get_phy_c45_ids(bus, addr, phy_id, c45_ids); - /* Grab the bits from PHYIR1, and put them - * in the upper half */ + /* Grab the bits from PHYIR1, and put them in the upper half */ phy_reg = mdiobus_read(bus, addr, MII_PHYSID1); - if (phy_reg < 0) return -EIO; @@ -309,7 +337,6 @@ /* Grab the bits from PHYIR2, and put them in the lower half */ phy_reg = mdiobus_read(bus, addr, MII_PHYSID2); - if (phy_reg < 0) return -EIO; @@ -319,7 +346,8 @@ } /** - * get_phy_device - reads the specified PHY device and returns its @phy_device struct + * get_phy_device - reads the specified PHY device and returns its @phy_device + * struct * @bus: the target MII bus * @addr: PHY address on the MII bus * @is_c45: If true the PHY uses the 802.3 clause 45 protocol @@ -330,7 +358,6 @@ struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45) { struct phy_c45_device_ids c45_ids = {0}; - struct phy_device *dev = NULL; u32 phy_id = 0; int r; @@ -342,9 +369,7 @@ if ((phy_id & 0x1fffffff) == 0x1fffffff) return NULL; - dev = phy_device_create(bus, addr, phy_id, is_c45, &c45_ids); - - return dev; + return phy_device_create(bus, addr, phy_id, is_c45, &c45_ids); } EXPORT_SYMBOL(get_phy_device); @@ -356,14 +381,17 @@ { int err; - /* Don't register a phy if one is already registered at this - * address */ + /* Don't register a phy if one is already registered at this address */ if (phydev->bus->phy_map[phydev->addr]) return -EINVAL; phydev->bus->phy_map[phydev->addr] = phydev; /* Run all of the fixups for this PHY */ - phy_scan_fixups(phydev); + err = phy_scan_fixups(phydev); + if (err) { + pr_err("PHY %d failed to initialize\n", phydev->addr); + goto out; + } err = device_add(&phydev->dev); if (err) { @@ -380,6 +408,24 @@ EXPORT_SYMBOL(phy_device_register); /** + * phy_device_remove - Remove a previously registered phy device from the MDIO bus + * @phydev: phy_device structure to remove + * + * This doesn't free the phy_device itself, it merely reverses the effects + * of phy_device_register(). Use phy_device_free() to free the device + * after calling this function. + */ +void phy_device_remove(struct phy_device *phydev) +{ + struct mii_bus *bus = phydev->bus; + int addr = phydev->addr; + + device_del(&phydev->dev); + bus->phy_map[addr] = NULL; +} +EXPORT_SYMBOL(phy_device_remove); + +/** * phy_find_first - finds the first PHY device on the bus * @bus: the target MII bus */ @@ -408,7 +454,7 @@ * this function. */ static void phy_prepare_link(struct phy_device *phydev, - void (*handler)(struct net_device *)) + void (*handler)(struct net_device *)) { phydev->adjust_link = handler; } @@ -431,7 +477,7 @@ return rc; phy_prepare_link(phydev, handler); - phy_start_machine(phydev, NULL); + phy_start_machine(phydev); if (phydev->irq > 0) phy_start_interrupts(phydev); @@ -454,16 +500,17 @@ * choose to call only the subset of functions which provide * the desired functionality. */ -struct phy_device * phy_connect(struct net_device *dev, const char *bus_id, - void (*handler)(struct net_device *), - phy_interface_t interface) +struct phy_device *phy_connect(struct net_device *dev, const char *bus_id, + void (*handler)(struct net_device *), + phy_interface_t interface) { struct phy_device *phydev; struct device *d; int rc; /* Search the list of PHY devices on the mdio bus for the - * PHY with the requested name */ + * PHY with the requested name + */ d = bus_find_device_by_name(&mdio_bus_type, NULL, bus_id); if (!d) { pr_err("PHY %s not found\n", bus_id); @@ -480,7 +527,8 @@ EXPORT_SYMBOL(phy_connect); /** - * phy_disconnect - disable interrupts, stop state machine, and detach a PHY device + * phy_disconnect - disable interrupts, stop state machine, and detach a PHY + * device * @phydev: target phy_device struct */ void phy_disconnect(struct phy_device *phydev) @@ -489,26 +537,75 @@ phy_stop_interrupts(phydev); phy_stop_machine(phydev); - + phydev->adjust_link = NULL; phy_detach(phydev); } EXPORT_SYMBOL(phy_disconnect); -int phy_init_hw(struct phy_device *phydev) +/** + * phy_poll_reset - Safely wait until a PHY reset has properly completed + * @phydev: The PHY device to poll + * + * Description: According to IEEE 802.3, Section 2, Subsection 22.2.4.1.1, as + * published in 2008, a PHY reset may take up to 0.5 seconds. The MII BMCR + * register must be polled until the BMCR_RESET bit clears. + * + * Furthermore, any attempts to write to PHY registers may have no effect + * or even generate MDIO bus errors until this is complete. + * + * Some PHYs (such as the Marvell 88E1111) don't entirely conform to the + * standard and do not fully reset after the BMCR_RESET bit is set, and may + * even *REQUIRE* a soft-reset to properly restart autonegotiation. In an + * effort to support such broken PHYs, this function is separate from the + * standard phy_init_hw() which will zero all the other bits in the BMCR + * and reapply all driver-specific and board-specific fixups. + */ +static int phy_poll_reset(struct phy_device *phydev) { + /* Poll until the reset bit clears (50ms per retry == 0.6 sec) */ + unsigned int retries = 12; int ret; + do { + msleep(50); + ret = phy_read(phydev, MII_BMCR); + if (ret < 0) + return ret; + } while (ret & BMCR_RESET && --retries); + if (ret & BMCR_RESET) + return -ETIMEDOUT; + + /* Some chips (smsc911x) may still need up to another 1ms after the + * BMCR_RESET bit is cleared before they are usable. + */ + msleep(1); + return 0; +} + +int phy_init_hw(struct phy_device *phydev) +{ + int ret = 0; + if (!phydev->drv || !phydev->drv->config_init) return 0; + if (phydev->drv->soft_reset) + ret = phydev->drv->soft_reset(phydev); + else + ret = genphy_soft_reset(phydev); + + if (ret < 0) + return ret; + ret = phy_scan_fixups(phydev); if (ret < 0) return ret; return phydev->drv->config_init(phydev); } +EXPORT_SYMBOL(phy_init_hw); /** * phy_attach_direct - attach a network device to a given PHY device pointer @@ -519,38 +616,47 @@ * * Description: Called by drivers to attach to a particular PHY * device. The phy_device is found, and properly hooked up - * to the phy_driver. If no driver is attached, then the - * genphy_driver is used. The phy_device is given a ptr to + * to the phy_driver. If no driver is attached, then a + * generic driver is used. The phy_device is given a ptr to * the attaching device, and given a callback for link status * change. The phy_device is returned to the attaching driver. + * This function takes a reference on the phy device. */ -static int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, - u32 flags, phy_interface_t interface) +int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, + u32 flags, phy_interface_t interface) { + struct mii_bus *bus = phydev->bus; struct device *d = &phydev->dev; int err; - /* Assume that if there is no driver, that it doesn't - * exist, and we should use the genphy driver. */ - if (NULL == d->driver) { - if (phydev->is_c45) { - pr_err("No driver for phy %x\n", phydev->phy_id); - return -ENODEV; - } + if (!try_module_get(bus->owner)) { + dev_err(&dev->dev, "failed to get the bus module\n"); + return -EIO; + } - d->driver = &genphy_driver.driver; + get_device(d); + + /* Assume that if there is no driver, that it doesn't + * exist, and we should use the genphy driver. + */ + if (!d->driver) { + if (phydev->is_c45) + d->driver = &genphy_driver[GENPHY_DRV_10G].driver; + else + d->driver = &genphy_driver[GENPHY_DRV_1G].driver; err = d->driver->probe(d); if (err >= 0) err = device_bind_driver(d); if (err) - return err; + goto error; } if (phydev->attached_dev) { dev_err(&dev->dev, "PHY already attached\n"); - return -EBUSY; + err = -EBUSY; + goto error; } phydev->attached_dev = dev; @@ -564,13 +670,22 @@ /* Do initial configuration here, now that * we have certain key parameters - * (dev_flags and interface) */ + * (dev_flags and interface) + */ err = phy_init_hw(phydev); if (err) phy_detach(phydev); + else + phy_resume(phydev); + + return err; +error: + put_device(d); + module_put(bus->owner); return err; } +EXPORT_SYMBOL(phy_attach_direct); /** * phy_attach - attach a network device to a particular PHY device @@ -581,8 +696,8 @@ * Description: Same as phy_attach_direct() except that a PHY bus_id * string is passed instead of a pointer to a struct phy_device. */ -struct phy_device *phy_attach(struct net_device *dev, - const char *bus_id, phy_interface_t interface) +struct phy_device *phy_attach(struct net_device *dev, const char *bus_id, + phy_interface_t interface) { struct bus_type *bus = &mdio_bus_type; struct phy_device *phydev; @@ -590,7 +705,8 @@ int rc; /* Search the list of PHY devices on the mdio bus for the - * PHY with the requested name */ + * PHY with the requested name + */ d = bus_find_device_by_name(bus, NULL, bus_id); if (!d) { pr_err("PHY %s not found\n", bus_id); @@ -609,21 +725,84 @@ /** * phy_detach - detach a PHY device from its network device * @phydev: target phy_device struct + * + * This detaches the phy device from its network device and the phy + * driver, and drops the reference count taken in phy_attach_direct(). */ void phy_detach(struct phy_device *phydev) { + struct mii_bus *bus; + int i; + + if (phydev->drv && phydev->drv->detach) + phydev->drv->detach(phydev); + phydev->attached_dev->phydev = NULL; phydev->attached_dev = NULL; + phy_suspend(phydev); /* If the device had no specific driver before (i.e. - it * was using the generic driver), we unbind the device * from the generic driver so that there's a chance a - * real driver could be loaded */ - if (phydev->dev.driver == &genphy_driver.driver) - device_release_driver(&phydev->dev); + * real driver could be loaded + */ + for (i = 0; i < ARRAY_SIZE(genphy_driver); i++) { + if (phydev->dev.driver == &genphy_driver[i].driver) { + device_release_driver(&phydev->dev); + break; + } + } + + /* + * The phydev might go away on the put_device() below, so avoid + * a use-after-free bug by reading the underlying bus first. + */ + bus = phydev->bus; + + put_device(&phydev->dev); + module_put(bus->owner); } EXPORT_SYMBOL(phy_detach); +int phy_suspend(struct phy_device *phydev) +{ + struct phy_driver *phydrv = to_phy_driver(phydev->dev.driver); + struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL }; + int ret = 0; + + /* If the device has WOL enabled, we cannot suspend the PHY */ + phy_ethtool_get_wol(phydev, &wol); + if (wol.wolopts) + return -EBUSY; + + if (phydrv->suspend) + ret = phydrv->suspend(phydev); + + if (ret) + return ret; + + phydev->suspended = true; + + return ret; +} +EXPORT_SYMBOL(phy_suspend); + +int phy_resume(struct phy_device *phydev) +{ + struct phy_driver *phydrv = to_phy_driver(phydev->dev.driver); + int ret = 0; + + if (phydrv->resume) + ret = phydrv->resume(phydev); + + if (ret) + return ret; + + phydev->suspended = false; + + return ret; +} +EXPORT_SYMBOL(phy_resume); /* Generic PHY support and helper functions */ @@ -639,20 +818,19 @@ static 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 - * this PHY supports */ + /* Only allow advertising what this PHY supports */ phydev->advertising &= phydev->supported; advertise = phydev->advertising; /* Setup standard advertisement */ - oldadv = adv = phy_read(phydev, MII_ADVERTISE); - + adv = phy_read(phydev, MII_ADVERTISE); if (adv < 0) return adv; + oldadv = adv; adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4 | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM); adv |= ethtool_adv_to_mii_adv_t(advertise); @@ -665,25 +843,36 @@ changed = 1; } + bmsr = phy_read(phydev, MII_BMSR); + if (bmsr < 0) + return bmsr; + + /* Per 802.3-2008, Section 22.2.4.2.16 Extended status all + * 1000Mbits/sec capable PHYs shall have the BMSR_ESTATEN bit set to a + * logical 1. + */ + if (!(bmsr & BMSR_ESTATEN)) + return changed; + /* Configure gigabit if it's supported */ - if (phydev->supported & (SUPPORTED_1000baseT_Half | - SUPPORTED_1000baseT_Full)) { - oldadv = adv = phy_read(phydev, MII_CTRL1000); + adv = phy_read(phydev, MII_CTRL1000); + if (adv < 0) + return adv; - if (adv < 0) - return adv; + oldadv = adv; + adv &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); - adv &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); + if (phydev->supported & (SUPPORTED_1000baseT_Half | + SUPPORTED_1000baseT_Full)) { adv |= ethtool_adv_to_mii_ctrl1000_t(advertise); + } - if (adv != oldadv) { - err = phy_write(phydev, MII_CTRL1000, adv); + if (adv != oldadv) + changed = 1; - if (err < 0) - return err; - changed = 1; - } - } + err = phy_write(phydev, MII_CTRL1000, adv); + if (err < 0) + return err; return changed; } @@ -696,12 +885,12 @@ * to the values in phydev. Assumes that the values are valid. * Please see phy_sanitize_settings(). */ -static int genphy_setup_forced(struct phy_device *phydev) +int genphy_setup_forced(struct phy_device *phydev) { - int err; int ctl = 0; - phydev->pause = phydev->asym_pause = 0; + phydev->pause = 0; + phydev->asym_pause = 0; if (SPEED_1000 == phydev->speed) ctl |= BMCR_SPEED1000; @@ -710,12 +899,10 @@ if (DUPLEX_FULL == phydev->duplex) ctl |= BMCR_FULLDPLX; - - err = phy_write(phydev, MII_BMCR, ctl); - return err; + return phy_write(phydev, MII_BMCR, ctl); } - +EXPORT_SYMBOL(genphy_setup_forced); /** * genphy_restart_aneg - Enable and Restart Autonegotiation @@ -723,25 +910,20 @@ */ int genphy_restart_aneg(struct phy_device *phydev) { - int ctl; - - ctl = phy_read(phydev, MII_BMCR); + int ctl = phy_read(phydev, MII_BMCR); if (ctl < 0) return ctl; - ctl |= (BMCR_ANENABLE | BMCR_ANRESTART); + ctl |= BMCR_ANENABLE | BMCR_ANRESTART; /* Don't isolate the PHY if we're negotiating */ - ctl &= ~(BMCR_ISOLATE); - - ctl = phy_write(phydev, MII_BMCR, ctl); + ctl &= ~BMCR_ISOLATE; - return ctl; + return phy_write(phydev, MII_BMCR, ctl); } EXPORT_SYMBOL(genphy_restart_aneg); - /** * genphy_config_aneg - restart auto-negotiation or write BMCR * @phydev: target phy_device struct @@ -758,13 +940,12 @@ return genphy_setup_forced(phydev); result = genphy_config_advert(phydev); - if (result < 0) /* error */ return result; - if (result == 0) { /* Advertisement hasn't changed, but maybe aneg was never on to - * begin with? Or maybe phy was isolated? */ + * begin with? Or maybe phy was isolated? + */ int ctl = phy_read(phydev, MII_BMCR); if (ctl < 0) @@ -775,7 +956,8 @@ } /* Only restart aneg if we are advertising something different - * than we were before. */ + * than we were before. + */ if (result > 0) result = genphy_restart_aneg(phydev); @@ -784,6 +966,27 @@ EXPORT_SYMBOL(genphy_config_aneg); /** + * genphy_aneg_done - return auto-negotiation status + * @phydev: target phy_device struct + * + * Description: Reads the status register and returns 0 either if + * auto-negotiation is incomplete, or if there was an error. + * Returns BMSR_ANEGCOMPLETE if auto-negotiation is done. + */ +int genphy_aneg_done(struct phy_device *phydev) +{ + int retval = phy_read(phydev, MII_BMSR); + + return (retval < 0) ? retval : (retval & BMSR_ANEGCOMPLETE); +} +EXPORT_SYMBOL(genphy_aneg_done); + +static int gen10g_config_aneg(struct phy_device *phydev) +{ + return 0; +} + +/** * genphy_update_link - update link status in @phydev * @phydev: target phy_device struct * @@ -795,15 +998,16 @@ { int status; + if (phydev->drv->update_link) + return phydev->drv->update_link(phydev); + /* Do a fake read */ status = phy_read(phydev, MII_BMSR); - if (status < 0) return status; /* Read link and autonegotiation status */ status = phy_read(phydev, MII_BMSR); - if (status < 0) return status; @@ -831,65 +1035,70 @@ int err; int lpa; int lpagb = 0; + int common_adv; + int common_adv_gb = 0; - /* Update the link, but return if there - * was an error */ + /* Update the link, but return if there was an error */ err = genphy_update_link(phydev); if (err) return err; + phydev->lp_advertising = 0; + if (AUTONEG_ENABLE == phydev->autoneg) { if (phydev->supported & (SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full)) { lpagb = phy_read(phydev, MII_STAT1000); - if (lpagb < 0) return lpagb; adv = phy_read(phydev, MII_CTRL1000); - if (adv < 0) return adv; - lpagb &= adv << 2; + phydev->lp_advertising = + mii_stat1000_to_ethtool_lpa_t(lpagb); + common_adv_gb = lpagb & adv << 2; } lpa = phy_read(phydev, MII_LPA); - if (lpa < 0) return lpa; - adv = phy_read(phydev, MII_ADVERTISE); + phydev->lp_advertising |= mii_lpa_to_ethtool_lpa_t(lpa); + adv = phy_read(phydev, MII_ADVERTISE); if (adv < 0) return adv; - lpa &= adv; + common_adv = lpa & adv; phydev->speed = SPEED_10; phydev->duplex = DUPLEX_HALF; - phydev->pause = phydev->asym_pause = 0; + phydev->pause = 0; + phydev->asym_pause = 0; - if (lpagb & (LPA_1000FULL | LPA_1000HALF)) { + if (common_adv_gb & (LPA_1000FULL | LPA_1000HALF)) { phydev->speed = SPEED_1000; - if (lpagb & LPA_1000FULL) + if (common_adv_gb & LPA_1000FULL) phydev->duplex = DUPLEX_FULL; - } else if (lpa & (LPA_100FULL | LPA_100HALF)) { + } else if (common_adv & (LPA_100FULL | LPA_100HALF)) { phydev->speed = SPEED_100; - - if (lpa & LPA_100FULL) + + if (common_adv & LPA_100FULL) phydev->duplex = DUPLEX_FULL; } else - if (lpa & LPA_10FULL) + if (common_adv & LPA_10FULL) phydev->duplex = DUPLEX_FULL; - if (phydev->duplex == DUPLEX_FULL){ + if (phydev->duplex == DUPLEX_FULL) { phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0; phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0; } } else { int bmcr = phy_read(phydev, MII_BMCR); + if (bmcr < 0) return bmcr; @@ -905,27 +1114,73 @@ else phydev->speed = SPEED_10; - phydev->pause = phydev->asym_pause = 0; + phydev->pause = 0; + phydev->asym_pause = 0; } return 0; } EXPORT_SYMBOL(genphy_read_status); -static int genphy_config_init(struct phy_device *phydev) +static int gen10g_read_status(struct phy_device *phydev) +{ + int devad, reg; + u32 mmd_mask = phydev->c45_ids.devices_in_package; + + phydev->link = 1; + + /* For now just lie and say it's 10G all the time */ + phydev->speed = SPEED_10000; + phydev->duplex = DUPLEX_FULL; + + for (devad = 0; mmd_mask; devad++, mmd_mask = mmd_mask >> 1) { + if (!(mmd_mask & 1)) + continue; + + /* Read twice because link state is latched and a + * read moves the current state into the register + */ + phy_read_mmd(phydev, devad, MDIO_STAT1); + reg = phy_read_mmd(phydev, devad, MDIO_STAT1); + if (reg < 0 || !(reg & MDIO_STAT1_LSTATUS)) + phydev->link = 0; + } + + return 0; +} + +/** + * genphy_soft_reset - software reset the PHY via BMCR_RESET bit + * @phydev: target phy_device struct + * + * Description: Perform a software PHY reset using the standard + * BMCR_RESET bit and poll for the reset bit to be cleared. + * + * Returns: 0 on success, < 0 on failure + */ +int genphy_soft_reset(struct phy_device *phydev) +{ + int ret; + + ret = phy_write(phydev, MII_BMCR, BMCR_RESET); + if (ret < 0) + return ret; + + return phy_poll_reset(phydev); +} +EXPORT_SYMBOL(genphy_soft_reset); + +int genphy_config_init(struct phy_device *phydev) { int val; u32 features; - /* For now, I'll claim that the generic driver supports - * all possible port types */ features = (SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_AUI | SUPPORTED_FIBRE | SUPPORTED_BNC); /* Do we support autonegotiation? */ val = phy_read(phydev, MII_BMSR); - if (val < 0) return val; @@ -943,7 +1198,6 @@ if (val & BMSR_ESTATEN) { val = phy_read(phydev, MII_ESTATUS); - if (val < 0) return val; @@ -953,11 +1207,28 @@ features |= SUPPORTED_1000baseT_Half; } - phydev->supported = features; - phydev->advertising = features; + phydev->supported &= features; + phydev->advertising &= features; return 0; } + +static int no_soft_reset(struct phy_device *phydev) +{ + /* Do nothing for now */ + return 0; +} +EXPORT_SYMBOL(genphy_config_init); + +static int gen10g_config_init(struct phy_device *phydev) +{ + /* Temporarily just say we support everything */ + phydev->supported = SUPPORTED_10000baseT_Full; + phydev->advertising = SUPPORTED_10000baseT_Full; + + return 0; +} + int genphy_suspend(struct phy_device *phydev) { int value; @@ -965,7 +1236,7 @@ mutex_lock(&phydev->lock); value = phy_read(phydev, MII_BMCR); - phy_write(phydev, MII_BMCR, (value | BMCR_PDOWN)); + phy_write(phydev, MII_BMCR, value | BMCR_PDOWN); mutex_unlock(&phydev->lock); @@ -973,6 +1244,11 @@ } EXPORT_SYMBOL(genphy_suspend); +static int gen10g_suspend(struct phy_device *phydev) +{ + return 0; +} + int genphy_resume(struct phy_device *phydev) { int value; @@ -980,7 +1256,7 @@ mutex_lock(&phydev->lock); value = phy_read(phydev, MII_BMCR); - phy_write(phydev, MII_BMCR, (value & ~BMCR_PDOWN)); + phy_write(phydev, MII_BMCR, value & ~BMCR_PDOWN); mutex_unlock(&phydev->lock); @@ -988,6 +1264,64 @@ } EXPORT_SYMBOL(genphy_resume); +static int gen10g_resume(struct phy_device *phydev) +{ + return 0; +} + +static int __set_phy_supported(struct phy_device *phydev, u32 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 -ENOTSUPP; + case SPEED_1000: + phydev->supported |= PHY_1000BT_FEATURES; + /* fall through */ + case SPEED_100: + phydev->supported |= PHY_100BT_FEATURES; + /* fall through */ + case SPEED_10: + phydev->supported |= PHY_10BT_FEATURES; + } + + return 0; +} + +int phy_set_max_speed(struct phy_device *phydev, u32 max_speed) +{ + int err; + + err = __set_phy_supported(phydev, max_speed); + if (err) + return err; + + phydev->advertising = phydev->supported; + + return 0; +} +EXPORT_SYMBOL(phy_set_max_speed); + +static void of_set_phy_supported(struct phy_device *phydev) +{ + struct device_node *node = phydev->dev.of_node; + u32 max_speed; + + if (!IS_ENABLED(CONFIG_OF_MDIO)) + return; + + if (!node) + return; + + if (!of_property_read_u32(node, "max-speed", &max_speed)) + __set_phy_supported(phydev, max_speed); +} + /** * phy_probe - probe and init a PHY device * @dev: device to probe and init @@ -998,28 +1332,32 @@ */ static int phy_probe(struct device *dev) { - struct phy_device *phydev; - struct phy_driver *phydrv; - struct device_driver *drv; + struct phy_device *phydev = to_phy_device(dev); + struct device_driver *drv = phydev->dev.driver; + struct phy_driver *phydrv = to_phy_driver(drv); int err = 0; - phydev = to_phy_device(dev); - - drv = phydev->dev.driver; - phydrv = to_phy_driver(drv); phydev->drv = phydrv; - /* Disable the interrupt if the PHY doesn't support it */ - if (!(phydrv->flags & PHY_HAS_INTERRUPT)) + /* Disable the interrupt if the PHY doesn't support it + * but the interrupt is still a valid one + */ + if (!(phydrv->flags & PHY_HAS_INTERRUPT) && + phy_interrupt_is_valid(phydev)) phydev->irq = PHY_POLL; + if (phydrv->flags & PHY_IS_INTERNAL) + phydev->is_internal = true; + mutex_lock(&phydev->lock); /* Start out supporting everything. Eventually, * a controller will attach, and may modify one - * or both of these values */ + * or both of these values + */ phydev->supported = phydrv->features; - phydev->advertising = phydrv->features; + of_set_phy_supported(phydev); + phydev->advertising = phydev->supported; /* Set the state to READY by default */ phydev->state = PHY_READY; @@ -1030,14 +1368,11 @@ mutex_unlock(&phydev->lock); return err; - } static int phy_remove(struct device *dev) { - struct phy_device *phydev; - - phydev = to_phy_device(dev); + struct phy_device *phydev = to_phy_device(dev); mutex_lock(&phydev->lock); phydev->state = PHY_DOWN; @@ -1064,7 +1399,6 @@ new_driver->driver.remove = phy_remove; retval = driver_register(&new_driver->driver); - if (retval) { pr_err("%s: Error %d in registering driver\n", new_driver->name, retval); @@ -1103,24 +1437,41 @@ void phy_drivers_unregister(struct phy_driver *drv, int n) { int i; - for (i = 0; i < n; i++) { + + for (i = 0; i < n; i++) phy_driver_unregister(drv + i); - } } EXPORT_SYMBOL(phy_drivers_unregister); -static struct phy_driver genphy_driver = { +static struct phy_driver genphy_driver[] = { +{ .phy_id = 0xffffffff, .phy_id_mask = 0xffffffff, .name = "Generic PHY", + .soft_reset = no_soft_reset, .config_init = genphy_config_init, - .features = 0, + .features = PHY_GBIT_FEATURES | SUPPORTED_MII | + SUPPORTED_AUI | SUPPORTED_FIBRE | + SUPPORTED_BNC, .config_aneg = genphy_config_aneg, + .aneg_done = genphy_aneg_done, .read_status = genphy_read_status, .suspend = genphy_suspend, .resume = genphy_resume, - .driver = {.owner= THIS_MODULE, }, -}; + .driver = { .owner = THIS_MODULE, }, +}, { + .phy_id = 0xffffffff, + .phy_id_mask = 0xffffffff, + .name = "Generic 10G PHY", + .soft_reset = no_soft_reset, + .config_init = gen10g_config_init, + .features = 0, + .config_aneg = gen10g_config_aneg, + .read_status = gen10g_read_status, + .suspend = gen10g_suspend, + .resume = gen10g_resume, + .driver = {.owner = THIS_MODULE, }, +} }; static int __init phy_init(void) { @@ -1130,7 +1481,8 @@ if (rc) return rc; - rc = phy_driver_register(&genphy_driver); + rc = phy_drivers_register(genphy_driver, + ARRAY_SIZE(genphy_driver)); if (rc) mdio_bus_exit(); @@ -1139,7 +1491,8 @@ static void __exit phy_exit(void) { - phy_driver_unregister(&genphy_driver); + phy_drivers_unregister(genphy_driver, + ARRAY_SIZE(genphy_driver)); mdio_bus_exit(); }