|
@@ -53,6 +53,96 @@ static void phy_device_release(struct device *dev)
|
|
|
phy_device_free(to_phy_device(dev));
|
|
|
}
|
|
|
|
|
|
+static LIST_HEAD(phy_fixup_list);
|
|
|
+static DEFINE_MUTEX(phy_fixup_lock);
|
|
|
+
|
|
|
+/*
|
|
|
+ * 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
|
|
|
+ * @phy_uid_mask: Applied to phydev->phy_id and fixup->phy_uid before
|
|
|
+ * 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 *))
|
|
|
+{
|
|
|
+ struct phy_fixup *fixup;
|
|
|
+
|
|
|
+ fixup = kzalloc(sizeof(struct phy_fixup), GFP_KERNEL);
|
|
|
+ if (!fixup)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ strncpy(fixup->bus_id, bus_id, BUS_ID_SIZE);
|
|
|
+ fixup->phy_uid = phy_uid;
|
|
|
+ fixup->phy_uid_mask = phy_uid_mask;
|
|
|
+ fixup->run = run;
|
|
|
+
|
|
|
+ mutex_lock(&phy_fixup_lock);
|
|
|
+ list_add_tail(&fixup->list, &phy_fixup_list);
|
|
|
+ mutex_unlock(&phy_fixup_lock);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+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 *))
|
|
|
+{
|
|
|
+ return phy_register_fixup(PHY_ANY_ID, phy_uid, phy_uid_mask, run);
|
|
|
+}
|
|
|
+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 *))
|
|
|
+{
|
|
|
+ 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.
|
|
|
+ * 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)
|
|
|
+{
|
|
|
+ if (strcmp(fixup->bus_id, phydev->dev.bus_id) != 0)
|
|
|
+ if (strcmp(fixup->bus_id, PHY_ANY_ID) != 0)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if ((fixup->phy_uid & fixup->phy_uid_mask) !=
|
|
|
+ (phydev->phy_id & fixup->phy_uid_mask))
|
|
|
+ if (fixup->phy_uid != PHY_ANY_UID)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+/* Runs any matching fixups for this phydev */
|
|
|
+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);
|
|
|
+
|
|
|
+ if (err < 0)
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ 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)
|
|
|
{
|
|
|
struct phy_device *dev;
|
|
@@ -179,13 +269,13 @@ void phy_prepare_link(struct phy_device *phydev,
|
|
|
* choose to call only the subset of functions which provide
|
|
|
* the desired functionality.
|
|
|
*/
|
|
|
-struct phy_device * phy_connect(struct net_device *dev, const char *phy_id,
|
|
|
+struct phy_device * phy_connect(struct net_device *dev, const char *bus_id,
|
|
|
void (*handler)(struct net_device *), u32 flags,
|
|
|
phy_interface_t interface)
|
|
|
{
|
|
|
struct phy_device *phydev;
|
|
|
|
|
|
- phydev = phy_attach(dev, phy_id, flags, interface);
|
|
|
+ phydev = phy_attach(dev, bus_id, flags, interface);
|
|
|
|
|
|
if (IS_ERR(phydev))
|
|
|
return phydev;
|
|
@@ -226,7 +316,7 @@ static int phy_compare_id(struct device *dev, void *data)
|
|
|
/**
|
|
|
* phy_attach - attach a network device to a particular PHY device
|
|
|
* @dev: network device to attach
|
|
|
- * @phy_id: PHY device to attach
|
|
|
+ * @bus_id: PHY device to attach
|
|
|
* @flags: PHY device's dev_flags
|
|
|
* @interface: PHY device's interface
|
|
|
*
|
|
@@ -238,7 +328,7 @@ static int phy_compare_id(struct device *dev, void *data)
|
|
|
* change. The phy_device is returned to the attaching driver.
|
|
|
*/
|
|
|
struct phy_device *phy_attach(struct net_device *dev,
|
|
|
- const char *phy_id, u32 flags, phy_interface_t interface)
|
|
|
+ const char *bus_id, u32 flags, phy_interface_t interface)
|
|
|
{
|
|
|
struct bus_type *bus = &mdio_bus_type;
|
|
|
struct phy_device *phydev;
|
|
@@ -246,12 +336,12 @@ struct phy_device *phy_attach(struct net_device *dev,
|
|
|
|
|
|
/* Search the list of PHY devices on the mdio bus for the
|
|
|
* PHY with the requested name */
|
|
|
- d = bus_find_device(bus, NULL, (void *)phy_id, phy_compare_id);
|
|
|
+ d = bus_find_device(bus, NULL, (void *)bus_id, phy_compare_id);
|
|
|
|
|
|
if (d) {
|
|
|
phydev = to_phy_device(d);
|
|
|
} else {
|
|
|
- printk(KERN_ERR "%s not found\n", phy_id);
|
|
|
+ printk(KERN_ERR "%s not found\n", bus_id);
|
|
|
return ERR_PTR(-ENODEV);
|
|
|
}
|
|
|
|
|
@@ -271,7 +361,7 @@ struct phy_device *phy_attach(struct net_device *dev,
|
|
|
|
|
|
if (phydev->attached_dev) {
|
|
|
printk(KERN_ERR "%s: %s already attached\n",
|
|
|
- dev->name, phy_id);
|
|
|
+ dev->name, bus_id);
|
|
|
return ERR_PTR(-EBUSY);
|
|
|
}
|
|
|
|
|
@@ -287,6 +377,11 @@ struct phy_device *phy_attach(struct net_device *dev,
|
|
|
if (phydev->drv->config_init) {
|
|
|
int err;
|
|
|
|
|
|
+ err = phy_scan_fixups(phydev);
|
|
|
+
|
|
|
+ if (err < 0)
|
|
|
+ return ERR_PTR(err);
|
|
|
+
|
|
|
err = phydev->drv->config_init(phydev);
|
|
|
|
|
|
if (err < 0)
|
|
@@ -395,6 +490,7 @@ EXPORT_SYMBOL(genphy_config_advert);
|
|
|
*/
|
|
|
int genphy_setup_forced(struct phy_device *phydev)
|
|
|
{
|
|
|
+ int err;
|
|
|
int ctl = 0;
|
|
|
|
|
|
phydev->pause = phydev->asym_pause = 0;
|
|
@@ -407,17 +503,26 @@ int genphy_setup_forced(struct phy_device *phydev)
|
|
|
if (DUPLEX_FULL == phydev->duplex)
|
|
|
ctl |= BMCR_FULLDPLX;
|
|
|
|
|
|
- ctl = phy_write(phydev, MII_BMCR, ctl);
|
|
|
+ err = phy_write(phydev, MII_BMCR, ctl);
|
|
|
|
|
|
- if (ctl < 0)
|
|
|
- return ctl;
|
|
|
+ if (err < 0)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Run the fixups on this PHY, just in case the
|
|
|
+ * board code needs to change something after a reset
|
|
|
+ */
|
|
|
+ err = phy_scan_fixups(phydev);
|
|
|
+
|
|
|
+ if (err < 0)
|
|
|
+ return err;
|
|
|
|
|
|
/* We just reset the device, so we'd better configure any
|
|
|
* settings the PHY requires to operate */
|
|
|
if (phydev->drv->config_init)
|
|
|
- ctl = phydev->drv->config_init(phydev);
|
|
|
+ err = phydev->drv->config_init(phydev);
|
|
|
|
|
|
- return ctl;
|
|
|
+ return err;
|
|
|
}
|
|
|
|
|
|
|