diff options
author | Aaro Koskinen <aaro.koskinen@iki.fi> | 2013-12-06 09:13:07 -0500 |
---|---|---|
committer | Felipe Balbi <balbi@ti.com> | 2013-12-06 15:46:32 -0500 |
commit | 9ba96ae5074c9f15b357919e704ceba2bd34972d (patch) | |
tree | ae31e74f7a2dea56421028efda2aaaa6340cbd6c | |
parent | 449d2ba613a551046544ea75b99563ee643c8d7c (diff) |
usb: omap1: Tahvo USB transceiver driver
Add Tahvo USB transceiver driver.
Based on old code from linux-omap tree. The original driver was written
by Juha Yrjölä, Tony Lindgren, and Timo Teräs.
Signed-off-by: Aaro Koskinen <aaro.koskinen@iki.fi>
Signed-off-by: Felipe Balbi <balbi@ti.com>
-rw-r--r-- | Documentation/ABI/testing/sysfs-platform-tahvo-usb | 16 | ||||
-rw-r--r-- | drivers/usb/phy/Kconfig | 15 | ||||
-rw-r--r-- | drivers/usb/phy/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/phy/phy-tahvo.c | 463 |
4 files changed, 495 insertions, 0 deletions
diff --git a/Documentation/ABI/testing/sysfs-platform-tahvo-usb b/Documentation/ABI/testing/sysfs-platform-tahvo-usb new file mode 100644 index 000000000000..f6e20ce4b538 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-platform-tahvo-usb | |||
@@ -0,0 +1,16 @@ | |||
1 | What: /sys/bus/platform/devices/tahvo-usb/otg_mode | ||
2 | Date: December 2013 | ||
3 | Contact: Aaro Koskinen <aaro.koskinen@iki.fi> | ||
4 | Description: | ||
5 | Set or read the current OTG mode. Valid values are "host" and | ||
6 | "peripheral". | ||
7 | |||
8 | Reading: returns the current mode. | ||
9 | |||
10 | What: /sys/bus/platform/devices/tahvo-usb/vbus | ||
11 | Date: December 2013 | ||
12 | Contact: Aaro Koskinen <aaro.koskinen@iki.fi> | ||
13 | Description: | ||
14 | Read the current VBUS state. | ||
15 | |||
16 | Reading: returns "on" or "off". | ||
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 0dbab6f5c2d4..4f22762f3d6f 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig | |||
@@ -152,6 +152,21 @@ config OMAP_OTG | |||
152 | This driver can also be built as a module. If so, the module | 152 | This driver can also be built as a module. If so, the module |
153 | will be called omap-otg. | 153 | will be called omap-otg. |
154 | 154 | ||
155 | config TAHVO_USB | ||
156 | tristate "Tahvo USB transceiver driver" | ||
157 | depends on MFD_RETU && EXTCON | ||
158 | select USB_PHY | ||
159 | help | ||
160 | Enable this to support USB transceiver on Tahvo. This is used | ||
161 | at least on Nokia 770. | ||
162 | |||
163 | config TAHVO_USB_HOST_BY_DEFAULT | ||
164 | depends on TAHVO_USB | ||
165 | boolean "Device in USB host mode by default" | ||
166 | help | ||
167 | Say Y here, if you want the device to enter USB host mode | ||
168 | by default on bootup. | ||
169 | |||
155 | config USB_ISP1301 | 170 | config USB_ISP1301 |
156 | tristate "NXP ISP1301 USB transceiver support" | 171 | tristate "NXP ISP1301 USB transceiver support" |
157 | depends on USB || USB_GADGET | 172 | depends on USB || USB_GADGET |
diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 64a9345e5633..9b3be9e0aeb4 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile | |||
@@ -12,6 +12,7 @@ obj-$(CONFIG_FSL_USB2_OTG) += phy-fsl-usb.o | |||
12 | obj-$(CONFIG_ISP1301_OMAP) += phy-isp1301-omap.o | 12 | obj-$(CONFIG_ISP1301_OMAP) += phy-isp1301-omap.o |
13 | obj-$(CONFIG_MV_U3D_PHY) += phy-mv-u3d-usb.o | 13 | obj-$(CONFIG_MV_U3D_PHY) += phy-mv-u3d-usb.o |
14 | obj-$(CONFIG_NOP_USB_XCEIV) += phy-generic.o | 14 | obj-$(CONFIG_NOP_USB_XCEIV) += phy-generic.o |
15 | obj-$(CONFIG_TAHVO_USB) += phy-tahvo.o | ||
15 | obj-$(CONFIG_OMAP_CONTROL_USB) += phy-omap-control.o | 16 | obj-$(CONFIG_OMAP_CONTROL_USB) += phy-omap-control.o |
16 | obj-$(CONFIG_AM335X_CONTROL_USB) += phy-am335x-control.o | 17 | obj-$(CONFIG_AM335X_CONTROL_USB) += phy-am335x-control.o |
17 | obj-$(CONFIG_AM335X_PHY_USB) += phy-am335x.o | 18 | obj-$(CONFIG_AM335X_PHY_USB) += phy-am335x.o |
diff --git a/drivers/usb/phy/phy-tahvo.c b/drivers/usb/phy/phy-tahvo.c new file mode 100644 index 000000000000..8bb833e22d64 --- /dev/null +++ b/drivers/usb/phy/phy-tahvo.c | |||
@@ -0,0 +1,463 @@ | |||
1 | /* | ||
2 | * Tahvo USB transceiver driver | ||
3 | * | ||
4 | * Copyright (C) 2005-2006 Nokia Corporation | ||
5 | * | ||
6 | * Parts copied from isp1301_omap.c. | ||
7 | * Copyright (C) 2004 Texas Instruments | ||
8 | * Copyright (C) 2004 David Brownell | ||
9 | * | ||
10 | * Original driver written by Juha Yrjölä, Tony Lindgren and Timo Teräs. | ||
11 | * Modified for Retu/Tahvo MFD by Aaro Koskinen. | ||
12 | * | ||
13 | * This file is subject to the terms and conditions of the GNU General | ||
14 | * Public License. See the file "COPYING" in the main directory of this | ||
15 | * archive for more details. | ||
16 | * | ||
17 | * This program is distributed in the hope that it will be useful, | ||
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
20 | * GNU General Public License for more details. | ||
21 | */ | ||
22 | |||
23 | #include <linux/io.h> | ||
24 | #include <linux/clk.h> | ||
25 | #include <linux/usb.h> | ||
26 | #include <linux/extcon.h> | ||
27 | #include <linux/kernel.h> | ||
28 | #include <linux/module.h> | ||
29 | #include <linux/usb/otg.h> | ||
30 | #include <linux/mfd/retu.h> | ||
31 | #include <linux/usb/gadget.h> | ||
32 | #include <linux/platform_device.h> | ||
33 | |||
34 | #define DRIVER_NAME "tahvo-usb" | ||
35 | |||
36 | #define TAHVO_REG_IDSR 0x02 | ||
37 | #define TAHVO_REG_USBR 0x06 | ||
38 | |||
39 | #define USBR_SLAVE_CONTROL (1 << 8) | ||
40 | #define USBR_VPPVIO_SW (1 << 7) | ||
41 | #define USBR_SPEED (1 << 6) | ||
42 | #define USBR_REGOUT (1 << 5) | ||
43 | #define USBR_MASTER_SW2 (1 << 4) | ||
44 | #define USBR_MASTER_SW1 (1 << 3) | ||
45 | #define USBR_SLAVE_SW (1 << 2) | ||
46 | #define USBR_NSUSPEND (1 << 1) | ||
47 | #define USBR_SEMODE (1 << 0) | ||
48 | |||
49 | #define TAHVO_MODE_HOST 0 | ||
50 | #define TAHVO_MODE_PERIPHERAL 1 | ||
51 | |||
52 | struct tahvo_usb { | ||
53 | struct platform_device *pt_dev; | ||
54 | struct usb_phy phy; | ||
55 | int vbus_state; | ||
56 | struct mutex serialize; | ||
57 | struct clk *ick; | ||
58 | int irq; | ||
59 | int tahvo_mode; | ||
60 | struct extcon_dev extcon; | ||
61 | }; | ||
62 | |||
63 | static const char *tahvo_cable[] = { | ||
64 | "USB-HOST", | ||
65 | "USB", | ||
66 | NULL, | ||
67 | }; | ||
68 | |||
69 | static ssize_t vbus_state_show(struct device *device, | ||
70 | struct device_attribute *attr, char *buf) | ||
71 | { | ||
72 | struct tahvo_usb *tu = dev_get_drvdata(device); | ||
73 | return sprintf(buf, "%s\n", tu->vbus_state ? "on" : "off"); | ||
74 | } | ||
75 | static DEVICE_ATTR(vbus, 0444, vbus_state_show, NULL); | ||
76 | |||
77 | static void check_vbus_state(struct tahvo_usb *tu) | ||
78 | { | ||
79 | struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); | ||
80 | int reg, prev_state; | ||
81 | |||
82 | reg = retu_read(rdev, TAHVO_REG_IDSR); | ||
83 | if (reg & TAHVO_STAT_VBUS) { | ||
84 | switch (tu->phy.state) { | ||
85 | case OTG_STATE_B_IDLE: | ||
86 | /* Enable the gadget driver */ | ||
87 | if (tu->phy.otg->gadget) | ||
88 | usb_gadget_vbus_connect(tu->phy.otg->gadget); | ||
89 | tu->phy.state = OTG_STATE_B_PERIPHERAL; | ||
90 | break; | ||
91 | case OTG_STATE_A_IDLE: | ||
92 | /* | ||
93 | * Session is now valid assuming the USB hub is driving | ||
94 | * Vbus. | ||
95 | */ | ||
96 | tu->phy.state = OTG_STATE_A_HOST; | ||
97 | break; | ||
98 | default: | ||
99 | break; | ||
100 | } | ||
101 | dev_info(&tu->pt_dev->dev, "USB cable connected\n"); | ||
102 | } else { | ||
103 | switch (tu->phy.state) { | ||
104 | case OTG_STATE_B_PERIPHERAL: | ||
105 | if (tu->phy.otg->gadget) | ||
106 | usb_gadget_vbus_disconnect(tu->phy.otg->gadget); | ||
107 | tu->phy.state = OTG_STATE_B_IDLE; | ||
108 | break; | ||
109 | case OTG_STATE_A_HOST: | ||
110 | tu->phy.state = OTG_STATE_A_IDLE; | ||
111 | break; | ||
112 | default: | ||
113 | break; | ||
114 | } | ||
115 | dev_info(&tu->pt_dev->dev, "USB cable disconnected\n"); | ||
116 | } | ||
117 | |||
118 | prev_state = tu->vbus_state; | ||
119 | tu->vbus_state = reg & TAHVO_STAT_VBUS; | ||
120 | if (prev_state != tu->vbus_state) { | ||
121 | extcon_set_cable_state(&tu->extcon, "USB", tu->vbus_state); | ||
122 | sysfs_notify(&tu->pt_dev->dev.kobj, NULL, "vbus_state"); | ||
123 | } | ||
124 | } | ||
125 | |||
126 | static void tahvo_usb_become_host(struct tahvo_usb *tu) | ||
127 | { | ||
128 | struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); | ||
129 | |||
130 | extcon_set_cable_state(&tu->extcon, "USB-HOST", true); | ||
131 | |||
132 | /* Power up the transceiver in USB host mode */ | ||
133 | retu_write(rdev, TAHVO_REG_USBR, USBR_REGOUT | USBR_NSUSPEND | | ||
134 | USBR_MASTER_SW2 | USBR_MASTER_SW1); | ||
135 | tu->phy.state = OTG_STATE_A_IDLE; | ||
136 | |||
137 | check_vbus_state(tu); | ||
138 | } | ||
139 | |||
140 | static void tahvo_usb_stop_host(struct tahvo_usb *tu) | ||
141 | { | ||
142 | tu->phy.state = OTG_STATE_A_IDLE; | ||
143 | } | ||
144 | |||
145 | static void tahvo_usb_become_peripheral(struct tahvo_usb *tu) | ||
146 | { | ||
147 | struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); | ||
148 | |||
149 | extcon_set_cable_state(&tu->extcon, "USB-HOST", false); | ||
150 | |||
151 | /* Power up transceiver and set it in USB peripheral mode */ | ||
152 | retu_write(rdev, TAHVO_REG_USBR, USBR_SLAVE_CONTROL | USBR_REGOUT | | ||
153 | USBR_NSUSPEND | USBR_SLAVE_SW); | ||
154 | tu->phy.state = OTG_STATE_B_IDLE; | ||
155 | |||
156 | check_vbus_state(tu); | ||
157 | } | ||
158 | |||
159 | static void tahvo_usb_stop_peripheral(struct tahvo_usb *tu) | ||
160 | { | ||
161 | if (tu->phy.otg->gadget) | ||
162 | usb_gadget_vbus_disconnect(tu->phy.otg->gadget); | ||
163 | tu->phy.state = OTG_STATE_B_IDLE; | ||
164 | } | ||
165 | |||
166 | static void tahvo_usb_power_off(struct tahvo_usb *tu) | ||
167 | { | ||
168 | struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); | ||
169 | |||
170 | /* Disable gadget controller if any */ | ||
171 | if (tu->phy.otg->gadget) | ||
172 | usb_gadget_vbus_disconnect(tu->phy.otg->gadget); | ||
173 | |||
174 | /* Power off transceiver */ | ||
175 | retu_write(rdev, TAHVO_REG_USBR, 0); | ||
176 | tu->phy.state = OTG_STATE_UNDEFINED; | ||
177 | } | ||
178 | |||
179 | static int tahvo_usb_set_suspend(struct usb_phy *dev, int suspend) | ||
180 | { | ||
181 | struct tahvo_usb *tu = container_of(dev, struct tahvo_usb, phy); | ||
182 | struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); | ||
183 | u16 w; | ||
184 | |||
185 | dev_dbg(&tu->pt_dev->dev, "%s\n", __func__); | ||
186 | |||
187 | w = retu_read(rdev, TAHVO_REG_USBR); | ||
188 | if (suspend) | ||
189 | w &= ~USBR_NSUSPEND; | ||
190 | else | ||
191 | w |= USBR_NSUSPEND; | ||
192 | retu_write(rdev, TAHVO_REG_USBR, w); | ||
193 | |||
194 | return 0; | ||
195 | } | ||
196 | |||
197 | static int tahvo_usb_set_host(struct usb_otg *otg, struct usb_bus *host) | ||
198 | { | ||
199 | struct tahvo_usb *tu = container_of(otg->phy, struct tahvo_usb, phy); | ||
200 | |||
201 | dev_dbg(&tu->pt_dev->dev, "%s %p\n", __func__, host); | ||
202 | |||
203 | if (otg == NULL) | ||
204 | return -ENODEV; | ||
205 | |||
206 | mutex_lock(&tu->serialize); | ||
207 | |||
208 | if (host == NULL) { | ||
209 | if (tu->tahvo_mode == TAHVO_MODE_HOST) | ||
210 | tahvo_usb_power_off(tu); | ||
211 | otg->host = NULL; | ||
212 | mutex_unlock(&tu->serialize); | ||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | if (tu->tahvo_mode == TAHVO_MODE_HOST) { | ||
217 | otg->host = NULL; | ||
218 | tahvo_usb_become_host(tu); | ||
219 | } | ||
220 | |||
221 | otg->host = host; | ||
222 | |||
223 | mutex_unlock(&tu->serialize); | ||
224 | |||
225 | return 0; | ||
226 | } | ||
227 | |||
228 | static int tahvo_usb_set_peripheral(struct usb_otg *otg, | ||
229 | struct usb_gadget *gadget) | ||
230 | { | ||
231 | struct tahvo_usb *tu = container_of(otg->phy, struct tahvo_usb, phy); | ||
232 | |||
233 | dev_dbg(&tu->pt_dev->dev, "%s %p\n", __func__, gadget); | ||
234 | |||
235 | if (!otg) | ||
236 | return -ENODEV; | ||
237 | |||
238 | mutex_lock(&tu->serialize); | ||
239 | |||
240 | if (!gadget) { | ||
241 | if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL) | ||
242 | tahvo_usb_power_off(tu); | ||
243 | tu->phy.otg->gadget = NULL; | ||
244 | mutex_unlock(&tu->serialize); | ||
245 | return 0; | ||
246 | } | ||
247 | |||
248 | tu->phy.otg->gadget = gadget; | ||
249 | if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL) | ||
250 | tahvo_usb_become_peripheral(tu); | ||
251 | |||
252 | mutex_unlock(&tu->serialize); | ||
253 | |||
254 | return 0; | ||
255 | } | ||
256 | |||
257 | static irqreturn_t tahvo_usb_vbus_interrupt(int irq, void *_tu) | ||
258 | { | ||
259 | struct tahvo_usb *tu = _tu; | ||
260 | |||
261 | mutex_lock(&tu->serialize); | ||
262 | check_vbus_state(tu); | ||
263 | mutex_unlock(&tu->serialize); | ||
264 | |||
265 | return IRQ_HANDLED; | ||
266 | } | ||
267 | |||
268 | static ssize_t otg_mode_show(struct device *device, | ||
269 | struct device_attribute *attr, char *buf) | ||
270 | { | ||
271 | struct tahvo_usb *tu = dev_get_drvdata(device); | ||
272 | |||
273 | switch (tu->tahvo_mode) { | ||
274 | case TAHVO_MODE_HOST: | ||
275 | return sprintf(buf, "host\n"); | ||
276 | case TAHVO_MODE_PERIPHERAL: | ||
277 | return sprintf(buf, "peripheral\n"); | ||
278 | } | ||
279 | |||
280 | return -EINVAL; | ||
281 | } | ||
282 | |||
283 | static ssize_t otg_mode_store(struct device *device, | ||
284 | struct device_attribute *attr, | ||
285 | const char *buf, size_t count) | ||
286 | { | ||
287 | struct tahvo_usb *tu = dev_get_drvdata(device); | ||
288 | int r; | ||
289 | |||
290 | mutex_lock(&tu->serialize); | ||
291 | if (count >= 4 && strncmp(buf, "host", 4) == 0) { | ||
292 | if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL) | ||
293 | tahvo_usb_stop_peripheral(tu); | ||
294 | tu->tahvo_mode = TAHVO_MODE_HOST; | ||
295 | if (tu->phy.otg->host) { | ||
296 | dev_info(device, "HOST mode: host controller present\n"); | ||
297 | tahvo_usb_become_host(tu); | ||
298 | } else { | ||
299 | dev_info(device, "HOST mode: no host controller, powering off\n"); | ||
300 | tahvo_usb_power_off(tu); | ||
301 | } | ||
302 | r = strlen(buf); | ||
303 | } else if (count >= 10 && strncmp(buf, "peripheral", 10) == 0) { | ||
304 | if (tu->tahvo_mode == TAHVO_MODE_HOST) | ||
305 | tahvo_usb_stop_host(tu); | ||
306 | tu->tahvo_mode = TAHVO_MODE_PERIPHERAL; | ||
307 | if (tu->phy.otg->gadget) { | ||
308 | dev_info(device, "PERIPHERAL mode: gadget driver present\n"); | ||
309 | tahvo_usb_become_peripheral(tu); | ||
310 | } else { | ||
311 | dev_info(device, "PERIPHERAL mode: no gadget driver, powering off\n"); | ||
312 | tahvo_usb_power_off(tu); | ||
313 | } | ||
314 | r = strlen(buf); | ||
315 | } else { | ||
316 | r = -EINVAL; | ||
317 | } | ||
318 | mutex_unlock(&tu->serialize); | ||
319 | |||
320 | return r; | ||
321 | } | ||
322 | static DEVICE_ATTR(otg_mode, 0644, otg_mode_show, otg_mode_store); | ||
323 | |||
324 | static struct attribute *tahvo_attributes[] = { | ||
325 | &dev_attr_vbus.attr, | ||
326 | &dev_attr_otg_mode.attr, | ||
327 | NULL | ||
328 | }; | ||
329 | |||
330 | static struct attribute_group tahvo_attr_group = { | ||
331 | .attrs = tahvo_attributes, | ||
332 | }; | ||
333 | |||
334 | static int tahvo_usb_probe(struct platform_device *pdev) | ||
335 | { | ||
336 | struct retu_dev *rdev = dev_get_drvdata(pdev->dev.parent); | ||
337 | struct tahvo_usb *tu; | ||
338 | int ret; | ||
339 | |||
340 | tu = devm_kzalloc(&pdev->dev, sizeof(*tu), GFP_KERNEL); | ||
341 | if (!tu) | ||
342 | return -ENOMEM; | ||
343 | |||
344 | tu->phy.otg = devm_kzalloc(&pdev->dev, sizeof(*tu->phy.otg), | ||
345 | GFP_KERNEL); | ||
346 | if (!tu->phy.otg) | ||
347 | return -ENOMEM; | ||
348 | |||
349 | tu->pt_dev = pdev; | ||
350 | |||
351 | /* Default mode */ | ||
352 | #ifdef CONFIG_TAHVO_USB_HOST_BY_DEFAULT | ||
353 | tu->tahvo_mode = TAHVO_MODE_HOST; | ||
354 | #else | ||
355 | tu->tahvo_mode = TAHVO_MODE_PERIPHERAL; | ||
356 | #endif | ||
357 | |||
358 | mutex_init(&tu->serialize); | ||
359 | |||
360 | tu->ick = devm_clk_get(&pdev->dev, "usb_l4_ick"); | ||
361 | if (!IS_ERR(tu->ick)) | ||
362 | clk_enable(tu->ick); | ||
363 | |||
364 | /* | ||
365 | * Set initial state, so that we generate kevents only on state changes. | ||
366 | */ | ||
367 | tu->vbus_state = retu_read(rdev, TAHVO_REG_IDSR) & TAHVO_STAT_VBUS; | ||
368 | |||
369 | tu->extcon.name = DRIVER_NAME; | ||
370 | tu->extcon.supported_cable = tahvo_cable; | ||
371 | tu->extcon.dev.parent = &pdev->dev; | ||
372 | |||
373 | ret = extcon_dev_register(&tu->extcon); | ||
374 | if (ret) { | ||
375 | dev_err(&pdev->dev, "could not register extcon device: %d\n", | ||
376 | ret); | ||
377 | goto err_disable_clk; | ||
378 | } | ||
379 | |||
380 | /* Set the initial cable state. */ | ||
381 | extcon_set_cable_state(&tu->extcon, "USB-HOST", | ||
382 | tu->tahvo_mode == TAHVO_MODE_HOST); | ||
383 | extcon_set_cable_state(&tu->extcon, "USB", tu->vbus_state); | ||
384 | |||
385 | /* Create OTG interface */ | ||
386 | tahvo_usb_power_off(tu); | ||
387 | tu->phy.dev = &pdev->dev; | ||
388 | tu->phy.state = OTG_STATE_UNDEFINED; | ||
389 | tu->phy.label = DRIVER_NAME; | ||
390 | tu->phy.set_suspend = tahvo_usb_set_suspend; | ||
391 | |||
392 | tu->phy.otg->phy = &tu->phy; | ||
393 | tu->phy.otg->set_host = tahvo_usb_set_host; | ||
394 | tu->phy.otg->set_peripheral = tahvo_usb_set_peripheral; | ||
395 | |||
396 | ret = usb_add_phy(&tu->phy, USB_PHY_TYPE_USB2); | ||
397 | if (ret < 0) { | ||
398 | dev_err(&pdev->dev, "cannot register USB transceiver: %d\n", | ||
399 | ret); | ||
400 | goto err_extcon_unreg; | ||
401 | } | ||
402 | |||
403 | dev_set_drvdata(&pdev->dev, tu); | ||
404 | |||
405 | tu->irq = platform_get_irq(pdev, 0); | ||
406 | ret = request_threaded_irq(tu->irq, NULL, tahvo_usb_vbus_interrupt, 0, | ||
407 | "tahvo-vbus", tu); | ||
408 | if (ret) { | ||
409 | dev_err(&pdev->dev, "could not register tahvo-vbus irq: %d\n", | ||
410 | ret); | ||
411 | goto err_remove_phy; | ||
412 | } | ||
413 | |||
414 | /* Attributes */ | ||
415 | ret = sysfs_create_group(&pdev->dev.kobj, &tahvo_attr_group); | ||
416 | if (ret) { | ||
417 | dev_err(&pdev->dev, "cannot create sysfs group: %d\n", ret); | ||
418 | goto err_free_irq; | ||
419 | } | ||
420 | |||
421 | return 0; | ||
422 | |||
423 | err_free_irq: | ||
424 | free_irq(tu->irq, tu); | ||
425 | err_remove_phy: | ||
426 | usb_remove_phy(&tu->phy); | ||
427 | err_extcon_unreg: | ||
428 | extcon_dev_unregister(&tu->extcon); | ||
429 | err_disable_clk: | ||
430 | if (!IS_ERR(tu->ick)) | ||
431 | clk_disable(tu->ick); | ||
432 | |||
433 | return ret; | ||
434 | } | ||
435 | |||
436 | static int tahvo_usb_remove(struct platform_device *pdev) | ||
437 | { | ||
438 | struct tahvo_usb *tu = platform_get_drvdata(pdev); | ||
439 | |||
440 | sysfs_remove_group(&pdev->dev.kobj, &tahvo_attr_group); | ||
441 | free_irq(tu->irq, tu); | ||
442 | usb_remove_phy(&tu->phy); | ||
443 | extcon_dev_unregister(&tu->extcon); | ||
444 | if (!IS_ERR(tu->ick)) | ||
445 | clk_disable(tu->ick); | ||
446 | |||
447 | return 0; | ||
448 | } | ||
449 | |||
450 | static struct platform_driver tahvo_usb_driver = { | ||
451 | .probe = tahvo_usb_probe, | ||
452 | .remove = tahvo_usb_remove, | ||
453 | .driver = { | ||
454 | .name = "tahvo-usb", | ||
455 | .owner = THIS_MODULE, | ||
456 | }, | ||
457 | }; | ||
458 | module_platform_driver(tahvo_usb_driver); | ||
459 | |||
460 | MODULE_DESCRIPTION("Tahvo USB transceiver driver"); | ||
461 | MODULE_LICENSE("GPL"); | ||
462 | MODULE_AUTHOR("Juha Yrjölä, Tony Lindgren, and Timo Teräs"); | ||
463 | MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>"); | ||