diff options
Diffstat (limited to 'drivers/usb/musb/omap2430.c')
-rw-r--r-- | drivers/usb/musb/omap2430.c | 56 |
1 files changed, 54 insertions, 2 deletions
diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 6958ab9..c1982fc 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -230,15 +230,37 @@ static inline void omap2430_low_level_init(struct musb *musb) } /* blocking notifier support */ +static void musb_otg_notifier_work(struct work_struct *data_notifier_work); + static int musb_otg_notifications(struct notifier_block *nb, unsigned long event, void *unused) { struct musb *musb = container_of(nb, struct musb, nb); + struct musb_otg_work *otg_work; + + otg_work = kmalloc(sizeof(struct musb_otg_work), GFP_ATOMIC); + if (!otg_work) + return notifier_from_errno(-ENOMEM); + INIT_WORK(&otg_work->work, musb_otg_notifier_work); + otg_work->xceiv_event = event; + otg_work->musb = musb; + queue_work(musb->otg_notifier_wq, &otg_work->work); + return 0; +} + +static void musb_otg_notifier_work(struct work_struct *data_notifier_work) +{ + struct musb_otg_work *otg_work = + container_of(data_notifier_work, struct musb_otg_work, work); + struct musb *musb = otg_work->musb; struct device *dev = musb->controller; struct musb_hdrc_platform_data *pdata = dev->platform_data; struct omap_musb_board_data *data = pdata->board_data; + enum usb_xceiv_events xceiv_event = otg_work->xceiv_event; - switch (event) { + kfree(otg_work); + + switch (xceiv_event) { case USB_EVENT_ID: dev_dbg(musb->controller, "ID GND\n"); @@ -257,6 +279,11 @@ static int musb_otg_notifications(struct notifier_block *nb, } break; + case USB_EVENT_CHARGER: + dev_dbg(musb->controller, "Dedicated charger connect\n"); + musb->is_ac_charger = true; + break; + case USB_EVENT_VBUS: dev_dbg(musb->controller, "VBUS Connect\n"); @@ -268,6 +295,13 @@ static int musb_otg_notifications(struct notifier_block *nb, break; case USB_EVENT_NONE: + if (musb->is_ac_charger) { + dev_dbg(musb->controller, + "Dedicated charger disconnect\n"); + musb->is_ac_charger = false; + break; + } + dev_dbg(musb->controller, "VBUS Disconnect\n"); #ifdef CONFIG_USB_GADGET_MUSB_HDRC @@ -311,10 +345,17 @@ static int omap2430_musb_init(struct musb *musb) return -ENODEV; } + musb->otg_notifier_wq = create_singlethread_workqueue("musb-otg"); + if (!musb->otg_notifier_wq) { + pr_err("HS USB OTG: cannot allocate otg event wq\n"); + status = -ENOMEM; + goto err1; + } + status = pm_runtime_get_sync(dev); if (status < 0) { dev_err(dev, "pm_runtime_get_sync FAILED %d\n", status); - goto err1; + goto err2; } l = musb_readl(musb->mregs, OTG_INTERFSEL); @@ -347,7 +388,10 @@ static int omap2430_musb_init(struct musb *musb) return 0; +err2: + destroy_workqueue(musb->otg_notifier_wq); err1: + otg_put_transceiver(musb->xceiv); pm_runtime_disable(dev); return status; } @@ -401,6 +445,8 @@ static int omap2430_musb_exit(struct musb *musb) { del_timer_sync(&musb_idle_timer); + otg_unregister_notifier(musb->xceiv, &musb->nb); + destroy_workqueue(musb->otg_notifier_wq); omap2430_low_level_exit(musb); otg_put_transceiver(musb->xceiv); @@ -505,6 +551,9 @@ static int omap2430_runtime_suspend(struct device *dev) struct omap2430_glue *glue = dev_get_drvdata(dev); struct musb *musb = glue_to_musb(glue); + musb->context.otg_interfsel = musb_readl(musb->mregs, + OTG_INTERFSEL); + omap2430_low_level_exit(musb); otg_set_suspend(musb->xceiv, 1); @@ -517,6 +566,9 @@ static int omap2430_runtime_resume(struct device *dev) struct musb *musb = glue_to_musb(glue); omap2430_low_level_init(musb); + musb_writel(musb->mregs, OTG_INTERFSEL, + musb->context.otg_interfsel); + otg_set_suspend(musb->xceiv, 0); return 0; |