Merge tag 'dt-for-3.17' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
[cascardo/linux.git] / drivers / phy / phy-core.c
index 49c4465..ff5eec5 100644 (file)
@@ -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;
@@ -617,6 +653,9 @@ put_dev:
        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);
@@ -626,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
  *
@@ -634,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;
 
@@ -643,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);
@@ -800,6 +841,7 @@ static void phy_release(struct device *dev)
 
        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);
 }