Skip to content

Commit 289fcff

Browse files
Heikki KrogerusFelipe Balbi
authored andcommitted
usb: add bus type for USB ULPI
UTMI+ Low Pin Interface (ULPI) is a commonly used PHY interface for USB 2.0. The ULPI specification describes a standard set of registers which the vendors can extend for their specific needs. ULPI PHYs provide often functions such as charger detection and ADP sensing and probing. There are two major issues that the bus type is meant to tackle: Firstly, ULPI registers are accessed from the controller. The bus provides convenient method for the controller drivers to share that access with the actual PHY drivers. Secondly, there are already platforms that assume ULPI PHYs are runtime detected, such as many Intel Baytrail based platforms. They do not provide any kind of hardware description for the ULPI PHYs like separate ACPI device object that could be used to enumerate a device from. Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com> Acked-by: David Cohen <david.a.cohen@linux.intel.com> Signed-off-by: Felipe Balbi <balbi@ti.com>
1 parent 3521a39 commit 289fcff

File tree

11 files changed

+521
-132
lines changed

11 files changed

+521
-132
lines changed

MAINTAINERS

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10453,6 +10453,13 @@ S: Maintained
1045310453
F: Documentation/video4linux/zr364xx.txt
1045410454
F: drivers/media/usb/zr364xx/
1045510455

10456+
ULPI BUS
10457+
M: Heikki Krogerus <heikki.krogerus@linux.intel.com>
10458+
L: linux-usb@vger.kernel.org
10459+
S: Maintained
10460+
F: drivers/usb/common/ulpi.c
10461+
F: include/linux/ulpi/
10462+
1045610463
USER-MODE LINUX (UML)
1045710464
M: Jeff Dike <jdike@addtoit.com>
1045810465
M: Richard Weinberger <richard@nod.at>

drivers/usb/common/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ usb-common-y += common.o
77
usb-common-$(CONFIG_USB_LED_TRIG) += led.o
88

99
obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o
10+
obj-$(CONFIG_USB_ULPI_BUS) += ulpi.o

