diff options
author | Anshul Jain <anshulj@nvidia.com> | 2013-06-20 18:49:06 -0700 |
---|---|---|
committer | Anshul Jain (SW) <anshulj@nvidia.com> | 2013-06-21 11:24:24 -0700 |
commit | b9e37740c8614acd8e65210773133a4f0029705f (patch) | |
tree | 18ea82a7922861453a2ead3ed0dfc9f96a6293d8 | |
parent | 146cfe48b97eb51e21a09dda006726c1e2fcfdf1 (diff) |
misc: issp: Add usb js recovery mechanism
This change recovery the JS uC after USB resume failure by
unloading USB, resetting uC from ISSP and then reloading USB
Bug 1306389
Change-Id: I086636d4b7b91e3a2874f584fa6efbfd2cae6014
Signed-off-by: Michael Hsu <mhsu@nvidia.com>
Signed-off-by: Anshul Jain <anshulj@nvidia.com>
Reviewed-on: http://git-master/r/240004
-rw-r--r-- | arch/arm/mach-tegra/board-roth.c | 66 | ||||
-rw-r--r-- | drivers/misc/issp/issp.c | 54 | ||||
-rw-r--r-- | drivers/misc/issp/issp_priv.h | 1 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 22 |
4 files changed, 135 insertions, 8 deletions
diff --git a/arch/arm/mach-tegra/board-roth.c b/arch/arm/mach-tegra/board-roth.c index 3b1fa1adebf7..a7af001d4e35 100644 --- a/arch/arm/mach-tegra/board-roth.c +++ b/arch/arm/mach-tegra/board-roth.c @@ -568,6 +568,54 @@ static struct tegra_usb_otg_data tegra_otg_pdata = { .ehci_pdata = &tegra_ehci1_utmi_pdata, }; +static struct platform_device * +tegra_usb_hsic_host_register(struct platform_device *ehci_dev) +{ + struct platform_device *pdev; + int val; + + pdev = platform_device_alloc(ehci_dev->name, ehci_dev->id); + if (!pdev) + return NULL; + + val = platform_device_add_resources(pdev, ehci_dev->resource, + ehci_dev->num_resources); + if (val) + goto error; + + pdev->dev.dma_mask = ehci_dev->dev.dma_mask; + pdev->dev.coherent_dma_mask = ehci_dev->dev.coherent_dma_mask; + + val = platform_device_add_data(pdev, &tegra_ehci3_utmi_pdata, + sizeof(struct tegra_usb_platform_data)); + if (val) + goto error; + + val = platform_device_add(pdev); + if (val) + goto error; + + return pdev; + +error: + pr_err("%s: failed to add the host contoller device\n", __func__); + platform_device_put(pdev); + return NULL; +} + +static void tegra_usb_hsic_host_unregister(struct platform_device **platdev) +{ + struct platform_device *pdev = *platdev; + + if (pdev && &pdev->dev) { + platform_device_unregister(pdev); + *platdev = NULL; + } else + pr_err("%s: no platform device\n", __func__); +} + +static struct platform_device *roth_usb_ehci3; + static void roth_usb_init(void) { tegra_otg_device.dev.platform_data = &tegra_otg_pdata; @@ -576,10 +624,28 @@ static void roth_usb_init(void) /* Setup the udc platform data */ tegra_udc_device.dev.platform_data = &tegra_udc_pdata; +#if 0 tegra_ehci3_device.dev.platform_data = &tegra_ehci3_utmi_pdata; platform_device_register(&tegra_ehci3_device); +#else + roth_usb_ehci3 = tegra_usb_hsic_host_register(&tegra_ehci3_device); +#endif +} + +void roth_usb_unload(void) +{ + pr_info("%s\n", __func__); + + /* unload ehci3 usb host controller */ + tegra_usb_hsic_host_unregister(&roth_usb_ehci3); + } +void roth_usb_reload(void) +{ + pr_info("%s\n", __func__); + roth_usb_ehci3 = tegra_usb_hsic_host_register(&tegra_ehci3_device); +} #else static void roth_usb_init(void) { } #endif diff --git a/drivers/misc/issp/issp.c b/drivers/misc/issp/issp.c index 083a22b0c681..b1fb71d19bbd 100644 --- a/drivers/misc/issp/issp.c +++ b/drivers/misc/issp/issp.c @@ -28,6 +28,8 @@ #define DRIVER_NAME "issp" +struct issp_host *g_issp_host; + static int issp_check_fw(struct issp_host *host) { const struct ihex_binrec *rec; @@ -129,6 +131,41 @@ static int issp_need_update(struct issp_host *host, bool *update) } struct issp_host *g_issp_host; +extern void roth_usb_unload(void); +extern void roth_usb_reload(void); + +#define ISSP_RECOVERY_DELAY 10 + +struct workqueue_struct *issp_workqueue; +struct delayed_work issp_recovery_work; + +static void issp_recovery_work_func(struct delayed_work *dwork) +{ + int i; + extern void roth_usb_unload(void); + extern void roth_usb_reload(void); + + pr_info("%s\n", __func__); + + for (i = 0; i < 1; i++) { + pr_info("%s: recovery attempt #%d\n", __func__, i); + roth_usb_unload(); + issp_uc_reset(); + roth_usb_reload(); + } +} + +void issp_start_recovery_work(void) +{ + pr_info("%s\n", __func__); + if (!issp_workqueue) { + pr_err("%s: no workqueue!\n", __func__); + return; + } + queue_delayed_work(issp_workqueue, &issp_recovery_work, + msecs_to_jiffies(ISSP_RECOVERY_DELAY)); + +} static int __init issp_probe(struct platform_device *pdev) { @@ -198,6 +235,14 @@ static int __init issp_probe(struct platform_device *pdev) dev_err(dev, "Firmware update failed!\n"); } + /* create workqueue to recover from failed usb resume */ + issp_workqueue = create_workqueue("issp_recovery_wq"); + if (!issp_workqueue) { + dev_err(&pdev->dev, "can't create work queue\n"); + goto err; + } + INIT_DELAYED_WORK(&issp_recovery_work, issp_recovery_work_func); + err_id: issp_uc_run(host); gpio_direction_input(pdata->data_gpio); @@ -217,6 +262,13 @@ err: static int __exit issp_remove(struct platform_device *pdev) { g_issp_host = NULL; + + /* delete workqueue used to recover from failed usb resume */ + if (issp_workqueue) { + destroy_workqueue(issp_workqueue); + issp_workqueue = NULL; + } + return 0; } @@ -225,7 +277,7 @@ static struct platform_driver issp_driver = { .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, - } + }, }; static int __init issp_init(void) diff --git a/drivers/misc/issp/issp_priv.h b/drivers/misc/issp/issp_priv.h index daaec1bb9cd2..f1988a429db8 100644 --- a/drivers/misc/issp/issp_priv.h +++ b/drivers/misc/issp/issp_priv.h @@ -54,5 +54,6 @@ int issp_read_block(struct issp_host *host, uint8_t block_idx, uint8_t addr, void issp_fw_rewind(struct issp_host *host); void issp_fw_seek_security(struct issp_host *host); uint8_t issp_fw_get_byte(struct issp_host *host); +void issp_uc_reset(void); #endif diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index b1b0fe41e07b..082c3a553739 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2569,12 +2569,12 @@ static int finish_port_resume(struct usb_device *udev) retry_reset_resume: status = usb_reset_and_verify_device(udev); -/* For testing reset on every resume */ +/* For testing recover issp on every usb resume */ #if 0 if (udev->quirks & USB_QUIRK_RESET_DEVICE_ON_RESUME_FAIL) { - extern void issp_uc_reset(void); - dev_err(&udev->dev, "USB_QUIRK_RESET_DEVICE_ON_RESUME_FAIL\n"); - issp_uc_reset(); + extern void issp_start_recovery_work(void); + dev_err(&udev->dev, "USB_QUIRK_RESET_DEVICE_ON_RESUME_FAIL - start recovery work\n"); + issp_start_recovery_work(); /* device is gone after we reset it */ status = -ENODEV; } @@ -2595,10 +2595,10 @@ static int finish_port_resume(struct usb_device *udev) dev_err(&udev->dev, "retry with reset-resume\n"); if (udev->quirks & USB_QUIRK_RESET_DEVICE_ON_RESUME_FAIL) { - extern void issp_uc_reset(void); + extern void issp_start_recovery_work(void); dev_err(&udev->dev, "USB_QUIRK_RESET_DEVICE_ON_RESUME_FAIL\n"); - issp_uc_reset(); + issp_start_recovery_work(); /* device is gone after we reset it */ status = -ENODEV; } else { @@ -3257,11 +3257,19 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, if (hcd->driver->update_device) hcd->driver->update_device(hcd, udev); fail: - if (retval) { + if (retval && + !(udev->quirks & USB_QUIRK_RESET_DEVICE_ON_RESUME_FAIL)) { hub_port_disable(hub, port1, 0); update_devnum(udev, devnum); /* for disconnect processing */ } mutex_unlock(&usb_address0_mutex); + + if (udev->quirks & USB_QUIRK_RESET_DEVICE_ON_RESUME_FAIL) { + dev_err(&udev->dev, "Reset device-failure to recover from err\n"); + extern void issp_start_recovery_work(void); + issp_start_recovery_work(); + retval = 0; + } return retval; } |