diff options
Diffstat (limited to 'drivers/usb/common/ulpi.c')
-rw-r--r-- | drivers/usb/common/ulpi.c | 79 |
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) | |||
50 | static int ulpi_uevent(struct device *dev, struct kobj_uevent_env *env) | 57 | static 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) | |||
60 | static int ulpi_probe(struct device *dev) | 72 | static 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 = { | |||
87 | static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, | 104 | static 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 | ||
156 | static int ulpi_register(struct device *dev, struct ulpi *ulpi) | 178 | static 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 | |||
202 | static 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; | ||
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 */ | ||
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 | */ |
235 | void ulpi_unregister_interface(struct ulpi *ulpi) | 301 | void 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 | } |
239 | EXPORT_SYMBOL_GPL(ulpi_unregister_interface); | 306 | EXPORT_SYMBOL_GPL(ulpi_unregister_interface); |