@@ -25,6 +25,7 @@
static struct class *phy_class;
static DEFINE_MUTEX(phy_provider_mutex);
static LIST_HEAD(phy_provider_list);
+static LIST_HEAD(phy_lookup_list);
static DEFINE_IDA(phy_ida);
static void devm_phy_release(struct device *dev, void *res)
@@ -85,6 +86,94 @@ static struct phy *phy_lookup(struct device *device, const char *con_id,
return ERR_PTR(-ENODEV);
}
+/**
+ * phy_add_table() - register PHY/device association to the lookup list
+ * @table: association to register
+ */
+void phy_add_lookup_table(struct phy_lookup_table *table)
+{
+ mutex_lock(&phy_provider_mutex);
+ list_add_tail(&table->list, &phy_lookup_list);
+ mutex_unlock(&phy_provider_mutex);
+}
+
+/**
+ * phy_add_table() - remove PHY/device association from the lookup list
+ * @table: association to be removed
+ */
+void phy_del_lookup_table(struct phy_lookup_table *table)
+{
+ mutex_lock(&phy_provider_mutex);
+ list_del(&table->list);
+ mutex_unlock(&phy_provider_mutex);
+}
+
+static struct phy *find_phy_by_name(const char *name)
+{
+ struct class_dev_iter iter;
+ struct phy *phy = NULL;
+ struct device *dev;
+
+ class_dev_iter_init(&iter, phy_class, NULL, NULL);
+ while ((dev = class_dev_iter_next(&iter))) {
+ if (!strcmp(dev_name(dev), name)) {
+ phy = to_phy(dev);
+ break;
+ }
+ }
+ class_dev_iter_exit(&iter);
+
+ return phy;
+}
+
+static struct phy_lookup_table *phy_get_lookup_table(struct device *dev)
+{
+ const char *dev_id = dev ? dev_name(dev) : NULL;
+ struct phy_lookup_table *table;
+
+ mutex_lock(&phy_provider_mutex);
+ list_for_each_entry(table, &phy_lookup_list, list)
+ if (!strcmp(table->dev_id, dev_id))
+ goto out;
+ table = NULL;
+out:
+ mutex_unlock(&phy_provider_mutex);
+ return table;
+}
+
+static struct phy *phy_find(struct device *dev, const char *con_id,
+ unsigned int idx)
+{
+ struct phy_lookup_table *table;
+ struct phy_lookup *p;
+ struct phy *phy;
+
+ table = phy_get_lookup_table(dev);
+ if (!table)
+ /* fall-back to the old lookup method for now */
+ return phy_lookup(dev, con_id, idx);
+
+ for (p = &table->table[0]; p->phy_name; p++) {
+ /* index must always match exactly */
+ if (idx != p->idx)
+ continue;
+
+ /* If the lookup entry has a con_id, require exact match */
+ if (p->con_id && (!con_id || strcmp(p->con_id, con_id)))
+ continue;
+
+ phy = find_phy_by_name(p->phy_name);
+ if (!phy) {
+ dev_warn(dev, "no PHY by the name %s\n", p->phy_name);
+ return ERR_PTR(-ENODEV);
+ }
+
+ return phy;
+ }
+
+ return ERR_PTR(-ENODEV);
+}
+
static struct phy_provider *of_phy_provider_lookup(struct device_node *node)
{
struct phy_provider *phy_provider;
@@ -386,7 +475,7 @@ struct phy *phy_get_index(struct device *dev, const char *con_id,
*/
if (!phy || IS_ERR(phy)) {
dev_dbg(dev, "using lookup tables for PHY lookup");
- phy = phy_lookup(dev, con_id, idx);
+ phy = phy_find(dev, con_id, idx);
}
if (IS_ERR(phy)) {
@@ -98,6 +98,54 @@ struct phy_init_data {
.port = _port, \
}
+/**
+ * struct phy_lookup - Lookup entry for associating PHYs
+ * @phy_name: device name of the PHY
+ * @con_id: name of the PHY from device's point of view
+ * @idx: index of the PHY when name is not used
+ */
+struct phy_lookup {
+ const char *phy_name;
+ const char *con_id;
+ unsigned int idx;
+};
+
+/**
+ * struct phy_lookup_table - association of PHYs to specific device
+ * @list: entry in the lookup list
+ * @dev_id: the name of the device
+ * @table: table of PHYs attached to this device
+ */
+struct phy_lookup_table {
+ struct list_head list;
+ const char *dev_id;
+ struct phy_lookup table[];
+};
+
+/**
+ * Simple definition of a single PHY under a consumer
+ */
+#define PHY_LOOKUP(_phy_name, _con_id) \
+{ \
+ PHY_LOOKUP_IDX(_phy_name, _con_id, 0), \
+ { }, \
+}
+
+/**
+ * Use this macro if you need to have several PHYs under the same con_id.
+ * Each PHY needs to use a different index and can be accessed using
+ * phy_get_index()
+ */
+#define PHY_LOOKUP_IDX(_phy_name, _con_id, _idx) \
+{ \
+ .phy_name = _phy_name, \
+ .con_id = _con_id, \
+ .idx = _idx, \
+}
+
+void phy_add_lookup_table(struct phy_lookup_table *);
+void phy_del_lookup_table(struct phy_lookup_table *);
+
#define to_phy(dev) (container_of((dev), struct phy, dev))
#define of_phy_provider_register(dev, xlate) \
Removes the need for the phys to be aware of their users even when not using DT. The method is copied from gpiolib.c. Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com> --- drivers/phy/phy-core.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++- include/linux/phy/phy.h | 48 ++++++++++++++++++++++++++ 2 files changed, 138 insertions(+), 1 deletion(-)