From 9d1fbc5d12824f882a477f331966517dbc2db5b2 Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Sun, 2 Nov 2014 21:13:53 +0100 Subject: add generic PHY framework This brings in the generix PHY framework from Linux. I tried to strip it down as much as possible while keeping it useful. Signed-off-by: Lucas Stach Signed-off-by: Sascha Hauer --- drivers/phy/Kconfig | 18 +++ drivers/phy/Makefile | 5 + drivers/phy/phy-core.c | 318 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 341 insertions(+) create mode 100644 drivers/phy/Kconfig create mode 100644 drivers/phy/Makefile create mode 100644 drivers/phy/phy-core.c (limited to 'drivers/phy') diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig new file mode 100644 index 0000000000..e9461e1a25 --- /dev/null +++ b/drivers/phy/Kconfig @@ -0,0 +1,18 @@ +# +# PHY +# + +menu "PHY Subsystem" + +config GENERIC_PHY + bool "PHY Core" + help + Generic PHY support. + + This framework is designed to provide a generic interface for PHY + devices present in the kernel. This layer will have the generic + API by which phy drivers can create PHY using the phy framework and + phy users can obtain reference to the PHY. All the users of this + framework should select this config. + +endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile new file mode 100644 index 0000000000..74514aeaed --- /dev/null +++ b/drivers/phy/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the phy drivers. +# + +obj-$(CONFIG_GENERIC_PHY) += phy-core.o diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c new file mode 100644 index 0000000000..67af14f680 --- /dev/null +++ b/drivers/phy/phy-core.c @@ -0,0 +1,318 @@ +/* + * phy-core.c -- Generic Phy framework. + * + * Copyright (C) 2014 Lucas Stach + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com + * + * Author: Kishon Vijay Abraham I + * + * 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 +#include +#include + +static LIST_HEAD(phy_provider_list); +static int phy_ida; + +/** + * 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_d *dev, struct device_node *node, + const struct phy_ops *ops, + struct phy_init_data *init_data) +{ + int ret; + int id; + struct phy *phy; + + if (WARN_ON(!dev)) + return ERR_PTR(-EINVAL); + + phy = kzalloc(sizeof(*phy), GFP_KERNEL); + if (!phy) + return ERR_PTR(-ENOMEM); + + id = phy_ida++; + + snprintf(phy->dev.name, MAX_DRIVER_NAME, "phy"); + phy->dev.id = id; + phy->dev.parent = dev; + phy->dev.device_node = node ?: dev->device_node; + phy->id = id; + phy->ops = ops; + phy->init_data = init_data; + + ret = register_device(&phy->dev); + if (ret) + goto free_ida; + + return phy; + +free_ida: + phy_ida--; + kfree(phy); + return ERR_PTR(ret); +} + +/** + * __of_phy_provider_register() - create/register phy provider with the framework + * @dev: struct device of the phy provider + * @owner: the module owner containing of_xlate + * @of_xlate: function pointer to obtain phy instance from phy provider + * + * Creates struct phy_provider from dev and of_xlate function pointer. + * This is used in the case of dt boot for finding the phy instance from + * phy provider. + */ +struct phy_provider *__of_phy_provider_register(struct device_d *dev, + struct phy * (*of_xlate)(struct device_d *dev, + struct of_phandle_args *args)) +{ + struct phy_provider *phy_provider; + + phy_provider = kzalloc(sizeof(*phy_provider), GFP_KERNEL); + if (!phy_provider) + return ERR_PTR(-ENOMEM); + + phy_provider->dev = dev; + phy_provider->of_xlate = of_xlate; + + list_add_tail(&phy_provider->list, &phy_provider_list); + + return phy_provider; +} + +/** + * of_phy_provider_unregister() - unregister phy provider from the framework + * @phy_provider: phy provider returned by of_phy_provider_register() + * + * Removes the phy_provider created using of_phy_provider_register(). + */ +void of_phy_provider_unregister(struct phy_provider *phy_provider) +{ + if (IS_ERR(phy_provider)) + return; + + list_del(&phy_provider->list); + kfree(phy_provider); +} + +int phy_init(struct phy *phy) +{ + int ret; + + if (!phy) + return 0; + + if (phy->init_count == 0 && phy->ops->init) { + ret = phy->ops->init(phy); + if (ret < 0) { + dev_err(&phy->dev, "phy init failed --> %d\n", ret); + return ret; + } + } + ++phy->init_count; + + return 0; +} + +int phy_exit(struct phy *phy) +{ + int ret; + + if (!phy) + return 0; + + if (phy->init_count == 1 && phy->ops->exit) { + ret = phy->ops->exit(phy); + if (ret < 0) { + dev_err(&phy->dev, "phy exit failed --> %d\n", ret); + return ret; + } + } + --phy->init_count; + + return 0; +} + +int phy_power_on(struct phy *phy) +{ + int ret; + + if (!phy) + return 0; + + if (phy->pwr) { + ret = regulator_enable(phy->pwr); + if (ret) + return ret; + } + + if (phy->power_count == 0 && phy->ops->power_on) { + ret = phy->ops->power_on(phy); + if (ret < 0) { + dev_err(&phy->dev, "phy poweron failed --> %d\n", ret); + goto out; + } + } else { + ret = 0; /* Override possible ret == -ENOTSUPP */ + } + ++phy->power_count; + + return 0; + +out: + if (phy->pwr) + regulator_disable(phy->pwr); + + return ret; +} + +int phy_power_off(struct phy *phy) +{ + int ret; + + if (!phy) + return 0; + + if (phy->power_count == 1 && phy->ops->power_off) { + ret = phy->ops->power_off(phy); + if (ret < 0) { + dev_err(&phy->dev, "phy poweroff failed --> %d\n", ret); + return ret; + } + } + --phy->power_count; + + if (phy->pwr) + regulator_disable(phy->pwr); + + return 0; +} + +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->device_node == node) + return phy_provider; + + for_each_child_of_node(phy_provider->dev->device_node, child) + if (child == node) + return phy_provider; + } + + return ERR_PTR(-ENODEV); +} + +/** + * _of_phy_get() - lookup and obtain a reference to a phy by phandle + * @np: device_node for which to get the phy + * @index: the index of the phy + * + * Returns the phy associated with the given phandle value, + * after getting a refcount to it or -ENODEV if there is no such phy or + * -EPROBE_DEFER if there is a phandle to the phy, but the device is + * not yet loaded. This function uses of_xlate call back function provided + * while registering the phy_provider to find the phy instance. + */ +static struct phy *_of_phy_get(struct device_node *np, int index) +{ + int ret; + struct phy_provider *phy_provider; + struct of_phandle_args args; + + ret = of_parse_phandle_with_args(np, "phys", "#phy-cells", + index, &args); + if (ret) + return ERR_PTR(-ENODEV); + + phy_provider = of_phy_provider_lookup(args.np); + if (IS_ERR(phy_provider)) { + return ERR_PTR(-ENODEV); + } + + return phy_provider->of_xlate(phy_provider->dev, &args); +} + +/** + * of_phy_get() - lookup and obtain a reference to a phy using a device_node. + * @np: device_node for which to get the phy + * @con_id: name of the phy from device's point of view + * + * Returns the phy driver, after getting a refcount to it; or + * -ENODEV if there is no such phy. The caller is responsible for + * calling phy_put() to release that count. + */ +struct phy *of_phy_get(struct device_node *np, const char *con_id) +{ + int index = 0; + + if (con_id) + index = of_property_match_string(np, "phy-names", con_id); + + return _of_phy_get(np, index); +} + +/** + * phy_get() - lookup and obtain a reference to a phy. + * @dev: device that requests this phy + * @string: the phy name as given in the dt data or the name of the controller + * port for non-dt case + * + * Returns the phy driver, after getting a refcount to it; or + * -ENODEV if there is no such phy. The caller is responsible for + * calling phy_put() to release that count. + */ +struct phy *phy_get(struct device_d *dev, const char *string) +{ + int index = 0; + struct phy *phy = ERR_PTR(-ENODEV); + + if (string == NULL) { + dev_warn(dev, "missing string\n"); + return ERR_PTR(-EINVAL); + } + + if (dev->device_node) { + index = of_property_match_string(dev->device_node, "phy-names", + string); + phy = _of_phy_get(dev->device_node, index); + } + + return phy; +} + +/** + * phy_optional_get() - lookup and obtain a reference to an optional phy. + * @dev: device that requests this phy + * @string: the phy name as given in the dt data or the name of the controller + * port for non-dt case + * + * Returns the phy driver, after getting a refcount to it; or + * NULL if there is no such phy. The caller is responsible for + * calling phy_put() to release that count. + */ +struct phy *phy_optional_get(struct device_d *dev, const char *string) +{ + struct phy *phy = phy_get(dev, string); + + if (PTR_ERR(phy) == -ENODEV) + phy = NULL; + + return phy; +} + -- cgit v1.2.3