drivers/usb/common/ulpi.c

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
/**
2+
* ulpi.c - USB ULPI PHY bus
3+
*
4+
* Copyright (C) 2015 Intel Corporation
5+
*
6+
* Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
7+
*
8+
* This program is free software; you can redistribute it and/or modify
9+
* it under the terms of the GNU General Public License version 2 as
10+
* published by the Free Software Foundation.
11+
*/
12+
13+
#include <linux/ulpi/interface.h>
14+
#include <linux/ulpi/driver.h>
15+
#include <linux/ulpi/regs.h>
16+
#include <linux/module.h>
17+
#include <linux/slab.h>
18+
#include <linux/acpi.h>
19+
20+
/* -------------------------------------------------------------------------- */
21+
22+
int ulpi_read(struct ulpi *ulpi, u8 addr)
23+
{
24+
return ulpi->ops->read(ulpi->ops, addr);
25+
}
26+
EXPORT_SYMBOL_GPL(ulpi_read);
27+
28+
int ulpi_write(struct ulpi *ulpi, u8 addr, u8 val)
29+
{
30+
return ulpi->ops->write(ulpi->ops, addr, val);
31+
}
32+
EXPORT_SYMBOL_GPL(ulpi_write);
33+
34+
/* -------------------------------------------------------------------------- */
35+
36+
static int ulpi_match(struct device *dev, struct device_driver *driver)
37+
{
38+
struct ulpi_driver *drv = to_ulpi_driver(driver);
39+
struct ulpi *ulpi = to_ulpi_dev(dev);
40+
const struct ulpi_device_id *id;
41+
42+
for (id = drv->id_table; id->vendor; id++)
43+
if (id->vendor == ulpi->id.vendor &&
44+
id->product == ulpi->id.product)
45+
return 1;
46+
47+
return 0;
48+
}
49+
50+
static int ulpi_uevent(struct device *dev, struct kobj_uevent_env *env)
51+
{
52+
struct ulpi *ulpi = to_ulpi_dev(dev);
53+
54+
if (add_uevent_var(env, "MODALIAS=ulpi:v%04xp%04x",
55+
ulpi->id.vendor, ulpi->id.product))
56+
return -ENOMEM;
57+
return 0;
58+
}
59+
60+
static int ulpi_probe(struct device *dev)
61+
{
62+
struct ulpi_driver *drv = to_ulpi_driver(dev->driver);
63+
64+
return drv->probe(to_ulpi_dev(dev));
65+
}
66+
67+
static int ulpi_remove(struct device *dev)
68+
{
69+
struct ulpi_driver *drv = to_ulpi_driver(dev->driver);
70+
71+
if (drv->remove)
72+
drv->remove(to_ulpi_dev(dev));
73+
74+
return 0;
75+
}
76+
77+
static struct bus_type ulpi_bus = {
78+
.name = "ulpi",
79+
.match = ulpi_match,
80+
.uevent = ulpi_uevent,
81+
.probe = ulpi_probe,
82+
.remove = ulpi_remove,
83+
};
84+
85+
/* -------------------------------------------------------------------------- */
86+
87+
static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
88+
char *buf)
89+
{
90+
struct ulpi *ulpi = to_ulpi_dev(dev);
91+
92+
return sprintf(buf, "ulpi:v%04xp%04x\n",
93+
ulpi->id.vendor, ulpi->id.product);
94+
}
95+
static DEVICE_ATTR_RO(modalias);
96+
97+
static struct attribute *ulpi_dev_attrs[] = {
98+
&dev_attr_modalias.attr,
99+
NULL
100+
};
101+
102+
static struct attribute_group ulpi_dev_attr_group = {
103+
.attrs = ulpi_dev_attrs,
104+
};
105+
106+
static const struct attribute_group *ulpi_dev_attr_groups[] = {
107+
&ulpi_dev_attr_group,
108+
NULL
109+
};
110+
111+
static void ulpi_dev_release(struct device *dev)
112+
{
113+
kfree(to_ulpi_dev(dev));
114+
}
115+
116+
static struct device_type ulpi_dev_type = {
117+
.name = "ulpi_device",
118+
.groups = ulpi_dev_attr_groups,
119+
.release = ulpi_dev_release,
120+
};
121+
122+
/* -------------------------------------------------------------------------- */
123+
124+
/**
125+
* ulpi_register_driver - register a driver with the ULPI bus
126+
* @drv: driver being registered
127+
*
128+
* Registers a driver with the ULPI bus.
129+
*/
130+
int ulpi_register_driver(struct ulpi_driver *drv)
131+
{
132+
if (!drv->probe)
133+
return -EINVAL;
134+
135+
drv->driver.bus = &ulpi_bus;
136+
137+
return driver_register(&drv->driver);
138+
}
139+
EXPORT_SYMBOL_GPL(ulpi_register_driver);
140+
141+
/**
142+
* ulpi_unregister_driver - unregister a driver with the ULPI bus
143+
* @drv: driver to unregister
144+
*
145+
* Unregisters a driver with the ULPI bus.
146+
*/
147+
void ulpi_unregister_driver(struct ulpi_driver *drv)
148+
{
149+
driver_unregister(&drv->driver);
150+
}
151+
EXPORT_SYMBOL_GPL(ulpi_unregister_driver);
152+
153+
/* -------------------------------------------------------------------------- */
154+
155+
static int ulpi_register(struct device *dev, struct ulpi *ulpi)
156+
{
157+
int ret;
158+
159+
/* Test the interface */
160+
ret = ulpi_write(ulpi, ULPI_SCRATCH, 0xaa);
161+
if (ret < 0)
162+
return ret;
163+
164+
ret = ulpi_read(ulpi, ULPI_SCRATCH);
165+
if (ret < 0)
166+
return ret;
167+
168+
if (ret != 0xaa)
169+
return -ENODEV;
170+
171+
ulpi->id.vendor = ulpi_read(ulpi, ULPI_VENDOR_ID_LOW);
172+
ulpi->id.vendor |= ulpi_read(ulpi, ULPI_VENDOR_ID_HIGH) << 8;
173+
174+
ulpi->id.product = ulpi_read(ulpi, ULPI_PRODUCT_ID_LOW);
175+
ulpi->id.product |= ulpi_read(ulpi, ULPI_PRODUCT_ID_HIGH) << 8;
176+
177+
ulpi->dev.parent = dev;
178+
ulpi->dev.bus = &ulpi_bus;
179+
ulpi->dev.type = &ulpi_dev_type;
180+
dev_set_name(&ulpi->dev, "%s.ulpi", dev_name(dev));
181+
182+
ACPI_COMPANION_SET(&ulpi->dev, ACPI_COMPANION(dev));
183+
184+
request_module("ulpi:v%04xp%04x", ulpi->id.vendor, ulpi->id.product);
185+
186+
ret = device_register(&ulpi->dev);
187+
if (ret)
188+
return ret;
189+
190+
dev_dbg(&ulpi->dev, "registered ULPI PHY: vendor %04x, product %04x\n",
191+
ulpi->id.vendor, ulpi->id.product);
192+
193+
return 0;
194+
}
195+
196+
/**
197+
* ulpi_register_interface - instantiate new ULPI device
198+
* @dev: USB controller's device interface
199+
* @ops: ULPI register access
200+
*
201+
* Allocates and registers a ULPI device and an interface for it. Called from
202+
* the USB controller that provides the ULPI interface.
203+
*/
204+
struct ulpi *ulpi_register_interface(struct device *dev, struct ulpi_ops *ops)
205+
{
206+
struct ulpi *ulpi;
207+
int ret;
208+
209+
ulpi = kzalloc(sizeof(*ulpi), GFP_KERNEL);
210+
if (!ulpi)
211+
return ERR_PTR(-ENOMEM);
212+
213+
ulpi->ops = ops;
214+
ops->dev = dev;
215+
216+
ret = ulpi_register(dev, ulpi);
217+
if (ret) {
218+
kfree(ulpi);
219+
return ERR_PTR(ret);
220+
}
221+
222+
return ulpi;
223+
}
224+
EXPORT_SYMBOL_GPL(ulpi_register_interface);
225+
226+
/**
227+
* ulpi_unregister_interface - unregister ULPI interface
228+
* @intrf: struct ulpi_interface
229+
*
230+
* Unregisters a ULPI device and it's interface that was created with
231+
* ulpi_create_interface().
232+
*/
233+
void ulpi_unregister_interface(struct ulpi *ulpi)
234+
{
235+
device_unregister(&ulpi->dev);
236+
}
237+
EXPORT_SYMBOL_GPL(ulpi_unregister_interface);
238+
239+
/* -------------------------------------------------------------------------- */
240+
241+
static int __init ulpi_init(void)
242+
{
243+
return bus_register(&ulpi_bus);
244+
}
245+
module_init(ulpi_init);
246+
247+
static void __exit ulpi_exit(void)
248+
{
249+
bus_unregister(&ulpi_bus);
250+
}
251+
module_exit(ulpi_exit);
252+
253+
MODULE_AUTHOR("Intel Corporation");
254+
MODULE_LICENSE("GPL v2");
255+
MODULE_DESCRIPTION("USB ULPI PHY bus");

