Skip to content

Commit

Permalink
usb: phy: twl4030-usb: poll for ID disconnect
Browse files Browse the repository at this point in the history
On pandora, STS_USB interrupt doesn't arrive on USB host cable disconnect
for some reason while VBUS is driven by twl itself, but STS_HW_CONDITIONS
is updated correctly. It does work fine when PHY is powered down though.
To work around that we have to poll.

This patch also moves twl->linkstat update code to callers so that
changes can be handled in thread safe way (as polling work can trigger
at the same time as real irq now).

TI PSP kernels have similar workarounds, so (many?) more boards are likely
affected.

Signed-off-by: Grazvydas Ignotas <notasas@gmail.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
  • Loading branch information
Grazvydas Ignotas authored and Felipe Balbi committed Mar 21, 2013
1 parent 15460de commit 249751f
Showing 1 changed file with 57 additions and 7 deletions.
64 changes: 57 additions & 7 deletions drivers/usb/phy/phy-twl4030-usb.c
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,8 @@ struct twl4030_usb {
bool vbus_supplied;
u8 asleep;
bool irq_enabled;

struct delayed_work id_workaround_work;
};

/* internal define on top of container_of */
Expand Down Expand Up @@ -287,10 +289,6 @@ static enum omap_musb_vbus_id_status
* are registered, and that both are active...
*/

spin_lock_irq(&twl->lock);
twl->linkstat = linkstat;
spin_unlock_irq(&twl->lock);

return linkstat;
}

Expand Down Expand Up @@ -412,6 +410,16 @@ static void twl4030_phy_resume(struct twl4030_usb *twl)
__twl4030_phy_resume(twl);
twl->asleep = 0;
dev_dbg(twl->dev, "%s\n", __func__);

/*
* XXX When VBUS gets driven after musb goes to A mode,
* ID_PRES related interrupts no longer arrive, why?
* Register itself is updated fine though, so we must poll.
*/
if (twl->linkstat == OMAP_MUSB_ID_GROUND) {
cancel_delayed_work(&twl->id_workaround_work);
schedule_delayed_work(&twl->id_workaround_work, HZ);
}
}

static int twl4030_usb_ldo_init(struct twl4030_usb *twl)
Expand Down Expand Up @@ -483,10 +491,18 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
{
struct twl4030_usb *twl = _twl;
enum omap_musb_vbus_id_status status;
enum omap_musb_vbus_id_status status_prev = twl->linkstat;
bool status_changed = false;

status = twl4030_usb_linkstat(twl);
if (status > 0 && status != status_prev) {

spin_lock_irq(&twl->lock);
if (status >= 0 && status != twl->linkstat) {
twl->linkstat = status;
status_changed = true;
}
spin_unlock_irq(&twl->lock);

if (status_changed) {
/* FIXME add a set_power() method so that B-devices can
* configure the charger appropriately. It's not always
* correct to consume VBUS power, and how much current to
Expand All @@ -498,13 +514,42 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
* USB_LINK_VBUS state. musb_hdrc won't care until it
* starts to handle softconnect right.
*/
omap_musb_mailbox(twl->linkstat);
omap_musb_mailbox(status);
}
sysfs_notify(&twl->dev->kobj, NULL, "vbus");

return IRQ_HANDLED;
}

static void twl4030_id_workaround_work(struct work_struct *work)
{
struct twl4030_usb *twl = container_of(work, struct twl4030_usb,
id_workaround_work.work);
enum omap_musb_vbus_id_status status;
bool status_changed = false;

status = twl4030_usb_linkstat(twl);

spin_lock_irq(&twl->lock);
if (status >= 0 && status != twl->linkstat) {
twl->linkstat = status;
status_changed = true;
}
spin_unlock_irq(&twl->lock);

if (status_changed) {
dev_dbg(twl->dev, "handle missing status change to %d\n",
status);
omap_musb_mailbox(status);
}

/* don't schedule during sleep - irq works right then */
if (status == OMAP_MUSB_ID_GROUND && !twl->asleep) {
cancel_delayed_work(&twl->id_workaround_work);
schedule_delayed_work(&twl->id_workaround_work, HZ);
}
}

static int twl4030_usb_phy_init(struct usb_phy *phy)
{
struct twl4030_usb *twl = phy_to_twl(phy);
Expand All @@ -518,6 +563,8 @@ static int twl4030_usb_phy_init(struct usb_phy *phy)
twl->asleep = 1;

status = twl4030_usb_linkstat(twl);
twl->linkstat = status;

if (status == OMAP_MUSB_ID_GROUND || status == OMAP_MUSB_VBUS_VALID)
omap_musb_mailbox(twl->linkstat);

Expand Down Expand Up @@ -608,6 +655,8 @@ static int twl4030_usb_probe(struct platform_device *pdev)
/* init spinlock for workqueue */
spin_lock_init(&twl->lock);

INIT_DELAYED_WORK(&twl->id_workaround_work, twl4030_id_workaround_work);

err = twl4030_usb_ldo_init(twl);
if (err) {
dev_err(&pdev->dev, "ldo init failed\n");
Expand Down Expand Up @@ -646,6 +695,7 @@ static int __exit twl4030_usb_remove(struct platform_device *pdev)
struct twl4030_usb *twl = platform_get_drvdata(pdev);
int val;

cancel_delayed_work(&twl->id_workaround_work);
device_remove_file(twl->dev, &dev_attr_vbus);

/* set transceiver mode to power on defaults */
Expand Down

0 comments on commit 249751f

Please sign in to comment.