Message ID | 1347623899-14485-1-git-send-email-kishon@ti.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On 09/14/2012 01:58 PM, Kishon Vijay Abraham I wrote: > The PHY framework provides a set of API's for the PHY drivers to > create/remove a PHY and the PHY users to obtain a reference to the PHY > using or without using phandle. If the PHY users has to obtain a reference to > the PHY without using phandle, the platform specfic intialization code (say > from board file) should have already called phy_bind with the binding > information. The binding information consists of phy's device name, phy > user device name and an index. The index is used when the same phy user > binds to mulitple phys. > > PHY drivers should create the PHY by passing phy_descriptor that has > information about the PHY and ops like init, exit, suspend, resume, > poweron, shutdown. Some comments inside. While looking over the code, I was thinking why not abstract the phy with a "bus" in the linux kernel. The ethernet phys are on the mdio_bus, see /sys/bus/mdio_bus. This saves you hand crafting devices, drivers and bindings, Marc > > Nyet-signed-off-by: Kishon Vijay Abraham I <kishon@ti.com> > --- > This framework is actually intended to be used by all the PHY drivers in the > kernel. Though it's going to take a while for that, I intend to migrate > existing USB/OTG phy drivers to use this framework as we align on the design > of this framework. Once I migrate these phy drivers, I'll be able to test this > framework (I haven't tested this framework so far). I sent this patch early > so as to get review comments and align on the design. Thanks :-) > > drivers/Kconfig | 2 + > drivers/Makefile | 2 + > drivers/phy/Kconfig | 13 ++ > drivers/phy/Makefile | 5 + > drivers/phy/phy-core.c | 437 +++++++++++++++++++++++++++++++++++++++++++++++ > include/linux/phy/phy.h | 181 ++++++++++++++++++++ > 6 files changed, 640 insertions(+) > create mode 100644 drivers/phy/Kconfig > create mode 100644 drivers/phy/Makefile > create mode 100644 drivers/phy/phy-core.c > create mode 100644 include/linux/phy/phy.h > > diff --git a/drivers/Kconfig b/drivers/Kconfig > index ece958d..8488818 100644 > --- a/drivers/Kconfig > +++ b/drivers/Kconfig > @@ -152,4 +152,6 @@ source "drivers/vme/Kconfig" > > source "drivers/pwm/Kconfig" > > +source "drivers/phy/Kconfig" > + > endmenu > diff --git a/drivers/Makefile b/drivers/Makefile > index 5b42184..63d6bbe 100644 > --- a/drivers/Makefile > +++ b/drivers/Makefile > @@ -38,6 +38,8 @@ obj-y += char/ > # gpu/ comes after char for AGP vs DRM startup > obj-y += gpu/ > > +obj-y += phy/ > + > obj-$(CONFIG_CONNECTOR) += connector/ > > # i810fb and intelfb depend on char/agp/ > diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig > new file mode 100644 > index 0000000..34f7077 > --- /dev/null > +++ b/drivers/phy/Kconfig > @@ -0,0 +1,13 @@ > +# > +# PHY > +# > + > +menuconfig GENERIC_PHY > + tristate "Generic PHY Support" > + 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. > diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile > new file mode 100644 > index 0000000..9e9560f > --- /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 0000000..c55446a > --- /dev/null > +++ b/drivers/phy/phy-core.c > @@ -0,0 +1,437 @@ > +/* > + * phy-core.c -- Generic Phy framework. > + * > + * Copyright (C) 2012 Texas Instruments > + * > + * 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. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program. If not, see <http://www.gnu.org/licenses/>. > + */ > + > +#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> > + > +static struct class *phy_class; > +static LIST_HEAD(phy_list); > +static DEFINE_MUTEX(phy_list_mutex); > +static LIST_HEAD(phy_bind_list); > + > +static void devm_phy_release(struct device *dev, void *res) > +{ > + struct phy *phy = *(struct phy **)res; What about adding a struct phy_res, doing so,m you don't need these casts, and it's easier to add more pointers if needed. > + > + phy_put(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 *dev, u8 index) > +{ > + struct phy_bind *phy_bind = NULL; > + > + list_for_each_entry(phy_bind, &phy_bind_list, list) { > + if (!(strcmp(phy_bind->dev_name, dev_name(dev))) && > + phy_bind->index == index) > + return phy_bind->phy; > + } > + > + return ERR_PTR(-ENODEV); > +} > + > +static struct phy *of_phy_lookup(struct device *dev, struct device_node *node) > +{ > + int index = 0; > + struct phy *phy; ^^ Please remove that stray space. > + struct phy_bind *phy_map = NULL; > + > + list_for_each_entry(phy_map, &phy_bind_list, list) > + if (!(strcmp(phy_map->dev_name, dev_name(dev)))) > + index++; > + > + list_for_each_entry(phy, &phy_list, head) { > + if (node != phy->desc->of_node) > + continue; > + > + phy_map = phy_bind(dev_name(dev), index, dev_name(&phy->dev)); > + if (!IS_ERR(phy_map)) { > + phy_map->phy = phy; > + phy_map->auto_bind = true; > + } > + > + return phy; > + } > + > + return ERR_PTR(-ENODEV); > +} > + > +/** > + * devm_phy_get - lookup and obtain a reference to a phy. > + * @dev: device that requests this phy > + * @index: the index of the phy > + * > + * Gets the phy using phy_get(), and associates a device with it using > + * devres. On driver detach, release function is invoked on the devres data, > + * then, devres data is freed. > + */ > +struct phy *devm_phy_get(struct device *dev, u8 index) > +{ > + struct phy **ptr, *phy; > + > + ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL); > + if (!ptr) > + return NULL; > + > + phy = phy_get(dev, index); > + if (!IS_ERR(phy)) { > + *ptr = phy; > + devres_add(dev, ptr); > + } else > + devres_free(ptr); nitpick: when when if has { }, else should have, too. > + > + return phy; > +} > +EXPORT_SYMBOL_GPL(devm_phy_get); > + > +/** > + * devm_phy_put - release the PHY > + * @dev: device that wants to release this phy > + * @phy: the phy returned by devm_phy_get() > + * > + * destroys the devres associated with this phy and invokes phy_put > + * to release the phy. > + */ > +void devm_phy_put(struct device *dev, struct phy *phy) > +{ > + int r; > + > + r = devres_destroy(dev, devm_phy_release, devm_phy_match, phy); > + dev_WARN_ONCE(dev, r, "couldn't find PHY resource\n"); > +} > +EXPORT_SYMBOL_GPL(devm_phy_put); > + > +/** > + * devm_of_phy_get - lookup and obtain a reference to a phy by phandle > + * @dev: device that requests this phy > + * @phandle: name of the property holding the phy phandle value > + * > + * Returns the phy driver associated with the given phandle value, > + * after getting a refcount to it or -ENODEV if there is no such phy. > + * 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_of_phy_get(struct device *dev, const char *phandle) We should discuss first how the DT binding for a phy should look like. I don't like that you hardcode the index (in of_parse_phandle()) to "0". Then we have the problem with USB2 and USB3 phys for the same usb device. > +{ > + struct phy *phy = NULL, **ptr; > + struct device_node *node; > + > + if (!dev->of_node) { > + dev_dbg(dev, "device does not have a device node entry\n"); > + return ERR_PTR(-EINVAL); > + } > + > + node = of_parse_phandle(dev->of_node, phandle, 0); > + if (!node) { > + dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle, > + dev->of_node->full_name); > + return ERR_PTR(-ENODEV); > + } > + > + ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL); > + if (!ptr) { > + dev_dbg(dev, "failed to allocate memory for devres\n"); > + return ERR_PTR(-ENOMEM); > + } > + > + mutex_lock(&phy_list_mutex); > + > + phy = of_phy_lookup(dev, node); > + if (IS_ERR(phy) || !try_module_get(phy->dev.driver->owner)) { > + devres_free(ptr); > + goto err0; > + } Please return -EPROBE_DEFER is the phy is not yet registered. > + > + *ptr = phy; > + devres_add(dev, ptr); > + > + get_device(&phy->dev); > + > +err0: > + mutex_unlock(&phy_list_mutex); > + > + return phy; > +} > +EXPORT_SYMBOL_GPL(devm_of_phy_get); > + > +/** > + * phy_get - lookup and obtain a reference to a phy. > + * @dev: device that requests this phy > + * @index: the index of the phy > + * > + * 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 *dev, u8 index) > +{ > + struct phy *phy = NULL; > + > + mutex_lock(&phy_list_mutex); > + > + phy = phy_lookup(dev, index); > + if (IS_ERR(phy)) { > + pr_err("unable to find phy\n"); dev_err() > + goto err0; > + } > + > + get_device(&phy->dev); > + > +err0: > + mutex_unlock(&phy_list_mutex); > + > + return phy; > +} > +EXPORT_SYMBOL_GPL(phy_get); > + > +/** > + * phy_put - release the PHY > + * @phy: the phy returned by phy_get() > + * > + * Releases a refcount the caller received from phy_get(). > + */ > +void phy_put(struct phy *phy) > +{ > + if (phy) > + put_device(&phy->dev); > +} > +EXPORT_SYMBOL_GPL(phy_put); > + > +/** > + * create_phy - create a new phy > + * @dev: device that is creating the new phy > + * @desc: descriptor of the phy > + * > + * Called to create a phy using phy framework. > + */ > +struct phy *create_phy(struct device *dev, struct phy_descriptor *desc) All other functions are named phy_*, please rename this, too. > +{ > + int ret; > + struct phy *phy; > + struct phy_bind *phy_bind; > + const char *devname = NULL; > + > + if (!dev || !desc) { > + dev_err(dev, "no descriptor/device provided for PHY\n"); > + ret = -EINVAL; > + goto err0; > + } > + > + if (!desc->ops) { > + dev_err(dev, "no PHY ops provided\n"); > + ret = -EINVAL; > + goto err0; > + } > + > + phy = kzalloc(sizeof(*phy), GFP_KERNEL); > + if (!phy) { > + dev_err(dev, "no memory for PHY\n"); > + ret = -ENOMEM; > + goto err0; > + } > + > + devname = dev_name(dev); > + device_initialize(&phy->dev); > + phy->desc = desc; > + phy->dev.class = phy_class; > + phy->dev.parent = dev; > + ret = dev_set_name(&phy->dev, "%s", devname); > + if (ret) > + goto err1; > + > + mutex_lock(&phy_list_mutex); > + list_for_each_entry(phy_bind, &phy_bind_list, list) > + if (!(strcmp(phy_bind->phy_dev_name, devname))) > + phy_bind->phy = phy; > + > + list_add_tail(&phy->head, &phy_list); > + > + ret = device_add(&phy->dev); > + if (ret) > + goto err2; > + > + mutex_unlock(&phy_list_mutex); > + > + return phy; > + > +err2: > + phy_bind->phy = NULL; > + list_del(&phy->head); > + mutex_unlock(&phy_list_mutex); > + > +err1: > + put_device(&phy->dev); > + kfree(phy); > + > +err0: > + return ERR_PTR(ret); > +} > +EXPORT_SYMBOL_GPL(create_phy); > + > +/** > + * destroy_phy - destroy the phy > + * @phy: the phy to be destroyed > + * > + * Called to destroy the phy. > + */ > +void destroy_phy(struct phy *phy) > +{ > + struct phy_bind *phy_bind; > + > + mutex_lock(&phy_list_mutex); > + list_for_each_entry(phy_bind, &phy_bind_list, list) { > + if (phy_bind->phy == phy) > + phy_bind->phy = NULL; > + > + if (phy_bind->auto_bind) { > + list_del(&phy_bind->list); > + kfree(phy_bind); > + } > + } > + > + list_del(&phy->head); > + mutex_unlock(&phy_list_mutex); > + > + device_unregister(&phy->dev); > +} > +EXPORT_SYMBOL_GPL(destroy_phy); > + > +/** > + * phy_bind - bind the phy and the controller that uses the phy > + * @dev_name: the device name of the device that will bind to the phy > + * @index: index to specify the port number > + * @phy_dev_name: the device name of the phy > + * > + * Fills the phy_bind structure with the dev_name and phy_dev_name. This will > + * be used when the phy driver registers the phy and when the controller > + * requests this phy. > + * > + * To be used by platform specific initialization code. > + */ > +struct phy_bind *phy_bind(const char *dev_name, u8 index, > + const char *phy_dev_name) > +{ > + struct phy_bind *phy_bind; > + > + phy_bind = kzalloc(sizeof(*phy_bind), GFP_KERNEL); > + if (!phy_bind) { > + pr_err("phy_bind(): No memory for phy_bind"); > + return ERR_PTR(-ENOMEM); > + } > + > + phy_bind->dev_name = dev_name; > + phy_bind->phy_dev_name = phy_dev_name; > + phy_bind->index = index; > + phy_bind->auto_bind = false; > + > + list_add_tail(&phy_bind->list, &phy_bind_list); > + > + return phy_bind; > +} > +EXPORT_SYMBOL_GPL(phy_bind); > + > +static ssize_t phy_name_show(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct phy *phy; > + > + phy = container_of(dev, struct phy, dev); > + > + return sprintf(buf, "%s\n", phy->desc->label); > +} > + > +static ssize_t phy_bind_show(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct phy_bind *phy_bind; > + struct phy *phy; > + char *p = buf; > + int len; > + > + phy = container_of(dev, struct phy, dev); > + > + list_for_each_entry(phy_bind, &phy_bind_list, list) > + if (phy_bind->phy == phy) > + p += sprintf(p, "%s %d\n", phy_bind->dev_name, > + phy_bind->index); > + len = (p - buf); > + > + return len; > +} > + > +static struct device_attribute phy_dev_attrs[] = { > + __ATTR(label, 0444, phy_name_show, NULL), > + __ATTR(bind, 0444, phy_bind_show, NULL), > + __ATTR_NULL, > +}; > + > +/** > + * phy_release - release the phy > + * @dev: the dev member within phy > + * > + * when the last reference to the device is removed; it is called > + * from the embedded kobject as release method. > + */ > +static void phy_release(struct device *dev) > +{ > + struct phy *phy; > + > + phy = container_of(dev, struct phy, dev); > + dev_dbg(dev, "releasing '%s'\n", dev_name(dev)); > + kfree(phy); > +} > + > +static int __init phy_core_init(void) > +{ > + phy_class = class_create(THIS_MODULE, "phy"); > + if (IS_ERR(phy_class)) { > + pr_err("failed to create phy class --> %ld\n", > + PTR_ERR(phy_class)); > + return PTR_ERR(phy_class); > + } > + > + phy_class->dev_release = phy_release; > + phy_class->dev_attrs = phy_dev_attrs; > + > + return 0; > +} > +subsys_initcall(phy_core_init); > + > +static void __exit phy_core_exit(void) > +{ > + class_destroy(phy_class); > +} > +module_exit(phy_core_exit); > + > +MODULE_DESCRIPTION("Generic Phy Framework"); > +MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>"); > +MODULE_LICENSE("GPL v2"); > diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h > new file mode 100644 > index 0000000..831c1c0 > --- /dev/null > +++ b/include/linux/phy/phy.h > @@ -0,0 +1,181 @@ > +/* > + * phy.h -- generic phy header file > + * > + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.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. > + * > + * Author: Kishon Vijay Abraham I <kishon@ti.com> > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + */ > + > +#ifndef __DRIVERS_PHY_H > +#define __DRIVERS_PHY_H > + > +struct phy; > + > +/** > + * struct phy_descriptor - structure that describes the PHY > + * @label: label given to phy > + * @type: specifies the phy type > + * @of_node: device node of the phy > + * @ops: function pointers for performing phy operations > + */ > +struct phy_descriptor { > + const char *label; > + int type; > + struct device_node *of_node; > + struct phy_ops *ops; > +}; > + > +/** > + * struct phy_ops - set of function pointers for performing phy operations > + * @init: operation to be performed for initializing phy > + * @exit: operation to be performed while exiting > + * @suspend: suspending the phy > + * @resume: resuming the phy > + * @poweron: powering on the phy > + * @shutdown: shutting down the phy > + */ > +struct phy_ops { > + int (*init)(struct phy_descriptor *desc); > + int (*exit)(struct phy_descriptor *desc); > + int (*suspend)(struct phy_descriptor *desc); > + int (*resume)(struct phy_descriptor *desc); > + int (*poweron)(struct phy_descriptor *desc); > + int (*shutdown)(struct phy_descriptor *desc); > +}; > + > +/** > + * struct phy - represent the phy device > + * @desc: descriptor for the phy > + * @head: to support multiple transceivers > + */ > +struct phy { > + struct device dev; > + struct phy_descriptor *desc; > + struct list_head head; > +}; > + > +/** > + * struct phy_bind - represent the binding for the phy > + * @dev_name: the device name of the device that will bind to the phy > + * @phy_dev_name: the device name of the phy > + * @index: used if a single controller uses multiple phys > + * @auto_bind: tells if the binding is done explicitly from board file or not > + * @phy: reference to the phy > + * @list: to maintain a linked list of the binding information > + */ > +struct phy_bind { > + const char *dev_name; > + const char *phy_dev_name; > + u8 index; > + int auto_bind:1; > + struct phy *phy; > + struct list_head list; > +}; > + > +#if defined(CONFIG_GENERIC_PHY) || defined(CONFIG_GENERIC_PHY_MODULE) > +extern struct phy *devm_phy_get(struct device *dev, u8 index); > +extern void devm_phy_put(struct device *dev, struct phy *phy); > +extern struct phy *devm_of_phy_get(struct device *dev, const char *phandle); > +extern struct phy *phy_get(struct device *dev, u8 index); > +extern void phy_put(struct phy *phy); > +extern struct phy *create_phy(struct device *dev, struct phy_descriptor *desc); > +extern void destroy_phy(struct phy *phy); > +extern struct phy_bind *phy_bind(const char *dev_name, u8 index, > + const char *phy_dev_name); > +#else > +static inline struct phy *devm_phy_get(struct device *dev, u8 index) > +{ > + return ERR_PTR(-EINVAL); > +} > + > +static inline void devm_phy_put(struct device *dev, struct phy *phy) > +{ > +} > + > +static inline struct phy *devm_of_phy_get(struct device *dev, > + const char *phandle) > +{ > + return ERR_PTR(-EINVAL); > +} > + > +static inline struct phy *phy_get(struct device *dev, u8 index) > +{ > + return ERR_PTR(-EINVAL); > +} > + > +static inline void phy_put(struct phy *phy) > +{ > +} > + > +static inline struct phy *create_phy(struct device *dev, > + struct phy_descriptor *desc) > +{ > + return ERR_PTR(-EINVAL); > +} > + > +static inline void destroy_phy(struct phy *phy) > +{ > +} > + > +static inline struct phy_bind *phy_bind(const char *dev_name, u8 index, > + const char *phy_dev_name) > +{ > +} > +#endif > + > +static inline int phy_init(struct phy *phy) > +{ > + if (phy->desc->ops->init) > + return phy->desc->ops->init(phy->desc); > + > + return -EINVAL; > +} > + > +static inline int phy_exit(struct phy *phy) > +{ > + if (phy->desc->ops->exit) > + return phy->desc->ops->exit(phy->desc); > + > + return -EINVAL; > +} > + > +static inline int phy_suspend(struct phy *phy) > +{ > + if (phy->desc->ops->suspend) > + return phy->desc->ops->suspend(phy->desc); > + > + return -EINVAL; > +} > + > +static inline int phy_resume(struct phy *phy) > +{ > + if (phy->desc->ops->resume) > + return phy->desc->ops->resume(phy->desc); > + > + return -EINVAL; > +} > + > +static inline int phy_poweron(struct phy *phy) > +{ > + if (phy->desc->ops->poweron) > + return phy->desc->ops->poweron(phy->desc); > + > + return -EINVAL; > +} > + > +static inline void phy_shutdown(struct phy *phy) > +{ > + if (phy->desc->ops->shutdown) > + phy->desc->ops->shutdown(phy->desc); > +} > +#endif /* __DRIVERS_PHY_H */ >
Hi, On Fri, Sep 14, 2012 at 5:58 PM, Marc Kleine-Budde <mkl@pengutronix.de> wrote: > On 09/14/2012 01:58 PM, Kishon Vijay Abraham I wrote: >> The PHY framework provides a set of API's for the PHY drivers to >> create/remove a PHY and the PHY users to obtain a reference to the PHY >> using or without using phandle. If the PHY users has to obtain a reference to >> the PHY without using phandle, the platform specfic intialization code (say >> from board file) should have already called phy_bind with the binding >> information. The binding information consists of phy's device name, phy >> user device name and an index. The index is used when the same phy user >> binds to mulitple phys. >> >> PHY drivers should create the PHY by passing phy_descriptor that has >> information about the PHY and ops like init, exit, suspend, resume, >> poweron, shutdown. > > Some comments inside. > > While looking over the code, I was thinking why not abstract the phy > with a "bus" in the linux kernel. The ethernet phys are on the mdio_bus, > see /sys/bus/mdio_bus. This saves you hand crafting devices, drivers and > bindings, well... have to think about it. > > Marc > >> >> Nyet-signed-off-by: Kishon Vijay Abraham I <kishon@ti.com> >> --- >> This framework is actually intended to be used by all the PHY drivers in the >> kernel. Though it's going to take a while for that, I intend to migrate >> existing USB/OTG phy drivers to use this framework as we align on the design >> of this framework. Once I migrate these phy drivers, I'll be able to test this >> framework (I haven't tested this framework so far). I sent this patch early >> so as to get review comments and align on the design. Thanks :-) >> >> drivers/Kconfig | 2 + >> drivers/Makefile | 2 + >> drivers/phy/Kconfig | 13 ++ >> drivers/phy/Makefile | 5 + >> drivers/phy/phy-core.c | 437 +++++++++++++++++++++++++++++++++++++++++++++++ >> include/linux/phy/phy.h | 181 ++++++++++++++++++++ >> 6 files changed, 640 insertions(+) >> create mode 100644 drivers/phy/Kconfig >> create mode 100644 drivers/phy/Makefile >> create mode 100644 drivers/phy/phy-core.c >> create mode 100644 include/linux/phy/phy.h >> >> diff --git a/drivers/Kconfig b/drivers/Kconfig >> index ece958d..8488818 100644 >> --- a/drivers/Kconfig >> +++ b/drivers/Kconfig >> @@ -152,4 +152,6 @@ source "drivers/vme/Kconfig" >> >> source "drivers/pwm/Kconfig" >> >> +source "drivers/phy/Kconfig" >> + >> endmenu >> diff --git a/drivers/Makefile b/drivers/Makefile >> index 5b42184..63d6bbe 100644 >> --- a/drivers/Makefile >> +++ b/drivers/Makefile >> @@ -38,6 +38,8 @@ obj-y += char/ >> # gpu/ comes after char for AGP vs DRM startup >> obj-y += gpu/ >> >> +obj-y += phy/ >> + >> obj-$(CONFIG_CONNECTOR) += connector/ >> >> # i810fb and intelfb depend on char/agp/ >> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig >> new file mode 100644 >> index 0000000..34f7077 >> --- /dev/null >> +++ b/drivers/phy/Kconfig >> @@ -0,0 +1,13 @@ >> +# >> +# PHY >> +# >> + >> +menuconfig GENERIC_PHY >> + tristate "Generic PHY Support" >> + 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. >> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile >> new file mode 100644 >> index 0000000..9e9560f >> --- /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 0000000..c55446a >> --- /dev/null >> +++ b/drivers/phy/phy-core.c >> @@ -0,0 +1,437 @@ >> +/* >> + * phy-core.c -- Generic Phy framework. >> + * >> + * Copyright (C) 2012 Texas Instruments >> + * >> + * 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. >> + * >> + * This program is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >> + * GNU General Public License for more details. >> + * >> + * You should have received a copy of the GNU General Public License >> + * along with this program. If not, see <http://www.gnu.org/licenses/>. >> + */ >> + >> +#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> >> + >> +static struct class *phy_class; >> +static LIST_HEAD(phy_list); >> +static DEFINE_MUTEX(phy_list_mutex); >> +static LIST_HEAD(phy_bind_list); >> + >> +static void devm_phy_release(struct device *dev, void *res) >> +{ >> + struct phy *phy = *(struct phy **)res; > > What about adding a struct phy_res, doing so,m you don't need these > casts, and it's easier to add more pointers if needed. Wont we still need to do the cast since you get only a void pointer. Maybe I'm not getting you. > >> + >> + phy_put(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 *dev, u8 index) >> +{ >> + struct phy_bind *phy_bind = NULL; >> + >> + list_for_each_entry(phy_bind, &phy_bind_list, list) { >> + if (!(strcmp(phy_bind->dev_name, dev_name(dev))) && >> + phy_bind->index == index) >> + return phy_bind->phy; >> + } >> + >> + return ERR_PTR(-ENODEV); >> +} >> + >> +static struct phy *of_phy_lookup(struct device *dev, struct device_node *node) >> +{ >> + int index = 0; >> + struct phy *phy; > ^^ > > Please remove that stray space. Sure. > >> + struct phy_bind *phy_map = NULL; >> + >> + list_for_each_entry(phy_map, &phy_bind_list, list) >> + if (!(strcmp(phy_map->dev_name, dev_name(dev)))) >> + index++; >> + >> + list_for_each_entry(phy, &phy_list, head) { >> + if (node != phy->desc->of_node) >> + continue; >> + >> + phy_map = phy_bind(dev_name(dev), index, dev_name(&phy->dev)); >> + if (!IS_ERR(phy_map)) { >> + phy_map->phy = phy; >> + phy_map->auto_bind = true; >> + } >> + >> + return phy; >> + } >> + >> + return ERR_PTR(-ENODEV); >> +} >> + >> +/** >> + * devm_phy_get - lookup and obtain a reference to a phy. >> + * @dev: device that requests this phy >> + * @index: the index of the phy >> + * >> + * Gets the phy using phy_get(), and associates a device with it using >> + * devres. On driver detach, release function is invoked on the devres data, >> + * then, devres data is freed. >> + */ >> +struct phy *devm_phy_get(struct device *dev, u8 index) >> +{ >> + struct phy **ptr, *phy; >> + >> + ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL); >> + if (!ptr) >> + return NULL; >> + >> + phy = phy_get(dev, index); >> + if (!IS_ERR(phy)) { >> + *ptr = phy; >> + devres_add(dev, ptr); >> + } else >> + devres_free(ptr); > > nitpick: when when if has { }, else should have, too. Sure. > >> + >> + return phy; >> +} >> +EXPORT_SYMBOL_GPL(devm_phy_get); >> + >> +/** >> + * devm_phy_put - release the PHY >> + * @dev: device that wants to release this phy >> + * @phy: the phy returned by devm_phy_get() >> + * >> + * destroys the devres associated with this phy and invokes phy_put >> + * to release the phy. >> + */ >> +void devm_phy_put(struct device *dev, struct phy *phy) >> +{ >> + int r; >> + >> + r = devres_destroy(dev, devm_phy_release, devm_phy_match, phy); >> + dev_WARN_ONCE(dev, r, "couldn't find PHY resource\n"); >> +} >> +EXPORT_SYMBOL_GPL(devm_phy_put); >> + >> +/** >> + * devm_of_phy_get - lookup and obtain a reference to a phy by phandle >> + * @dev: device that requests this phy >> + * @phandle: name of the property holding the phy phandle value >> + * >> + * Returns the phy driver associated with the given phandle value, >> + * after getting a refcount to it or -ENODEV if there is no such phy. >> + * 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_of_phy_get(struct device *dev, const char *phandle) > > We should discuss first how the DT binding for a phy should look like. I > don't like that you hardcode the index (in of_parse_phandle()) to "0". > Then we have the problem with USB2 and USB3 phys for the same usb device. I think then we should modify this API to take *index* which should be used when we have a single controller using multiple phys. By that we'll make both dt and non-dt similar in that, both of them will take this index. > >> +{ >> + struct phy *phy = NULL, **ptr; >> + struct device_node *node; >> + >> + if (!dev->of_node) { >> + dev_dbg(dev, "device does not have a device node entry\n"); >> + return ERR_PTR(-EINVAL); >> + } >> + >> + node = of_parse_phandle(dev->of_node, phandle, 0); >> + if (!node) { >> + dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle, >> + dev->of_node->full_name); >> + return ERR_PTR(-ENODEV); >> + } >> + >> + ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL); >> + if (!ptr) { >> + dev_dbg(dev, "failed to allocate memory for devres\n"); >> + return ERR_PTR(-ENOMEM); >> + } >> + >> + mutex_lock(&phy_list_mutex); >> + >> + phy = of_phy_lookup(dev, node); >> + if (IS_ERR(phy) || !try_module_get(phy->dev.driver->owner)) { >> + devres_free(ptr); >> + goto err0; >> + } > > Please return -EPROBE_DEFER is the phy is not yet registered. I really dont prefer to return -EPROBE_DEFER from the *framework*. IMHO, it's up-to the caller of this API to return *probe* errors. > >> + >> + *ptr = phy; >> + devres_add(dev, ptr); >> + >> + get_device(&phy->dev); >> + >> +err0: >> + mutex_unlock(&phy_list_mutex); >> + >> + return phy; >> +} >> +EXPORT_SYMBOL_GPL(devm_of_phy_get); >> + >> +/** >> + * phy_get - lookup and obtain a reference to a phy. >> + * @dev: device that requests this phy >> + * @index: the index of the phy >> + * >> + * 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 *dev, u8 index) >> +{ >> + struct phy *phy = NULL; >> + >> + mutex_lock(&phy_list_mutex); >> + >> + phy = phy_lookup(dev, index); >> + if (IS_ERR(phy)) { >> + pr_err("unable to find phy\n"); > > dev_err() Sure. > >> + goto err0; >> + } >> + >> + get_device(&phy->dev); >> + >> +err0: >> + mutex_unlock(&phy_list_mutex); >> + >> + return phy; >> +} >> +EXPORT_SYMBOL_GPL(phy_get); >> + >> +/** >> + * phy_put - release the PHY >> + * @phy: the phy returned by phy_get() >> + * >> + * Releases a refcount the caller received from phy_get(). >> + */ >> +void phy_put(struct phy *phy) >> +{ >> + if (phy) >> + put_device(&phy->dev); >> +} >> +EXPORT_SYMBOL_GPL(phy_put); >> + >> +/** >> + * create_phy - create a new phy >> + * @dev: device that is creating the new phy >> + * @desc: descriptor of the phy >> + * >> + * Called to create a phy using phy framework. >> + */ >> +struct phy *create_phy(struct device *dev, struct phy_descriptor *desc) > > All other functions are named phy_*, please rename this, too. Sure. Thanks Kishon -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Fri, Sep 14, 2012 at 02:28:19PM +0200, Marc Kleine-Budde wrote: > On 09/14/2012 01:58 PM, Kishon Vijay Abraham I wrote: > > The PHY framework provides a set of API's for the PHY drivers to > > create/remove a PHY and the PHY users to obtain a reference to the PHY > > using or without using phandle. If the PHY users has to obtain a reference to > > the PHY without using phandle, the platform specfic intialization code (say > > from board file) should have already called phy_bind with the binding > > information. The binding information consists of phy's device name, phy > > user device name and an index. The index is used when the same phy user > > binds to mulitple phys. > > > > PHY drivers should create the PHY by passing phy_descriptor that has > > information about the PHY and ops like init, exit, suspend, resume, > > poweron, shutdown. > > Some comments inside. > > While looking over the code, I was thinking why not abstract the phy > with a "bus" in the linux kernel. The ethernet phys are on the mdio_bus, > see /sys/bus/mdio_bus. This saves you hand crafting devices, drivers and > bindings, I don't think that's a good idea, actually. You can have USB PHYs which are memory mapped, or connected through i2c, or connected through any other bus. If the PHYs layer itself is a bus, it means we will need to register a device on two different buses, which doesn't sound very nice IMHO.
> > The PHY framework provides a set of API's for the PHY drivers to > create/remove a PHY and the PHY users to obtain a reference to the PHY > using or without using phandle. If the PHY users has to obtain a > reference to > the PHY without using phandle, the platform specfic intialization code > (say > from board file) should have already called phy_bind with the binding > information. The binding information consists of phy's device name, phy > user device name and an index. The index is used when the same phy user > binds to mulitple phys. > What's an example of "the same phy user binds to multiple phys"? I only remembered that Felipe said there are two phy users for one single phy at omap5 that is both usb3 and sata uses the same phy. -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi, On Mon, Sep 17, 2012 at 6:50 AM, Chen Peter-B29397 <B29397@freescale.com> wrote: > >> >> The PHY framework provides a set of API's for the PHY drivers to >> create/remove a PHY and the PHY users to obtain a reference to the PHY >> using or without using phandle. If the PHY users has to obtain a >> reference to >> the PHY without using phandle, the platform specfic intialization code >> (say >> from board file) should have already called phy_bind with the binding >> information. The binding information consists of phy's device name, phy >> user device name and an index. The index is used when the same phy user >> binds to mulitple phys. >> > > What's an example of "the same phy user binds to multiple phys"? Single controller using multiple phys.. > I only remembered that Felipe said there are two phy users for one single phy at > omap5 that is both usb3 and sata uses the same phy. *index* is used when a single controller uses multiple phys. For example it could be used for dwc3 (usb3 controller) where it uses usb2 phy and usb3 phy. Thanks Kishon -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On 09/14/2012 03:06 PM, ABRAHAM, KISHON VIJAY wrote: [...] >>> diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c >>> new file mode 100644 >>> index 0000000..c55446a >>> --- /dev/null >>> +++ b/drivers/phy/phy-core.c >>> @@ -0,0 +1,437 @@ >>> +/* >>> + * phy-core.c -- Generic Phy framework. >>> + * >>> + * Copyright (C) 2012 Texas Instruments >>> + * >>> + * 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. >>> + * >>> + * This program is distributed in the hope that it will be useful, >>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >>> + * GNU General Public License for more details. >>> + * >>> + * You should have received a copy of the GNU General Public License >>> + * along with this program. If not, see <http://www.gnu.org/licenses/>. >>> + */ >>> + >>> +#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> >>> + >>> +static struct class *phy_class; >>> +static LIST_HEAD(phy_list); >>> +static DEFINE_MUTEX(phy_list_mutex); >>> +static LIST_HEAD(phy_bind_list); >>> + >>> +static void devm_phy_release(struct device *dev, void *res) >>> +{ >>> + struct phy *phy = *(struct phy **)res; >> >> What about adding a struct phy_res, doing so,m you don't need these >> casts, and it's easier to add more pointers if needed. > > Wont we still need to do the cast since you get only a void pointer. > Maybe I'm not getting you. As "res" is a void pointer, no need to hast to to a "struct phy_res" pointer, if you think that's unclean code, you can still cast it. But IMHO the code is far more readable. >>> + >>> + phy_put(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 *dev, u8 index) >>> +{ >>> + struct phy_bind *phy_bind = NULL; >>> + >>> + list_for_each_entry(phy_bind, &phy_bind_list, list) { >>> + if (!(strcmp(phy_bind->dev_name, dev_name(dev))) && >>> + phy_bind->index == index) >>> + return phy_bind->phy; >>> + } >>> + >>> + return ERR_PTR(-ENODEV); >>> +} >>> + >>> +static struct phy *of_phy_lookup(struct device *dev, struct device_node *node) >>> +{ >>> + int index = 0; >>> + struct phy *phy; >> ^^ >> >> Please remove that stray space. > > Sure. >> >>> + struct phy_bind *phy_map = NULL; >>> + >>> + list_for_each_entry(phy_map, &phy_bind_list, list) >>> + if (!(strcmp(phy_map->dev_name, dev_name(dev)))) >>> + index++; >>> + >>> + list_for_each_entry(phy, &phy_list, head) { >>> + if (node != phy->desc->of_node) >>> + continue; >>> + >>> + phy_map = phy_bind(dev_name(dev), index, dev_name(&phy->dev)); >>> + if (!IS_ERR(phy_map)) { >>> + phy_map->phy = phy; >>> + phy_map->auto_bind = true; >>> + } >>> + >>> + return phy; >>> + } >>> + >>> + return ERR_PTR(-ENODEV); >>> +} >>> + >>> +/** >>> + * devm_phy_get - lookup and obtain a reference to a phy. >>> + * @dev: device that requests this phy >>> + * @index: the index of the phy >>> + * >>> + * Gets the phy using phy_get(), and associates a device with it using >>> + * devres. On driver detach, release function is invoked on the devres data, >>> + * then, devres data is freed. >>> + */ >>> +struct phy *devm_phy_get(struct device *dev, u8 index) >>> +{ >>> + struct phy **ptr, *phy; >>> + >>> + ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL); >>> + if (!ptr) >>> + return NULL; >>> + >>> + phy = phy_get(dev, index); >>> + if (!IS_ERR(phy)) { >>> + *ptr = phy; >>> + devres_add(dev, ptr); >>> + } else >>> + devres_free(ptr); >> >> nitpick: when when if has { }, else should have, too. > > Sure. >> >>> + >>> + return phy; >>> +} >>> +EXPORT_SYMBOL_GPL(devm_phy_get); >>> + >>> +/** >>> + * devm_phy_put - release the PHY >>> + * @dev: device that wants to release this phy >>> + * @phy: the phy returned by devm_phy_get() >>> + * >>> + * destroys the devres associated with this phy and invokes phy_put >>> + * to release the phy. >>> + */ >>> +void devm_phy_put(struct device *dev, struct phy *phy) >>> +{ >>> + int r; >>> + >>> + r = devres_destroy(dev, devm_phy_release, devm_phy_match, phy); >>> + dev_WARN_ONCE(dev, r, "couldn't find PHY resource\n"); >>> +} >>> +EXPORT_SYMBOL_GPL(devm_phy_put); >>> + >>> +/** >>> + * devm_of_phy_get - lookup and obtain a reference to a phy by phandle >>> + * @dev: device that requests this phy >>> + * @phandle: name of the property holding the phy phandle value >>> + * >>> + * Returns the phy driver associated with the given phandle value, >>> + * after getting a refcount to it or -ENODEV if there is no such phy. >>> + * 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_of_phy_get(struct device *dev, const char *phandle) >> >> We should discuss first how the DT binding for a phy should look like. I >> don't like that you hardcode the index (in of_parse_phandle()) to "0". >> Then we have the problem with USB2 and USB3 phys for the same usb device. > > I think then we should modify this API to take *index* which should be > used when we have a single controller using multiple phys. By that > we'll make both dt and non-dt similar in that, both of them will take > this index. That would be a plus. >> >>> +{ >>> + struct phy *phy = NULL, **ptr; nitpick: stray spaces >>> + struct device_node *node; >>> + >>> + if (!dev->of_node) { >>> + dev_dbg(dev, "device does not have a device node entry\n"); >>> + return ERR_PTR(-EINVAL); >>> + } >>> + >>> + node = of_parse_phandle(dev->of_node, phandle, 0); BTW: Is the node freed somewhere? >>> + if (!node) { >>> + dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle, >>> + dev->of_node->full_name); >>> + return ERR_PTR(-ENODEV); >>> + } >>> + >>> + ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL); >>> + if (!ptr) { >>> + dev_dbg(dev, "failed to allocate memory for devres\n"); >>> + return ERR_PTR(-ENOMEM); >>> + } >>> + >>> + mutex_lock(&phy_list_mutex); >>> + >>> + phy = of_phy_lookup(dev, node); >>> + if (IS_ERR(phy) || !try_module_get(phy->dev.driver->owner)) { Where's the corresponding module_put? You should add module_get to the non dt function, too. >>> + devres_free(ptr); >>> + goto err0; >>> + } >> >> Please return -EPROBE_DEFER is the phy is not yet registered. > > I really dont prefer to return -EPROBE_DEFER from the *framework*. > IMHO, it's up-to the caller of this API to return *probe* errors. If we leave it to the caller, you have to proper design your return values. There is a difference betweeen: a) the phandle + index point to a non existing device node b) the device node cannot be found in the list of phys (yet) Error a) is permanent (in case of a static DT). Case b) indeed can be solved by deferring the probe. Why should the framework not return -EPROBE_DEFER in this case? >> >>> + >>> + *ptr = phy; >>> + devres_add(dev, ptr); >>> + >>> + get_device(&phy->dev); >>> + >>> +err0: >>> + mutex_unlock(&phy_list_mutex); >>> + >>> + return phy; >>> +} >>> +EXPORT_SYMBOL_GPL(devm_of_phy_get); >>> + [...] > Marc
On Mon, Sep 17, 2012 at 11:19:53AM +0530, ABRAHAM, KISHON VIJAY wrote: > Hi, > > On Mon, Sep 17, 2012 at 6:50 AM, Chen Peter-B29397 <B29397@freescale.com> wrote: > > > >> > >> The PHY framework provides a set of API's for the PHY drivers to > >> create/remove a PHY and the PHY users to obtain a reference to the PHY > >> using or without using phandle. If the PHY users has to obtain a > >> reference to > >> the PHY without using phandle, the platform specfic intialization code > >> (say > >> from board file) should have already called phy_bind with the binding > >> information. The binding information consists of phy's device name, phy > >> user device name and an index. The index is used when the same phy user > >> binds to mulitple phys. > >> > > > > What's an example of "the same phy user binds to multiple phys"? > > Single controller using multiple phys.. to be more specific here: any usb3 controller needs a USB2 PHY and USB3 PHY.
Hi, On Mon, Sep 17, 2012 at 3:03 PM, Marc Kleine-Budde <mkl@pengutronix.de> wrote: > On 09/14/2012 03:06 PM, ABRAHAM, KISHON VIJAY wrote: > > [...] > >>>> diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c >>>> new file mode 100644 >>>> index 0000000..c55446a >>>> --- /dev/null >>>> +++ b/drivers/phy/phy-core.c >>>> @@ -0,0 +1,437 @@ >>>> +/* >>>> + * phy-core.c -- Generic Phy framework. >>>> + * >>>> + * Copyright (C) 2012 Texas Instruments >>>> + * >>>> + * 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. >>>> + * >>>> + * This program is distributed in the hope that it will be useful, >>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >>>> + * GNU General Public License for more details. >>>> + * >>>> + * You should have received a copy of the GNU General Public License >>>> + * along with this program. If not, see <http://www.gnu.org/licenses/>. >>>> + */ >>>> + >>>> +#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> >>>> + >>>> +static struct class *phy_class; >>>> +static LIST_HEAD(phy_list); >>>> +static DEFINE_MUTEX(phy_list_mutex); >>>> +static LIST_HEAD(phy_bind_list); >>>> + >>>> +static void devm_phy_release(struct device *dev, void *res) >>>> +{ >>>> + struct phy *phy = *(struct phy **)res; >>> >>> What about adding a struct phy_res, doing so,m you don't need these >>> casts, and it's easier to add more pointers if needed. >> >> Wont we still need to do the cast since you get only a void pointer. >> Maybe I'm not getting you. > > As "res" is a void pointer, no need to hast to to a "struct phy_res" > pointer, if you think that's unclean code, you can still cast it. But > IMHO the code is far more readable. > >>>> + >>>> + phy_put(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 *dev, u8 index) >>>> +{ >>>> + struct phy_bind *phy_bind = NULL; >>>> + >>>> + list_for_each_entry(phy_bind, &phy_bind_list, list) { >>>> + if (!(strcmp(phy_bind->dev_name, dev_name(dev))) && >>>> + phy_bind->index == index) >>>> + return phy_bind->phy; >>>> + } >>>> + >>>> + return ERR_PTR(-ENODEV); >>>> +} >>>> + >>>> +static struct phy *of_phy_lookup(struct device *dev, struct device_node *node) >>>> +{ >>>> + int index = 0; >>>> + struct phy *phy; >>> ^^ >>> >>> Please remove that stray space. >> >> Sure. >>> >>>> + struct phy_bind *phy_map = NULL; >>>> + >>>> + list_for_each_entry(phy_map, &phy_bind_list, list) >>>> + if (!(strcmp(phy_map->dev_name, dev_name(dev)))) >>>> + index++; >>>> + >>>> + list_for_each_entry(phy, &phy_list, head) { >>>> + if (node != phy->desc->of_node) >>>> + continue; >>>> + >>>> + phy_map = phy_bind(dev_name(dev), index, dev_name(&phy->dev)); >>>> + if (!IS_ERR(phy_map)) { >>>> + phy_map->phy = phy; >>>> + phy_map->auto_bind = true; >>>> + } >>>> + >>>> + return phy; >>>> + } >>>> + >>>> + return ERR_PTR(-ENODEV); >>>> +} >>>> + >>>> +/** >>>> + * devm_phy_get - lookup and obtain a reference to a phy. >>>> + * @dev: device that requests this phy >>>> + * @index: the index of the phy >>>> + * >>>> + * Gets the phy using phy_get(), and associates a device with it using >>>> + * devres. On driver detach, release function is invoked on the devres data, >>>> + * then, devres data is freed. >>>> + */ >>>> +struct phy *devm_phy_get(struct device *dev, u8 index) >>>> +{ >>>> + struct phy **ptr, *phy; >>>> + >>>> + ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL); >>>> + if (!ptr) >>>> + return NULL; >>>> + >>>> + phy = phy_get(dev, index); >>>> + if (!IS_ERR(phy)) { >>>> + *ptr = phy; >>>> + devres_add(dev, ptr); >>>> + } else >>>> + devres_free(ptr); >>> >>> nitpick: when when if has { }, else should have, too. >> >> Sure. >>> >>>> + >>>> + return phy; >>>> +} >>>> +EXPORT_SYMBOL_GPL(devm_phy_get); >>>> + >>>> +/** >>>> + * devm_phy_put - release the PHY >>>> + * @dev: device that wants to release this phy >>>> + * @phy: the phy returned by devm_phy_get() >>>> + * >>>> + * destroys the devres associated with this phy and invokes phy_put >>>> + * to release the phy. >>>> + */ >>>> +void devm_phy_put(struct device *dev, struct phy *phy) >>>> +{ >>>> + int r; >>>> + >>>> + r = devres_destroy(dev, devm_phy_release, devm_phy_match, phy); >>>> + dev_WARN_ONCE(dev, r, "couldn't find PHY resource\n"); >>>> +} >>>> +EXPORT_SYMBOL_GPL(devm_phy_put); >>>> + >>>> +/** >>>> + * devm_of_phy_get - lookup and obtain a reference to a phy by phandle >>>> + * @dev: device that requests this phy >>>> + * @phandle: name of the property holding the phy phandle value >>>> + * >>>> + * Returns the phy driver associated with the given phandle value, >>>> + * after getting a refcount to it or -ENODEV if there is no such phy. >>>> + * 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_of_phy_get(struct device *dev, const char *phandle) >>> >>> We should discuss first how the DT binding for a phy should look like. I >>> don't like that you hardcode the index (in of_parse_phandle()) to "0". >>> Then we have the problem with USB2 and USB3 phys for the same usb device. >> >> I think then we should modify this API to take *index* which should be >> used when we have a single controller using multiple phys. By that >> we'll make both dt and non-dt similar in that, both of them will take >> this index. > > That would be a plus. > >>> >>>> +{ >>>> + struct phy *phy = NULL, **ptr; > > nitpick: stray spaces > >>>> + struct device_node *node; >>>> + >>>> + if (!dev->of_node) { >>>> + dev_dbg(dev, "device does not have a device node entry\n"); >>>> + return ERR_PTR(-EINVAL); >>>> + } >>>> + >>>> + node = of_parse_phandle(dev->of_node, phandle, 0); > > BTW: Is the node freed somewhere? > >>>> + if (!node) { >>>> + dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle, >>>> + dev->of_node->full_name); >>>> + return ERR_PTR(-ENODEV); >>>> + } >>>> + >>>> + ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL); >>>> + if (!ptr) { >>>> + dev_dbg(dev, "failed to allocate memory for devres\n"); >>>> + return ERR_PTR(-ENOMEM); >>>> + } >>>> + >>>> + mutex_lock(&phy_list_mutex); >>>> + >>>> + phy = of_phy_lookup(dev, node); >>>> + if (IS_ERR(phy) || !try_module_get(phy->dev.driver->owner)) { > > Where's the corresponding module_put? You should add module_get to the > non dt function, too. Do you think try_module_get() is necessary here? And just figured out *this* phy device does not have a associated driver (so phy->dev.driver will be NULL). Thanks Kishon -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On 09/26/2012 11:20 AM, ABRAHAM, KISHON VIJAY wrote: > Hi, > > On Mon, Sep 17, 2012 at 3:03 PM, Marc Kleine-Budde <mkl@pengutronix.de> wrote: >> On 09/14/2012 03:06 PM, ABRAHAM, KISHON VIJAY wrote: >> >> [...] >> >>>>> diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c >>>>> new file mode 100644 >>>>> index 0000000..c55446a >>>>> --- /dev/null >>>>> +++ b/drivers/phy/phy-core.c >>>>> @@ -0,0 +1,437 @@ >>>>> +/* >>>>> + * phy-core.c -- Generic Phy framework. >>>>> + * >>>>> + * Copyright (C) 2012 Texas Instruments >>>>> + * >>>>> + * 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. >>>>> + * >>>>> + * This program is distributed in the hope that it will be useful, >>>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >>>>> + * GNU General Public License for more details. >>>>> + * >>>>> + * You should have received a copy of the GNU General Public License >>>>> + * along with this program. If not, see <http://www.gnu.org/licenses/>. >>>>> + */ >>>>> + >>>>> +#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> >>>>> + >>>>> +static struct class *phy_class; >>>>> +static LIST_HEAD(phy_list); >>>>> +static DEFINE_MUTEX(phy_list_mutex); >>>>> +static LIST_HEAD(phy_bind_list); >>>>> + >>>>> +static void devm_phy_release(struct device *dev, void *res) >>>>> +{ >>>>> + struct phy *phy = *(struct phy **)res; >>>> >>>> What about adding a struct phy_res, doing so,m you don't need these >>>> casts, and it's easier to add more pointers if needed. >>> >>> Wont we still need to do the cast since you get only a void pointer. >>> Maybe I'm not getting you. >> >> As "res" is a void pointer, no need to hast to to a "struct phy_res" >> pointer, if you think that's unclean code, you can still cast it. But >> IMHO the code is far more readable. >> >>>>> + >>>>> + phy_put(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 *dev, u8 index) >>>>> +{ >>>>> + struct phy_bind *phy_bind = NULL; >>>>> + >>>>> + list_for_each_entry(phy_bind, &phy_bind_list, list) { >>>>> + if (!(strcmp(phy_bind->dev_name, dev_name(dev))) && >>>>> + phy_bind->index == index) >>>>> + return phy_bind->phy; >>>>> + } >>>>> + >>>>> + return ERR_PTR(-ENODEV); >>>>> +} >>>>> + >>>>> +static struct phy *of_phy_lookup(struct device *dev, struct device_node *node) >>>>> +{ >>>>> + int index = 0; >>>>> + struct phy *phy; >>>> ^^ >>>> >>>> Please remove that stray space. >>> >>> Sure. >>>> >>>>> + struct phy_bind *phy_map = NULL; >>>>> + >>>>> + list_for_each_entry(phy_map, &phy_bind_list, list) >>>>> + if (!(strcmp(phy_map->dev_name, dev_name(dev)))) >>>>> + index++; >>>>> + >>>>> + list_for_each_entry(phy, &phy_list, head) { >>>>> + if (node != phy->desc->of_node) >>>>> + continue; >>>>> + >>>>> + phy_map = phy_bind(dev_name(dev), index, dev_name(&phy->dev)); >>>>> + if (!IS_ERR(phy_map)) { >>>>> + phy_map->phy = phy; >>>>> + phy_map->auto_bind = true; >>>>> + } >>>>> + >>>>> + return phy; >>>>> + } >>>>> + >>>>> + return ERR_PTR(-ENODEV); >>>>> +} >>>>> + >>>>> +/** >>>>> + * devm_phy_get - lookup and obtain a reference to a phy. >>>>> + * @dev: device that requests this phy >>>>> + * @index: the index of the phy >>>>> + * >>>>> + * Gets the phy using phy_get(), and associates a device with it using >>>>> + * devres. On driver detach, release function is invoked on the devres data, >>>>> + * then, devres data is freed. >>>>> + */ >>>>> +struct phy *devm_phy_get(struct device *dev, u8 index) >>>>> +{ >>>>> + struct phy **ptr, *phy; >>>>> + >>>>> + ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL); >>>>> + if (!ptr) >>>>> + return NULL; >>>>> + >>>>> + phy = phy_get(dev, index); >>>>> + if (!IS_ERR(phy)) { >>>>> + *ptr = phy; >>>>> + devres_add(dev, ptr); >>>>> + } else >>>>> + devres_free(ptr); >>>> >>>> nitpick: when when if has { }, else should have, too. >>> >>> Sure. >>>> >>>>> + >>>>> + return phy; >>>>> +} >>>>> +EXPORT_SYMBOL_GPL(devm_phy_get); >>>>> + >>>>> +/** >>>>> + * devm_phy_put - release the PHY >>>>> + * @dev: device that wants to release this phy >>>>> + * @phy: the phy returned by devm_phy_get() >>>>> + * >>>>> + * destroys the devres associated with this phy and invokes phy_put >>>>> + * to release the phy. >>>>> + */ >>>>> +void devm_phy_put(struct device *dev, struct phy *phy) >>>>> +{ >>>>> + int r; >>>>> + >>>>> + r = devres_destroy(dev, devm_phy_release, devm_phy_match, phy); >>>>> + dev_WARN_ONCE(dev, r, "couldn't find PHY resource\n"); >>>>> +} >>>>> +EXPORT_SYMBOL_GPL(devm_phy_put); >>>>> + >>>>> +/** >>>>> + * devm_of_phy_get - lookup and obtain a reference to a phy by phandle >>>>> + * @dev: device that requests this phy >>>>> + * @phandle: name of the property holding the phy phandle value >>>>> + * >>>>> + * Returns the phy driver associated with the given phandle value, >>>>> + * after getting a refcount to it or -ENODEV if there is no such phy. >>>>> + * 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_of_phy_get(struct device *dev, const char *phandle) >>>> >>>> We should discuss first how the DT binding for a phy should look like. I >>>> don't like that you hardcode the index (in of_parse_phandle()) to "0". >>>> Then we have the problem with USB2 and USB3 phys for the same usb device. >>> >>> I think then we should modify this API to take *index* which should be >>> used when we have a single controller using multiple phys. By that >>> we'll make both dt and non-dt similar in that, both of them will take >>> this index. >> >> That would be a plus. >> >>>> >>>>> +{ >>>>> + struct phy *phy = NULL, **ptr; >> >> nitpick: stray spaces >> >>>>> + struct device_node *node; >>>>> + >>>>> + if (!dev->of_node) { >>>>> + dev_dbg(dev, "device does not have a device node entry\n"); >>>>> + return ERR_PTR(-EINVAL); >>>>> + } >>>>> + >>>>> + node = of_parse_phandle(dev->of_node, phandle, 0); >> >> BTW: Is the node freed somewhere? >> >>>>> + if (!node) { >>>>> + dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle, >>>>> + dev->of_node->full_name); >>>>> + return ERR_PTR(-ENODEV); >>>>> + } >>>>> + >>>>> + ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL); >>>>> + if (!ptr) { >>>>> + dev_dbg(dev, "failed to allocate memory for devres\n"); >>>>> + return ERR_PTR(-ENOMEM); >>>>> + } >>>>> + >>>>> + mutex_lock(&phy_list_mutex); >>>>> + >>>>> + phy = of_phy_lookup(dev, node); >>>>> + if (IS_ERR(phy) || !try_module_get(phy->dev.driver->owner)) { >> >> Where's the corresponding module_put? You should add module_get to the >> non dt function, too. > > Do you think try_module_get() is necessary here? And just figured out > *this* phy device does not have a associated driver (so > phy->dev.driver will be NULL). If your phy driver is a loadable module, it is needed. Otherweise the module use counter is 0, you can unload the module and the kernel will oops when someone will access the driver. Marc
diff --git a/drivers/Kconfig b/drivers/Kconfig index ece958d..8488818 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -152,4 +152,6 @@ source "drivers/vme/Kconfig" source "drivers/pwm/Kconfig" +source "drivers/phy/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 5b42184..63d6bbe 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -38,6 +38,8 @@ obj-y += char/ # gpu/ comes after char for AGP vs DRM startup obj-y += gpu/ +obj-y += phy/ + obj-$(CONFIG_CONNECTOR) += connector/ # i810fb and intelfb depend on char/agp/ diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig new file mode 100644 index 0000000..34f7077 --- /dev/null +++ b/drivers/phy/Kconfig @@ -0,0 +1,13 @@ +# +# PHY +# + +menuconfig GENERIC_PHY + tristate "Generic PHY Support" + 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. diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile new file mode 100644 index 0000000..9e9560f --- /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 0000000..c55446a --- /dev/null +++ b/drivers/phy/phy-core.c @@ -0,0 +1,437 @@ +/* + * phy-core.c -- Generic Phy framework. + * + * Copyright (C) 2012 Texas Instruments + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#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> + +static struct class *phy_class; +static LIST_HEAD(phy_list); +static DEFINE_MUTEX(phy_list_mutex); +static LIST_HEAD(phy_bind_list); + +static void devm_phy_release(struct device *dev, void *res) +{ + struct phy *phy = *(struct phy **)res; + + phy_put(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 *dev, u8 index) +{ + struct phy_bind *phy_bind = NULL; + + list_for_each_entry(phy_bind, &phy_bind_list, list) { + if (!(strcmp(phy_bind->dev_name, dev_name(dev))) && + phy_bind->index == index) + return phy_bind->phy; + } + + return ERR_PTR(-ENODEV); +} + +static struct phy *of_phy_lookup(struct device *dev, struct device_node *node) +{ + int index = 0; + struct phy *phy; + struct phy_bind *phy_map = NULL; + + list_for_each_entry(phy_map, &phy_bind_list, list) + if (!(strcmp(phy_map->dev_name, dev_name(dev)))) + index++; + + list_for_each_entry(phy, &phy_list, head) { + if (node != phy->desc->of_node) + continue; + + phy_map = phy_bind(dev_name(dev), index, dev_name(&phy->dev)); + if (!IS_ERR(phy_map)) { + phy_map->phy = phy; + phy_map->auto_bind = true; + } + + return phy; + } + + return ERR_PTR(-ENODEV); +} + +/** + * devm_phy_get - lookup and obtain a reference to a phy. + * @dev: device that requests this phy + * @index: the index of the phy + * + * Gets the phy using phy_get(), and associates a device with it using + * devres. On driver detach, release function is invoked on the devres data, + * then, devres data is freed. + */ +struct phy *devm_phy_get(struct device *dev, u8 index) +{ + struct phy **ptr, *phy; + + ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return NULL; + + phy = phy_get(dev, index); + if (!IS_ERR(phy)) { + *ptr = phy; + devres_add(dev, ptr); + } else + devres_free(ptr); + + return phy; +} +EXPORT_SYMBOL_GPL(devm_phy_get); + +/** + * devm_phy_put - release the PHY + * @dev: device that wants to release this phy + * @phy: the phy returned by devm_phy_get() + * + * destroys the devres associated with this phy and invokes phy_put + * to release the phy. + */ +void devm_phy_put(struct device *dev, struct phy *phy) +{ + int r; + + r = devres_destroy(dev, devm_phy_release, devm_phy_match, phy); + dev_WARN_ONCE(dev, r, "couldn't find PHY resource\n"); +} +EXPORT_SYMBOL_GPL(devm_phy_put); + +/** + * devm_of_phy_get - lookup and obtain a reference to a phy by phandle + * @dev: device that requests this phy + * @phandle: name of the property holding the phy phandle value + * + * Returns the phy driver associated with the given phandle value, + * after getting a refcount to it or -ENODEV if there is no such phy. + * 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_of_phy_get(struct device *dev, const char *phandle) +{ + struct phy *phy = NULL, **ptr; + struct device_node *node; + + if (!dev->of_node) { + dev_dbg(dev, "device does not have a device node entry\n"); + return ERR_PTR(-EINVAL); + } + + node = of_parse_phandle(dev->of_node, phandle, 0); + if (!node) { + dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle, + dev->of_node->full_name); + return ERR_PTR(-ENODEV); + } + + ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) { + dev_dbg(dev, "failed to allocate memory for devres\n"); + return ERR_PTR(-ENOMEM); + } + + mutex_lock(&phy_list_mutex); + + phy = of_phy_lookup(dev, node); + if (IS_ERR(phy) || !try_module_get(phy->dev.driver->owner)) { + devres_free(ptr); + goto err0; + } + + *ptr = phy; + devres_add(dev, ptr); + + get_device(&phy->dev); + +err0: + mutex_unlock(&phy_list_mutex); + + return phy; +} +EXPORT_SYMBOL_GPL(devm_of_phy_get); + +/** + * phy_get - lookup and obtain a reference to a phy. + * @dev: device that requests this phy + * @index: the index of the phy + * + * 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 *dev, u8 index) +{ + struct phy *phy = NULL; + + mutex_lock(&phy_list_mutex); + + phy = phy_lookup(dev, index); + if (IS_ERR(phy)) { + pr_err("unable to find phy\n"); + goto err0; + } + + get_device(&phy->dev); + +err0: + mutex_unlock(&phy_list_mutex); + + return phy; +} +EXPORT_SYMBOL_GPL(phy_get); + +/** + * phy_put - release the PHY + * @phy: the phy returned by phy_get() + * + * Releases a refcount the caller received from phy_get(). + */ +void phy_put(struct phy *phy) +{ + if (phy) + put_device(&phy->dev); +} +EXPORT_SYMBOL_GPL(phy_put); + +/** + * create_phy - create a new phy + * @dev: device that is creating the new phy + * @desc: descriptor of the phy + * + * Called to create a phy using phy framework. + */ +struct phy *create_phy(struct device *dev, struct phy_descriptor *desc) +{ + int ret; + struct phy *phy; + struct phy_bind *phy_bind; + const char *devname = NULL; + + if (!dev || !desc) { + dev_err(dev, "no descriptor/device provided for PHY\n"); + ret = -EINVAL; + goto err0; + } + + if (!desc->ops) { + dev_err(dev, "no PHY ops provided\n"); + ret = -EINVAL; + goto err0; + } + + phy = kzalloc(sizeof(*phy), GFP_KERNEL); + if (!phy) { + dev_err(dev, "no memory for PHY\n"); + ret = -ENOMEM; + goto err0; + } + + devname = dev_name(dev); + device_initialize(&phy->dev); + phy->desc = desc; + phy->dev.class = phy_class; + phy->dev.parent = dev; + ret = dev_set_name(&phy->dev, "%s", devname); + if (ret) + goto err1; + + mutex_lock(&phy_list_mutex); + list_for_each_entry(phy_bind, &phy_bind_list, list) + if (!(strcmp(phy_bind->phy_dev_name, devname))) + phy_bind->phy = phy; + + list_add_tail(&phy->head, &phy_list); + + ret = device_add(&phy->dev); + if (ret) + goto err2; + + mutex_unlock(&phy_list_mutex); + + return phy; + +err2: + phy_bind->phy = NULL; + list_del(&phy->head); + mutex_unlock(&phy_list_mutex); + +err1: + put_device(&phy->dev); + kfree(phy); + +err0: + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(create_phy); + +/** + * destroy_phy - destroy the phy + * @phy: the phy to be destroyed + * + * Called to destroy the phy. + */ +void destroy_phy(struct phy *phy) +{ + struct phy_bind *phy_bind; + + mutex_lock(&phy_list_mutex); + list_for_each_entry(phy_bind, &phy_bind_list, list) { + if (phy_bind->phy == phy) + phy_bind->phy = NULL; + + if (phy_bind->auto_bind) { + list_del(&phy_bind->list); + kfree(phy_bind); + } + } + + list_del(&phy->head); + mutex_unlock(&phy_list_mutex); + + device_unregister(&phy->dev); +} +EXPORT_SYMBOL_GPL(destroy_phy); + +/** + * phy_bind - bind the phy and the controller that uses the phy + * @dev_name: the device name of the device that will bind to the phy + * @index: index to specify the port number + * @phy_dev_name: the device name of the phy + * + * Fills the phy_bind structure with the dev_name and phy_dev_name. This will + * be used when the phy driver registers the phy and when the controller + * requests this phy. + * + * To be used by platform specific initialization code. + */ +struct phy_bind *phy_bind(const char *dev_name, u8 index, + const char *phy_dev_name) +{ + struct phy_bind *phy_bind; + + phy_bind = kzalloc(sizeof(*phy_bind), GFP_KERNEL); + if (!phy_bind) { + pr_err("phy_bind(): No memory for phy_bind"); + return ERR_PTR(-ENOMEM); + } + + phy_bind->dev_name = dev_name; + phy_bind->phy_dev_name = phy_dev_name; + phy_bind->index = index; + phy_bind->auto_bind = false; + + list_add_tail(&phy_bind->list, &phy_bind_list); + + return phy_bind; +} +EXPORT_SYMBOL_GPL(phy_bind); + +static ssize_t phy_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct phy *phy; + + phy = container_of(dev, struct phy, dev); + + return sprintf(buf, "%s\n", phy->desc->label); +} + +static ssize_t phy_bind_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct phy_bind *phy_bind; + struct phy *phy; + char *p = buf; + int len; + + phy = container_of(dev, struct phy, dev); + + list_for_each_entry(phy_bind, &phy_bind_list, list) + if (phy_bind->phy == phy) + p += sprintf(p, "%s %d\n", phy_bind->dev_name, + phy_bind->index); + len = (p - buf); + + return len; +} + +static struct device_attribute phy_dev_attrs[] = { + __ATTR(label, 0444, phy_name_show, NULL), + __ATTR(bind, 0444, phy_bind_show, NULL), + __ATTR_NULL, +}; + +/** + * phy_release - release the phy + * @dev: the dev member within phy + * + * when the last reference to the device is removed; it is called + * from the embedded kobject as release method. + */ +static void phy_release(struct device *dev) +{ + struct phy *phy; + + phy = container_of(dev, struct phy, dev); + dev_dbg(dev, "releasing '%s'\n", dev_name(dev)); + kfree(phy); +} + +static int __init phy_core_init(void) +{ + phy_class = class_create(THIS_MODULE, "phy"); + if (IS_ERR(phy_class)) { + pr_err("failed to create phy class --> %ld\n", + PTR_ERR(phy_class)); + return PTR_ERR(phy_class); + } + + phy_class->dev_release = phy_release; + phy_class->dev_attrs = phy_dev_attrs; + + return 0; +} +subsys_initcall(phy_core_init); + +static void __exit phy_core_exit(void) +{ + class_destroy(phy_class); +} +module_exit(phy_core_exit); + +MODULE_DESCRIPTION("Generic Phy Framework"); +MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h new file mode 100644 index 0000000..831c1c0 --- /dev/null +++ b/include/linux/phy/phy.h @@ -0,0 +1,181 @@ +/* + * phy.h -- generic phy header file + * + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.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. + * + * Author: Kishon Vijay Abraham I <kishon@ti.com> + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __DRIVERS_PHY_H +#define __DRIVERS_PHY_H + +struct phy; + +/** + * struct phy_descriptor - structure that describes the PHY + * @label: label given to phy + * @type: specifies the phy type + * @of_node: device node of the phy + * @ops: function pointers for performing phy operations + */ +struct phy_descriptor { + const char *label; + int type; + struct device_node *of_node; + struct phy_ops *ops; +}; + +/** + * struct phy_ops - set of function pointers for performing phy operations + * @init: operation to be performed for initializing phy + * @exit: operation to be performed while exiting + * @suspend: suspending the phy + * @resume: resuming the phy + * @poweron: powering on the phy + * @shutdown: shutting down the phy + */ +struct phy_ops { + int (*init)(struct phy_descriptor *desc); + int (*exit)(struct phy_descriptor *desc); + int (*suspend)(struct phy_descriptor *desc); + int (*resume)(struct phy_descriptor *desc); + int (*poweron)(struct phy_descriptor *desc); + int (*shutdown)(struct phy_descriptor *desc); +}; + +/** + * struct phy - represent the phy device + * @desc: descriptor for the phy + * @head: to support multiple transceivers + */ +struct phy { + struct device dev; + struct phy_descriptor *desc; + struct list_head head; +}; + +/** + * struct phy_bind - represent the binding for the phy + * @dev_name: the device name of the device that will bind to the phy + * @phy_dev_name: the device name of the phy + * @index: used if a single controller uses multiple phys + * @auto_bind: tells if the binding is done explicitly from board file or not + * @phy: reference to the phy + * @list: to maintain a linked list of the binding information + */ +struct phy_bind { + const char *dev_name; + const char *phy_dev_name; + u8 index; + int auto_bind:1; + struct phy *phy; + struct list_head list; +}; + +#if defined(CONFIG_GENERIC_PHY) || defined(CONFIG_GENERIC_PHY_MODULE) +extern struct phy *devm_phy_get(struct device *dev, u8 index); +extern void devm_phy_put(struct device *dev, struct phy *phy); +extern struct phy *devm_of_phy_get(struct device *dev, const char *phandle); +extern struct phy *phy_get(struct device *dev, u8 index); +extern void phy_put(struct phy *phy); +extern struct phy *create_phy(struct device *dev, struct phy_descriptor *desc); +extern void destroy_phy(struct phy *phy); +extern struct phy_bind *phy_bind(const char *dev_name, u8 index, + const char *phy_dev_name); +#else +static inline struct phy *devm_phy_get(struct device *dev, u8 index) +{ + return ERR_PTR(-EINVAL); +} + +static inline void devm_phy_put(struct device *dev, struct phy *phy) +{ +} + +static inline struct phy *devm_of_phy_get(struct device *dev, + const char *phandle) +{ + return ERR_PTR(-EINVAL); +} + +static inline struct phy *phy_get(struct device *dev, u8 index) +{ + return ERR_PTR(-EINVAL); +} + +static inline void phy_put(struct phy *phy) +{ +} + +static inline struct phy *create_phy(struct device *dev, + struct phy_descriptor *desc) +{ + return ERR_PTR(-EINVAL); +} + +static inline void destroy_phy(struct phy *phy) +{ +} + +static inline struct phy_bind *phy_bind(const char *dev_name, u8 index, + const char *phy_dev_name) +{ +} +#endif + +static inline int phy_init(struct phy *phy) +{ + if (phy->desc->ops->init) + return phy->desc->ops->init(phy->desc); + + return -EINVAL; +} + +static inline int phy_exit(struct phy *phy) +{ + if (phy->desc->ops->exit) + return phy->desc->ops->exit(phy->desc); + + return -EINVAL; +} + +static inline int phy_suspend(struct phy *phy) +{ + if (phy->desc->ops->suspend) + return phy->desc->ops->suspend(phy->desc); + + return -EINVAL; +} + +static inline int phy_resume(struct phy *phy) +{ + if (phy->desc->ops->resume) + return phy->desc->ops->resume(phy->desc); + + return -EINVAL; +} + +static inline int phy_poweron(struct phy *phy) +{ + if (phy->desc->ops->poweron) + return phy->desc->ops->poweron(phy->desc); + + return -EINVAL; +} + +static inline void phy_shutdown(struct phy *phy) +{ + if (phy->desc->ops->shutdown) + phy->desc->ops->shutdown(phy->desc); +} +#endif /* __DRIVERS_PHY_H */