summaryrefslogtreecommitdiffstats
path: root/drivers/usb/common
diff options
context:
space:
mode:
authorStephen Boyd <stephen.boyd@linaro.org>2016-12-28 17:56:49 -0500
committerPeter Chen <peter.chen@nxp.com>2017-01-19 22:24:06 -0500
commitef6a7bcfb01c9c8df172ad06fb547216ca788711 (patch)
treef59a462b93721de919c6347fc6afb4e2be387273 /drivers/usb/common
parent7a3b7cd332db08546f3cdd984f11773e0d1999e7 (diff)
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 <gregkh@linuxfoundation.org> Acked-by: Heikki Krogerus <heikki.krogerus@linux.intel.com> Cc: <devicetree@vger.kernel.org> Acked-by: Rob Herring <robh@kernel.org> Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org> Signed-off-by: Peter Chen <peter.chen@nxp.com>
Diffstat (limited to 'drivers/usb/common')
-rw-r--r--drivers/usb/common/ulpi.c79
1 files changed, 73 insertions, 6 deletions
diff --git a/drivers/usb/common/ulpi.c b/drivers/usb/common/ulpi.c
index 8b317702d761..c9480d77810c 100644
--- a/drivers/usb/common/ulpi.c
+++ b/drivers/usb/common/ulpi.c
@@ -16,6 +16,9 @@
16#include <linux/module.h> 16#include <linux/module.h>
17#include <linux/slab.h> 17#include <linux/slab.h>
18#include <linux/acpi.h> 18#include <linux/acpi.h>
19#include <linux/of.h>
20#include <linux/of_device.h>
21#include <linux/clk/clk-conf.h>
19 22
20/* -------------------------------------------------------------------------- */ 23/* -------------------------------------------------------------------------- */
21 24
@@ -39,6 +42,10 @@ static int ulpi_match(struct device *dev, struct device_driver *driver)
39 struct ulpi *ulpi = to_ulpi_dev(dev); 42 struct ulpi *ulpi = to_ulpi_dev(dev);
40 const struct ulpi_device_id *id; 43 const struct ulpi_device_id *id;
41 44
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
42 for (id = drv->id_table; id->vendor; id++) 49 for (id = drv->id_table; id->vendor; id++)
43 if (id->vendor == ulpi->id.vendor && 50 if (id->vendor == ulpi->id.vendor &&
44 id->product == ulpi->id.product) 51 id->product == ulpi->id.product)
@@ -50,6 +57,11 @@ static int ulpi_match(struct device *dev, struct device_driver *driver)
50static int ulpi_uevent(struct device *dev, struct kobj_uevent_env *env) 57static int ulpi_uevent(struct device *dev, struct kobj_uevent_env *env)
51{ 58{
52 struct ulpi *ulpi = to_ulpi_dev(dev); 59 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;
53 65
54 if (add_uevent_var(env, "MODALIAS=ulpi:v%04xp%04x", 66 if (add_uevent_var(env, "MODALIAS=ulpi:v%04xp%04x",
55 ulpi->id.vendor, ulpi->id.product)) 67 ulpi->id.vendor, ulpi->id.product))
@@ -60,6 +72,11 @@ static int ulpi_uevent(struct device *dev, struct kobj_uevent_env *env)
60static int ulpi_probe(struct device *dev) 72static int ulpi_probe(struct device *dev)
61{ 73{
62 struct ulpi_driver *drv = to_ulpi_driver(dev->driver); 74 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;
63 80
64 return drv->probe(to_ulpi_dev(dev)); 81 return drv->probe(to_ulpi_dev(dev));
65} 82}
@@ -87,8 +104,13 @@ static struct bus_type ulpi_bus = {
87static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, 104static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
88 char *buf) 105 char *buf)
89{ 106{
107 int len;
90 struct ulpi *ulpi = to_ulpi_dev(dev); 108 struct ulpi *ulpi = to_ulpi_dev(dev);
91 109
110 len = of_device_get_modalias(dev, buf, PAGE_SIZE - 1);
111 if (len != -ENODEV)
112 return len;
113
92 return sprintf(buf, "ulpi:v%04xp%04x\n", 114 return sprintf(buf, "ulpi:v%04xp%04x\n",
93 ulpi->id.vendor, ulpi->id.product); 115 ulpi->id.vendor, ulpi->id.product);
94} 116}
@@ -153,23 +175,45 @@ EXPORT_SYMBOL_GPL(ulpi_unregister_driver);
153 175
154/* -------------------------------------------------------------------------- */ 176/* -------------------------------------------------------------------------- */
155 177
156static int ulpi_register(struct device *dev, struct ulpi *ulpi) 178static int ulpi_of_register(struct ulpi *ulpi)
157{ 179{
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;
159 196
160 ulpi->dev.parent = dev; /* needed early for ops */ 197 ulpi->dev.of_node = child;
198
199 return 0;
200}
201
202static int ulpi_read_id(struct ulpi *ulpi)
203{
204 int ret;
161 205
162 /* Test the interface */ 206 /* Test the interface */
163 ret = ulpi_write(ulpi, ULPI_SCRATCH, 0xaa); 207 ret = ulpi_write(ulpi, ULPI_SCRATCH, 0xaa);
164 if (ret < 0) 208 if (ret < 0)
165 return ret; 209 goto err;
166 210
167 ret = ulpi_read(ulpi, ULPI_SCRATCH); 211 ret = ulpi_read(ulpi, ULPI_SCRATCH);
168 if (ret < 0) 212 if (ret < 0)
169 return ret; 213 return ret;
170 214
171 if (ret != 0xaa) 215 if (ret != 0xaa)
172 return -ENODEV; 216 goto err;
173 217
174 ulpi->id.vendor = ulpi_read(ulpi, ULPI_VENDOR_ID_LOW); 218 ulpi->id.vendor = ulpi_read(ulpi, ULPI_VENDOR_ID_LOW);
175 ulpi->id.vendor |= ulpi_read(ulpi, ULPI_VENDOR_ID_HIGH) << 8; 219 ulpi->id.vendor |= ulpi_read(ulpi, ULPI_VENDOR_ID_HIGH) << 8;
@@ -177,13 +221,35 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi)
177 ulpi->id.product = ulpi_read(ulpi, ULPI_PRODUCT_ID_LOW); 221 ulpi->id.product = ulpi_read(ulpi, ULPI_PRODUCT_ID_LOW);
178 ulpi->id.product |= ulpi_read(ulpi, ULPI_PRODUCT_ID_HIGH) << 8; 222 ulpi->id.product |= ulpi_read(ulpi, ULPI_PRODUCT_ID_HIGH) << 8;
179 223
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;
230err:
231 of_device_request_module(&ulpi->dev);
232 return 0;
233}
234
235static int ulpi_register(struct device *dev, struct ulpi *ulpi)
236{
237 int ret;
238
239 ulpi->dev.parent = dev; /* needed early for ops */
180 ulpi->dev.bus = &ulpi_bus; 240 ulpi->dev.bus = &ulpi_bus;
181 ulpi->dev.type = &ulpi_dev_type; 241 ulpi->dev.type = &ulpi_dev_type;
182 dev_set_name(&ulpi->dev, "%s.ulpi", dev_name(dev)); 242 dev_set_name(&ulpi->dev, "%s.ulpi", dev_name(dev));
183 243
184 ACPI_COMPANION_SET(&ulpi->dev, ACPI_COMPANION(dev)); 244 ACPI_COMPANION_SET(&ulpi->dev, ACPI_COMPANION(dev));
185 245
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;
187 253
188 ret = device_register(&ulpi->dev); 254 ret = device_register(&ulpi->dev);
189 if (ret) 255 if (ret)
@@ -234,6 +300,7 @@ EXPORT_SYMBOL_GPL(ulpi_register_interface);
234 */ 300 */
235void ulpi_unregister_interface(struct ulpi *ulpi) 301void ulpi_unregister_interface(struct ulpi *ulpi)
236{ 302{
303 of_node_put(ulpi->dev.of_node);
237 device_unregister(&ulpi->dev); 304 device_unregister(&ulpi->dev);
238} 305}
239EXPORT_SYMBOL_GPL(ulpi_unregister_interface); 306EXPORT_SYMBOL_GPL(ulpi_unregister_interface);