/*
* phy-core.c -- Generic Phy framework.
*
* Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
*
* Author: Kishon Vijay Abraham I <kishon@ti.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/kernel.h>
#include <linux/export.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/of.h>
#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 DEFINE_IDA(phy_ida);
static void devm_phy_release(struct device *dev, void *res)
{
struct phy *phy = *(struct phy **)res;
phy_put(phy);
}
static void devm_phy_provider_release(struct device *dev, void *res)
{
struct phy_provider *phy_provider = *(struct phy_provider **)res;
of_phy_provider_unregister(phy_provider);
}
static void devm_phy_consume(struct device *dev, void *res)
{
struct phy *phy = *(struct phy **)res;
phy_destroy(phy);
}
static int devm_phy_match(struct device *dev, void *res, void *match_data)
{
return res == match_data;
}
static struct phy *phy_lookup(struct device *device, const char *port)
{
unsigned int count;
struct phy *phy;
struct device *dev;
struct phy_consumer *consumers;
struct class_dev_iter iter;
class_dev_iter_init(&iter, phy_class, NULL, NULL);
while ((dev = class_dev_iter_next(&iter))) {
phy = to_phy(dev);
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++;
}
}
class_dev_iter_exit(&iter);
return 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);
}
int phy_pm_runtime_get(struct phy *phy)
{
int ret;
if (!pm_runtime_enabled(&phy->dev))
return -ENOTSUPP;
ret = pm_runtime_get(&phy->dev);
if (ret < 0 && ret != -EINPROGRESS)
pm_runtime_put_noidle(&phy->dev);
return ret;
}
EXPORT_SYMBOL_GPL(phy_pm_runtime_get);
int phy_pm_runtime_get_sync(struct phy *phy)
{
int ret;
if (!pm_runtime_enabled(&phy->dev))
return -ENOTSUPP;
ret = pm_runtime_get_sync(&phy->dev);
if (ret < 0)
pm_runtime_put_sync(&phy->dev);
return ret;
}
EXPORT_SYMBOL_GPL(phy_pm_runtime_get_sync);
int phy_pm_runtime_put(struct phy *phy)
{
if (!pm_runtime_enabled(&phy->dev))
return -ENOTSUPP;
return pm_runtime_put(&phy->dev);
}
EXPORT_SYMBOL_GPL(phy_pm_runtime_put);
int phy_pm_runtime_put_sync(struct phy *phy)
{
if (!pm_runtime_enabled(&phy->dev))
return -ENOTSUPP;
return pm_runtime_put_sync(&phy->dev);
}
EXPORT_SYMBOL_GPL(phy_pm_runtime_put_sync);
void phy_pm_runtime_allow(struct phy *phy)
{
if (!pm_runtime_enabled(&phy->dev))
return;
pm_runtime_allow(&phy->dev);
}
EXPORT_SYMBOL_GPL(phy_pm_runtime_allow);
void phy_pm_runtime_forbid(struct phy *phy)
{
if (!pm_runtime_enabled(&phy->dev))
return;
pm_runtime_forbid(&phy->dev);
}
EXPORT_SYMBOL_GPL(phy_pm_runtime_forbid);
int phy_init(struct phy *phy)
{
int ret;
if (!phy)
return 0;
ret = phy_pm_runtime_get_sync(phy);
if (ret < 0 && ret != -ENOTSUPP)
return ret;
mutex_lock(&phy->mutex);
if (phy->init_count == 0 && phy->ops->init) {
ret =