diff options
Diffstat (limited to 'arch/arm')
-rw-r--r-- | arch/arm/mach-tegra/board-enterprise-baseband.c | 62 | ||||
-rw-r--r-- | arch/arm/mach-tegra/board-whistler-baseband.c | 57 | ||||
-rw-r--r-- | arch/arm/mach-tegra/include/mach/tegra_usb_modem_power.h | 12 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra_usb_modem_power.c | 291 |
4 files changed, 264 insertions, 158 deletions
diff --git a/arch/arm/mach-tegra/board-enterprise-baseband.c b/arch/arm/mach-tegra/board-enterprise-baseband.c index 3ad83ad4fe8a..c73a7ad5e5b4 100644 --- a/arch/arm/mach-tegra/board-enterprise-baseband.c +++ b/arch/arm/mach-tegra/board-enterprise-baseband.c @@ -22,10 +22,7 @@ #include <linux/platform_device.h> #include <linux/delay.h> #include <linux/gpio.h> -#include <linux/interrupt.h> -#include <linux/irq.h> #include <linux/err.h> -#include <linux/wakelock.h> #include <linux/platform_data/tegra_usb.h> #include <mach/tegra_usb_modem_power.h> #include "devices.h" @@ -36,23 +33,19 @@ #define MODEM_RESET TEGRA_GPIO_PE1 #define BB_RST_OUT TEGRA_GPIO_PV1 -/* Icera BB GPIO */ +/* Icera modem handshaking GPIO */ #define AP2MDM_ACK TEGRA_GPIO_PE3 #define MDM2AP_ACK TEGRA_GPIO_PU5 #define AP2MDM_ACK2 TEGRA_GPIO_PE2 #define MDM2AP_ACK2 TEGRA_GPIO_PV0 -static struct wake_lock mdm_wake_lock; - static struct gpio modem_gpios[] = { {MODEM_PWR_ON, GPIOF_OUT_INIT_LOW, "MODEM PWR ON"}, {MODEM_RESET, GPIOF_IN, "MODEM RESET"}, - {BB_RST_OUT, GPIOF_IN, "BB RST OUT"}, {AP2MDM_ACK2, GPIOF_OUT_INIT_HIGH, "AP2MDM ACK2"}, {AP2MDM_ACK, GPIOF_OUT_INIT_LOW, "AP2MDM ACK"}, }; - static void baseband_post_phy_on(void); static void baseband_pre_phy_off(void); @@ -85,27 +78,6 @@ static struct tegra_usb_platform_data tegra_ehci2_ulpi_null_pdata = { .ops = &ulpi_null_plat_ops, }; -static int __init tegra_null_ulpi_init(void) -{ - tegra_ehci2_device.dev.platform_data = &tegra_ehci2_ulpi_null_pdata; - platform_device_register(&tegra_ehci2_device); - return 0; -} - -static irqreturn_t mdm_start_thread(int irq, void *data) -{ - if (gpio_get_value(BB_RST_OUT)) { - pr_info("BB_RST_OUT high\n"); - } else { - pr_info("BB_RST_OUT low\n"); - } - - /* hold wait lock to complete the enumeration */ - wake_lock_timeout(&mdm_wake_lock, HZ * 10); - - return IRQ_HANDLED; -} - static void baseband_post_phy_on(void) { /* set AP2MDM_ACK2 low */ @@ -139,7 +111,6 @@ static void baseband_reset(void) static int baseband_init(void) { - int irq; int ret; ret = gpio_request_array(modem_gpios, ARRAY_SIZE(modem_gpios)); @@ -157,29 +128,6 @@ static int baseband_init(void) /* export GPIO for user space access through sysfs */ gpio_export(MODEM_PWR_ON, false); - /* phy init */ - tegra_null_ulpi_init(); - - wake_lock_init(&mdm_wake_lock, WAKE_LOCK_SUSPEND, "mdm_lock"); - - /* enable IRQ for BB_RST_OUT */ - irq = gpio_to_irq(BB_RST_OUT); - - ret = request_threaded_irq(irq, NULL, mdm_start_thread, - IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, - "mdm_start", NULL); - if (ret < 0) { - pr_err("%s: request_threaded_irq error\n", __func__); - return ret; - } - - ret = enable_irq_wake(irq); - if (ret) { - pr_err("%s: enable_irq_wake error\n", __func__); - free_irq(irq, NULL); - return ret; - } - return 0; } @@ -192,7 +140,13 @@ static const struct tegra_modem_operations baseband_operations = { static struct tegra_usb_modem_power_platform_data baseband_pdata = { .ops = &baseband_operations, .wake_gpio = MDM2AP_ACK2, - .flags = IRQF_TRIGGER_FALLING, + .wake_irq_flags = IRQF_TRIGGER_FALLING, + .boot_gpio = BB_RST_OUT, + .boot_irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + .autosuspend_delay = 2000, + .short_autosuspend_delay = 50, + .tegra_ehci_device = &tegra_ehci2_device, + .tegra_ehci_pdata = &tegra_ehci2_ulpi_null_pdata, }; static struct platform_device icera_baseband_device = { diff --git a/arch/arm/mach-tegra/board-whistler-baseband.c b/arch/arm/mach-tegra/board-whistler-baseband.c index ad3dbd316ded..4a479cfb871c 100644 --- a/arch/arm/mach-tegra/board-whistler-baseband.c +++ b/arch/arm/mach-tegra/board-whistler-baseband.c @@ -20,12 +20,9 @@ #include "board.h" #include "board-whistler-baseband.h" -static struct wake_lock mdm_wake_lock; - static struct gpio modem_gpios[] = { {MODEM_PWR_ON, GPIOF_OUT_INIT_LOW, "MODEM PWR ON"}, {MODEM_RESET, GPIOF_IN, "MODEM RESET"}, - {BB_RST_OUT, GPIOF_IN, "BB RST OUT"}, {AP2MDM_ACK2, GPIOF_OUT_INIT_HIGH, "AP2MDM ACK2"}, {AP2MDM_ACK, GPIOF_OUT_INIT_LOW, "AP2MDM ACK"}, }; @@ -61,7 +58,7 @@ static struct tegra_usb_platform_data tegra_ehci2_ulpi_null_pdata = { .vbus_reg = NULL, .hot_plug = false, .remote_wakeup_supported = false, - .power_off_on_suspend = false, + .power_off_on_suspend = true, }, .u_cfg.ulpi = { .shadow_clk_delay = 10, @@ -75,26 +72,6 @@ static struct tegra_usb_platform_data tegra_ehci2_ulpi_null_pdata = { .ops = &ulpi_null_plat_ops, }; -static int __init tegra_null_ulpi_init(void) -{ - tegra_ehci2_device.dev.platform_data = &tegra_ehci2_ulpi_null_pdata; - platform_device_register(&tegra_ehci2_device); - return 0; -} - -static irqreturn_t mdm_start_thread(int irq, void *data) -{ - if (gpio_get_value(BB_RST_OUT)) { - pr_info("BB_RST_OUT high\n"); - } else { - pr_info("BB_RST_OUT low\n"); - /* hold wait lock to complete the enumeration */ - wake_lock_timeout(&mdm_wake_lock, HZ * 10); - } - - return IRQ_HANDLED; -} - static void baseband_post_phy_on(void) { /* set AP2MDM_ACK2 low */ @@ -128,7 +105,6 @@ static void baseband_reset(void) static int baseband_init(void) { - int irq; int ret; ret = gpio_request_array(modem_gpios, ARRAY_SIZE(modem_gpios)); @@ -142,29 +118,6 @@ static int baseband_init(void) /* export GPIO for user space access through sysfs */ gpio_export(MODEM_PWR_ON, false); - /* phy init */ - tegra_null_ulpi_init(); - - wake_lock_init(&mdm_wake_lock, WAKE_LOCK_SUSPEND, "mdm_lock"); - - /* enable IRQ for BB_RST_OUT */ - irq = gpio_to_irq(BB_RST_OUT); - - ret = request_threaded_irq(irq, NULL, mdm_start_thread, - IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, - "mdm_start", NULL); - if (ret < 0) { - pr_err("%s: request_threaded_irq error\n", __func__); - return ret; - } - - ret = enable_irq_wake(irq); - if (ret) { - pr_err("%s: enable_irq_wake error\n", __func__); - free_irq(irq, NULL); - return ret; - } - return 0; } @@ -177,7 +130,13 @@ static const struct tegra_modem_operations baseband_operations = { static struct tegra_usb_modem_power_platform_data baseband_pdata = { .ops = &baseband_operations, .wake_gpio = MDM2AP_ACK2, - .flags = IRQF_TRIGGER_FALLING, + .wake_irq_flags = IRQF_TRIGGER_FALLING, + .boot_gpio = BB_RST_OUT, + .boot_irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + .autosuspend_delay = 2000, + .short_autosuspend_delay = 50, + .tegra_ehci_device = &tegra_ehci2_device, + .tegra_ehci_pdata = &tegra_ehci2_ulpi_null_pdata, }; static struct platform_device icera_baseband_device = { diff --git a/arch/arm/mach-tegra/include/mach/tegra_usb_modem_power.h b/arch/arm/mach-tegra/include/mach/tegra_usb_modem_power.h index 0ce7fa40eb2e..210b9f61ecb5 100644 --- a/arch/arm/mach-tegra/include/mach/tegra_usb_modem_power.h +++ b/arch/arm/mach-tegra/include/mach/tegra_usb_modem_power.h @@ -1,7 +1,7 @@ /* * arch/arm/mach-tegra/include/mach/tegra_usb_modem_power.c * - * Copyright (c) 2011, NVIDIA Corporation. + * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -40,8 +40,14 @@ struct tegra_modem_operations { /* tegra usb modem power platform data */ struct tegra_usb_modem_power_platform_data { const struct tegra_modem_operations *ops; - unsigned int wake_gpio; /* remote wakeup gpio */ - unsigned int flags; /* remote wakeup irq flags */ + unsigned int wake_gpio; /* remote wakeup gpio */ + unsigned long wake_irq_flags; /* remote wakeup irq flags */ + unsigned int boot_gpio; /* modem boot gpio */ + unsigned long boot_irq_flags; /* modem boot irq flags */ + int autosuspend_delay; /* autosuspend delay in milliseconds */ + int short_autosuspend_delay; /* short autosuspend delay in ms */ + const struct platform_device *tegra_ehci_device; + const struct tegra_usb_platform_data *tegra_ehci_pdata; }; #endif /* __MACH_TEGRA_USB_MODEM_POWER_H */ diff --git a/arch/arm/mach-tegra/tegra_usb_modem_power.c b/arch/arm/mach-tegra/tegra_usb_modem_power.c index 88543397f974..7590754d0133 100644 --- a/arch/arm/mach-tegra/tegra_usb_modem_power.c +++ b/arch/arm/mach-tegra/tegra_usb_modem_power.c @@ -1,7 +1,7 @@ /* * arch/arm/mach-tegra/tegra_usb_modem_power.c * - * Copyright (c) 2011, NVIDIA Corporation. + * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,6 +22,7 @@ #include <linux/init.h> #include <linux/interrupt.h> #include <linux/platform_device.h> +#include <linux/platform_data/tegra_usb.h> #include <linux/workqueue.h> #include <linux/gpio.h> #include <linux/usb.h> @@ -32,15 +33,20 @@ #include <linux/wakelock.h> #include <mach/tegra_usb_modem_power.h> +#define WAKELOCK_TIMEOUT_FOR_USB_ENUM (HZ * 10) +#define WAKELOCK_TIMEOUT_FOR_REMOTE_WAKE (HZ) + struct tegra_usb_modem { - unsigned int wake_gpio; /* remote wakeup gpio */ + struct tegra_usb_modem_power_platform_data *pdata; unsigned int wake_cnt; /* remote wakeup counter */ - int irq; /* remote wakeup irq */ + unsigned int wake_irq; /* remote wakeup irq */ + unsigned int boot_irq; /* modem boot irq */ struct mutex lock; struct wake_lock wake_lock; /* modem wake lock */ unsigned int vid; /* modem vendor id */ unsigned int pid; /* modem product id */ struct usb_device *udev; /* modem usb device */ + struct usb_device *parent; /* parent device */ struct usb_interface *intf; /* first modem usb interface */ struct workqueue_struct *wq; /* modem workqueue */ struct delayed_work recovery_work; /* modem recovery work */ @@ -49,8 +55,15 @@ struct tegra_usb_modem { int system_suspend; /* system suspend flag */ struct notifier_block pm_notifier; /* pm event notifier */ struct notifier_block usb_notifier; /* usb event notifier */ + int sysfs_file_created; + int short_autosuspend_enabled; }; +static struct platform_device *hc = NULL; /* USB host controller */ +static struct mutex hc_lock; +static const struct platform_device *hc_device; +static const struct tegra_usb_platform_data *hc_pdata; + /* supported modems */ static const struct usb_device_id modem_list[] = { {USB_DEVICE(0x1983, 0x0310), /* Icera 450 rev1 */ @@ -69,23 +82,54 @@ static irqreturn_t tegra_usb_modem_wake_thread(int irq, void *data) { struct tegra_usb_modem *modem = (struct tegra_usb_modem *)data; - wake_lock_timeout(&modem->wake_lock, HZ); mutex_lock(&modem->lock); - if (modem->udev) { + if (modem->udev && modem->udev->state != USB_STATE_NOTATTACHED) { pr_info("modem wake (%u)\n", ++(modem->wake_cnt)); if (!modem->system_suspend) { + wake_lock_timeout(&modem->wake_lock, + WAKELOCK_TIMEOUT_FOR_REMOTE_WAKE); + usb_lock_device(modem->udev); if (usb_autopm_get_interface(modem->intf) == 0) usb_autopm_put_interface_async(modem->intf); usb_unlock_device(modem->udev); } +#ifdef CONFIG_PM + if (modem->capability & TEGRA_MODEM_AUTOSUSPEND && + modem->short_autosuspend_enabled) { + pm_runtime_set_autosuspend_delay(&modem->udev->dev, + modem->pdata->autosuspend_delay); + modem->short_autosuspend_enabled = 0; + } +#endif } mutex_unlock(&modem->lock); return IRQ_HANDLED; } +static irqreturn_t tegra_usb_modem_boot_thread(int irq, void *data) +{ + struct tegra_usb_modem *modem = (struct tegra_usb_modem *)data; + + if (gpio_get_value(modem->pdata->boot_gpio)) + pr_info("BB_RST_OUT high\n"); + else + pr_info("BB_RST_OUT low\n"); + + /* hold wait lock to complete the enumeration */ + wake_lock_timeout(&modem->wake_lock, WAKELOCK_TIMEOUT_FOR_USB_ENUM); + + /* USB disconnect maybe on going... */ + mutex_lock(&modem->lock); + if (modem->udev && modem->udev->state != USB_STATE_NOTATTACHED) + pr_warn("Device is not disconnected!\n"); + mutex_unlock(&modem->lock); + + return IRQ_HANDLED; +} + static void tegra_usb_modem_recovery(struct work_struct *ws) { struct tegra_usb_modem *modem = container_of(ws, struct tegra_usb_modem, @@ -108,13 +152,15 @@ static void device_add_handler(struct tegra_usb_modem *modem, if (id) { /* hold wakelock to ensure ril has enough time to restart */ - wake_lock_timeout(&modem->wake_lock, HZ * 10); + wake_lock_timeout(&modem->wake_lock, + WAKELOCK_TIMEOUT_FOR_USB_ENUM); pr_info("Add device %d <%s %s>\n", udev->devnum, udev->manufacturer, udev->product); mutex_lock(&modem->lock); modem->udev = udev; + modem->parent = udev->parent; modem->intf = intf; modem->vid = desc->idVendor; modem->pid = desc->idProduct; @@ -126,7 +172,9 @@ static void device_add_handler(struct tegra_usb_modem *modem, #ifdef CONFIG_PM if (modem->capability & TEGRA_MODEM_AUTOSUSPEND) { - pm_runtime_set_autosuspend_delay(&udev->dev, 2000); + pm_runtime_set_autosuspend_delay(&udev->dev, + modem->pdata->autosuspend_delay); + modem->short_autosuspend_enabled = 0; usb_enable_autosuspend(udev); pr_info("enable autosuspend for %s %s\n", udev->manufacturer, udev->product); @@ -140,8 +188,7 @@ static void device_remove_handler(struct tegra_usb_modem *modem, { const struct usb_device_descriptor *desc = &udev->descriptor; - if (desc->idVendor == modem->vid && - desc->idProduct == modem->pid) { + if (desc->idVendor == modem->vid && desc->idProduct == modem->pid) { pr_info("Remove device %d <%s %s>\n", udev->devnum, udev->manufacturer, udev->product); @@ -158,11 +205,10 @@ static void device_remove_handler(struct tegra_usb_modem *modem, } static int mdm_usb_notifier(struct notifier_block *notifier, - unsigned long usb_event, - void *udev) + unsigned long usb_event, void *udev) { struct tegra_usb_modem *modem = - container_of(notifier, struct tegra_usb_modem, usb_notifier); + container_of(notifier, struct tegra_usb_modem, usb_notifier); switch (usb_event) { case USB_DEVICE_ADD: @@ -176,11 +222,10 @@ static int mdm_usb_notifier(struct notifier_block *notifier, } static int mdm_pm_notifier(struct notifier_block *notifier, - unsigned long pm_event, - void *unused) + unsigned long pm_event, void *unused) { struct tegra_usb_modem *modem = - container_of(notifier, struct tegra_usb_modem, pm_notifier); + container_of(notifier, struct tegra_usb_modem, pm_notifier); mutex_lock(&modem->lock); if (!modem->udev) { @@ -194,10 +239,20 @@ static int mdm_pm_notifier(struct notifier_block *notifier, if (wake_lock_active(&modem->wake_lock)) { pr_warn("%s: wakelock was active, aborting suspend\n", __func__); + mutex_unlock(&modem->lock); return NOTIFY_STOP; } modem->system_suspend = 1; +#ifdef CONFIG_PM + if (modem->capability & TEGRA_MODEM_AUTOSUSPEND && + modem->udev && + modem->udev->state != USB_STATE_NOTATTACHED) { + pm_runtime_set_autosuspend_delay(&modem->udev->dev, + modem->pdata->short_autosuspend_delay); + modem->short_autosuspend_enabled = 1; + } +#endif mutex_unlock(&modem->lock); return NOTIFY_OK; case PM_POST_SUSPEND: @@ -210,12 +265,125 @@ static int mdm_pm_notifier(struct notifier_block *notifier, return NOTIFY_DONE; } +static int mdm_request_wakeable_irq(struct tegra_usb_modem *modem, + irq_handler_t thread_fn, + unsigned int irq_gpio, + unsigned long irq_flags, + const char *label, unsigned int *irq) +{ + int ret; + + ret = gpio_request(irq_gpio, label); + if (ret) + return ret; + + tegra_gpio_enable(irq_gpio); + + /* enable IRQ for GPIO */ + *irq = gpio_to_irq(irq_gpio); + + /* request threaded irq for GPIO */ + ret = request_threaded_irq(*irq, NULL, thread_fn, irq_flags, label, + modem); + if (ret) + return ret; + + ret = enable_irq_wake(*irq); + if (ret) { + free_irq(*irq, modem); + return ret; + } + + return ret; +} + +/* load USB host controller */ +static struct platform_device *tegra_usb_null_ulpi_host_register(void) +{ + struct platform_device *pdev; + int val; + + pdev = platform_device_alloc(hc_device->name, hc_device->id); + if (!pdev) + return NULL; + + val = platform_device_add_resources(pdev, hc_device->resource, + hc_device->num_resources); + if (val) + goto error; + + pdev->dev.dma_mask = hc_device->dev.dma_mask; + pdev->dev.coherent_dma_mask = hc_device->dev.coherent_dma_mask; + + val = platform_device_add_data(pdev, hc_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: err %d\n", __func__, val); + platform_device_put(pdev); + return NULL; +} + +/* unload USB host controller */ +static void tegra_usb_null_ulpi_host_unregister(struct platform_device *pdev) +{ + platform_device_unregister(pdev); +} + +static ssize_t show_usb_host(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", (hc) ? 1 : 0); +} + +static ssize_t load_unload_usb_host(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int host; + + if (sscanf(buf, "%d", &host) != 1 || host < 0 || host > 1) + return -EINVAL; + + pr_info("%s USB host\n", (host) ? "load" : "unload"); + + mutex_lock(&hc_lock); + if (host) { + if (!hc) + hc = tegra_usb_null_ulpi_host_register(); + } else { + if (hc) { + tegra_usb_null_ulpi_host_unregister(hc); + hc = NULL; + } + } + mutex_unlock(&hc_lock); + + return count; +} + +static DEVICE_ATTR(load_host, 0666, show_usb_host, load_unload_usb_host); + static int mdm_init(struct tegra_usb_modem *modem, struct platform_device *pdev) { struct tegra_usb_modem_power_platform_data *pdata = pdev->dev.platform_data; int ret = 0; + modem->pdata = pdata; + + hc_device = pdata->tegra_ehci_device; + hc_pdata = pdata->tegra_ehci_pdata; + mutex_init(&hc_lock); + /* get modem operations from platform data */ modem->ops = (const struct tegra_modem_operations *)pdata->ops; @@ -232,46 +400,41 @@ static int mdm_init(struct tegra_usb_modem *modem, struct platform_device *pdev) modem->ops->start(); } + /* create sysfs node to load/unload host controller */ + ret = device_create_file(&pdev->dev, &dev_attr_load_host); + if (ret) { + dev_err(&pdev->dev, "can't create sysfs file\n"); + goto error; + } + modem->sysfs_file_created = 1; + mutex_init(&(modem->lock)); - wake_lock_init(&modem->wake_lock, WAKE_LOCK_SUSPEND, - "tegra_usb_mdm_lock"); + wake_lock_init(&modem->wake_lock, WAKE_LOCK_SUSPEND, "mdm_lock"); - /* create work queue platform_driver_registe*/ + /* create work queue platform_driver_registe */ modem->wq = create_workqueue("tegra_usb_mdm_queue"); INIT_DELAYED_WORK(&(modem->recovery_work), tegra_usb_modem_recovery); - /* create threaded irq for remote wakeup */ - if (gpio_is_valid(pdata->wake_gpio)) { - /* get remote wakeup gpio from platform data */ - modem->wake_gpio = pdata->wake_gpio; - - ret = gpio_request(modem->wake_gpio, "usb_mdm_wake"); - if (ret) - return ret; - - tegra_gpio_enable(modem->wake_gpio); - - /* enable IRQ for remote wakeup */ - modem->irq = gpio_to_irq(modem->wake_gpio); - - ret = - request_threaded_irq(modem->irq, NULL, - tegra_usb_modem_wake_thread, - pdata->flags, "tegra_usb_mdm_wake", - modem); - if (ret < 0) { - dev_err(&pdev->dev, "%s: request_threaded_irq error\n", - __func__); - return ret; - } + /* request remote wakeup irq from platform data */ + ret = mdm_request_wakeable_irq(modem, + tegra_usb_modem_wake_thread, + pdata->wake_gpio, + pdata->wake_irq_flags, + "mdm_wake", &modem->wake_irq); + if (ret) { + dev_err(&pdev->dev, "request wake irq error\n"); + goto error; + } - ret = enable_irq_wake(modem->irq); - if (ret) { - dev_err(&pdev->dev, "%s: enable_irq_wake error\n", - __func__); - free_irq(modem->irq, modem); - return ret; - } + /* request boot irq from platform data */ + ret = mdm_request_wakeable_irq(modem, + tegra_usb_modem_boot_thread, + pdata->boot_gpio, + pdata->boot_irq_flags, + "mdm_boot", &modem->boot_irq); + if (ret) { + dev_err(&pdev->dev, "request boot irq error\n"); + goto error; } modem->pm_notifier.notifier_call = mdm_pm_notifier; @@ -281,6 +444,21 @@ static int mdm_init(struct tegra_usb_modem *modem, struct platform_device *pdev) register_pm_notifier(&modem->pm_notifier); return ret; +error: + if (modem->sysfs_file_created) + device_remove_file(&pdev->dev, &dev_attr_load_host); + + if (modem->wake_irq) { + disable_irq_wake(modem->wake_irq); + free_irq(modem->wake_irq, modem); + } + + if (modem->boot_irq) { + disable_irq_wake(modem->boot_irq); + free_irq(modem->boot_irq, modem); + } + + return ret; } static int tegra_usb_modem_probe(struct platform_device *pdev) @@ -319,10 +497,19 @@ static int __exit tegra_usb_modem_remove(struct platform_device *pdev) unregister_pm_notifier(&modem->pm_notifier); usb_unregister_notify(&modem->usb_notifier); - if (modem->irq) { - disable_irq_wake(modem->irq); - free_irq(modem->irq, modem); + if (modem->wake_irq) { + disable_irq_wake(modem->wake_irq); + free_irq(modem->wake_irq, modem); + } + + if (modem->boot_irq) { + disable_irq_wake(modem->boot_irq); + free_irq(modem->boot_irq, modem); } + + if (modem->sysfs_file_created) + device_remove_file(&pdev->dev, &dev_attr_load_host); + kfree(modem); return 0; } |