diff options
Diffstat (limited to 'drivers/usb/otg')
-rw-r--r-- | drivers/usb/otg/Kconfig | 10 | ||||
-rw-r--r-- | drivers/usb/otg/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/otg/gpio_vbus.c | 42 | ||||
-rw-r--r-- | drivers/usb/otg/nop-usb-xceiv.c | 180 | ||||
-rw-r--r-- | drivers/usb/otg/twl4030-usb.c | 73 |
5 files changed, 285 insertions, 21 deletions
diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig index ee55b449ffd..aa884d072f0 100644 --- a/drivers/usb/otg/Kconfig +++ b/drivers/usb/otg/Kconfig | |||
@@ -43,7 +43,7 @@ config ISP1301_OMAP | |||
43 | 43 | ||
44 | config TWL4030_USB | 44 | config TWL4030_USB |
45 | tristate "TWL4030 USB Transceiver Driver" | 45 | tristate "TWL4030 USB Transceiver Driver" |
46 | depends on TWL4030_CORE | 46 | depends on TWL4030_CORE && REGULATOR_TWL4030 |
47 | select USB_OTG_UTILS | 47 | select USB_OTG_UTILS |
48 | help | 48 | help |
49 | Enable this to support the USB OTG transceiver on TWL4030 | 49 | Enable this to support the USB OTG transceiver on TWL4030 |
@@ -51,4 +51,12 @@ config TWL4030_USB | |||
51 | This transceiver supports high and full speed devices plus, | 51 | This transceiver supports high and full speed devices plus, |
52 | in host mode, low speed. | 52 | in host mode, low speed. |
53 | 53 | ||
54 | config NOP_USB_XCEIV | ||
55 | tristate "NOP USB Transceiver Driver" | ||
56 | select USB_OTG_UTILS | ||
57 | help | ||
58 | this driver is to be used by all the usb transceiver which are either | ||
59 | built-in with usb ip or which are autonomous and doesn't require any | ||
60 | phy programming such as ISP1x04 etc. | ||
61 | |||
54 | endif # USB || OTG | 62 | endif # USB || OTG |
diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile index d73c7cf5e2f..20816785652 100644 --- a/drivers/usb/otg/Makefile +++ b/drivers/usb/otg/Makefile | |||
@@ -9,6 +9,7 @@ obj-$(CONFIG_USB_OTG_UTILS) += otg.o | |||
9 | obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o | 9 | obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o |
10 | obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o | 10 | obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o |
11 | obj-$(CONFIG_TWL4030_USB) += twl4030-usb.o | 11 | obj-$(CONFIG_TWL4030_USB) += twl4030-usb.o |
12 | obj-$(CONFIG_NOP_USB_XCEIV) += nop-usb-xceiv.o | ||
12 | 13 | ||
13 | ccflags-$(CONFIG_USB_DEBUG) += -DDEBUG | 14 | ccflags-$(CONFIG_USB_DEBUG) += -DDEBUG |
14 | ccflags-$(CONFIG_USB_GADGET_DEBUG) += -DDEBUG | 15 | ccflags-$(CONFIG_USB_GADGET_DEBUG) += -DDEBUG |
diff --git a/drivers/usb/otg/gpio_vbus.c b/drivers/usb/otg/gpio_vbus.c index 63a6036f04b..1c26c94513e 100644 --- a/drivers/usb/otg/gpio_vbus.c +++ b/drivers/usb/otg/gpio_vbus.c | |||
@@ -13,6 +13,7 @@ | |||
13 | #include <linux/gpio.h> | 13 | #include <linux/gpio.h> |
14 | #include <linux/interrupt.h> | 14 | #include <linux/interrupt.h> |
15 | #include <linux/usb.h> | 15 | #include <linux/usb.h> |
16 | #include <linux/workqueue.h> | ||
16 | 17 | ||
17 | #include <linux/regulator/consumer.h> | 18 | #include <linux/regulator/consumer.h> |
18 | 19 | ||
@@ -34,6 +35,7 @@ struct gpio_vbus_data { | |||
34 | struct regulator *vbus_draw; | 35 | struct regulator *vbus_draw; |
35 | int vbus_draw_enabled; | 36 | int vbus_draw_enabled; |
36 | unsigned mA; | 37 | unsigned mA; |
38 | struct work_struct work; | ||
37 | }; | 39 | }; |
38 | 40 | ||
39 | 41 | ||
@@ -76,24 +78,26 @@ static void set_vbus_draw(struct gpio_vbus_data *gpio_vbus, unsigned mA) | |||
76 | gpio_vbus->mA = mA; | 78 | gpio_vbus->mA = mA; |
77 | } | 79 | } |
78 | 80 | ||
79 | /* VBUS change IRQ handler */ | 81 | static int is_vbus_powered(struct gpio_vbus_mach_info *pdata) |
80 | static irqreturn_t gpio_vbus_irq(int irq, void *data) | ||
81 | { | 82 | { |
82 | struct platform_device *pdev = data; | 83 | int vbus; |
83 | struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data; | ||
84 | struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev); | ||
85 | int gpio, vbus; | ||
86 | 84 | ||
87 | vbus = gpio_get_value(pdata->gpio_vbus); | 85 | vbus = gpio_get_value(pdata->gpio_vbus); |
88 | if (pdata->gpio_vbus_inverted) | 86 | if (pdata->gpio_vbus_inverted) |
89 | vbus = !vbus; | 87 | vbus = !vbus; |
90 | 88 | ||
91 | dev_dbg(&pdev->dev, "VBUS %s (gadget: %s)\n", | 89 | return vbus; |
92 | vbus ? "supplied" : "inactive", | 90 | } |
93 | gpio_vbus->otg.gadget ? gpio_vbus->otg.gadget->name : "none"); | 91 | |
92 | static void gpio_vbus_work(struct work_struct *work) | ||
93 | { | ||
94 | struct gpio_vbus_data *gpio_vbus = | ||
95 | container_of(work, struct gpio_vbus_data, work); | ||
96 | struct gpio_vbus_mach_info *pdata = gpio_vbus->dev->platform_data; | ||
97 | int gpio; | ||
94 | 98 | ||
95 | if (!gpio_vbus->otg.gadget) | 99 | if (!gpio_vbus->otg.gadget) |
96 | return IRQ_HANDLED; | 100 | return; |
97 | 101 | ||
98 | /* Peripheral controllers which manage the pullup themselves won't have | 102 | /* Peripheral controllers which manage the pullup themselves won't have |
99 | * gpio_pullup configured here. If it's configured here, we'll do what | 103 | * gpio_pullup configured here. If it's configured here, we'll do what |
@@ -101,7 +105,7 @@ static irqreturn_t gpio_vbus_irq(int irq, void *data) | |||
101 | * that may complicate usb_gadget_{,dis}connect() support. | 105 | * that may complicate usb_gadget_{,dis}connect() support. |
102 | */ | 106 | */ |
103 | gpio = pdata->gpio_pullup; | 107 | gpio = pdata->gpio_pullup; |
104 | if (vbus) { | 108 | if (is_vbus_powered(pdata)) { |
105 | gpio_vbus->otg.state = OTG_STATE_B_PERIPHERAL; | 109 | gpio_vbus->otg.state = OTG_STATE_B_PERIPHERAL; |
106 | usb_gadget_vbus_connect(gpio_vbus->otg.gadget); | 110 | usb_gadget_vbus_connect(gpio_vbus->otg.gadget); |
107 | 111 | ||
@@ -121,6 +125,21 @@ static irqreturn_t gpio_vbus_irq(int irq, void *data) | |||
121 | usb_gadget_vbus_disconnect(gpio_vbus->otg.gadget); | 125 | usb_gadget_vbus_disconnect(gpio_vbus->otg.gadget); |
122 | gpio_vbus->otg.state = OTG_STATE_B_IDLE; | 126 | gpio_vbus->otg.state = OTG_STATE_B_IDLE; |
123 | } | 127 | } |
128 | } | ||
129 | |||
130 | /* VBUS change IRQ handler */ | ||
131 | static irqreturn_t gpio_vbus_irq(int irq, void *data) | ||
132 | { | ||
133 | struct platform_device *pdev = data; | ||
134 | struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data; | ||
135 | struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev); | ||
136 | |||
137 | dev_dbg(&pdev->dev, "VBUS %s (gadget: %s)\n", | ||
138 | is_vbus_powered(pdata) ? "supplied" : "inactive", | ||
139 | gpio_vbus->otg.gadget ? gpio_vbus->otg.gadget->name : "none"); | ||
140 | |||
141 | if (gpio_vbus->otg.gadget) | ||
142 | schedule_work(&gpio_vbus->work); | ||
124 | 143 | ||
125 | return IRQ_HANDLED; | 144 | return IRQ_HANDLED; |
126 | } | 145 | } |
@@ -257,6 +276,7 @@ static int __init gpio_vbus_probe(struct platform_device *pdev) | |||
257 | irq, err); | 276 | irq, err); |
258 | goto err_irq; | 277 | goto err_irq; |
259 | } | 278 | } |
279 | INIT_WORK(&gpio_vbus->work, gpio_vbus_work); | ||
260 | 280 | ||
261 | /* only active when a gadget is registered */ | 281 | /* only active when a gadget is registered */ |
262 | err = otg_set_transceiver(&gpio_vbus->otg); | 282 | err = otg_set_transceiver(&gpio_vbus->otg); |
diff --git a/drivers/usb/otg/nop-usb-xceiv.c b/drivers/usb/otg/nop-usb-xceiv.c new file mode 100644 index 00000000000..4b933f646f2 --- /dev/null +++ b/drivers/usb/otg/nop-usb-xceiv.c | |||
@@ -0,0 +1,180 @@ | |||
1 | /* | ||
2 | * drivers/usb/otg/nop-usb-xceiv.c | ||
3 | * | ||
4 | * NOP USB transceiver for all USB transceiver which are either built-in | ||
5 | * into USB IP or which are mostly autonomous. | ||
6 | * | ||
7 | * Copyright (C) 2009 Texas Instruments Inc | ||
8 | * Author: Ajay Kumar Gupta <ajay.gupta@ti.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
23 | * | ||
24 | * Current status: | ||
25 | * this is to add "nop" transceiver for all those phy which is | ||
26 | * autonomous such as isp1504 etc. | ||
27 | */ | ||
28 | |||
29 | #include <linux/module.h> | ||
30 | #include <linux/platform_device.h> | ||
31 | #include <linux/dma-mapping.h> | ||
32 | #include <linux/usb/otg.h> | ||
33 | |||
34 | struct nop_usb_xceiv { | ||
35 | struct otg_transceiver otg; | ||
36 | struct device *dev; | ||
37 | }; | ||
38 | |||
39 | static u64 nop_xceiv_dmamask = DMA_32BIT_MASK; | ||
40 | |||
41 | static struct platform_device nop_xceiv_device = { | ||
42 | .name = "nop_usb_xceiv", | ||
43 | .id = -1, | ||
44 | .dev = { | ||
45 | .dma_mask = &nop_xceiv_dmamask, | ||
46 | .coherent_dma_mask = DMA_32BIT_MASK, | ||
47 | .platform_data = NULL, | ||
48 | }, | ||
49 | }; | ||
50 | |||
51 | void usb_nop_xceiv_register(void) | ||
52 | { | ||
53 | if (platform_device_register(&nop_xceiv_device) < 0) { | ||
54 | printk(KERN_ERR "Unable to register usb nop transceiver\n"); | ||
55 | return; | ||
56 | } | ||
57 | } | ||
58 | |||
59 | void usb_nop_xceiv_unregister(void) | ||
60 | { | ||
61 | platform_device_unregister(&nop_xceiv_device); | ||
62 | } | ||
63 | |||
64 | static inline struct nop_usb_xceiv *xceiv_to_nop(struct otg_transceiver *x) | ||
65 | { | ||
66 | return container_of(x, struct nop_usb_xceiv, otg); | ||
67 | } | ||
68 | |||
69 | static int nop_set_suspend(struct otg_transceiver *x, int suspend) | ||
70 | { | ||
71 | return 0; | ||
72 | } | ||
73 | |||
74 | static int nop_set_peripheral(struct otg_transceiver *x, | ||
75 | struct usb_gadget *gadget) | ||
76 | { | ||
77 | struct nop_usb_xceiv *nop; | ||
78 | |||
79 | if (!x) | ||
80 | return -ENODEV; | ||
81 | |||
82 | nop = xceiv_to_nop(x); | ||
83 | |||
84 | if (!gadget) { | ||
85 | nop->otg.gadget = NULL; | ||
86 | return -ENODEV; | ||
87 | } | ||
88 | |||
89 | nop->otg.gadget = gadget; | ||
90 | nop->otg.state = OTG_STATE_B_IDLE; | ||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | static int nop_set_host(struct otg_transceiver *x, struct usb_bus *host) | ||
95 | { | ||
96 | struct nop_usb_xceiv *nop; | ||
97 | |||
98 | if (!x) | ||
99 | return -ENODEV; | ||
100 | |||
101 | nop = xceiv_to_nop(x); | ||
102 | |||
103 | if (!host) { | ||
104 | nop->otg.host = NULL; | ||
105 | return -ENODEV; | ||
106 | } | ||
107 | |||
108 | nop->otg.host = host; | ||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | static int __devinit nop_usb_xceiv_probe(struct platform_device *pdev) | ||
113 | { | ||
114 | struct nop_usb_xceiv *nop; | ||
115 | int err; | ||
116 | |||
117 | nop = kzalloc(sizeof *nop, GFP_KERNEL); | ||
118 | if (!nop) | ||
119 | return -ENOMEM; | ||
120 | |||
121 | nop->dev = &pdev->dev; | ||
122 | nop->otg.dev = nop->dev; | ||
123 | nop->otg.label = "nop-xceiv"; | ||
124 | nop->otg.state = OTG_STATE_UNDEFINED; | ||
125 | nop->otg.set_host = nop_set_host; | ||
126 | nop->otg.set_peripheral = nop_set_peripheral; | ||
127 | nop->otg.set_suspend = nop_set_suspend; | ||
128 | |||
129 | err = otg_set_transceiver(&nop->otg); | ||
130 | if (err) { | ||
131 | dev_err(&pdev->dev, "can't register transceiver, err: %d\n", | ||
132 | err); | ||
133 | goto exit; | ||
134 | } | ||
135 | |||
136 | platform_set_drvdata(pdev, nop); | ||
137 | |||
138 | return 0; | ||
139 | exit: | ||
140 | kfree(nop); | ||
141 | return err; | ||
142 | } | ||
143 | |||
144 | static int __devexit nop_usb_xceiv_remove(struct platform_device *pdev) | ||
145 | { | ||
146 | struct nop_usb_xceiv *nop = platform_get_drvdata(pdev); | ||
147 | |||
148 | otg_set_transceiver(NULL); | ||
149 | |||
150 | platform_set_drvdata(pdev, NULL); | ||
151 | kfree(nop); | ||
152 | |||
153 | return 0; | ||
154 | } | ||
155 | |||
156 | static struct platform_driver nop_usb_xceiv_driver = { | ||
157 | .probe = nop_usb_xceiv_probe, | ||
158 | .remove = __devexit_p(nop_usb_xceiv_remove), | ||
159 | .driver = { | ||
160 | .name = "nop_usb_xceiv", | ||
161 | .owner = THIS_MODULE, | ||
162 | }, | ||
163 | }; | ||
164 | |||
165 | static int __init nop_usb_xceiv_init(void) | ||
166 | { | ||
167 | return platform_driver_register(&nop_usb_xceiv_driver); | ||
168 | } | ||
169 | subsys_initcall(nop_usb_xceiv_init); | ||
170 | |||
171 | static void __exit nop_usb_xceiv_exit(void) | ||
172 | { | ||
173 | platform_driver_unregister(&nop_usb_xceiv_driver); | ||
174 | } | ||
175 | module_exit(nop_usb_xceiv_exit); | ||
176 | |||
177 | MODULE_ALIAS("platform:nop_usb_xceiv"); | ||
178 | MODULE_AUTHOR("Texas Instruments Inc"); | ||
179 | MODULE_DESCRIPTION("NOP USB Transceiver driver"); | ||
180 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c index 416e4410be0..d9478d0e1c8 100644 --- a/drivers/usb/otg/twl4030-usb.c +++ b/drivers/usb/otg/twl4030-usb.c | |||
@@ -34,6 +34,8 @@ | |||
34 | #include <linux/delay.h> | 34 | #include <linux/delay.h> |
35 | #include <linux/usb/otg.h> | 35 | #include <linux/usb/otg.h> |
36 | #include <linux/i2c/twl4030.h> | 36 | #include <linux/i2c/twl4030.h> |
37 | #include <linux/regulator/consumer.h> | ||
38 | #include <linux/err.h> | ||
37 | 39 | ||
38 | 40 | ||
39 | /* Register defines */ | 41 | /* Register defines */ |
@@ -246,6 +248,11 @@ struct twl4030_usb { | |||
246 | struct otg_transceiver otg; | 248 | struct otg_transceiver otg; |
247 | struct device *dev; | 249 | struct device *dev; |
248 | 250 | ||
251 | /* TWL4030 internal USB regulator supplies */ | ||
252 | struct regulator *usb1v5; | ||
253 | struct regulator *usb1v8; | ||
254 | struct regulator *usb3v1; | ||
255 | |||
249 | /* for vbus reporting with irqs disabled */ | 256 | /* for vbus reporting with irqs disabled */ |
250 | spinlock_t lock; | 257 | spinlock_t lock; |
251 | 258 | ||
@@ -434,6 +441,18 @@ static void twl4030_phy_power(struct twl4030_usb *twl, int on) | |||
434 | 441 | ||
435 | pwr = twl4030_usb_read(twl, PHY_PWR_CTRL); | 442 | pwr = twl4030_usb_read(twl, PHY_PWR_CTRL); |
436 | if (on) { | 443 | if (on) { |
444 | regulator_enable(twl->usb3v1); | ||
445 | regulator_enable(twl->usb1v8); | ||
446 | /* | ||
447 | * Disabling usb3v1 regulator (= writing 0 to VUSB3V1_DEV_GRP | ||
448 | * in twl4030) resets the VUSB_DEDICATED2 register. This reset | ||
449 | * enables VUSB3V1_SLEEP bit that remaps usb3v1 ACTIVE state to | ||
450 | * SLEEP. We work around this by clearing the bit after usv3v1 | ||
451 | * is re-activated. This ensures that VUSB3V1 is really active. | ||
452 | */ | ||
453 | twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, | ||
454 | VUSB_DEDICATED2); | ||
455 | regulator_enable(twl->usb1v5); | ||
437 | pwr &= ~PHY_PWR_PHYPWD; | 456 | pwr &= ~PHY_PWR_PHYPWD; |
438 | WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0); | 457 | WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0); |
439 | twl4030_usb_write(twl, PHY_CLK_CTRL, | 458 | twl4030_usb_write(twl, PHY_CLK_CTRL, |
@@ -443,6 +462,9 @@ static void twl4030_phy_power(struct twl4030_usb *twl, int on) | |||
443 | } else { | 462 | } else { |
444 | pwr |= PHY_PWR_PHYPWD; | 463 | pwr |= PHY_PWR_PHYPWD; |
445 | WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0); | 464 | WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0); |
465 | regulator_disable(twl->usb1v5); | ||
466 | regulator_disable(twl->usb1v8); | ||
467 | regulator_disable(twl->usb3v1); | ||
446 | } | 468 | } |
447 | } | 469 | } |
448 | 470 | ||
@@ -468,7 +490,7 @@ static void twl4030_phy_resume(struct twl4030_usb *twl) | |||
468 | twl->asleep = 0; | 490 | twl->asleep = 0; |
469 | } | 491 | } |
470 | 492 | ||
471 | static void twl4030_usb_ldo_init(struct twl4030_usb *twl) | 493 | static int twl4030_usb_ldo_init(struct twl4030_usb *twl) |
472 | { | 494 | { |
473 | /* Enable writing to power configuration registers */ | 495 | /* Enable writing to power configuration registers */ |
474 | twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0xC0, PROTECT_KEY); | 496 | twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0xC0, PROTECT_KEY); |
@@ -480,20 +502,45 @@ static void twl4030_usb_ldo_init(struct twl4030_usb *twl) | |||
480 | /* input to VUSB3V1 LDO is from VBAT, not VBUS */ | 502 | /* input to VUSB3V1 LDO is from VBAT, not VBUS */ |
481 | twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x14, VUSB_DEDICATED1); | 503 | twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x14, VUSB_DEDICATED1); |
482 | 504 | ||
483 | /* turn on 3.1V regulator */ | 505 | /* Initialize 3.1V regulator */ |
484 | twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x20, VUSB3V1_DEV_GRP); | 506 | twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB3V1_DEV_GRP); |
507 | |||
508 | twl->usb3v1 = regulator_get(twl->dev, "usb3v1"); | ||
509 | if (IS_ERR(twl->usb3v1)) | ||
510 | return -ENODEV; | ||
511 | |||
485 | twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB3V1_TYPE); | 512 | twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB3V1_TYPE); |
486 | 513 | ||
487 | /* turn on 1.5V regulator */ | 514 | /* Initialize 1.5V regulator */ |
488 | twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x20, VUSB1V5_DEV_GRP); | 515 | twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V5_DEV_GRP); |
516 | |||
517 | twl->usb1v5 = regulator_get(twl->dev, "usb1v5"); | ||
518 | if (IS_ERR(twl->usb1v5)) | ||
519 | goto fail1; | ||
520 | |||
489 | twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V5_TYPE); | 521 | twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V5_TYPE); |
490 | 522 | ||
491 | /* turn on 1.8V regulator */ | 523 | /* Initialize 1.8V regulator */ |
492 | twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x20, VUSB1V8_DEV_GRP); | 524 | twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V8_DEV_GRP); |
525 | |||
526 | twl->usb1v8 = regulator_get(twl->dev, "usb1v8"); | ||
527 | if (IS_ERR(twl->usb1v8)) | ||
528 | goto fail2; | ||
529 | |||
493 | twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V8_TYPE); | 530 | twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V8_TYPE); |
494 | 531 | ||
495 | /* disable access to power configuration registers */ | 532 | /* disable access to power configuration registers */ |
496 | twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, PROTECT_KEY); | 533 | twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, PROTECT_KEY); |
534 | |||
535 | return 0; | ||
536 | |||
537 | fail2: | ||
538 | regulator_put(twl->usb1v5); | ||
539 | twl->usb1v5 = NULL; | ||
540 | fail1: | ||
541 | regulator_put(twl->usb3v1); | ||
542 | twl->usb3v1 = NULL; | ||
543 | return -ENODEV; | ||
497 | } | 544 | } |
498 | 545 | ||
499 | static ssize_t twl4030_usb_vbus_show(struct device *dev, | 546 | static ssize_t twl4030_usb_vbus_show(struct device *dev, |
@@ -598,7 +645,7 @@ static int __init twl4030_usb_probe(struct platform_device *pdev) | |||
598 | { | 645 | { |
599 | struct twl4030_usb_data *pdata = pdev->dev.platform_data; | 646 | struct twl4030_usb_data *pdata = pdev->dev.platform_data; |
600 | struct twl4030_usb *twl; | 647 | struct twl4030_usb *twl; |
601 | int status; | 648 | int status, err; |
602 | 649 | ||
603 | if (!pdata) { | 650 | if (!pdata) { |
604 | dev_dbg(&pdev->dev, "platform_data not available\n"); | 651 | dev_dbg(&pdev->dev, "platform_data not available\n"); |
@@ -622,7 +669,12 @@ static int __init twl4030_usb_probe(struct platform_device *pdev) | |||
622 | /* init spinlock for workqueue */ | 669 | /* init spinlock for workqueue */ |
623 | spin_lock_init(&twl->lock); | 670 | spin_lock_init(&twl->lock); |
624 | 671 | ||
625 | twl4030_usb_ldo_init(twl); | 672 | err = twl4030_usb_ldo_init(twl); |
673 | if (err) { | ||
674 | dev_err(&pdev->dev, "ldo init failed\n"); | ||
675 | kfree(twl); | ||
676 | return err; | ||
677 | } | ||
626 | otg_set_transceiver(&twl->otg); | 678 | otg_set_transceiver(&twl->otg); |
627 | 679 | ||
628 | platform_set_drvdata(pdev, twl); | 680 | platform_set_drvdata(pdev, twl); |
@@ -688,6 +740,9 @@ static int __exit twl4030_usb_remove(struct platform_device *pdev) | |||
688 | twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); | 740 | twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); |
689 | 741 | ||
690 | twl4030_phy_power(twl, 0); | 742 | twl4030_phy_power(twl, 0); |
743 | regulator_put(twl->usb1v5); | ||
744 | regulator_put(twl->usb1v8); | ||
745 | regulator_put(twl->usb3v1); | ||
691 | 746 | ||
692 | kfree(twl); | 747 | kfree(twl); |
693 | 748 | ||