drivers/usb/core/Kconfig

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,23 @@ config USB_OTG_FSM
8484
Implements OTG Finite State Machine as specified in On-The-Go
8585
and Embedded Host Supplement to the USB Revision 2.0 Specification.
8686

87+
config USB_ULPI_BUS
88+
tristate "USB ULPI PHY interface support"
89+
depends on USB_SUPPORT
90+
help
91+
UTMI+ Low Pin Interface (ULPI) is specification for a commonly used
92+
USB 2.0 PHY interface. The ULPI specification defines a standard set
93+
of registers that can be used to detect the vendor and product which
94+
allows ULPI to be handled as a bus. This module is the driver for that
95+
bus.
96+
97+
The ULPI interfaces (the buses) are registered by the drivers for USB
98+
controllers which support ULPI register access and have ULPI PHY
99+
attached to them. The ULPI PHY drivers themselves are normal PHY
100+
drivers.
101+
102+
ULPI PHYs provide often functions such as ADP sensing/probing (OTG
103+
protocol) and USB charger detection.
104+
105+
To compile this driver as a module, choose M here: the module will
106+
be called ulpi.

include/linux/mod_devicetable.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,4 +629,10 @@ struct mcb_device_id {
629629
kernel_ulong_t driver_data;
630630
};
631631

