aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/phy/phy-core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/phy/phy-core.c')
-rw-r--r--drivers/phy/phy-core.c63
1 files changed, 53 insertions, 10 deletions
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index c64a2f3b2d62..ff5eec5af817 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -21,6 +21,7 @@
#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);
@@ -86,10 +87,15 @@ static struct phy *phy_lookup(struct device *device, const char *port)
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);
@@ -226,6 +232,12 @@ int phy_power_on(struct phy *phy)
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;
@@ -247,6 +259,8 @@ int phy_power_on(struct phy *phy)
out:
mutex_unlock(&phy->mutex);
phy_pm_runtime_put_sync(phy);
+ if (phy->pwr)
+ regulator_disable(phy->pwr);
return ret;
}
@@ -272,6 +286,9 @@ int phy_power_off(struct phy *phy)
mutex_unlock(&phy->mutex);
phy_pm_runtime_put(phy);
+ if (phy->pwr)
+ regulator_disable(phy->pwr);
+
return 0;
}
EXPORT_SYMBOL_GPL(phy_power_off);
@@ -398,13 +415,20 @@ struct phy *of_phy_simple_xlate(struct device *dev, struct of_phandle_args
struct phy *phy;
struct class_dev_iter iter;
struct device_node *node = dev->of_node;
+ struct device_node *child;
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 (node != phy->dev.of_node) {
+ for_each_child_of_node(node, child) {
+ if (child == phy->dev.of_node)
+ goto phy_found;
+ }
continue;
+ }
+phy_found:
class_dev_iter_exit(&iter);
return phy;
}
@@ -562,13 +586,15 @@ EXPORT_SYMBOL_GPL(devm_of_phy_get);
/**
* 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,
+ struct phy_init_data *init_data)
{
int ret;
int id;
@@ -588,12 +614,22 @@ struct phy *phy_create(struct device *dev, const struct phy_ops *ops,
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;
@@ -614,8 +650,12 @@ struct phy *phy_create(struct device *dev, const struct phy_ops *ops,
return phy;
put_dev:
- put_device(&phy->dev);
- ida_remove(&phy_ida, phy->id);
+ 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);
@@ -625,6 +665,7 @@ EXPORT_SYMBOL_GPL(phy_create);
/**
* 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
*
@@ -633,8 +674,9 @@ EXPORT_SYMBOL_GPL(phy_create);
* 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_init_data *init_data)
{
struct phy **ptr, *phy;
@@ -642,7 +684,7 @@ struct phy *devm_phy_create(struct device *dev, const struct phy_ops *ops,
if (!ptr)
return ERR_PTR(-ENOMEM);
- phy = phy_create(dev, ops, init_data);
+ phy = phy_create(dev, node, ops, init_data);
if (!IS_ERR(phy)) {
*ptr = phy;
devres_add(dev, ptr);
@@ -799,7 +841,8 @@ static void phy_release(struct device *dev)
phy = to_phy(dev);
dev_vdbg(dev, "releasing '%s'\n", dev_name(dev));
- ida_remove(&phy_ida, phy->id);
+ regulator_put(phy->pwr);
+ ida_simple_remove(&phy_ida, phy->id);
kfree(phy);
}