diff options
Diffstat (limited to 'drivers/net/phy/phy_device.c')
| -rw-r--r-- | drivers/net/phy/phy_device.c | 129 |
1 files changed, 117 insertions, 12 deletions
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 8b1121b02f98..ddf8d51832a6 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c | |||
| @@ -53,6 +53,96 @@ static void phy_device_release(struct device *dev) | |||
| 53 | phy_device_free(to_phy_device(dev)); | 53 | phy_device_free(to_phy_device(dev)); |
| 54 | } | 54 | } |
| 55 | 55 | ||
| 56 | static LIST_HEAD(phy_fixup_list); | ||
| 57 | static DEFINE_MUTEX(phy_fixup_lock); | ||
| 58 | |||
| 59 | /* | ||
| 60 | * Creates a new phy_fixup and adds it to the list | ||
| 61 | * @bus_id: A string which matches phydev->dev.bus_id (or PHY_ANY_ID) | ||
| 62 | * @phy_uid: Used to match against phydev->phy_id (the UID of the PHY) | ||
| 63 | * It can also be PHY_ANY_UID | ||
| 64 | * @phy_uid_mask: Applied to phydev->phy_id and fixup->phy_uid before | ||
| 65 | * comparison | ||
| 66 | * @run: The actual code to be run when a matching PHY is found | ||
| 67 | */ | ||
| 68 | int phy_register_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask, | ||
| 69 | int (*run)(struct phy_device *)) | ||
| 70 | { | ||
| 71 | struct phy_fixup *fixup; | ||
| 72 | |||
| 73 | fixup = kzalloc(sizeof(struct phy_fixup), GFP_KERNEL); | ||
| 74 | if (!fixup) | ||
| 75 | return -ENOMEM; | ||
| 76 | |||
| 77 | strncpy(fixup->bus_id, bus_id, BUS_ID_SIZE); | ||
| 78 | fixup->phy_uid = phy_uid; | ||
| 79 | fixup->phy_uid_mask = phy_uid_mask; | ||
| 80 | fixup->run = run; | ||
| 81 | |||
| 82 | mutex_lock(&phy_fixup_lock); | ||
| 83 | list_add_tail(&fixup->list, &phy_fixup_list); | ||
| 84 | mutex_unlock(&phy_fixup_lock); | ||
| 85 | |||
| 86 | return 0; | ||
| 87 | } | ||
| 88 | EXPORT_SYMBOL(phy_register_fixup); | ||
| 89 | |||
| 90 | /* Registers a fixup to be run on any PHY with the UID in phy_uid */ | ||
| 91 | int phy_register_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask, | ||
| 92 | int (*run)(struct phy_device *)) | ||
| 93 | { | ||
| 94 | return phy_register_fixup(PHY_ANY_ID, phy_uid, phy_uid_mask, run); | ||
| 95 | } | ||
| 96 | EXPORT_SYMBOL(phy_register_fixup_for_uid); | ||
| 97 | |||
| 98 | /* Registers a fixup to be run on the PHY with id string bus_id */ | ||
| 99 | int phy_register_fixup_for_id(const char *bus_id, | ||
| 100 | int (*run)(struct phy_device *)) | ||
| 101 | { | ||
| 102 | return phy_register_fixup(bus_id, PHY_ANY_UID, 0xffffffff, run); | ||
| 103 | } | ||
| 104 | EXPORT_SYMBOL(phy_register_fixup_for_id); | ||
| 105 | |||
| 106 | /* | ||
| 107 | * Returns 1 if fixup matches phydev in bus_id and phy_uid. | ||
| 108 | * Fixups can be set to match any in one or more fields. | ||
| 109 | */ | ||
| 110 | static int phy_needs_fixup(struct phy_device *phydev, struct phy_fixup *fixup) | ||
| 111 | { | ||
| 112 | if (strcmp(fixup->bus_id, phydev->dev.bus_id) != 0) | ||
| 113 | if (strcmp(fixup->bus_id, PHY_ANY_ID) != 0) | ||
| 114 | return 0; | ||
| 115 | |||
| 116 | if ((fixup->phy_uid & fixup->phy_uid_mask) != | ||
| 117 | (phydev->phy_id & fixup->phy_uid_mask)) | ||
| 118 | if (fixup->phy_uid != PHY_ANY_UID) | ||
| 119 | return 0; | ||
| 120 | |||
| 121 | return 1; | ||
| 122 | } | ||
| 123 | |||
| 124 | /* Runs any matching fixups for this phydev */ | ||
| 125 | int phy_scan_fixups(struct phy_device *phydev) | ||
| 126 | { | ||
| 127 | struct phy_fixup *fixup; | ||
| 128 | |||
| 129 | mutex_lock(&phy_fixup_lock); | ||
| 130 | list_for_each_entry(fixup, &phy_fixup_list, list) { | ||
| 131 | if (phy_needs_fixup(phydev, fixup)) { | ||
| 132 | int err; | ||
| 133 | |||
| 134 | err = fixup->run(phydev); | ||
| 135 | |||
| 136 | if (err < 0) | ||
| 137 | return err; | ||
| 138 | } | ||
| 139 | } | ||
| 140 | mutex_unlock(&phy_fixup_lock); | ||
| 141 | |||
| 142 | return 0; | ||
| 143 | } | ||
| 144 | EXPORT_SYMBOL(phy_scan_fixups); | ||
| 145 | |||
| 56 | struct phy_device* phy_device_create(struct mii_bus *bus, int addr, int phy_id) | 146 | struct phy_device* phy_device_create(struct mii_bus *bus, int addr, int phy_id) |
| 57 | { | 147 | { |
| 58 | struct phy_device *dev; | 148 | struct phy_device *dev; |
| @@ -179,13 +269,13 @@ void phy_prepare_link(struct phy_device *phydev, | |||
| 179 | * choose to call only the subset of functions which provide | 269 | * choose to call only the subset of functions which provide |
| 180 | * the desired functionality. | 270 | * the desired functionality. |
| 181 | */ | 271 | */ |
| 182 | struct phy_device * phy_connect(struct net_device *dev, const char *phy_id, | 272 | struct phy_device * phy_connect(struct net_device *dev, const char *bus_id, |
| 183 | void (*handler)(struct net_device *), u32 flags, | 273 | void (*handler)(struct net_device *), u32 flags, |
| 184 | phy_interface_t interface) | 274 | phy_interface_t interface) |
| 185 | { | 275 | { |
| 186 | struct phy_device *phydev; | 276 | struct phy_device *phydev; |
| 187 | 277 | ||
| 188 | phydev = phy_attach(dev, phy_id, flags, interface); | 278 | phydev = phy_attach(dev, bus_id, flags, interface); |
| 189 | 279 | ||
| 190 | if (IS_ERR(phydev)) | 280 | if (IS_ERR(phydev)) |
| 191 | return phydev; | 281 | return phydev; |
| @@ -226,7 +316,7 @@ static int phy_compare_id(struct device *dev, void *data) | |||
| 226 | /** | 316 | /** |
| 227 | * phy_attach - attach a network device to a particular PHY device | 317 | * phy_attach - attach a network device to a particular PHY device |
| 228 | * @dev: network device to attach | 318 | * @dev: network device to attach |
| 229 | * @phy_id: PHY device to attach | 319 | * @bus_id: PHY device to attach |
| 230 | * @flags: PHY device's dev_flags | 320 | * @flags: PHY device's dev_flags |
| 231 | * @interface: PHY device's interface | 321 | * @interface: PHY device's interface |
| 232 | * | 322 | * |
| @@ -238,7 +328,7 @@ static int phy_compare_id(struct device *dev, void *data) | |||
| 238 | * change. The phy_device is returned to the attaching driver. | 328 | * change. The phy_device is returned to the attaching driver. |
| 239 | */ | 329 | */ |
| 240 | struct phy_device *phy_attach(struct net_device *dev, | 330 | struct phy_device *phy_attach(struct net_device *dev, |
| 241 | const char *phy_id, u32 flags, phy_interface_t interface) | 331 | const char *bus_id, u32 flags, phy_interface_t interface) |
| 242 | { | 332 | { |
| 243 | struct bus_type *bus = &mdio_bus_type; | 333 | struct bus_type *bus = &mdio_bus_type; |
| 244 | struct phy_device *phydev; | 334 | struct phy_device *phydev; |
| @@ -246,12 +336,12 @@ struct phy_device *phy_attach(struct net_device *dev, | |||
| 246 | 336 | ||
| 247 | /* Search the list of PHY devices on the mdio bus for the | 337 | /* Search the list of PHY devices on the mdio bus for the |
| 248 | * PHY with the requested name */ | 338 | * PHY with the requested name */ |
| 249 | d = bus_find_device(bus, NULL, (void *)phy_id, phy_compare_id); | 339 | d = bus_find_device(bus, NULL, (void *)bus_id, phy_compare_id); |
| 250 | 340 | ||
| 251 | if (d) { | 341 | if (d) { |
| 252 | phydev = to_phy_device(d); | 342 | phydev = to_phy_device(d); |
| 253 | } else { | 343 | } else { |
| 254 | printk(KERN_ERR "%s not found\n", phy_id); | 344 | printk(KERN_ERR "%s not found\n", bus_id); |
| 255 | return ERR_PTR(-ENODEV); | 345 | return ERR_PTR(-ENODEV); |
| 256 | } | 346 | } |
| 257 | 347 | ||
| @@ -271,7 +361,7 @@ struct phy_device *phy_attach(struct net_device *dev, | |||
| 271 | 361 | ||
| 272 | if (phydev->attached_dev) { | 362 | if (phydev->attached_dev) { |
| 273 | printk(KERN_ERR "%s: %s already attached\n", | 363 | printk(KERN_ERR "%s: %s already attached\n", |
| 274 | dev->name, phy_id); | 364 | dev->name, bus_id); |
| 275 | return ERR_PTR(-EBUSY); | 365 | return ERR_PTR(-EBUSY); |
| 276 | } | 366 | } |
| 277 | 367 | ||
| @@ -287,6 +377,11 @@ struct phy_device *phy_attach(struct net_device *dev, | |||
| 287 | if (phydev->drv->config_init) { | 377 | if (phydev->drv->config_init) { |
| 288 | int err; | 378 | int err; |
| 289 | 379 | ||
| 380 | err = phy_scan_fixups(phydev); | ||
| 381 | |||
| 382 | if (err < 0) | ||
| 383 | return ERR_PTR(err); | ||
| 384 | |||
| 290 | err = phydev->drv->config_init(phydev); | 385 | err = phydev->drv->config_init(phydev); |
| 291 | 386 | ||
| 292 | if (err < 0) | 387 | if (err < 0) |
| @@ -395,6 +490,7 @@ EXPORT_SYMBOL(genphy_config_advert); | |||
| 395 | */ | 490 | */ |
| 396 | int genphy_setup_forced(struct phy_device *phydev) | 491 | int genphy_setup_forced(struct phy_device *phydev) |
| 397 | { | 492 | { |
| 493 | int err; | ||
| 398 | int ctl = 0; | 494 | int ctl = 0; |
| 399 | 495 | ||
| 400 | phydev->pause = phydev->asym_pause = 0; | 496 | phydev->pause = phydev->asym_pause = 0; |
| @@ -407,17 +503,26 @@ int genphy_setup_forced(struct phy_device *phydev) | |||
| 407 | if (DUPLEX_FULL == phydev->duplex) | 503 | if (DUPLEX_FULL == phydev->duplex) |
| 408 | ctl |= BMCR_FULLDPLX; | 504 | ctl |= BMCR_FULLDPLX; |
| 409 | 505 | ||
| 410 | ctl = phy_write(phydev, MII_BMCR, ctl); | 506 | err = phy_write(phydev, MII_BMCR, ctl); |
| 411 | 507 | ||
| 412 | if (ctl < 0) | 508 | if (err < 0) |
| 413 | return ctl; | 509 | return err; |
| 510 | |||
| 511 | /* | ||
| 512 | * Run the fixups on this PHY, just in case the | ||
| 513 | * board code needs to change something after a reset | ||
| 514 | */ | ||
| 515 | err = phy_scan_fixups(phydev); | ||
| 516 | |||
| 517 | if (err < 0) | ||
| 518 | return err; | ||
| 414 | 519 | ||
| 415 | /* We just reset the device, so we'd better configure any | 520 | /* We just reset the device, so we'd better configure any |
| 416 | * settings the PHY requires to operate */ | 521 | * settings the PHY requires to operate */ |
| 417 | if (phydev->drv->config_init) | 522 | if (phydev->drv->config_init) |
| 418 | ctl = phydev->drv->config_init(phydev); | 523 | err = phydev->drv->config_init(phydev); |
| 419 | 524 | ||
| 420 | return ctl; | 525 | return err; |
| 421 | } | 526 | } |
| 422 | 527 | ||
| 423 | 528 | ||