632+
struct ulpi_device_id {
633+
__u16 vendor;
634+
__u16 product;
635+
kernel_ulong_t driver_data;
636+
};
637+
632638
#endif /* LINUX_MOD_DEVICETABLE_H */

include/linux/ulpi/driver.h

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#ifndef __LINUX_ULPI_DRIVER_H
2+
#define __LINUX_ULPI_DRIVER_H
3+
4+
#include <linux/mod_devicetable.h>
5+
6+
#include <linux/device.h>
7+
8+
struct ulpi_ops;
9+
10+
/**
11+
* struct ulpi - describes ULPI PHY device
12+
* @id: vendor and product ids for ULPI device
13+
* @ops: I/O access
14+
* @dev: device interface
15+
*/
16+
struct ulpi {
17+
struct ulpi_device_id id;
18+
struct ulpi_ops *ops;
19+
struct device dev;
20+
};
21+
22+
#define to_ulpi_dev(d) container_of(d, struct ulpi, dev)
23+
24+
static inline void ulpi_set_drvdata(struct ulpi *ulpi, void *data)
25+
{
26+
dev_set_drvdata(&ulpi->dev, data);
27+
}
28+
29+
static inline void *ulpi_get_drvdata(struct ulpi *ulpi)
30+
{
31+
return dev_get_drvdata(&ulpi->dev);
32+
}
33+
34+
/**
35+
* struct ulpi_driver - describes a ULPI PHY driver
36+
* @id_table: array of device identifiers supported by this driver
37+
* @probe: binds this driver to ULPI device
38+
* @remove: unbinds this driver from ULPI device
39+
* @driver: the name and owner members must be initialized by the drivers
40+
*/
41+
struct ulpi_driver {
42+
const struct ulpi_device_id *id_table;
43+
int (*probe)(struct ulpi *ulpi);
44+
void (*remove)(struct ulpi *ulpi);
45+
struct device_driver driver;
46+
};
47+
48+
#define to_ulpi_driver(d) container_of(d, struct ulpi_driver, driver)
49+
50+
int ulpi_register_driver(struct ulpi_driver *drv);
51+
void ulpi_unregister_driver(struct ulpi_driver *drv);
52+
53+
#define module_ulpi_driver(__ulpi_driver) \
54+
module_driver(__ulpi_driver, ulpi_register_driver, \
55+
ulpi_unregister_driver)
56+
57+
int ulpi_read(struct ulpi *ulpi, u8 addr);
58+
int ulpi_write(struct ulpi *ulpi, u8 addr, u8 val);
59+
60+
#endif /* __LINUX_ULPI_DRIVER_H */

include/linux/ulpi/interface.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#ifndef __LINUX_ULPI_INTERFACE_H
2+
#define __LINUX_ULPI_INTERFACE_H
3+
4+
#include <linux/types.h>
5+
6+
struct ulpi;
7+
8+
/**
9+
* struct ulpi_ops - ULPI register access
10+
* @dev: the interface provider
11+
* @read: read operation for ULPI register access
12+
* @write: write operation for ULPI register access
13+
*/
14+
struct ulpi_ops {
15+
struct device *dev;
16+
int (*read)(struct ulpi_ops *ops, u8 addr);
17+
int (*write)(struct ulpi_ops *ops, u8 addr, u8 val);
18+
};
19+
20+
struct ulpi *ulpi_register_interface(struct device *, struct ulpi_ops *);
21+
void ulpi_unregister_interface(struct ulpi *);
22+
23+
#endif /* __LINUX_ULPI_INTERFACE_H */

0 commit comments

Comments
 (0)