misc: tegra-baseband: Add usb core notification support.
Raj Jayaraman [Mon, 30 Jan 2012 22:34:37 +0000 (14:34 -0800)]
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

drivers/misc/tegra-baseband/bb-m7400.c
drivers/misc/tegra-baseband/bb-power.c
drivers/misc/tegra-baseband/bb-power.h

index 4c87245..4209151 100644 (file)
@@ -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 = {
index 225d766..9210a8f 100644 (file)
@@ -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 */
index 4f85cca..84f9e85 100644 (file)
@@ -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;