diff options
Diffstat (limited to 'drivers/net/phy/phy_device.c')
-rw-r--r-- | drivers/net/phy/phy_device.c | 451 |
1 files changed, 296 insertions, 155 deletions
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index d6447b3f740..82514e72b3d 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -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,11 @@ #include <linux/mii.h> #include <linux/ethtool.h> #include <linux/phy.h> +#include <linux/mdio.h> +#include <linux/io.h> +#include <linux/uaccess.h> -#include <asm/io.h> #include <asm/irq.h> -#include <asm/uaccess.h> MODULE_DESCRIPTION("PHY library"); MODULE_AUTHOR("Andy Fleming"); @@ -53,31 +51,31 @@ static void phy_device_release(struct device *dev) 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 +94,7 @@ EXPORT_SYMBOL(phy_register_fixup); /* 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 +102,13 @@ EXPORT_SYMBOL(phy_register_fixup_for_uid); /* 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 +118,7 @@ static int phy_needs_fixup(struct phy_device *phydev, struct phy_fixup *fixup) 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,16 +126,14 @@ static int phy_needs_fixup(struct phy_device *phydev, struct phy_fixup *fixup) } /* 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); @@ -150,25 +145,24 @@ int phy_scan_fixups(struct phy_device *phydev) 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); + return (struct phy_device *)PTR_ERR((void *)-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; @@ -192,14 +186,15 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, 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); @@ -299,10 +294,8 @@ static int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id, 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; @@ -310,7 +303,6 @@ static int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id, /* 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; @@ -320,7 +312,8 @@ static int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id, } /** - * 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 @@ -331,7 +324,6 @@ static int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id, 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; @@ -343,9 +335,7 @@ struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45) 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); @@ -357,14 +347,17 @@ int phy_device_register(struct phy_device *phydev) { 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_init_hw(phydev); + if (err) { + pr_err("PHY %d failed to initialize\n", phydev->addr); + goto out; + } err = device_add(&phydev->dev); if (err) { @@ -409,7 +402,7 @@ EXPORT_SYMBOL(phy_find_first); * 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; } @@ -432,7 +425,7 @@ int phy_connect_direct(struct net_device *dev, struct phy_device *phydev, return rc; phy_prepare_link(phydev, handler); - phy_start_machine(phydev, NULL); + phy_start_machine(phydev); if (phydev->irq > 0) phy_start_interrupts(phydev); @@ -455,16 +448,17 @@ EXPORT_SYMBOL(phy_connect_direct); * 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); @@ -481,7 +475,8 @@ struct phy_device * phy_connect(struct net_device *dev, const char *bus_id, 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) @@ -490,13 +485,53 @@ void phy_disconnect(struct phy_device *phydev) phy_stop_interrupts(phydev); phy_stop_machine(phydev); - + phydev->adjust_link = NULL; phy_detach(phydev); } EXPORT_SYMBOL(phy_disconnect); +/** + * 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; @@ -504,12 +539,21 @@ int phy_init_hw(struct phy_device *phydev) if (!phydev->drv || !phydev->drv->config_init) return 0; + ret = phy_write(phydev, MII_BMCR, BMCR_RESET); + if (ret < 0) + return ret; + + ret = phy_poll_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 @@ -520,26 +564,25 @@ int phy_init_hw(struct phy_device *phydev) * * 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. */ -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 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. */ + * 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; - } - - d->driver = &genphy_driver.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) @@ -565,13 +608,17 @@ static int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, /* 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); + phy_resume(phydev); + return err; } +EXPORT_SYMBOL(phy_attach_direct); /** * phy_attach - attach a network device to a particular PHY device @@ -582,8 +629,8 @@ static int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, * 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; @@ -591,7 +638,8 @@ struct phy_device *phy_attach(struct net_device *dev, 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); @@ -613,18 +661,49 @@ EXPORT_SYMBOL(phy_attach); */ void phy_detach(struct phy_device *phydev) { + int i; 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; + } + } } 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; + + /* If the device has WOL enabled, we cannot suspend the PHY */ + wol.cmd = ETHTOOL_GWOL; + phy_ethtool_get_wol(phydev, &wol); + if (wol.wolopts) + return -EBUSY; + + if (phydrv->suspend) + return phydrv->suspend(phydev); + return 0; +} + +int phy_resume(struct phy_device *phydev) +{ + struct phy_driver *phydrv = to_phy_driver(phydev->dev.driver); + + if (phydrv->resume) + return phydrv->resume(phydev); + return 0; +} /* Generic PHY support and helper functions */ @@ -640,20 +719,19 @@ EXPORT_SYMBOL(phy_detach); 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); @@ -666,26 +744,36 @@ static int genphy_config_advert(struct phy_device *phydev) changed = 1; } - /* Configure gigabit if it's supported */ - if (phydev->supported & (SUPPORTED_1000baseT_Half | - SUPPORTED_1000baseT_Full)) { - oldadv = adv = phy_read(phydev, MII_CTRL1000); + bmsr = phy_read(phydev, MII_BMSR); + if (bmsr < 0) + return bmsr; - if (adv < 0) - return adv; + /* 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; - adv &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); - adv |= ethtool_adv_to_mii_ctrl1000_t(advertise); + /* Configure gigabit if it's supported */ + adv = phy_read(phydev, MII_CTRL1000); + if (adv < 0) + return adv; - if (adv != oldadv) { - err = phy_write(phydev, MII_CTRL1000, adv); + oldadv = adv; + adv &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); - if (err < 0) - return err; + if (phydev->supported & (SUPPORTED_1000baseT_Half | + SUPPORTED_1000baseT_Full)) { + adv |= ethtool_adv_to_mii_ctrl1000_t(advertise); + if (adv != oldadv) changed = 1; - } } + err = phy_write(phydev, MII_CTRL1000, adv); + if (err < 0) + return err; + return changed; } @@ -699,10 +787,10 @@ static int genphy_config_advert(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; @@ -711,10 +799,8 @@ int genphy_setup_forced(struct phy_device *phydev) 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); @@ -724,25 +810,20 @@ EXPORT_SYMBOL(genphy_setup_forced); */ 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 &= ~BMCR_ISOLATE; - ctl = phy_write(phydev, MII_BMCR, ctl); - - 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 @@ -759,13 +840,12 @@ int genphy_config_aneg(struct phy_device *phydev) 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) @@ -776,7 +856,8 @@ int genphy_config_aneg(struct phy_device *phydev) } /* 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 +865,11 @@ int genphy_config_aneg(struct phy_device *phydev) } EXPORT_SYMBOL(genphy_config_aneg); +static int gen10g_config_aneg(struct phy_device *phydev) +{ + return 0; +} + /** * genphy_update_link - update link status in @phydev * @phydev: target phy_device struct @@ -798,13 +884,11 @@ int genphy_update_link(struct phy_device *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; @@ -833,35 +917,36 @@ int genphy_read_status(struct phy_device *phydev) int lpa; int lpagb = 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; + phydev->lp_advertising = + mii_stat1000_to_ethtool_lpa_t(lpagb); 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; @@ -869,7 +954,8 @@ int genphy_read_status(struct phy_device *phydev) 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)) { phydev->speed = SPEED_1000; @@ -878,19 +964,20 @@ int genphy_read_status(struct phy_device *phydev) phydev->duplex = DUPLEX_FULL; } else if (lpa & (LPA_100FULL | LPA_100HALF)) { phydev->speed = SPEED_100; - + if (lpa & LPA_100FULL) phydev->duplex = DUPLEX_FULL; } else if (lpa & 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; @@ -906,27 +993,55 @@ int genphy_read_status(struct phy_device *phydev) 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 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; +} + static 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 */ + * 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; @@ -944,7 +1059,6 @@ static int genphy_config_init(struct phy_device *phydev) if (val & BMSR_ESTATEN) { val = phy_read(phydev, MII_ESTATUS); - if (val < 0) return val; @@ -959,6 +1073,16 @@ static int genphy_config_init(struct phy_device *phydev) return 0; } + +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; @@ -966,7 +1090,7 @@ int genphy_suspend(struct phy_device *phydev) 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); @@ -974,6 +1098,11 @@ int genphy_suspend(struct phy_device *phydev) } EXPORT_SYMBOL(genphy_suspend); +static int gen10g_suspend(struct phy_device *phydev) +{ + return 0; +} + int genphy_resume(struct phy_device *phydev) { int value; @@ -981,7 +1110,7 @@ int genphy_resume(struct phy_device *phydev) 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); @@ -989,6 +1118,11 @@ int genphy_resume(struct phy_device *phydev) } EXPORT_SYMBOL(genphy_resume); +static int gen10g_resume(struct phy_device *phydev) +{ + return 0; +} + /** * phy_probe - probe and init a PHY device * @dev: device to probe and init @@ -999,22 +1133,18 @@ EXPORT_SYMBOL(genphy_resume); */ 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 * but the interrupt is still a valid one */ if (!(phydrv->flags & PHY_HAS_INTERRUPT) && - phy_interrupt_is_valid(phydev)) + phy_interrupt_is_valid(phydev)) phydev->irq = PHY_POLL; if (phydrv->flags & PHY_IS_INTERNAL) @@ -1024,7 +1154,8 @@ static int phy_probe(struct device *dev) /* 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; @@ -1037,14 +1168,11 @@ static int phy_probe(struct device *dev) 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; @@ -1071,7 +1199,6 @@ int phy_driver_register(struct phy_driver *new_driver) 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); @@ -1110,13 +1237,14 @@ EXPORT_SYMBOL(phy_driver_unregister); 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", @@ -1126,8 +1254,19 @@ static struct phy_driver genphy_driver = { .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", + .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) { @@ -1137,7 +1276,8 @@ static int __init phy_init(void) 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(); @@ -1146,7 +1286,8 @@ static int __init phy_init(void) static void __exit phy_exit(void) { - phy_driver_unregister(&genphy_driver); + phy_drivers_unregister(genphy_driver, + ARRAY_SIZE(genphy_driver)); mdio_bus_exit(); } |