summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>2012-11-18 13:49:44 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2012-11-20 09:38:33 +0100
commit4a1e4d4b1da62a02f6ca32f44b709326bd764efc (patch)
tree783c28edb6d1b6dceed5455d54aee86d2aef6119
parent30298c0f3ce43c364090f14618ccf012260841ca (diff)
downloadbarebox-4a1e4d4b1da62a02f6ca32f44b709326bd764efc.tar.gz
barebox-4a1e4d4b1da62a02f6ca32f44b709326bd764efc.tar.xz
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>
-rw-r--r--drivers/net/phy/mdio_bus.c19
-rw-r--r--drivers/net/phy/phy.c96
-rw-r--r--include/linux/phy.h18
3 files changed, 125 insertions, 8 deletions
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index 3e79345089..1d20bb0503 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -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)
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 2fd0440a01..59df742526 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -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,
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 791d657c92..b39eca5fbf 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -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__ */