aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/phy
diff options
context:
space:
mode:
authorAndy Fleming <afleming@freescale.com>2008-04-18 18:29:54 -0400
committerJeff Garzik <jgarzik@redhat.com>2008-04-25 02:08:52 -0400
commitf62220d3a9ccb879c3f90f845ae57b724b7bbb62 (patch)
tree72697d5d0b7bfdebaf0fd74bea07212c9820a6df /drivers/net/phy
parent8ec7226a93dcd4a314e2387d1033aef01145061b (diff)
phylib: Add support for board-level PHY fixups
Sometimes the specific interaction between the platform and the PHY requires special handling. For instance, to change where the PHY's clock input is, or to add a delay to account for latency issues in the data path. We add a mechanism for registering a callback with the PHY Lib to be called on matching PHYs when they are brought up, or reset. Signed-off-by: Andy Fleming <afleming@freescale.com> Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
Diffstat (limited to 'drivers/net/phy')
-rw-r--r--drivers/net/phy/mdio_bus.c3
-rw-r--r--drivers/net/phy/phy.c4
-rw-r--r--drivers/net/phy/phy_device.c129
3 files changed, 123 insertions, 13 deletions
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index 963630c65ca..94e0b7ed76f 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -89,6 +89,9 @@ int mdiobus_register(struct mii_bus *bus)
89 89
90 phydev->bus = bus; 90 phydev->bus = bus;
91 91
92 /* Run all of the fixups for this PHY */
93 phy_scan_fixups(phydev);
94
92 err = device_register(&phydev->dev); 95 err = device_register(&phydev->dev);
93 96
94 if (err) { 97 if (err) {
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 12fccb1c76d..3c18bb59495 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -406,8 +406,10 @@ int phy_mii_ioctl(struct phy_device *phydev,
406 406
407 if (mii_data->reg_num == MII_BMCR 407 if (mii_data->reg_num == MII_BMCR
408 && val & BMCR_RESET 408 && val & BMCR_RESET
409 && phydev->drv->config_init) 409 && phydev->drv->config_init) {
410 phy_scan_fixups(phydev);
410 phydev->drv->config_init(phydev); 411 phydev->drv->config_init(phydev);
412 }
411 break; 413 break;
412 414
413 default: 415 default:
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 8b1121b02f9..ddf8d51832a 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
56static LIST_HEAD(phy_fixup_list);
57static 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 */
68int 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}
88EXPORT_SYMBOL(phy_register_fixup);
89
90/* Registers a fixup to be run on any PHY with the UID in phy_uid */
91int 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}
96EXPORT_SYMBOL(phy_register_fixup_for_uid);
97
98/* Registers a fixup to be run on the PHY with id string bus_id */
99int 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}
104EXPORT_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 */
110static 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 */
125int 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}
144EXPORT_SYMBOL(phy_scan_fixups);
145
56struct phy_device* phy_device_create(struct mii_bus *bus, int addr, int phy_id) 146struct 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 */
182struct phy_device * phy_connect(struct net_device *dev, const char *phy_id, 272struct 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 */
240struct phy_device *phy_attach(struct net_device *dev, 330struct 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 */
396int genphy_setup_forced(struct phy_device *phydev) 491int 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