summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnshul Jain <anshulj@nvidia.com>2013-06-20 18:49:06 -0700
committerAnshul Jain (SW) <anshulj@nvidia.com>2013-06-21 11:24:24 -0700
commitb9e37740c8614acd8e65210773133a4f0029705f (patch)
tree18ea82a7922861453a2ead3ed0dfc9f96a6293d8
parent146cfe48b97eb51e21a09dda006726c1e2fcfdf1 (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.c66
-rw-r--r--drivers/misc/issp/issp.c54
-rw-r--r--drivers/misc/issp/issp_priv.h1
-rw-r--r--drivers/usb/core/hub.c22
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;
}