#include <linux/phy/phy.h>
#include <linux/idr.h>
#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
static struct class *phy_class;
static DEFINE_MUTEX(phy_provider_mutex);
static LIST_HEAD(phy_provider_list);
+static LIST_HEAD(phys);
static DEFINE_IDA(phy_ida);
static void devm_phy_release(struct device *dev, void *res)
return res == match_data;
}
-static struct phy *phy_lookup(struct device *device, const char *port)
+/**
+ * phy_create_lookup() - allocate and register PHY/device association
+ * @phy: the phy of the association
+ * @con_id: connection ID string on device
+ * @dev_id: the device of the association
+ *
+ * Creates and registers phy_lookup entry.
+ */
+int phy_create_lookup(struct phy *phy, const char *con_id, const char *dev_id)
{
- unsigned int count;
- struct phy *phy;
- struct device *dev;
- struct phy_consumer *consumers;
- struct class_dev_iter iter;
+ struct phy_lookup *pl;
- class_dev_iter_init(&iter, phy_class, NULL, NULL);
- while ((dev = class_dev_iter_next(&iter))) {
- phy = to_phy(dev);
+ if (!phy || !dev_id || !con_id)
+ return -EINVAL;
- if (!phy->init_data)
- continue;
- count = phy->init_data->num_consumers;
- consumers = phy->init_data->consumers;
- while (count--) {
- if (!strcmp(consumers->dev_name, dev_name(device)) &&
- !strcmp(consumers->port, port)) {
- class_dev_iter_exit(&iter);
- return phy;
- }
- consumers++;
+ pl = kzalloc(sizeof(*pl), GFP_KERNEL);
+ if (!pl)
+ return -ENOMEM;
+
+ pl->dev_id = dev_id;
+ pl->con_id = con_id;
+ pl->phy = phy;
+
+ mutex_lock(&phy_provider_mutex);
+ list_add_tail(&pl->node, &phys);
+ mutex_unlock(&phy_provider_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(phy_create_lookup);
+
+/**
+ * phy_remove_lookup() - find and remove PHY/device association
+ * @phy: the phy of the association
+ * @con_id: connection ID string on device
+ * @dev_id: the device of the association
+ *
+ * Finds and unregisters phy_lookup entry that was created with
+ * phy_create_lookup().
+ */
+void phy_remove_lookup(struct phy *phy, const char *con_id, const char *dev_id)
+{
+ struct phy_lookup *pl;
+
+ if (!phy || !dev_id || !con_id)
+ return;
+
+ mutex_lock(&phy_provider_mutex);
+ list_for_each_entry(pl, &phys, node)
+ if (pl->phy == phy && !strcmp(pl->dev_id, dev_id) &&
+ !strcmp(pl->con_id, con_id)) {
+ list_del(&pl->node);
+ kfree(pl);
+ break;
}
- }
+ mutex_unlock(&phy_provider_mutex);
+}
+EXPORT_SYMBOL_GPL(phy_remove_lookup);
- class_dev_iter_exit(&iter);
- return ERR_PTR(-ENODEV);
+static struct phy *phy_find(struct device *dev, const char *con_id)
+{
+ const char *dev_id = dev_name(dev);
+ struct phy_lookup *p, *pl = NULL;
+
+ mutex_lock(&phy_provider_mutex);
+ list_for_each_entry(p, &phys, node)
+ if (!strcmp(p->dev_id, dev_id) && !strcmp(p->con_id, con_id)) {
+ pl = p;
+ break;
+ }
+ mutex_unlock(&phy_provider_mutex);
+
+ return pl ? pl->phy : ERR_PTR(-ENODEV);
}
static struct phy_provider *of_phy_provider_lookup(struct device_node *node)
{
struct phy_provider *phy_provider;
+ struct device_node *child;
list_for_each_entry(phy_provider, &phy_provider_list, list) {
if (phy_provider->dev->of_node == node)
return phy_provider;
+
+ for_each_child_of_node(phy_provider->dev->of_node, child)
+ if (child == node)
+ return phy_provider;
}
return ERR_PTR(-EPROBE_DEFER);
if (!phy)
return 0;
+ if (phy->pwr) {
+ ret = regulator_enable(phy->pwr);
+ if (ret)
+ return ret;
+ }
+
ret = phy_pm_runtime_get_sync(phy);
if (ret < 0 && ret != -ENOTSUPP)
return ret;
out:
mutex_unlock(&phy->mutex);
phy_pm_runtime_put_sync(phy);
+ if (phy->pwr)
+ regulator_disable(phy->pwr);
return ret;
}
mutex_unlock(&phy->mutex);
phy_pm_runtime_put(phy);
+ if (phy->pwr)
+ regulator_disable(phy->pwr);
+
return 0;
}
EXPORT_SYMBOL_GPL(phy_power_off);
{
struct phy *phy;
struct class_dev_iter iter;
- struct device_node *node = dev->of_node;
class_dev_iter_init(&iter, phy_class, NULL, NULL);
while ((dev = class_dev_iter_next(&iter))) {
phy = to_phy(dev);
- if (node != phy->dev.of_node)
+ if (args->np != phy->dev.of_node)
continue;
class_dev_iter_exit(&iter);
string);
phy = _of_phy_get(dev->of_node, index);
} else {
- phy = phy_lookup(dev, string);
+ phy = phy_find(dev, string);
}
if (IS_ERR(phy))
return phy;
/**
* phy_create() - create a new phy
* @dev: device that is creating the new phy
+ * @node: device node of the phy
* @ops: function pointers for performing phy operations
- * @init_data: contains the list of PHY consumers or NULL
*
* Called to create a phy using phy framework.
*/
-struct phy *phy_create(struct device *dev, const struct phy_ops *ops,
- struct phy_init_data *init_data)
+struct phy *phy_create(struct device *dev, struct device_node *node,
+ const struct phy_ops *ops)
{
int ret;
int id;
goto free_phy;
}
+ /* phy-supply */
+ phy->pwr = regulator_get_optional(dev, "phy");
+ if (IS_ERR(phy->pwr)) {
+ if (PTR_ERR(phy->pwr) == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ goto free_ida;
+ }
+ phy->pwr = NULL;
+ }
+
device_initialize(&phy->dev);
mutex_init(&phy->mutex);
phy->dev.class = phy_class;
phy->dev.parent = dev;
- phy->dev.of_node = dev->of_node;
+ phy->dev.of_node = node ?: dev->of_node;
phy->id = id;
phy->ops = ops;
- phy->init_data = init_data;
ret = dev_set_name(&phy->dev, "phy-%s.%d", dev_name(dev), id);
if (ret)
put_device(&phy->dev); /* calls phy_release() which frees resources */
return ERR_PTR(ret);
+free_ida:
+ ida_simple_remove(&phy_ida, phy->id);
+
free_phy:
kfree(phy);
return ERR_PTR(ret);
/**
* devm_phy_create() - create a new phy
* @dev: device that is creating the new phy
+ * @node: device node of the phy
* @ops: function pointers for performing phy operations
- * @init_data: contains the list of PHY consumers or NULL
*
* Creates a new PHY device adding it to the PHY class.
* While at that, it also associates the device with the phy using devres.
* On driver detach, release function is invoked on the devres data,
* then, devres data is freed.
*/
-struct phy *devm_phy_create(struct device *dev, const struct phy_ops *ops,
- struct phy_init_data *init_data)
+struct phy *devm_phy_create(struct device *dev, struct device_node *node,
+ const struct phy_ops *ops)
{
struct phy **ptr, *phy;
if (!ptr)
return ERR_PTR(-ENOMEM);
- phy = phy_create(dev, ops, init_data);
+ phy = phy_create(dev, node, ops);
if (!IS_ERR(phy)) {
*ptr = phy;
devres_add(dev, ptr);
phy = to_phy(dev);
dev_vdbg(dev, "releasing '%s'\n", dev_name(dev));
+ regulator_put(phy->pwr);
ida_simple_remove(&phy_ida, phy->id);
kfree(phy);
}