Commit 5bf54506 authored by Moiz Sonasath's avatar Moiz Sonasath Committed by Greg Kroah-Hartman
Browse files

USB: OTG: Use work_queue in set_vbus for TWL6030 transciever



With this commit: cccad6d4
usb: otg: notifier: switch to atomic notifier

Following dumps are observed on attach/detach for MUSB HOST
mode and on a detach for MUSB Device mode.

BUG: sleeping function called from invalid context at kernel/mutex.c:85
where, the source is:
twl6030_usb_irq
->atomic_notifier_call_chain
 ->musb_otg_notifications
  ->twl6030_set_vbus
   ->twl_i2c_write_u8
    ->mutex_lock

This patch moves the i2c writes in set_vbus function to a
work-queue thereby avoiding I2C writes in atomic context.

Tested HOST and Device mode functionality on OMAP4460

Signed-off-by: default avatarMoiz Sonasath <m-sonasath@ti.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 28f75f4d
Loading
Loading
Loading
Loading
+24 −6
Original line number Diff line number Diff line
@@ -95,11 +95,15 @@ struct twl6030_usb {

	struct regulator		*usb3v3;

	/* used to set vbus, in atomic path */
	struct work_struct	set_vbus_work;

	int			irq1;
	int			irq2;
	u8			linkstat;
	u8			asleep;
	bool			irq_enabled;
	bool			vbus_enable;
	unsigned long		features;
};

@@ -370,20 +374,31 @@ static int twl6030_enable_irq(struct otg_transceiver *x)
	return 0;
}

static int twl6030_set_vbus(struct otg_transceiver *x, bool enabled)
static void otg_set_vbus_work(struct work_struct *data)
{
	struct twl6030_usb *twl = xceiv_to_twl(x);
	struct twl6030_usb *twl = container_of(data, struct twl6030_usb,
								set_vbus_work);

	/*
	 * Start driving VBUS. Set OPA_MODE bit in CHARGERUSB_CTRL1
	 * register. This enables boost mode.
	 */
	if (enabled)

	if (twl->vbus_enable)
		twl6030_writeb(twl, TWL_MODULE_MAIN_CHARGE , 0x40,
							CHARGERUSB_CTRL1);
	else
		twl6030_writeb(twl, TWL_MODULE_MAIN_CHARGE , 0x00,
							CHARGERUSB_CTRL1);
}

static int twl6030_set_vbus(struct otg_transceiver *x, bool enabled)
{
	struct twl6030_usb *twl = xceiv_to_twl(x);

	twl->vbus_enable = enabled;
	schedule_work(&twl->set_vbus_work);

	return 0;
}

@@ -444,6 +459,8 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev)

	ATOMIC_INIT_NOTIFIER_HEAD(&twl->otg.notifier);

	INIT_WORK(&twl->set_vbus_work, otg_set_vbus_work);

	twl->irq_enabled = true;
	status = request_threaded_irq(twl->irq1, NULL, twl6030_usbotg_irq,
			IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
@@ -494,6 +511,7 @@ static int __exit twl6030_usb_remove(struct platform_device *pdev)
	regulator_put(twl->usb3v3);
	pdata->phy_exit(twl->dev);
	device_remove_file(twl->dev, &dev_attr_vbus);
	cancel_work_sync(&twl->set_vbus_work);
	kfree(twl);

	return 0;