diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2012-12-07 16:43:21 +0100 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2012-12-07 16:43:21 +0100 |
commit | 8f32824c6e374092f3c1dec431e5e83f839353fe (patch) | |
tree | 9ceed735e77d1329ebf70c9f9abb78263358d2dd /drivers/net/phy/phy.c | |
parent | 759946809a350b0a8158842924ab139e93eeef13 (diff) | |
parent | bba73109ce1f54c7ffca044b00aacb320be888c2 (diff) | |
download | barebox-8f32824c6e374092f3c1dec431e5e83f839353fe.tar.gz barebox-8f32824c6e374092f3c1dec431e5e83f839353fe.tar.xz |
Merge branch 'for-next/phylib'
Conflicts:
drivers/net/phy/phy.c
Diffstat (limited to 'drivers/net/phy/phy.c')
-rw-r--r-- | drivers/net/phy/phy.c | 130 |
1 files changed, 128 insertions, 2 deletions
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 62ff2d9d13..ed185e1471 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -19,6 +19,7 @@ #include <common.h> #include <driver.h> +#include <init.h> #include <net.h> #include <malloc.h> #include <linux/phy.h> @@ -27,6 +28,7 @@ #define PHY_AN_TIMEOUT 10 +static struct phy_driver genphy_driver; static int genphy_config_init(struct phy_device *phydev); int phy_update_status(struct phy_device *dev) @@ -53,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; +} + static struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id) { struct phy_device *dev; @@ -142,6 +225,21 @@ struct phy_device *get_phy_device(struct mii_bus *bus, int addr) return dev; } +static int phy_register_device(struct phy_device* dev) +{ + int ret; + + ret = register_device(&dev->dev); + if (ret) + return ret; + + if (dev->dev.driver) + return 0; + + dev->dev.driver = &genphy_driver.drv; + return device_probe(&dev->dev); +} + /* Automatically gets and returns the PHY device */ int phy_device_connect(struct eth_device *edev, struct mii_bus *bus, int addr, void (*adjust_link) (struct eth_device *edev), @@ -164,7 +262,7 @@ int phy_device_connect(struct eth_device *edev, struct mii_bus *bus, int addr, dev->interface = interface; dev->dev_flags = flags; - ret = register_device(&dev->dev); + ret = phy_register_device(dev); if (ret) goto fail; } else { @@ -181,7 +279,7 @@ int phy_device_connect(struct eth_device *edev, struct mii_bus *bus, int addr, dev->interface = interface; dev->dev_flags = flags; - ret = register_device(&dev->dev); + ret = phy_register_device(dev); if (ret) goto fail; @@ -597,3 +695,31 @@ 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, + .phy_id_mask = PHY_ANY_UID, + .features = 0, +}; + +static int generic_phy_register(void) +{ + return phy_driver_register(&genphy_driver); +} +device_initcall(generic_phy_register); |