Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ef6a7bc

Browse files
bebarinoPeter Chen
authored andcommitted
usb: ulpi: Support device discovery via DT
The qcom HSIC ULPI phy doesn't have any bits set in the vendor or product ID registers. This makes it impossible to make a ULPI driver match against the ID registers. Add support to discover the ULPI phys via DT help alleviate this problem. In the DT case, we'll look for a ULPI bus node underneath the device registering the ULPI viewport (or the parent of that device to support chipidea's device layout) and then match up the phy node underneath that with the ULPI device that's created. The side benefit of this is that we can use standard properties in the phy node like clks, regulators, gpios, etc. because we don't have firmware like ACPI to turn these things on for us. And we can use the DT phy binding to point our phy consumer to the phy provider. The ULPI bus code supports native enumeration by reading the vendor ID and product ID registers at device creation time, but we can't be certain that those register reads will succeed if the phy is not powered up. To avoid any problems with reading the ID registers before the phy is powered we fallback to DT matching when the ID reads fail. If the ULPI spec had some generic power sequencing for these registers we could put that into the ULPI bus layer and power up the device before reading the ID registers. Unfortunately this doesn't exist and the power sequence is usually device specific. By having the device matched up with DT we can avoid this problem. Cc: Greg Kroah-Hartman <[email protected]> Acked-by: Heikki Krogerus <[email protected]> Cc: <[email protected]> Acked-by: Rob Herring <[email protected]> Signed-off-by: Stephen Boyd <[email protected]> Signed-off-by: Peter Chen <[email protected]>
1 parent 7a3b7cd commit ef6a7bc

File tree

2 files changed

+93
-6
lines changed

2 files changed

+93
-6
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
ULPI bus binding
2+
----------------
3+
4+
Phys that are behind a ULPI connection can be described with the following
5+
binding. The host controller shall have a "ulpi" named node as a child, and
6+
that node shall have one enabled node underneath it representing the ulpi
7+
device on the bus.
8+
9+
EXAMPLE
10+
-------
11+
12+
usb {
13+
compatible = "vendor,usb-controller";
14+
15+
ulpi {
16+
phy {
17+
compatible = "vendor,phy";
18+
};
19+
};
20+
};

drivers/usb/common/ulpi.c

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
#include <linux/module.h>
1717
#include <linux/slab.h>
1818
#include <linux/acpi.h>
19+
#include <linux/of.h>
20+
#include <linux/of_device.h>
21+
#include <linux/clk/clk-conf.h>
1922

2023
/* -------------------------------------------------------------------------- */
2124

@@ -39,6 +42,10 @@ static int ulpi_match(struct device *dev, struct device_driver *driver)
3942
struct ulpi *ulpi = to_ulpi_dev(dev);
4043
const struct ulpi_device_id *id;
4144

45+
/* Some ULPI devices don't have a vendor id so rely on OF match */
46+
if (ulpi->id.vendor == 0)
47+
return of_driver_match_device(dev, driver);
48+
4249
for (id = drv->id_table; id->vendor; id++)
4350
if (id->vendor == ulpi->id.vendor &&
4451
id->product == ulpi->id.product)
@@ -50,6 +57,11 @@ static int ulpi_match(struct device *dev, struct device_driver *driver)
5057
static int ulpi_uevent(struct device *dev, struct kobj_uevent_env *env)
5158
{
5259
struct ulpi *ulpi = to_ulpi_dev(dev);
60+
int ret;
61+
62+
ret = of_device_uevent_modalias(dev, env);
63+
if (ret != -ENODEV)
64+
return ret;
5365

5466
if (add_uevent_var(env, "MODALIAS=ulpi:v%04xp%04x",
5567
ulpi->id.vendor, ulpi->id.product))
@@ -60,6 +72,11 @@ static int ulpi_uevent(struct device *dev, struct kobj_uevent_env *env)
6072
static int ulpi_probe(struct device *dev)
6173
{
6274
struct ulpi_driver *drv = to_ulpi_driver(dev->driver);
75+
int ret;
76+
77+
ret = of_clk_set_defaults(dev->of_node, false);
78+
if (ret < 0)
79+
return ret;
6380

6481
return drv->probe(to_ulpi_dev(dev));
6582
}
@@ -87,8 +104,13 @@ static struct bus_type ulpi_bus = {
87104
static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
88105
char *buf)
89106
{
107+
int len;
90108
struct ulpi *ulpi = to_ulpi_dev(dev);
91109

110+
len = of_device_get_modalias(dev, buf, PAGE_SIZE - 1);
111+
if (len != -ENODEV)
112+
return len;
113+
92114
return sprintf(buf, "ulpi:v%04xp%04x\n",
93115
ulpi->id.vendor, ulpi->id.product);
94116
}
@@ -153,37 +175,81 @@ EXPORT_SYMBOL_GPL(ulpi_unregister_driver);
153175

154176
/* -------------------------------------------------------------------------- */
155177

156-
static int ulpi_register(struct device *dev, struct ulpi *ulpi)
178+
static int ulpi_of_register(struct ulpi *ulpi)
157179
{
158-
int ret;
180+
struct device_node *np = NULL, *child;
181+
struct device *parent;
182+
183+
/* Find a ulpi bus underneath the parent or the grandparent */
184+
parent = ulpi->dev.parent;
185+
if (parent->of_node)
186+
np = of_find_node_by_name(parent->of_node, "ulpi");
187+
else if (parent->parent && parent->parent->of_node)
188+
np = of_find_node_by_name(parent->parent->of_node, "ulpi");
189+
if (!np)
190+
return 0;
191+
192+
child = of_get_next_available_child(np, NULL);
193+
of_node_put(np);
194+
if (!child)
195+
return -EINVAL;
159196

160-
ulpi->dev.parent = dev; /* needed early for ops */
197+
ulpi->dev.of_node = child;
198+
199+
return 0;
200+
}
201+
202+
static int ulpi_read_id(struct ulpi *ulpi)
203+
{
204+
int ret;
161205

162206
/* Test the interface */
163207
ret = ulpi_write(ulpi, ULPI_SCRATCH, 0xaa);
164208
if (ret < 0)
165-
return ret;
209+
goto err;
166210

167211
ret = ulpi_read(ulpi, ULPI_SCRATCH);
168212
if (ret < 0)
169213
return ret;
170214

171215
if (ret != 0xaa)
172-
return -ENODEV;
216+
goto err;
173217

174218
ulpi->id.vendor = ulpi_read(ulpi, ULPI_VENDOR_ID_LOW);
175219
ulpi->id.vendor |= ulpi_read(ulpi, ULPI_VENDOR_ID_HIGH) << 8;
176220

177221
ulpi->id.product = ulpi_read(ulpi, ULPI_PRODUCT_ID_LOW);
178222
ulpi->id.product |= ulpi_read(ulpi, ULPI_PRODUCT_ID_HIGH) << 8;
179223

224+
/* Some ULPI devices don't have a vendor id so rely on OF match */
225+
if (ulpi->id.vendor == 0)
226+
goto err;
227+
228+
request_module("ulpi:v%04xp%04x", ulpi->id.vendor, ulpi->id.product);
229+
return 0;
230+
err:
231+
of_device_request_module(&ulpi->dev);
232+
return 0;
233+
}
234+
235+
static int ulpi_register(struct device *dev, struct ulpi *ulpi)
236+
{
237+
int ret;
238+
239+
ulpi->dev.parent = dev; /* needed early for ops */
180240
ulpi->dev.bus = &ulpi_bus;
181241
ulpi->dev.type = &ulpi_dev_type;
182242
dev_set_name(&ulpi->dev, "%s.ulpi", dev_name(dev));
183243

184244
ACPI_COMPANION_SET(&ulpi->dev, ACPI_COMPANION(dev));
185245

186-
request_module("ulpi:v%04xp%04x", ulpi->id.vendor, ulpi->id.product);
246+
ret = ulpi_of_register(ulpi);
247+
if (ret)
248+
return ret;
249+
250+
ret = ulpi_read_id(ulpi);
251+
if (ret)
252+
return ret;
187253

188254
ret = device_register(&ulpi->dev);
189255
if (ret)
@@ -234,6 +300,7 @@ EXPORT_SYMBOL_GPL(ulpi_register_interface);
234300
*/
235301
void ulpi_unregister_interface(struct ulpi *ulpi)
236302
{
303+
of_node_put(ulpi->dev.of_node);
237304
device_unregister(&ulpi->dev);
238305
}
239306
EXPORT_SYMBOL_GPL(ulpi_unregister_interface);

0 commit comments

Comments
 (0)