Re: [RFC PATH 1/3] phy: add USB ULPI abstraction layer

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

On Mon, Dec 02, 2013 at 04:20:51PM +0530, Kishon Vijay Abraham I wrote:
> Hi,
> 
> On Thursday 28 November 2013 09:29 PM, Heikki Krogerus wrote:
> > ULPI PHY is an USB2 PHY that is accessed from the USB
> > controller. ULPI PHYs allow discovery based on vendor and
> > product ids which allows binding the PHY to a driver.
> > 
> > For USB controllers that are enumerated from buses such as
> > PCI, that do not provide any information about the PHY, ULPI
> > abstraction layer allows runtime detection. This makes it
> > possible to take advantage of vendor specific functions of
> > the PHYs with product specific drivers without the need for
> > platform or device specific quirks.
> > 
> > Signed-off-by: Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx>
> > ---
> >  drivers/phy/Kconfig       |   2 +
> >  drivers/phy/Makefile      |   1 +
> >  drivers/phy/ulpi/Kconfig  |  11 ++
> >  drivers/phy/ulpi/Makefile |   1 +
> >  drivers/phy/ulpi/ulpi.c   | 273 ++++++++++++++++++++++++++++++++++++++++++++++
> >  include/linux/phy/ulpi.h  | 105 ++++++++++++++++++
> >  6 files changed, 393 insertions(+)
> >  create mode 100644 drivers/phy/ulpi/Kconfig
> >  create mode 100644 drivers/phy/ulpi/Makefile
> >  create mode 100644 drivers/phy/ulpi/ulpi.c
> >  create mode 100644 include/linux/phy/ulpi.h
> > 
> > diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> > index a344f3d..6c03824 100644
> > --- a/drivers/phy/Kconfig
> > +++ b/drivers/phy/Kconfig
> > @@ -51,4 +51,6 @@ config PHY_EXYNOS_DP_VIDEO
> >  	help
> >  	  Support for Display Port PHY found on Samsung EXYNOS SoCs.
> >  
> > +source "drivers/phy/ulpi/Kconfig"
> > +
> >  endmenu
> > diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
> > index d0caae9..d6af605 100644
> > --- a/drivers/phy/Makefile
> > +++ b/drivers/phy/Makefile
> > @@ -7,3 +7,4 @@ obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO)	+= phy-exynos-dp-video.o
> >  obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO)	+= phy-exynos-mipi-video.o
> >  obj-$(CONFIG_OMAP_USB2)			+= phy-omap-usb2.o
> >  obj-$(CONFIG_TWL4030_USB)		+= phy-twl4030-usb.o
> > +obj-$(CONFIG_ULPI_PHY)			+= ulpi/
> > diff --git a/drivers/phy/ulpi/Kconfig b/drivers/phy/ulpi/Kconfig
> > new file mode 100644
> > index 0000000..3211aaa
> > --- /dev/null
> > +++ b/drivers/phy/ulpi/Kconfig
> > @@ -0,0 +1,11 @@
> > +
> > +config ULPI_PHY
> > +	tristate "USB ULPI PHY interface support"
> > +	depends on USB || USB_GADGET
> > +	select GENERIC_PHY
> > +	help
> > +	  Say yes if you have ULPI PHY attached to your USB controller. If no
> > +	  vendor modules are selected, the driver will act as NOP PHY driver.
> > +
> > +	  If unsure, say Y.
> > +
> > diff --git a/drivers/phy/ulpi/Makefile b/drivers/phy/ulpi/Makefile
> > new file mode 100644
> > index 0000000..7ed0895
> > --- /dev/null
> > +++ b/drivers/phy/ulpi/Makefile
> > @@ -0,0 +1 @@
> > +obj-$(CONFIG_ULPI_PHY)		+= ulpi.o
> > diff --git a/drivers/phy/ulpi/ulpi.c b/drivers/phy/ulpi/ulpi.c
> > new file mode 100644
> > index 0000000..7aa2f5d
> > --- /dev/null
> > +++ b/drivers/phy/ulpi/ulpi.c
> > @@ -0,0 +1,273 @@
> > +/**
> > + * ulpi.c - USB ULPI PHY abstraction module
> > + *
> > + * Copyright (C) 2013 Intel Corporation
> > + *
> > + * Author: Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx>
> > + *
> > + * 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/phy/ulpi.h>
> > +#include <linux/phy/phy.h>
> > +#include <linux/module.h>
> > +#include <linux/slab.h>
> > +
> > +/* -------------------------------------------------------------------------- */
> > +
> > +static struct phy_consumer phy_consumer[] = {
> > +	{ .port = ULPI_PORT_NAME, },
> 
> We had to introduce the entire phy_consumer stuff to support legacy pdata. Not
> sure if it is a good idea to use it here.

Well, maybe we can improve it at least a bit. I don't see how it's
possible to get rid of the device name matching, but at least the port
name dependency could be replaced with possibility to use index
instead. Check how they made the gpiod lookup to work in gpiolib.

Let's get back to this one.

> > +};
> > +
> > +static struct phy_init_data phy_data = {
> > +	.num_consumers = 1,
> > +	.consumers = phy_consumer,
> > +};
> > +
> > +static int ulpi_power_on(struct phy *phy)
> > +{
> > +	struct ulpi_dev *ulpi = phy_get_drvdata(phy);
> > +	struct ulpi_driver *drv = to_ulpi_driver(ulpi->dev.driver);
> > +
> > +	if (drv && drv->phy_ops && drv->phy_ops->power_on)
> > +		return drv->phy_ops->power_on(phy);
> > +
> > +	return 0;
> > +}
> > +
> > +static int ulpi_power_off(struct phy *phy)
> > +{
> > +	struct ulpi_dev *ulpi = phy_get_drvdata(phy);
> > +	struct ulpi_driver *drv = to_ulpi_driver(ulpi->dev.driver);
> > +
> > +	if (drv && drv->phy_ops && drv->phy_ops->power_off)
> > +		return drv->phy_ops->power_off(phy);
> > +
> > +	return 0;
> > +}
> > +
> > +static struct phy_ops phy_ops = {
> > +	.owner = THIS_MODULE,
> > +	.power_on = ulpi_power_on,
> > +	.power_off = ulpi_power_off,
> > +};
> > +
> > +/* -------------------------------------------------------------------------- */
> > +
> > +static int ulpi_match(struct device *dev, struct device_driver *driver)
> > +{
> > +	struct ulpi_driver *drv = to_ulpi_driver(driver);
> > +	struct ulpi_dev *ulpi = to_ulpi_dev(dev);
> > +	const struct ulpi_device_id *id;
> > +
> > +	for (id = drv->id_table; id->vendor; id++)
> > +		if (id->vendor == ulpi->id.vendor &&
> > +		    id->product == ulpi->id.product)
> > +			return 1;
> > +
> > +	return 0;
> > +}
> > +
> > +static int ulpi_probe(struct device *dev)
> > +{
> > +	struct ulpi_driver *drv = to_ulpi_driver(dev->driver);
> > +
> > +	return drv->probe(to_ulpi_dev(dev));
> > +}
> > +
> > +static int ulpi_remove(struct device *dev)
> > +{
> > +	struct ulpi_driver *drv = to_ulpi_driver(dev->driver);
> > +
> > +	if (drv->remove)
> > +		drv->remove(to_ulpi_dev(dev));
> > +
> > +	return 0;
> > +}
> > +
> > +static struct bus_type ulpi_bus = {
> > +	.name = "ulpi",
> > +	.match = ulpi_match,
> > +	.probe = ulpi_probe,
> > +	.remove = ulpi_remove,
> > +};
> > +
> > +/**
> > + * ulpi_register_driver - register driver with the bus
> > + * @drv: the driver
> > + */
> > +int ulpi_register_driver(struct ulpi_driver *drv)
> > +{
> > +	if (!drv->probe)
> > +		return -EINVAL;
> > +
> > +	drv->driver.bus = &ulpi_bus;
> > +
> > +	return driver_register(&drv->driver);
> > +}
> > +EXPORT_SYMBOL_GPL(ulpi_register_driver);
> > +
> > +void ulpi_unregister_driver(struct ulpi_driver *drv)
> > +{
> > +	driver_unregister(&drv->driver);
> > +}
> > +EXPORT_SYMBOL_GPL(ulpi_unregister_driver);
> > +
> > +/* -------------------------------------------------------------------------- */
> > +
> > +/**
> > + * ulpi_alloc_interface - allocate new ULPI interface
> > + *
> > + * Allows USB controller to create struct ulpi_interface without
> > + * registering the interface immediately.
> > + */
> > +struct ulpi_interface *ulpi_alloc_interface(void)
> > +{
> > +	struct ulpi_dev *ulpi;
> > +
> > +	ulpi = kzalloc(sizeof(*ulpi), GFP_KERNEL);
> > +	if (!ulpi)
> > +		return NULL;
> > +
> > +	return &ulpi->interface;
> > +}
> > +EXPORT_SYMBOL_GPL(ulpi_alloc_interface);
> 
> Should this be exported?

