aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorMing Lei <tom.leiming@gmail.com>2010-09-06 11:27:09 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2010-09-20 19:05:00 -0400
commitfc8f2a7608d855b911e35a33e771e6358c705c43 (patch)
tree27c603b3234177c4a580ae3797ee56c45b528968 /drivers
parentfc9282506114d4be188a464af2d373db31dd781c (diff)
USB: otg: twl4030: fix phy initialization(v1)
Commit 461c317705eca5cac09a360f488715927fd0a927(into 2.6.36-v3) is put forward to power down phy if no usb cable is connected, but does introduce the two issues below: 1), phy is not into work state if usb cable is connected with PC during poweron, so musb device mode is not usable in such case, follows the reasons: -twl4030_phy_resume is not called, so regulators are not enabled i2c access are not enabled usb mode not configurated 2), The kernel warings[1] of regulators 'unbalanced disables' is caused if poweron without usb cable connected with PC or b-device. This patch fixes the two issues above: -power down phy only if no usb cable is connected with PC and b-device -do phy initialization(via __twl4030_phy_resume) if usb cable is connected with PC(vbus event) or another b-device(ID event) in twl4030_usb_probe. This patch also doesn't put VUSB3V1 LDO into active mode in twl4030_usb_ldo_init until VBUS/ID change detected, so we can save more power consumption than before. This patch is verified OK on Beagle board either connected with usb cable or not when poweron. [1]. warnings of 'unbalanced disables' of regulators. [root@OMAP3EVM /]# dmesg ------------[ cut here ]------------ WARNING: at drivers/regulator/core.c:1357 _regulator_disable+0x38/0x128() unbalanced disables for VUSB1V8 Modules linked in: Backtrace: [<c0030c48>] (dump_backtrace+0x0/0x110) from [<c034f5a8>] (dump_stack+0x18/0x1c) r7:c78179d8 r6:c01ed6b8 r5:c0410822 r4:0000054d [<c034f590>] (dump_stack+0x0/0x1c) from [<c0057da8>] (warn_slowpath_common+0x54/0x6c) [<c0057d54>] (warn_slowpath_common+0x0/0x6c) from [<c0057e64>] (warn_slowpath_fmt+0x38/0x40) r9:00000000 r8:00000000 r7:c78e6608 r6:00000000 r5:fffffffb r4:c78e6c00 [<c0057e2c>] (warn_slowpath_fmt+0x0/0x40) from [<c01ed6b8>] (_regulator_disable+0x38/0x128) r3:c0410e53 r2:c0410ad5 [<c01ed680>] (_regulator_disable+0x0/0x128) from [<c01ed87c>] (regulator_disable+0x24/0x38) r7:c78e6608 r6:00000000 r5:c78e6c40 r4:c78e6c00 [<c01ed858>] (regulator_disable+0x0/0x38) from [<c02382dc>] (twl4030_phy_power+0x15c/0x17c) r5:c78595c0 r4:00000000 [<c0238180>] (twl4030_phy_power+0x0/0x17c) from [<c023831c>] (twl4030_phy_suspend+0x20/0x2c) r6:00000000 r5:c78595c0 r4:c78595c0 [<c02382fc>] (twl4030_phy_suspend+0x0/0x2c) from [<c0238638>] (twl4030_usb_irq+0x11c/0x16c) r5:c78595c0 r4:00000040 [<c023851c>] (twl4030_usb_irq+0x0/0x16c) from [<c034ec18>] (twl4030_usb_probe+0x2c4/0x32c) r6:00000000 r5:00000000 r4:c78595c0 [<c034e954>] (twl4030_usb_probe+0x0/0x32c) from [<c02152a0>] (platform_drv_probe+0x20/0x24) r7:00000000 r6:c047d49c r5:c78e6608 r4:c047d49c [<c0215280>] (platform_drv_probe+0x0/0x24) from [<c0214244>] (driver_probe_device+0xd0/0x190) [<c0214174>] (driver_probe_device+0x0/0x190) from [<c02143d4>] (__device_attach+0x44/0x48) r7:00000000 r6:c78e6608 r5:c78e6608 r4:c047d49c [<c0214390>] (__device_attach+0x0/0x48) from [<c0213694>] (bus_for_each_drv+0x50/0x90) r5:c0214390 r4:00000000 [<c0213644>] (bus_for_each_drv+0x0/0x90) from [<c0214474>] (device_attach+0x70/0x94) r6:c78e663c r5:c78e6608 r4:c78e6608 [<c0214404>] (device_attach+0x0/0x94) from [<c02134fc>] (bus_probe_device+0x2c/0x48) r7:00000000 r6:00000002 r5:c78e6608 r4:c78e6600 [<c02134d0>] (bus_probe_device+0x0/0x48) from [<c0211e48>] (device_add+0x340/0x4b4) [<c0211b08>] (device_add+0x0/0x4b4) from [<c021597c>] (platform_device_add+0x110/0x16c) [<c021586c>] (platform_device_add+0x0/0x16c) from [<c0220cb0>] (add_numbered_child+0xd8/0x118) r7:00000000 r6:c045f15c r5:c78e6600 r4:00000000 [<c0220bd8>] (add_numbered_child+0x0/0x118) from [<c001c618>] (twl_probe+0x3a4/0x72c) [<c001c274>] (twl_probe+0x0/0x72c) from [<c02601ac>] (i2c_device_probe+0x7c/0xa4) [<c0260130>] (i2c_device_probe+0x0/0xa4) from [<c0214244>] (driver_probe_device+0xd0/0x190) r5:c7856e20 r4:c047c860 [<c0214174>] (driver_probe_device+0x0/0x190) from [<c02143d4>] (__device_attach+0x44/0x48) r7:c7856e04 r6:c7856e20 r5:c7856e20 r4:c047c860 [<c0214390>] (__device_attach+0x0/0x48) from [<c0213694>] (bus_for_each_drv+0x50/0x90) r5:c0214390 r4:00000000 [<c0213644>] (bus_for_each_drv+0x0/0x90) from [<c0214474>] (device_attach+0x70/0x94) r6:c7856e54 r5:c7856e20 r4:c7856e20 [<c0214404>] (device_attach+0x0/0x94) from [<c02134fc>] (bus_probe_device+0x2c/0x48) r7:c7856e04 r6:c78fd048 r5:c7856e20 r4:c7856e20 [<c02134d0>] (bus_probe_device+0x0/0x48) from [<c0211e48>] (device_add+0x340/0x4b4) [<c0211b08>] (device_add+0x0/0x4b4) from [<c0211fd8>] (device_register+0x1c/0x20) [<c0211fbc>] (device_register+0x0/0x20) from [<c0260aa8>] (i2c_new_device+0xec/0x150) r5:c7856e00 r4:c7856e20 [<c02609bc>] (i2c_new_device+0x0/0x150) from [<c0260dc0>] (i2c_register_adapter+0xa0/0x1c4) r7:00000000 r6:c78fd078 r5:c78fd048 r4:c781d5c0 [<c0260d20>] (i2c_register_adapter+0x0/0x1c4) from [<c0260f80>] (i2c_add_numbered_adapter+0x9c/0xb4) r7:00000a28 r6:c04600a8 r5:c78fd048 r4:00000000 [<c0260ee4>] (i2c_add_numbered_adapter+0x0/0xb4) from [<c034efa4>] (omap_i2c_probe+0x324/0x3e8) r5:00000000 r4:c78fd000 [<c034ec80>] (omap_i2c_probe+0x0/0x3e8) from [<c02152a0>] (platform_drv_probe+0x20/0x24) [<c0215280>] (platform_drv_probe+0x0/0x24) from [<c0214244>] (driver_probe_device+0xd0/0x190) [<c0214174>] (driver_probe_device+0x0/0x190) from [<c021436c>] (__driver_attach+0x68/0x8c) r7:c78b2140 r6:c047e214 r5:c04600e4 r4:c04600b0 [<c0214304>] (__driver_attach+0x0/0x8c) from [<c021399c>] (bus_for_each_dev+0x50/0x84) r7:c78b2140 r6:c047e214 r5:c0214304 r4:00000000 [<c021394c>] (bus_for_each_dev+0x0/0x84) from [<c0214068>] (driver_attach+0x20/0x28) r6:c047e214 r5:c047e214 r4:c00270d0 [<c0214048>] (driver_attach+0x0/0x28) from [<c0213274>] (bus_add_driver+0xa8/0x228) [<c02131cc>] (bus_add_driver+0x0/0x228) from [<c02146a4>] (driver_register+0xb0/0x13c) [<c02145f4>] (driver_register+0x0/0x13c) from [<c0215744>] (platform_driver_register+0x4c/0x60) r9:00000000 r8:c001f688 r7:00000013 r6:c005b6fc r5:c00083dc r4:c00270d0 [<c02156f8>] (platform_driver_register+0x0/0x60) from [<c001f69c>] (omap_i2c_init_driver+0x14/0x1c) [<c001f688>] (omap_i2c_init_driver+0x0/0x1c) from [<c002c460>] (do_one_initcall+0xd0/0x1a4) [<c002c390>] (do_one_initcall+0x0/0x1a4) from [<c0008478>] (kernel_init+0x9c/0x154) [<c00083dc>] (kernel_init+0x0/0x154) from [<c005b6fc>] (do_exit+0x0/0x688) r5:c00083dc r4:00000000 ---[ end trace 1b75b31a2719ed1d ]--- Signed-off-by: Ming Lei <tom.leiming@gmail.com> Cc: David Brownell <dbrownell@users.sourceforge.net> Cc: Felipe Balbi <me@felipebalbi.com> Cc: Anand Gadiyar <gadiyar@ti.com> Cc: Mike Frysinger <vapier@gentoo.org> Cc: Sergei Shtylyov <sshtylyov@ru.mvista.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/otg/twl4030-usb.c78
1 files changed, 51 insertions, 27 deletions
diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c
index 05aaac1c3861..0bc97698af15 100644
--- a/drivers/usb/otg/twl4030-usb.c
+++ b/drivers/usb/otg/twl4030-usb.c
@@ -347,11 +347,20 @@ static void twl4030_i2c_access(struct twl4030_usb *twl, int on)
347 } 347 }
348} 348}
349 349
350static void twl4030_phy_power(struct twl4030_usb *twl, int on) 350static void __twl4030_phy_power(struct twl4030_usb *twl, int on)
351{ 351{
352 u8 pwr; 352 u8 pwr = twl4030_usb_read(twl, PHY_PWR_CTRL);
353
354 if (on)
355 pwr &= ~PHY_PWR_PHYPWD;
356 else
357 pwr |= PHY_PWR_PHYPWD;
353 358
354 pwr = twl4030_usb_read(twl, PHY_PWR_CTRL); 359 WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0);
360}
361
362static void twl4030_phy_power(struct twl4030_usb *twl, int on)
363{
355 if (on) { 364 if (on) {
356 regulator_enable(twl->usb3v1); 365 regulator_enable(twl->usb3v1);
357 regulator_enable(twl->usb1v8); 366 regulator_enable(twl->usb1v8);
@@ -365,15 +374,13 @@ static void twl4030_phy_power(struct twl4030_usb *twl, int on)
365 twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, 374 twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0,
366 VUSB_DEDICATED2); 375 VUSB_DEDICATED2);
367 regulator_enable(twl->usb1v5); 376 regulator_enable(twl->usb1v5);
368 pwr &= ~PHY_PWR_PHYPWD; 377 __twl4030_phy_power(twl, 1);
369 WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0);
370 twl4030_usb_write(twl, PHY_CLK_CTRL, 378 twl4030_usb_write(twl, PHY_CLK_CTRL,
371 twl4030_usb_read(twl, PHY_CLK_CTRL) | 379 twl4030_usb_read(twl, PHY_CLK_CTRL) |
372 (PHY_CLK_CTRL_CLOCKGATING_EN | 380 (PHY_CLK_CTRL_CLOCKGATING_EN |
373 PHY_CLK_CTRL_CLK32K_EN)); 381 PHY_CLK_CTRL_CLK32K_EN));
374 } else { 382 } else {
375 pwr |= PHY_PWR_PHYPWD; 383 __twl4030_phy_power(twl, 0);
376 WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0);
377 regulator_disable(twl->usb1v5); 384 regulator_disable(twl->usb1v5);
378 regulator_disable(twl->usb1v8); 385 regulator_disable(twl->usb1v8);
379 regulator_disable(twl->usb3v1); 386 regulator_disable(twl->usb3v1);
@@ -387,19 +394,25 @@ static void twl4030_phy_suspend(struct twl4030_usb *twl, int controller_off)
387 394
388 twl4030_phy_power(twl, 0); 395 twl4030_phy_power(twl, 0);
389 twl->asleep = 1; 396 twl->asleep = 1;
397 dev_dbg(twl->dev, "%s\n", __func__);
390} 398}
391 399
392static void twl4030_phy_resume(struct twl4030_usb *twl) 400static void __twl4030_phy_resume(struct twl4030_usb *twl)
393{ 401{
394 if (!twl->asleep)
395 return;
396
397 twl4030_phy_power(twl, 1); 402 twl4030_phy_power(twl, 1);
398 twl4030_i2c_access(twl, 1); 403 twl4030_i2c_access(twl, 1);
399 twl4030_usb_set_mode(twl, twl->usb_mode); 404 twl4030_usb_set_mode(twl, twl->usb_mode);
400 if (twl->usb_mode == T2_USB_MODE_ULPI) 405 if (twl->usb_mode == T2_USB_MODE_ULPI)
401 twl4030_i2c_access(twl, 0); 406 twl4030_i2c_access(twl, 0);
407}
408
409static void twl4030_phy_resume(struct twl4030_usb *twl)
410{
411 if (!twl->asleep)
412 return;
413 __twl4030_phy_resume(twl);
402 twl->asleep = 0; 414 twl->asleep = 0;
415 dev_dbg(twl->dev, "%s\n", __func__);
403} 416}
404 417
405static int twl4030_usb_ldo_init(struct twl4030_usb *twl) 418static int twl4030_usb_ldo_init(struct twl4030_usb *twl)
@@ -408,8 +421,8 @@ static int twl4030_usb_ldo_init(struct twl4030_usb *twl)
408 twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0xC0, PROTECT_KEY); 421 twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0xC0, PROTECT_KEY);
409 twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0x0C, PROTECT_KEY); 422 twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0x0C, PROTECT_KEY);
410 423
411 /* put VUSB3V1 LDO in active state */ 424 /* Keep VUSB3V1 LDO in sleep state until VBUS/ID change detected*/
412 twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2); 425 /*twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2);*/
413 426
414 /* input to VUSB3V1 LDO is from VBAT, not VBUS */ 427 /* input to VUSB3V1 LDO is from VBAT, not VBUS */
415 twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x14, VUSB_DEDICATED1); 428 twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x14, VUSB_DEDICATED1);
@@ -502,6 +515,26 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
502 return IRQ_HANDLED; 515 return IRQ_HANDLED;
503} 516}
504 517
518static void twl4030_usb_phy_init(struct twl4030_usb *twl)
519{
520 int status;
521
522 status = twl4030_usb_linkstat(twl);
523 if (status >= 0) {
524 if (status == USB_EVENT_NONE) {
525 __twl4030_phy_power(twl, 0);
526 twl->asleep = 1;
527 } else {
528 __twl4030_phy_resume(twl);
529 twl->asleep = 0;
530 }
531
532 blocking_notifier_call_chain(&twl->otg.notifier, status,
533 twl->otg.gadget);
534 }
535 sysfs_notify(&twl->dev->kobj, NULL, "vbus");
536}
537
505static int twl4030_set_suspend(struct otg_transceiver *x, int suspend) 538static int twl4030_set_suspend(struct otg_transceiver *x, int suspend)
506{ 539{
507 struct twl4030_usb *twl = xceiv_to_twl(x); 540 struct twl4030_usb *twl = xceiv_to_twl(x);
@@ -550,7 +583,6 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev)
550 struct twl4030_usb_data *pdata = pdev->dev.platform_data; 583 struct twl4030_usb_data *pdata = pdev->dev.platform_data;
551 struct twl4030_usb *twl; 584 struct twl4030_usb *twl;
552 int status, err; 585 int status, err;
553 u8 pwr;
554 586
555 if (!pdata) { 587 if (!pdata) {
556 dev_dbg(&pdev->dev, "platform_data not available\n"); 588 dev_dbg(&pdev->dev, "platform_data not available\n");
@@ -569,10 +601,7 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev)
569 twl->otg.set_peripheral = twl4030_set_peripheral; 601 twl->otg.set_peripheral = twl4030_set_peripheral;
570 twl->otg.set_suspend = twl4030_set_suspend; 602 twl->otg.set_suspend = twl4030_set_suspend;
571 twl->usb_mode = pdata->usb_mode; 603 twl->usb_mode = pdata->usb_mode;
572 604 twl->asleep = 1;
573 pwr = twl4030_usb_read(twl, PHY_PWR_CTRL);
574
575 twl->asleep = (pwr & PHY_PWR_PHYPWD);
576 605
577 /* init spinlock for workqueue */ 606 /* init spinlock for workqueue */
578 spin_lock_init(&twl->lock); 607 spin_lock_init(&twl->lock);
@@ -610,15 +639,10 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev)
610 return status; 639 return status;
611 } 640 }
612 641
613 /* The IRQ handler just handles changes from the previous states 642 /* Power down phy or make it work according to
614 * of the ID and VBUS pins ... in probe() we must initialize that 643 * current link state.
615 * previous state. The easy way: fake an IRQ.
616 *
617 * REVISIT: a real IRQ might have happened already, if PREEMPT is
618 * enabled. Else the IRQ may not yet be configured or enabled,
619 * because of scheduling delays.
620 */ 644 */
621 twl4030_usb_irq(twl->irq, twl); 645 twl4030_usb_phy_init(twl);
622 646
623 dev_info(&pdev->dev, "Initialized TWL4030 USB module\n"); 647 dev_info(&pdev->dev, "Initialized TWL4030 USB module\n");
624 return 0; 648 return 0;