summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve Lin <stlin@nvidia.com>2012-06-14 18:12:46 -0700
committerSimone Willett <swillett@nvidia.com>2012-06-27 17:28:52 -0700
commit025ead78f874daf44daaf5512c77f334ba6d339d (patch)
tree630bf389b1dacd9e41a4df5b0bf5a0ccebb12686
parentbdf416ebd0dcca569fdac1bb4230177310534be0 (diff)
arm: tegra: baseband: add sysfs file for modem boot, etc.
1. Add sysfs file so the fild can load/unload host controller before modem power cycle. 2. Move modem boot irq to modem PM driver. 3. Add short autosuspend to optimize power consumption if the wake source of system resume is not modem. 4. Avoid LP0 abort if remote wakeup happens during L0/L2 -> L3 transition. 5. Fix deadlock in pm_notifier function. Bug 975990 Signed-off-by: Steve Lin <stlin@nvidia.com> Reviewed-on: http://git-master/r/109079 (cherry picked from commit 0b60aade303a022ff3335b4a238ba2dbae4da4b5) Change-Id: I9bcac40e2f93f95c702b42a2eb5e4e9aa7a9d721 Reviewed-on: http://git-master/r/103981 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Uday Raval <uraval@nvidia.com> Tested-by: Steve Lin <stlin@nvidia.com> GVS: Gerrit_Virtual_Submit Reviewed-by: Steve Lin <stlin@nvidia.com>
-rw-r--r--arch/arm/mach-tegra/board-enterprise-baseband.c62
-rw-r--r--arch/arm/mach-tegra/board-whistler-baseband.c57
-rw-r--r--arch/arm/mach-tegra/include/mach/tegra_usb_modem_power.h12
-rw-r--r--arch/arm/mach-tegra/tegra_usb_modem_power.c291
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;
}