What's wrong with it? It allows, what ever is providing the ULPI
interface, to get the handle to the interface structure in advance and
registering it later when it's ready.

This is the way many bus drivers handle adapters/masters/whatever.
If you feel strongly about this, we can simply only provide
ulpi_new_interface() as the only way to register an interface.

> > +
> > +#define interface_to_ulpi(c) container_of(c, struct ulpi_dev, interface)
> > +
> > +/**
> > + * ulpi_register - register new ULPI device
> > + * @dev: USB controller's device interface
> > + * @interface: Controller which the device is connected
> > + *
> > + * Companion function to ulpi_alloc_interface. Creates device for the
> > + * ULPI PHY attached to the interface, binds it to a driver and
> > + * creates phy instance for it.
> > + */
> > +int ulpi_register(struct device *dev, struct ulpi_interface *interface)
> > +{
> > +	struct ulpi_dev *ulpi = interface_to_ulpi(interface);
> > +	int ret;
> > +
> > +	/* Test the interface */
> > +	ret = ulpi_write(ulpi, ULPI_SCRATCH, 0xaa);
> > +	if (ret < 0)
> > +		goto err;
> > +
> > +	ret = ulpi_read(ulpi, ULPI_SCRATCH);
> > +	if (ret < 0)
> > +		goto err;
> > +
> > +	if (ret != 0xaa) {
> > +		ret = -ENODEV;
> > +		goto err;
> > +	}
> > +
> > +	ulpi->id.vendor = ulpi_read(ulpi, ULPI_VENDOR_ID_LOW);
> > +	ulpi->id.vendor |= ulpi_read(ulpi, ULPI_VENDOR_ID_HIGH) << 8;
> > +
> > +	ulpi->id.product = ulpi_read(ulpi, ULPI_PRODUCT_ID_LOW);
> > +	ulpi->id.product |= ulpi_read(ulpi, ULPI_PRODUCT_ID_HIGH) << 8;
> > +
> > +	ulpi->dev.parent = dev;
> > +	ulpi->dev.bus = &ulpi_bus;
> > +	dev_set_name(&ulpi->dev, "%s.ulpi", dev_name(dev));
> > +
> > +	ret = device_register(&ulpi->dev);
> > +	if (ret)
> > +		goto err;
> > +
> > +	/**
> > +	 * Create phy for the device.
> > +	 * The controller is always the consumer
> > +	 */
> > +	phy_consumer[0].dev_name = dev_name(dev);
> > +
> > +	ulpi->phy = phy_create(&ulpi->dev, &phy_ops, &phy_data);
> 
> Instead of creating ulpi device and phy device here, phy device (phy_create)
> should be created when the driver is probed (in this case when NXP ISP170X is
> probed).

No. The phy should always be tied to the device and not to the
driver. The controller driver can not depend on a particular ULPI
driver. You want to allow the controller driver to probe and get the
phy before any ULPI drivers are loaded. Thats why it's called
abstraction.

> > +	if (IS_ERR(ulpi->phy)) {
> > +		ret = PTR_ERR(ulpi->phy);
> > +		device_unregister(&ulpi->dev);
> > +		goto err;
> > +	}
> > +
> > +	phy_set_drvdata(ulpi->phy, ulpi);
> > +
> > +	dev_dbg(&ulpi->dev, "registered vendor %04x, product %04x\n",
> > +		ulpi->id.vendor, ulpi->id.product);
> > +
> > +	return 0;
> > +err:
> > +	kfree(ulpi);
> 
> It's difficult to follow if you have allocation in one function and free it in
> the error path of some other function. This can be improved.

So what do you have in mind?

Thanks,

-- 
heikki
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux