9
0
Fork 0

phylib: add fixup support

if board need specific phy fixup they can register it and then the code will
executed only if needed

Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
This commit is contained in:
Jean-Christophe PLAGNIOL-VILLARD 2012-11-18 13:49:44 +01:00 committed by Sascha Hauer
parent 30298c0f3c
commit 4a1e4d4b1d
3 changed files with 125 additions and 8 deletions

View File

@ -153,6 +153,7 @@ static int mdio_bus_probe(struct device_d *_dev)
struct phy_device *dev = to_phy_device(_dev);
struct phy_driver *drv = to_phy_driver(_dev->driver);
int ret;
char str[16];
dev->attached_dev->phydev = dev;
@ -160,14 +161,9 @@ static int mdio_bus_probe(struct device_d *_dev)
dev_add_child(dev->dev.parent, _dev);
if (drv->probe) {
int ret;
ret = drv->probe(dev);
if (ret) {
dev->attached_dev->phydev = NULL;
dev->attached_dev = NULL;
return ret;
}
if (ret)
goto err;
}
if (dev->dev_flags) {
@ -188,7 +184,9 @@ static int mdio_bus_probe(struct device_d *_dev)
dev->supported = drv->features;
dev->advertising = drv->features;
drv->config_init(dev);
ret = phy_init_hw(dev);
if (ret)
goto err;
/* Sanitize settings based on PHY capabilities */
if ((dev->supported & SUPPORTED_Autoneg) == 0)
@ -208,6 +206,11 @@ static int mdio_bus_probe(struct device_d *_dev)
devfs_create(&dev->cdev);
return 0;
err:
dev->attached_dev->phydev = NULL;
dev->attached_dev = NULL;
return ret;
}
static void mdio_bus_remove(struct device_d *_dev)

View File

@ -55,6 +55,87 @@ int phy_update_status(struct phy_device *dev)
return 0;
}
static LIST_HEAD(phy_fixup_list);
/*
* 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;
strlcpy(fixup->bus_id, bus_id, sizeof(fixup->bus_id));
fixup->phy_uid = phy_uid;
fixup->phy_uid_mask = phy_uid_mask;
fixup->run = run;
list_add_tail(&fixup->list, &phy_fixup_list);
return 0;
}
/* 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);
}
/* 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);
}
/*
* 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, dev_name(&phydev->dev)) != 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;
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;
}
}
}
return 0;
}
struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id)
{
struct phy_device *dev;
@ -615,6 +696,21 @@ int phy_drivers_register(struct phy_driver *new_driver, int n)
return ret;
}
int phy_init_hw(struct phy_device *phydev)
{
struct phy_driver *phydrv = to_phy_driver(phydev->dev.driver);
int ret;
if (!phydrv || !phydrv->config_init)
return 0;
ret = phy_scan_fixups(phydev);
if (ret < 0)
return ret;
return phydrv->config_init(phydev);
}
static struct phy_driver genphy_driver = {
.drv.name = "Generic PHY",
.phy_id = PHY_ANY_UID,

View File

@ -226,10 +226,20 @@ struct phy_driver {
#define PHY_ANY_ID "MATCH ANY PHY"
#define PHY_ANY_UID 0xffffffff
/* A Structure for boards to register fixups with the PHY Lib */
struct phy_fixup {
struct list_head list;
char bus_id[20];
u32 phy_uid;
u32 phy_uid_mask;
int (*run)(struct phy_device *phydev);
};
int phy_driver_register(struct phy_driver *drv);
int phy_drivers_register(struct phy_driver *new_driver, int n);
struct phy_device *get_phy_device(struct mii_bus *bus, int addr);
int phy_init(void);
int phy_init_hw(struct phy_device *phydev);
/**
* phy_read - Convenience function for reading a given PHY register
@ -267,5 +277,13 @@ int genphy_config_advert(struct phy_device *phydev);
int genphy_setup_forced(struct phy_device *phydev);
int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id);
int phy_register_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask,
int (*run)(struct phy_device *));
int phy_register_fixup_for_id(const char *bus_id,
int (*run)(struct phy_device *));
int phy_register_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask,
int (*run)(struct phy_device *));
int phy_scan_fixups(struct phy_device *phydev);
extern struct bus_type mdio_bus_type;
#endif /* __PHYDEV_H__ */