diff options
author | Raj Jayaraman <rjayaraman@nvidia.com> | 2012-01-30 14:34:37 -0800 |
---|---|---|
committer | Rohan Somvanshi <rsomvanshi@nvidia.com> | 2012-02-03 05:56:17 -0800 |
commit | da0048ebc17e2a45ef752588db33049d4aa4580a (patch) | |
tree | b48a459a595bb0cefa04d7ac02ccb7d94467f054 /drivers/misc | |
parent | de067308cce5e1adacc29e5d2fa4459a7f380fb2 (diff) |
misc: tegra-baseband: Add usb core notification support.
Add support for notification from USB core about device
atachment and removal. Add ARR support for M7400 modem.
Bug 886459
Reviewed-on: http://git-master/r/78301
Change-Id: I583714498e17501cbfb620440a24b43094bc9573
Signed-off-by: Raj Jayaraman <rjayaraman@nvidia.com>
Signed-off-by: Varun Wadekar <vwadekar@nvidia.com>
Reviewed-on: http://git-master/r/78900
Reviewed-by: Automatic_Commit_Validation_User
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/tegra-baseband/bb-m7400.c | 56 | ||||
-rw-r--r-- | drivers/misc/tegra-baseband/bb-power.c | 76 | ||||
-rw-r--r-- | drivers/misc/tegra-baseband/bb-power.h | 25 |
3 files changed, 137 insertions, 20 deletions
diff --git a/drivers/misc/tegra-baseband/bb-m7400.c b/drivers/misc/tegra-baseband/bb-m7400.c index 4c87245eede4..4209151e5f39 100644 --- a/drivers/misc/tegra-baseband/bb-m7400.c +++ b/drivers/misc/tegra-baseband/bb-m7400.c @@ -41,11 +41,14 @@ static struct tegra_bb_gpio_data m7400_gpios[] = { { { GPIO_INVALID, GPIOF_OUT_INIT_LOW, "MDM_USB_AWR" }, false }, { { GPIO_INVALID, GPIOF_IN, "MDM_USB_CWR" }, false }, { { GPIO_INVALID, GPIOF_IN, "MDM_RESOUT2" }, true }, + { { GPIO_INVALID, GPIOF_OUT_INIT_LOW, "MDM_USB_ARR" }, false }, { { GPIO_INVALID, 0, NULL }, false }, /* End of table */ }; static bool ehci_registered; static int gpio_awr; static int gpio_cwr; +static int gpio_arr; +static struct usb_device *m7400_usb_device; static int gpio_wait_timeout(int gpio, int value, int timeout_msec) { @@ -62,15 +65,16 @@ static int m7400_enum_handshake(void) { int retval = 0; - /* Wait for CP to indicate ready - by driving USB_CWR high. */ + /* Wait for CP to indicate ready - by driving CWR high. */ if (gpio_wait_timeout(gpio_cwr, 1, 10) != 0) { pr_info("%s: Error: timeout waiting for modem resume.\n", __func__); retval = -1; } - /* Signal AP ready - Drive USB_AWR high. */ + /* Signal AP ready - Drive AWR and ARR high. */ gpio_set_value(gpio_awr, 1); + gpio_set_value(gpio_arr, 1); return retval; } @@ -79,11 +83,11 @@ static int m7400_apup_handshake(bool checkresponse) { int retval = 0; - /* Signal AP ready - Drive USB_AWR high. */ + /* Signal AP ready - Drive AWR high. */ gpio_set_value(gpio_awr, 1); if (checkresponse) { - /* Wait for CP ack - by driving USB_CWR high. */ + /* Wait for CP ack - by driving CWR high. */ if (gpio_wait_timeout(gpio_cwr, 1, 10) != 0) { pr_info("%s: Error: timeout waiting for modem ack.\n", __func__); @@ -95,18 +99,29 @@ static int m7400_apup_handshake(bool checkresponse) static void m7400_apdown_handshake(void) { - /* Signal AP going down to modem - Drive USB_AWR low. */ + /* Signal AP going down to modem - Drive AWR low. */ /* No need to wait for a CP response */ gpio_set_value(gpio_awr, 0); } static int m7400_l2_suspend(void) { + /* Post bus suspend: Drive ARR low. */ + gpio_set_value(gpio_arr, 0); return 0; } static int m7400_l2_resume(void) { + /* Pre bus resume: Drive ARR high. */ + gpio_set_value(gpio_arr, 1); + + /* Wait for CP ack - by driving CWR high. */ + if (gpio_wait_timeout(gpio_cwr, 1, 10) != 0) { + pr_info("%s: Error: timeout waiting for modem ack.\n", + __func__); + return -1; + } return 0; } @@ -208,9 +223,16 @@ static int m7400_attrib_write(struct device *dev, int value) return 0; } +static int m7400_registered(struct usb_device *udev) +{ + pr_info("%s called.\n", __func__); + m7400_usb_device = udev; + return 0; +} + static struct tegra_bb_gpio_irqdata m7400_gpioirqs[] = { { GPIO_INVALID, "tegra_bb_wake", m7400_wake_irq, - IRQF_TRIGGER_FALLING, NULL }, + IRQF_TRIGGER_RISING, true, NULL }, { GPIO_INVALID, NULL, NULL, 0, NULL }, /* End of table */ }; @@ -219,6 +241,19 @@ static struct tegra_bb_power_gdata m7400_gdata = { .gpioirq = m7400_gpioirqs, }; +static struct tegra_bb_power_mdata m7400_mdata = { + .vid = 0x04cc, + .pid = 0x230f, + .wake_capable = true, + .autosuspend_ready = true, + .reg_cb = m7400_registered, +}; + +static struct tegra_bb_power_data m7400_data = { + .gpio_data = &m7400_gdata, + .modem_data = &m7400_mdata, +}; + static void *m7400_init(void *pdata) { struct tegra_bb_pdata *platdata = (struct tegra_bb_pdata *) pdata; @@ -231,6 +266,7 @@ static void *m7400_init(void *pdata) m7400_gpios[3].data.gpio = id->m7400.usb_awr; m7400_gpios[4].data.gpio = id->m7400.usb_cwr; m7400_gpios[5].data.gpio = id->m7400.resout2; + m7400_gpios[6].data.gpio = id->m7400.uart_awr; m7400_gpioirqs[0].id = id->m7400.usb_cwr; if (!platdata->ehci_register || !platdata->ehci_unregister) { @@ -241,18 +277,20 @@ static void *m7400_init(void *pdata) gpio_awr = m7400_gpios[3].data.gpio; gpio_cwr = m7400_gpios[4].data.gpio; - if (gpio_awr == GPIO_INVALID || gpio_cwr == GPIO_INVALID) { + gpio_arr = m7400_gpios[6].data.gpio; + if (gpio_awr == GPIO_INVALID || gpio_cwr == GPIO_INVALID + || gpio_arr == GPIO_INVALID) { pr_info("%s - Error: Invalid gpio data.\n", __func__); return 0; } ehci_registered = false; - return (void *) &m7400_gdata; + return (void *) &m7400_data; } static void *m7400_deinit(void) { - return (void *) &m7400_gdata; + return (void *) &m7400_data; } static struct tegra_bb_callback m7400_callbacks = { diff --git a/drivers/misc/tegra-baseband/bb-power.c b/drivers/misc/tegra-baseband/bb-power.c index 225d7667f86f..9210a8f3e84a 100644 --- a/drivers/misc/tegra-baseband/bb-power.c +++ b/drivers/misc/tegra-baseband/bb-power.c @@ -24,6 +24,7 @@ #include <linux/workqueue.h> #include <linux/delay.h> #include <linux/fs.h> +#include <linux/usb.h> #include <linux/uaccess.h> #include <linux/platform_data/tegra_usb.h> #include <mach/usb_phy.h> @@ -32,6 +33,7 @@ static struct tegra_bb_callback *callback; static int attr_load_val; +static struct tegra_bb_power_mdata *mdata; static bb_get_cblist get_cblist[] = { NULL, NULL, @@ -90,11 +92,14 @@ static int tegra_bb_power_gpio_init(struct tegra_bb_power_gdata *gdata) , __func__); return ret; } - ret = enable_irq_wake(irq); - if (ret) { - pr_err("%s: Error: enable_irq_wake failed.\n", + + if (gpioirq->wake_capable) { + ret = enable_irq_wake(irq); + if (ret) { + pr_err("%s: Error: irqwake req fail.\n", __func__); - return ret; + return ret; + } } } } @@ -147,10 +152,56 @@ static ssize_t tegra_bb_attr_read(struct device *dev, static DEVICE_ATTR(load, S_IRUSR | S_IWUSR | S_IRGRP, tegra_bb_attr_read, tegra_bb_attr_write); +static void tegra_usbdevice_added(struct usb_device *udev) +{ + const struct usb_device_descriptor *desc = &udev->descriptor; + + if (desc->idVendor == mdata->vid && + desc->idProduct == mdata->pid) { + pr_debug("%s: Device %s added.\n", udev->product, __func__); + + if (mdata->wake_capable) + device_set_wakeup_enable(&udev->dev, true); + if (mdata->autosuspend_ready) + usb_enable_autosuspend(udev); + if (mdata->reg_cb) + mdata->reg_cb(udev); + } +} + +static void tegra_usbdevice_removed(struct usb_device *udev) +{ + const struct usb_device_descriptor *desc = &udev->descriptor; + + if (desc->idVendor == mdata->vid && + desc->idProduct == mdata->pid) { + pr_debug("%s: Device %s removed.\n", udev->product, __func__); + } +} + +static int tegra_usb_notify(struct notifier_block *self, unsigned long action, + void *dev) +{ + switch (action) { + case USB_DEVICE_ADD: + tegra_usbdevice_added((struct usb_device *)dev); + break; + case USB_DEVICE_REMOVE: + tegra_usbdevice_removed((struct usb_device *)dev); + break; + } + return NOTIFY_OK; +} + +static struct notifier_block tegra_usb_nb = { + .notifier_call = tegra_usb_notify, +}; + static int tegra_bb_power_probe(struct platform_device *device) { struct device *dev = &device->dev; struct tegra_bb_pdata *pdata; + struct tegra_bb_power_data *data; struct tegra_bb_power_gdata *gdata; int err; unsigned int bb_id; @@ -166,9 +217,10 @@ static int tegra_bb_power_probe(struct platform_device *device) if (get_cblist[bb_id] != NULL) { callback = (struct tegra_bb_callback *) get_cblist[bb_id](); if (callback && callback->init) { - gdata = (struct tegra_bb_power_gdata *) + data = (struct tegra_bb_power_data *) callback->init((void *)pdata); + gdata = data->gpio_data; if (!gdata) { pr_err("%s - Error: Gpio data is empty.\n", __func__); @@ -177,6 +229,11 @@ static int tegra_bb_power_probe(struct platform_device *device) /* Initialize gpio as required */ tegra_bb_power_gpio_init(gdata); + + mdata = data->modem_data; + if (mdata && mdata->vid && mdata->pid) + /* Register to notifications from usb core */ + usb_register_notify(&tegra_usb_nb); } else { pr_err("%s - Error: init callback is empty.\n", __func__); @@ -201,20 +258,27 @@ static int tegra_bb_power_probe(struct platform_device *device) static int tegra_bb_power_remove(struct platform_device *device) { struct device *dev = &device->dev; + struct tegra_bb_power_data *data; struct tegra_bb_power_gdata *gdata; /* BB specific callback */ if (callback && callback->deinit) { - gdata = (struct tegra_bb_power_gdata *) + data = (struct tegra_bb_power_data *) callback->deinit(); /* Deinitialize gpios */ + gdata = data->gpio_data; if (gdata) tegra_bb_power_gpio_deinit(gdata); else { pr_err("%s - Error: Gpio data is empty.\n", __func__); return -ENODEV; } + + mdata = data->modem_data; + if (mdata && mdata->vid && mdata->pid) + /* Register to notifications from usb core */ + usb_unregister_notify(&tegra_usb_nb); } /* Remove the control sysfs node */ diff --git a/drivers/misc/tegra-baseband/bb-power.h b/drivers/misc/tegra-baseband/bb-power.h index 4f85cca712e6..84f9e85994b1 100644 --- a/drivers/misc/tegra-baseband/bb-power.h +++ b/drivers/misc/tegra-baseband/bb-power.h @@ -30,19 +30,34 @@ struct tegra_bb_gpio_irqdata { const char *name; irq_handler_t handler; int flags; + bool wake_capable; void *cookie; }; -struct tegra_bb_power_gdata { - struct tegra_bb_gpio_data *gpio; - struct tegra_bb_gpio_irqdata *gpioirq; -}; - typedef void* (*bb_get_cblist)(void); typedef void* (*bb_init_cb)(void *pdata); typedef void* (*bb_deinit_cb)(void); typedef int (*bb_power_cb)(int code); typedef int (*bb_attrib_cb)(struct device *dev, int value); +typedef int (*modem_register_cb)(struct usb_device *udev); + +struct tegra_bb_power_gdata { + struct tegra_bb_gpio_data *gpio; + struct tegra_bb_gpio_irqdata *gpioirq; +}; + +struct tegra_bb_power_mdata { + int vid; + int pid; + bool wake_capable; + bool autosuspend_ready; + modem_register_cb reg_cb; +}; + +struct tegra_bb_power_data { + struct tegra_bb_power_gdata *gpio_data; + struct tegra_bb_power_mdata *modem_data; +}; struct tegra_bb_callback { bb_init_cb init; |