misc: tegra-baseband: Add partner modem support.
Raj Jayaraman [Tue, 9 Oct 2012 18:46:12 +0000 (11:46 -0700)]
Bug 1054808

Change-Id: I5bca87b1cf034addbb5c2acb3740a87809212f90
Signed-off-by: Raj Jayaraman <rjayaraman@nvidia.com>
Reviewed-on: http://git-master/r/160037
(cherry picked from commit 059dec32ea2893be5ed651a7bd8f802ae008e351)
Reviewed-on: http://git-master/r/162298
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: WK Tsai <wtsai@nvidia.com>
Reviewed-by: Steve Lin <stlin@nvidia.com>

arch/arm/mach-tegra/include/mach/tegra-bb-power.h
drivers/misc/tegra-baseband/Kconfig
drivers/misc/tegra-baseband/Makefile
drivers/misc/tegra-baseband/bb-oem1.c [new file with mode: 0644]
drivers/misc/tegra-baseband/bb-oem1.h [new file with mode: 0644]
drivers/misc/tegra-baseband/bb-oem2.c [moved from drivers/misc/tegra-baseband/bb-m7400.c with 98% similarity]
drivers/misc/tegra-baseband/bb-power.c
drivers/misc/tegra-baseband/bb-power.h

index 96e3611..e7d8a77 100644 (file)
@@ -28,34 +28,32 @@ union tegra_bb_gpio_id {
                int rsvd2;
        } generic;
        struct {
-               int bb_rst;
-               int bb_on;
-               int ipc_bb_wake;
-               int ipc_ap_wake;
-               int ipc_hsic_active;
-               int ipc_hsic_sus_req;
+               int reset;
+               int pwron;
+               int awr;
+               int cwr;
+               int spare;
+               int wdi;
                int rsvd1;
                int rsvd2;
-       } xmm;
-       struct {
-               int pwr_status;
-               int pwr_on;
-               int uart_awr;
-               int uart_cwr;
-               int usb_awr;
-               int usb_cwr;
-               int service;
-               int resout2;
-       } m7400;
+       } oem1;
 };
 
-typedef struct platform_device* (*ehci_register_cb)(void);
+typedef struct platform_device* (*ehci_register_cb)(struct platform_device *);
 typedef void (*ehci_unregister_cb)(struct platform_device **);
 
 struct tegra_bb_pdata {
+       /* List of platform gpios for modem */
        union tegra_bb_gpio_id *id;
+       /* Ehci device pointer */
        struct platform_device *device;
+       /* Ehci register callback */
        ehci_register_cb ehci_register;
+       /* Ehci unregister callback */
        ehci_unregister_cb ehci_unregister;
+       /* Baseband ID */
        int bb_id;
+       /* HSIC rail regulator name. Provide a name if --
+       rail is shared and the co-owner will turn it off when done */
+       char *regulator;
 };
index 1f11691..2a920fc 100644 (file)
@@ -17,16 +17,25 @@ config TEGRA_BB_POWER
 
         This driver should work with at least the following devices:
 
-            * STE M7400
+            * OEM #1
             * ...
 
         Disabled by default. Choose Y here if you want to build the driver.
 
-config TEGRA_BB_M7400
-       bool "Enable driver for M7400 modem"
+config TEGRA_BB_OEM1
+       bool "Enable driver for oem modem #1"
        ---help---
-        Enables driver for M7400 modem.
+        Enables driver for oem modem #1.
+        Needs TEGRA_BB_SUPPORT to be enabled.
+        Disabled by default.
+        Choose Y here if you want to build the driver.
 
-        Disabled by default. Choose Y here if you want to build the driver.
+config TEGRA_BB_OEM2
+       bool "Enable driver for oem modem #2"
+       ---help---
+        Enables driver for oem modem #2.
+        Needs TEGRA_BB_SUPPORT to be enabled.
+        Disabled by default.
+        Choose Y here if you want to build the driver.
 
 endif # TEGRA_BB_SUPPORT
index 68ec4fe..ab54501 100644 (file)
@@ -5,4 +5,5 @@
 subdir-ccflags-y := -Werror
 
 obj-$(CONFIG_TEGRA_BB_POWER)   += bb-power.o
-obj-$(CONFIG_TEGRA_BB_M7400)   += bb-m7400.o
+obj-$(CONFIG_TEGRA_BB_OEM1)    += bb-oem1.o
+obj-$(CONFIG_TEGRA_BB_OEM2)    += bb-oem2.o
diff --git a/drivers/misc/tegra-baseband/bb-oem1.c b/drivers/misc/tegra-baseband/bb-oem1.c
new file mode 100644 (file)
index 0000000..54189a1
--- /dev/null
@@ -0,0 +1,623 @@
+/*
+ * drivers/misc/tegra-baseband/bb-oem1.c
+ *
+ * Copyright (c) 2012, NVIDIA Corporation.
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/resource.h>
+#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/device.h>
+#include <linux/usb.h>
+#include <linux/wakelock.h>
+#include <linux/suspend.h>
+#include <linux/platform_data/tegra_usb.h>
+#include <linux/regulator/consumer.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <mach/tegra-bb-power.h>
+#include <mach/usb_phy.h>
+#include "bb-power.h"
+#include "bb-oem1.h"
+
+#define WAKE_TIMEOUT_MS 50
+
+static struct tegra_bb_gpio_data bb_gpios[] = {
+       { { GPIO_INVALID, GPIOF_OUT_INIT_LOW, "MDM4_RST" }, true },
+       { { GPIO_INVALID, GPIOF_OUT_INIT_LOW, "MDM4_ON" }, true },
+       { { GPIO_INVALID, GPIOF_OUT_INIT_LOW, "MDM4_USB_AWR" }, true },
+       { { GPIO_INVALID, GPIOF_IN, "MDM4_USB_CWR" }, true },
+       { { GPIO_INVALID, GPIOF_IN, "MDM4_SPARE" }, true },
+       { { GPIO_INVALID, GPIOF_IN, "MDM4_WDI" }, true },
+       { { GPIO_INVALID, 0, NULL }, false },   /* End of table */
+};
+
+static struct sdata bb_sdata;
+static struct opsdata bb_opdata;
+static struct locks bb_locks;
+static struct regulator *hsic_reg;
+static bool ehci_registered;
+static int dlevel;
+
+static int gpio_wait_timeout(int gpio, int value, int timeout_msec)
+{
+       int count;
+       for (count = 0; count < timeout_msec; ++count) {
+               if (gpio_get_value(gpio) == value)
+                       return 0;
+               mdelay(1);
+       }
+       return -1;
+}
+
+static void bb_setdata(struct opsdata *data)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&bb_locks.lock, flags);
+       if (data) {
+               if (data->powerstate != bb_opdata.powerstate)
+                       bb_opdata.powerstate = data->powerstate;
+               if (data->usbdev != bb_opdata.usbdev)
+                       bb_opdata.usbdev = data->usbdev;
+       }
+       spin_unlock_irqrestore(&bb_locks.lock, flags);
+
+       if (data->powerstate == BBSTATE_UNKNOWN) {
+               if (!wake_lock_active(&bb_locks.wlock)) {
+                       wake_lock(&bb_locks.wlock);
+                       if (dlevel & DLEVEL_MISC)
+                               pr_info("%s: Taking wakelock.\n", __func__);
+               } else
+                       pr_warn("%s: Active wakelock in UNK\n", __func__);
+       } else {
+               if (wake_lock_active(&bb_locks.wlock)) {
+                       wake_unlock(&bb_locks.wlock);
+                       if (dlevel & DLEVEL_MISC)
+                               pr_info("%s: Releasing wakelock.\n", __func__);
+               }
+       }
+}
+
+static void bb_getdata(struct opsdata *data)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&bb_locks.lock, flags);
+       if (data) {
+               data->powerstate = bb_opdata.powerstate;
+               data->usbdev = bb_opdata.usbdev;
+       }
+       spin_unlock_irqrestore(&bb_locks.lock, flags);
+}
+
+static int bb_getpowerstate(void)
+{
+       struct opsdata data;
+
+       bb_getdata(&data);
+       return data.powerstate;
+}
+
+static void bb_setpowerstate(int status)
+{
+       struct opsdata data;
+
+       bb_getdata(&data);
+       data.powerstate = status;
+       bb_setdata(&data);
+}
+
+static bool bb_crashed(void)
+{
+       bool flag;
+
+       flag = ((gpio_get_value(bb_sdata.gpio_wdi)) ? false : true);
+       return flag;
+}
+
+static bool bb_get_cwr(void)
+{
+       return gpio_get_value(bb_sdata.gpio_cwr);
+}
+
+static void bb_set_awr(int value)
+{
+       gpio_set_value(bb_sdata.gpio_awr, value);
+}
+
+static void bb_gpio_stat(void)
+{
+       pr_info("%s: AWR:%d, CWR:%d, WDI:%d.\n", __func__,
+               gpio_get_value(bb_sdata.gpio_awr),
+               gpio_get_value(bb_sdata.gpio_cwr),
+               gpio_get_value(bb_sdata.gpio_wdi));
+}
+
+static int apup_handshake(bool checkresponse)
+{
+       int retval = 0;
+
+       /* Signal AP ready - Drive USB_AWR high. */
+       bb_set_awr(1);
+
+       if (checkresponse) {
+               /* Wait for CP ack - by driving USB_CWR high. */
+               if (gpio_wait_timeout(bb_sdata.gpio_cwr, 1,
+                                       WAKE_TIMEOUT_MS) != 0) {
+                       pr_err("%s: Error: Timeout waiting for modem ack.\n",
+                                                       __func__);
+                       retval = -1;
+               }
+       }
+       return retval;
+}
+
+static void apdown_handshake(void)
+{
+       /* Signal AP going down to modem - Drive USB_AWR low. */
+       /* No need to wait for a CP response */
+       bb_set_awr(0);
+}
+
+static void pre_l2_suspend(void)
+{
+       bb_setpowerstate(BBSTATE_L02L2);
+}
+
+static void l2_suspend(void)
+{
+       int modemstate = bb_getpowerstate();
+
+       if (dlevel & DLEVEL_PM)
+               pr_info("%s.\n", __func__);
+
+       if (modemstate != BBSTATE_L02L2) {
+               pr_err("%s: Error. Unexp modemstate %x. Should be %d.\n",
+                                        __func__, modemstate, BBSTATE_L02L2);
+               return;
+       }
+       bb_setpowerstate(BBSTATE_L2);
+}
+
+static void l2_resume(void)
+{
+       int modemstate = bb_getpowerstate();
+       bool resumeok = false;
+
+       if (dlevel & DLEVEL_PM)
+               pr_info("%s.\n", __func__);
+
+       /* Gets called for two cases :
+               a) L2 resume.
+               b) bus resume phase of L3 resume. */
+       switch (modemstate) {
+       case BBSTATE_L2:
+               resumeok = true;
+               break;
+       case BBSTATE_L3:
+               if (apup_handshake(true) == 0)
+                       resumeok = true;
+               else {
+                       bb_gpio_stat();
+                       pr_err("%s: Error. Modem wakeup from L3 failed.\n",
+                                                                __func__);
+                       /* TBD: Add code to unregister ehci. */
+               }
+               break;
+       default:
+               pr_err("%s: Error. Unexp modemstate %x. Should be %d or %d.\n",
+                               __func__, modemstate, BBSTATE_L2, BBSTATE_L3);
+               break;
+       }
+       if (resumeok)
+               bb_setpowerstate(BBSTATE_L0);
+}
+
+static void phy_ready(void)
+{
+       int modemstate = bb_getpowerstate();
+
+       if (dlevel & DLEVEL_PM)
+               pr_info("%s.\n", __func__);
+
+       switch (modemstate) {
+       case BBSTATE_UNKNOWN:
+               /* Wait for CP to indicate ready - by driving USB_CWR high. */
+               if (gpio_wait_timeout(bb_sdata.gpio_cwr, 1, 10) != 0) {
+                       pr_info("%s: Timeout 4 modem ready. Maybe 1st enum ?\n",
+                                                        __func__);
+                       /* For first enumeration don't drive AWR high */
+                       break;
+               }
+
+               /* Signal AP ready - Drive USB_AWR high. */
+               pr_info("%s : Driving AWR high.\n", __func__);
+               bb_set_awr(1);
+               break;
+       default:
+               pr_err("%s: Error. Unexp modemstate %x. Should be %d.\n",
+                               __func__, modemstate, BBSTATE_UNKNOWN);
+               break;
+       }
+}
+
+static void pre_phy_off(void)
+{
+       if (dlevel & DLEVEL_PM)
+               pr_info("%s.\n", __func__);
+
+       /* Signal AP going down */
+       apdown_handshake();
+
+       /* Gets called for two cases :
+               a) L3 suspend.
+               b) EHCI unregister. */
+       if (bb_getpowerstate() == BBSTATE_L2)
+               bb_setpowerstate(BBSTATE_L3);
+}
+
+static int l3_suspend(void)
+{
+       int pwrstate = bb_getpowerstate();
+       bool wakeup_detected = bb_get_cwr();
+       bool crashed = bb_crashed();
+
+       if (dlevel & DLEVEL_PM)
+               pr_info("%s.\n", __func__);
+
+       /* If modem state during system suspend is not L3 (crashed)
+               or modem is initiating a wakeup, abort system suspend. */
+       if ((pwrstate != BBSTATE_L3) || wakeup_detected || crashed) {
+               if (pwrstate != BBSTATE_L3)
+                       pr_err("%s: Unexp modemstate %x. Should be %d.\n",
+                                __func__, pwrstate, BBSTATE_L3);
+               if (wakeup_detected)
+                       pr_info("%s : CWR high.\n", __func__);
+               if (crashed)
+                       pr_info("%s : WDI low.\n", __func__);
+               pr_info("%s: Aborting suspend.\n", __func__);
+               return 1;
+       }
+       return 0;
+}
+
+static int l3_suspend_noirq(void)
+{
+       return 0;
+}
+
+static int l3_resume(void)
+{
+       return 0;
+}
+
+static int l3_resume_noirq(void)
+{
+       return 0;
+}
+
+static irqreturn_t bb_wake_irq(int irq, void *dev_id)
+{
+       struct opsdata data;
+       struct usb_interface *iface;
+       int cwrlevel = bb_get_cwr();
+       bool pwrstate_l2 = false;
+
+       pr_info("%s. CWR = %d.\n", __func__, cwrlevel); /* for now */
+
+       bb_getdata(&data);
+       pwrstate_l2 = ((data.powerstate == BBSTATE_L02L2) ||
+                               (data.powerstate == BBSTATE_L2));
+       if (cwrlevel && pwrstate_l2) {
+               if (dlevel & DLEVEL_PM)
+                       pr_info("%s: Modem wakeup request from L2.\n",
+                                                       __func__);
+               if (data.usbdev) {
+                       usb_lock_device(data.usbdev);
+                       iface = usb_ifnum_to_if(data.usbdev, 0);
+                       if (iface) {
+                               /* Resume usb host activity. */
+                               usb_autopm_get_interface(iface);
+                               usb_autopm_put_interface_no_suspend(iface);
+                       }
+                       usb_unlock_device(data.usbdev);
+               }
+       }
+
+       if (!cwrlevel && data.powerstate == BBSTATE_UNKNOWN && data.usbdev) {
+               data.powerstate = BBSTATE_L0;
+               bb_setdata(&data);
+               if (dlevel & DLEVEL_PM)
+                       pr_info("%s: Network interface up.\n", __func__);
+       }
+       return IRQ_HANDLED;
+}
+
+static int bb_power(int code)
+{
+       int retval = 0;
+
+       switch (code) {
+       case PWRSTATE_L2L3:
+       retval = l3_suspend();
+       break;
+       case PWRSTATE_L2L3_NOIRQ:
+       retval = l3_suspend_noirq();
+       break;
+       case PWRSTATE_L3L0_NOIRQ:
+       retval = l3_resume_noirq();
+       break;
+       case PWRSTATE_L3L0:
+       retval = l3_resume();
+       break;
+       default:
+       break;
+       }
+       return retval;
+}
+
+static int bb_ehci_customize(struct tegra_bb_pdata *pdata)
+{
+       struct platform_device *pdev;
+       struct tegra_usb_platform_data *usb_pdata;
+       struct tegra_usb_phy_platform_ops *pops;
+
+       if (pdata && pdata->device) {
+               pdev = pdata->device;
+               usb_pdata = (struct tegra_usb_platform_data *)
+                       pdev->dev.platform_data;
+               pops = (struct tegra_usb_phy_platform_ops *)
+                       usb_pdata->ops;
+
+               /* Register PHY platform callbacks */
+               pops->pre_suspend = pre_l2_suspend;
+               pops->post_suspend = l2_suspend;
+               pops->pre_resume = l2_resume;
+               pops->port_power = phy_ready;
+               pops->pre_phy_off = pre_phy_off;
+
+               /* Override required settings */
+               usb_pdata->u_data.host.power_off_on_suspend = 0;
+
+       } else {
+               pr_err("%s: Error. Invalid platform data.\n", __func__);
+               return 0;
+       }
+       return 1;
+}
+
+static int bb_sysfs_load(struct device *dev, int value)
+{
+       struct tegra_bb_pdata *pdata;
+       static struct platform_device *ehci_device;
+       struct opsdata data;
+
+       if (dlevel & DLEVEL_SYS_CB)
+               pr_info("%s: Called with value : %d\n", __func__, value);
+
+       if (value > 1 || (!ehci_registered && !value) ||
+                               (ehci_registered && value)) {
+               /* Supported values are 0/1. */
+               pr_err("%s:  Error. Invalid data. Exiting.\n", __func__);
+               return -1;
+       }
+
+       pdata = (struct tegra_bb_pdata *) dev->platform_data;
+       if (value) {
+               /* Register ehci controller */
+               ehci_device = pdata->ehci_register(pdata->device);
+               if (ehci_device == NULL) {
+                       pr_err("%s: Error. ehci register failed.\n",
+                                                        __func__);
+                       return -1;
+               }
+               ehci_registered = true;
+       } else {
+               /* Mark usb device invalid */
+               data.usbdev = NULL;
+               data.powerstate = BBSTATE_UNKNOWN;
+               bb_setdata(&data);
+               ehci_registered = false;
+
+               /* Unregister ehci controller */
+               if (ehci_device != NULL)
+                       pdata->ehci_unregister(&ehci_device);
+
+               /* Signal AP going down */
+               apdown_handshake();
+       }
+       return 0;
+}
+
+static int bb_sysfs_dlevel(struct device *dev, int value)
+{
+       if (dlevel & DLEVEL_SYS_CB)
+               pr_info("%s: Called with value : %d\n", __func__, value);
+
+       dlevel = value;
+       return 0;
+}
+
+static int bb_usbnotify(struct usb_device *udev, bool registered)
+{
+       struct opsdata data;
+
+       data.powerstate = BBSTATE_UNKNOWN;
+       if (registered) {
+               data.usbdev = udev;
+               if (dlevel & DLEVEL_MISC)
+                       pr_info("%s: Modem attached.\n", __func__);
+
+       } else {
+               data.usbdev = NULL;
+               if (dlevel & DLEVEL_MISC)
+                       pr_info("%s: Modem detached.\n", __func__);
+       }
+
+       bb_setdata(&data);
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int bb_pmnotify(unsigned long event)
+{
+       int pwrstate = bb_getpowerstate();
+       int retval = NOTIFY_OK;
+
+       if (dlevel & DLEVEL_PM)
+               pr_info("%s: PM notification %ld.\n", __func__, event);
+
+       switch (event) {
+       case PM_SUSPEND_PREPARE:
+       if (pwrstate == BBSTATE_UNKNOWN) {
+               pr_err("%s: Suspend with pwrstate=%d. Aborting suspend.\n",
+                                                __func__, pwrstate);
+               retval = NOTIFY_BAD;
+       }
+       break;
+       case PM_POST_SUSPEND:
+       break;
+       default:
+       retval = NOTIFY_DONE;
+       break;
+       }
+       return retval;
+}
+#endif
+
+static struct tegra_bb_gpio_irqdata bb_gpioirqs[] = {
+       { GPIO_INVALID, "tegra_bb_wake", bb_wake_irq,
+               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, true, NULL },
+       { GPIO_INVALID, "tegra_bb_wdi", NULL,
+               IRQF_TRIGGER_NONE, true, NULL },
+       { GPIO_INVALID, NULL, NULL, 0, NULL },  /* End of table */
+};
+
+static struct tegra_bb_power_gdata bb_gdata = {
+       .gpio = bb_gpios,
+       .gpioirq = bb_gpioirqs,
+};
+
+static struct tegra_bb_power_mdata bb_mdata = {
+       .vid = 0x045B,
+       .pid = 0x020F,
+       .wake_capable = true,
+       .autosuspend_ready = false,
+};
+
+static struct tegra_bb_power_data bb_data = {
+       .gpio_data = &bb_gdata,
+       .modem_data = &bb_mdata,
+};
+
+
+static void *bb_init(void *pdata)
+{
+       struct tegra_bb_pdata *platdata = (struct tegra_bb_pdata *) pdata;
+       union tegra_bb_gpio_id *id = platdata->id;
+       struct opsdata data;
+
+       /* Fill the gpio ids allocated by hardware */
+       bb_gpios[0].data.gpio = id->oem1.reset;
+       bb_gpios[1].data.gpio = id->oem1.pwron;
+       bb_gpios[2].data.gpio = id->oem1.awr;
+       bb_gpios[3].data.gpio = id->oem1.cwr;
+       bb_gpios[4].data.gpio = id->oem1.spare;
+       bb_gpios[5].data.gpio = id->oem1.wdi;
+       bb_gpioirqs[0].id = id->oem1.cwr;
+       bb_gpioirqs[1].id = id->oem1.wdi;
+
+       if (!platdata->ehci_register || !platdata->ehci_unregister) {
+               pr_err("%s - Error: ehci reg/unreg functions missing.\n"
+                                                       , __func__);
+               return 0;
+       }
+
+       bb_sdata.gpio_awr = bb_gpios[2].data.gpio;
+       bb_sdata.gpio_cwr = bb_gpios[3].data.gpio;
+       bb_sdata.gpio_wdi = bb_gpios[5].data.gpio;
+       if (bb_sdata.gpio_awr == GPIO_INVALID ||
+               bb_sdata.gpio_cwr == GPIO_INVALID ||
+               bb_sdata.gpio_wdi == GPIO_INVALID) {
+               pr_err("%s: Error. Invalid gpio data.\n", __func__);
+               return 0;
+       }
+
+       /* Customize PHY setup/callbacks */
+       if (!bb_ehci_customize(platdata))
+               return 0;
+
+       /* Board specific regulator init */
+       if (platdata->regulator) {
+               hsic_reg = regulator_get(NULL,
+                                       (const char *)platdata->regulator);
+               if (IS_ERR_OR_NULL(hsic_reg)) {
+                       pr_err("%s: Error. regulator_get failed.\n",
+                                                        __func__);
+                       return 0;
+               }
+
+               if (regulator_enable(hsic_reg) < 0) {
+                       pr_err("%s: Error. regulator_enable failed.\n",
+                                                        __func__);
+                       return 0;
+               }
+       }
+
+       spin_lock_init(&bb_locks.lock);
+       wake_lock_init(&bb_locks.wlock, WAKE_LOCK_SUSPEND,
+                                               "tegra-bb-lock");
+       dlevel = DLEVEL_INIT;
+       ehci_registered = false;
+       data.usbdev = NULL;
+       data.powerstate = BBSTATE_UNKNOWN;
+       bb_setdata(&data);
+
+       return (void *) &bb_data;
+}
+
+static void *bb_deinit(void)
+{
+       /* destroy wake lock */
+       wake_lock_destroy(&bb_locks.wlock);
+
+       return (void *) &bb_gdata;
+}
+
+static struct tegra_bb_callback bb_callbacks = {
+       .init = bb_init,
+       .deinit = bb_deinit,
+       .load = bb_sysfs_load,
+       .dlevel = bb_sysfs_dlevel,
+       .usbnotify = bb_usbnotify,
+#ifdef CONFIG_PM
+       .power = bb_power,
+#endif
+#ifdef CONFIG_PM_SLEEP
+       .pmnotify = bb_pmnotify,
+#endif
+};
+
+void *bb_oem1_get_cblist(void)
+{
+       return (void *) &bb_callbacks;
+}
diff --git a/drivers/misc/tegra-baseband/bb-oem1.h b/drivers/misc/tegra-baseband/bb-oem1.h
new file mode 100644 (file)
index 0000000..9f9d1c4
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * drivers/misc/tegra-baseband/bb-modem4.h
+ *
+ * Copyright (C) 2012 NVIDIA Corporation
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/* Static data that doesn't change */
+struct sdata {
+       int gpio_awr;
+       int gpio_cwr;
+       int gpio_wdi;
+};
+
+/* Dynamic ops data */
+struct opsdata {
+       int powerstate;
+       struct usb_device *usbdev;
+};
+
+/* Locks */
+struct locks {
+       struct wake_lock wlock;
+       spinlock_t lock;
+};
similarity index 98%
rename from drivers/misc/tegra-baseband/bb-m7400.c
rename to drivers/misc/tegra-baseband/bb-oem2.c
index edde7d1..2ef909f 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * drivers/misc/tegra-baseband/bb-m7400.c
+ * drivers/misc/tegra-baseband/bb-oem2.c
  *
- * Copyright (c) 2011, NVIDIA Corporation.
+ * Copyright (c) 2012, NVIDIA Corporation.
  *
  * 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
index 0a8f406..a06efa5 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * drivers/misc/tegra-baseband/bb-power.c
  *
- * Copyright (C) 2011 NVIDIA Corporation
+ * Copyright (C) 2012 NVIDIA Corporation
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
 #include <linux/fs.h>
 #include <linux/usb.h>
 #include <linux/uaccess.h>
+#include <linux/suspend.h>
 #include <linux/platform_data/tegra_usb.h>
 #include <mach/usb_phy.h>
-#include <mach/tegra-bb-power.h>
 #include <mach/gpio-tegra.h>
+#include <mach/tegra-bb-power.h>
 #include "bb-power.h"
 
 static struct tegra_bb_callback *callback;
-static int attr_load_val;
+static int attr_load;
+static int attr_dlevel;
 static struct tegra_bb_power_mdata *mdata;
 static bb_get_cblist get_cblist[] = {
        NULL,
        NULL,
-       NULL,
-       M7400_CB,
+       OEM1_CB,
 };
 
 static int tegra_bb_power_gpio_init(struct tegra_bb_power_gdata *gdata)
@@ -79,10 +80,10 @@ static int tegra_bb_power_gpio_init(struct tegra_bb_power_gdata *gdata)
 
        gpioirq = gdata->gpioirq;
        for (; gpioirq->id != GPIO_INVALID; ++gpioirq) {
+               irq = gpio_to_irq(gpioirq->id);
 
                /* Create interrupt handler, if requested */
                if (gpioirq->handler != NULL) {
-                       irq = gpio_to_irq(gpioirq->id);
                        ret = request_threaded_irq(irq, NULL, gpioirq->handler,
                                gpioirq->flags, gpioirq->name, gpioirq->cookie);
                        if (ret < 0) {
@@ -90,14 +91,15 @@ static int tegra_bb_power_gpio_init(struct tegra_bb_power_gdata *gdata)
                                                                , __func__);
                                return ret;
                        }
+               }
 
-                       if (gpioirq->wake_capable) {
-                               ret = enable_irq_wake(irq);
-                               if (ret) {
-                                       pr_err("%s: Error: irqwake req fail.\n",
+               /* Enable wake, if requested */
+               if (gpioirq->wake_capable) {
+                       ret = enable_irq_wake(irq);
+                       if (ret) {
+                               pr_err("%s: Error: irqwake req fail.\n",
                                                                __func__);
-                                       return ret;
-                               }
+                               return ret;
                        }
                }
        }
@@ -125,7 +127,7 @@ static int tegra_bb_power_gpio_deinit(struct tegra_bb_power_gdata *gdata)
        return 0;
 }
 
-static ssize_t tegra_bb_attr_write(struct device *dev,
+static ssize_t tegra_bb_load_write(struct device *dev,
                        struct device_attribute *attr,
                        const char *buf, size_t count)
 {
@@ -134,21 +136,46 @@ static ssize_t tegra_bb_attr_write(struct device *dev,
        if (sscanf(buf, "%d", &val) != 1)
                return -EINVAL;
 
-       if (callback && callback->attrib) {
-               if (!callback->attrib(dev, val))
-                       attr_load_val = val;
+       if (callback && callback->load) {
+               if (!callback->load(dev, val))
+                       attr_load = val;
+       }
+       return count;
+}
+
+static ssize_t tegra_bb_load_read(struct device *dev,
+                       struct device_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%d", attr_load);
+}
+
+static ssize_t tegra_bb_dlevel_write(struct device *dev,
+                       struct device_attribute *attr,
+                       const char *buf, size_t count)
+{
+       int val;
+
+       if (sscanf(buf, "%x", &val) != 1)
+               return -EINVAL;
+
+       if ((val >= DLEVEL_INIT) && (val < DLEVEL_MAX)) {
+               attr_dlevel = val;
+               if (callback && callback->dlevel)
+                       callback->dlevel(dev, val);
        }
        return count;
 }
 
-static ssize_t tegra_bb_attr_read(struct device *dev,
+static ssize_t tegra_bb_dlevel_read(struct device *dev,
                        struct device_attribute *attr, char *buf)
 {
-       return sprintf(buf, "%d", attr_load_val);
+       return sprintf(buf, "%d", attr_dlevel);
 }
 
-static DEVICE_ATTR(load, S_IRUSR | S_IWUSR | S_IRGRP,
-               tegra_bb_attr_read, tegra_bb_attr_write);
+static DEVICE_ATTR(load, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
+               tegra_bb_load_read, tegra_bb_load_write);
+static DEVICE_ATTR(dlevel, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
+               tegra_bb_dlevel_read, tegra_bb_dlevel_write);
 
 static void tegra_usbdevice_added(struct usb_device *udev)
 {
@@ -158,12 +185,12 @@ static void tegra_usbdevice_added(struct usb_device *udev)
            desc->idProduct == mdata->pid) {
                pr_debug("%s: Device %s added.\n", udev->product, __func__);
 
+               if (callback && callback->usbnotify)
+                       callback->usbnotify(udev, true);
                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);
        }
 }
 
@@ -174,6 +201,8 @@ static void tegra_usbdevice_removed(struct usb_device *udev)
        if (desc->idVendor == mdata->vid &&
            desc->idProduct == mdata->pid) {
                pr_debug("%s: Device %s removed.\n", udev->product, __func__);
+               if (callback && callback->usbnotify)
+                       callback->usbnotify(udev, false);
        }
 }
 
@@ -195,6 +224,21 @@ static struct notifier_block tegra_usb_nb = {
        .notifier_call = tegra_usb_notify,
 };
 
+#ifdef CONFIG_PM_SLEEP
+static int pm_event(struct notifier_block *this, unsigned long event,
+                                                        void *ptr) {
+       int retval = NOTIFY_DONE;
+
+       if (callback && callback->pmnotify)
+               retval = callback->pmnotify(event);
+       return retval;
+};
+
+static struct notifier_block tegra_pm_notifier = {
+       .notifier_call = pm_event,
+};
+#endif
+
 static int tegra_bb_power_probe(struct platform_device *device)
 {
        struct device *dev = &device->dev;
@@ -211,7 +255,7 @@ static int tegra_bb_power_probe(struct platform_device *device)
        }
 
        /* Obtain BB specific callback list */
-       bb_id = pdata->bb_id;
+       bb_id = (pdata->bb_id)-1;
        if (get_cblist[bb_id] != NULL) {
                callback = (struct tegra_bb_callback *) get_cblist[bb_id]();
                if (callback && callback->init) {
@@ -248,8 +292,20 @@ static int tegra_bb_power_probe(struct platform_device *device)
                pr_err("%s - Error: device_create_file failed.\n", __func__);
                return -ENODEV;
        }
-       attr_load_val = 0;
+       attr_load = 0;
 
+       /* Create debug level sysfs node */
+       err = device_create_file(dev, &dev_attr_dlevel);
+       if (err < 0) {
+               pr_err("%s - Error: device_create_file failed.\n", __func__);
+               return -ENODEV;
+       }
+       attr_dlevel = DLEVEL_INIT;
+
+#ifdef CONFIG_PM_SLEEP
+       /* Register for PM notifications */
+       register_pm_notifier(&tegra_pm_notifier);
+#endif
        return 0;
 }
 
@@ -275,44 +331,70 @@ static int tegra_bb_power_remove(struct platform_device *device)
 
                mdata = data->modem_data;
                if (mdata && mdata->vid && mdata->pid)
-                       /* Register to notifications from usb core */
+                       /* Unregister notifications from usb core */
                        usb_unregister_notify(&tegra_usb_nb);
        }
 
-       /* Remove the control sysfs node */
+#ifdef CONFIG_PM_SLEEP
+       /* Unregister PM notifications */
+       unregister_pm_notifier(&tegra_pm_notifier);
+#endif
+       /* Remove sysfs nodes */
        device_remove_file(dev, &dev_attr_load);
+       device_remove_file(dev, &dev_attr_dlevel);
 
        return 0;
 }
 
 #ifdef CONFIG_PM
-static int tegra_bb_power_suspend(struct platform_device *device,
-       pm_message_t state)
+static int tegra_bb_driver_suspend(struct device *dev)
+{
+       /* BB specific callback */
+       if (callback && callback->power)
+               return callback->power(PWRSTATE_L2L3);
+       return 0;
+}
+
+static int tegra_bb_driver_resume(struct device *dev)
 {
        /* BB specific callback */
        if (callback && callback->power)
-               callback->power(PWRSTATE_L2L3);
+               return callback->power(PWRSTATE_L3L0);
        return 0;
 }
 
-static int tegra_bb_power_resume(struct platform_device *device)
+static int tegra_bb_suspend_noirq(struct device *dev)
 {
        /* BB specific callback */
        if (callback && callback->power)
-               callback->power(PWRSTATE_L3L0);
+               return callback->power(PWRSTATE_L2L3_NOIRQ);
        return 0;
 }
+
+static int tegra_bb_resume_noirq(struct device *dev)
+{
+       /* BB specific callback */
+       if (callback && callback->power)
+               return callback->power(PWRSTATE_L3L0_NOIRQ);
+       return 0;
+}
+
+static const struct dev_pm_ops tegra_bb_pm_ops = {
+       .suspend_noirq = tegra_bb_suspend_noirq,
+       .resume_noirq = tegra_bb_resume_noirq,
+       .suspend = tegra_bb_driver_suspend,
+       .resume = tegra_bb_driver_resume,
+};
 #endif
 
 static struct platform_driver tegra_bb_power_driver = {
        .probe = tegra_bb_power_probe,
        .remove = tegra_bb_power_remove,
-#ifdef CONFIG_PM
-       .suspend = tegra_bb_power_suspend,
-       .resume = tegra_bb_power_resume,
-#endif
        .driver = {
                .name = "tegra_baseband_power",
+#ifdef CONFIG_PM
+               .pm   = &tegra_bb_pm_ops,
+#endif
        },
 };
 
index cdd6938..949aadf 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * drivers/misc/tegra-baseband/bb-power.h
  *
- * Copyright (C) 2011 NVIDIA Corporation
+ * Copyright (C) 2012 NVIDIA Corporation
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
  */
 
 enum tegra_bb_state {
-       BBSTATE_UNKNOWN,
        /* Baseband state L0 - Running */
-       BBSTATE_L0,
+       BBSTATE_L0 = 0,
+       /* Baseband state L02L2 - Running->Suspend */
+       BBSTATE_L02L2 = 1,
        /* Baseband state L2 - Suspended */
-       BBSTATE_L2,
+       BBSTATE_L2 = 2,
        /* Baseband state L3 - Suspended and detached */
-       BBSTATE_L3,
+       BBSTATE_L3 = 3,
+       /* Invalid baseband state */
+       BBSTATE_UNKNOWN = 0xFF,
 };
 
 enum tegra_bb_pwrstate {
        /* System power state - Entering suspend */
        PWRSTATE_L2L3,
+       /* System power state - Entering suspend, no irq */
+       PWRSTATE_L2L3_NOIRQ,
        /* System power state - Resuming from suspend */
        PWRSTATE_L3L0,
+       /* System power state - Resuming from suspend, no irq */
+       PWRSTATE_L3L0_NOIRQ,
+       /* Invalid system power state */
        PWRSTATE_INVALID,
 };
 
+enum tegra_bb_dlevel {
+       /* Debug level - Initialization */
+       DLEVEL_INIT = 0,
+       /* Debug level - Sysfs callbacks */
+       DLEVEL_SYS_CB = 1U << 0,
+       /* Debug level - PM */
+       DLEVEL_PM = 1U << 1,
+       /* Debug level - Misc */
+       DLEVEL_MISC = 1U << 2,
+       /* Debug level - Max */
+       DLEVEL_MAX = 1U << 3,
+};
+
 struct tegra_bb_gpio_data {
        /* Baseband gpio data */
        struct gpio data;
@@ -54,11 +75,12 @@ struct tegra_bb_gpio_irqdata {
 };
 
 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);
+typedef void* (*cb_init)(void *pdata);
+typedef void* (*cb_deinit)(void);
+typedef int (*cb_power)(int code);
+typedef int (*cb_attrib_access)(struct device *dev, int value);
+typedef int (*cb_usbnotify)(struct usb_device *udev, bool registered);
+typedef int (*cb_pmnotify)(unsigned long event);
 
 struct tegra_bb_power_gdata {
        struct tegra_bb_gpio_data *gpio;
@@ -74,8 +96,6 @@ struct tegra_bb_power_mdata {
        bool wake_capable;
        /* Baseband capability - Can it be auto/runtime suspended ? */
        bool autosuspend_ready;
-       /* Baseband callback after a successful registration */
-       modem_register_cb reg_cb;
 };
 
 struct tegra_bb_power_data {
@@ -84,16 +104,26 @@ struct tegra_bb_power_data {
 };
 
 struct tegra_bb_callback {
-       bb_init_cb init;
-       bb_deinit_cb deinit;
-       bb_power_cb power;
-       bb_attrib_cb attrib;
+       /* Init callback */
+       cb_init init;
+       /* Deinit callback */
+       cb_deinit deinit;
+       /* Powerstate transitions callback */
+       cb_power power;
+       /* Sysfs "load" callback */
+       cb_attrib_access load;
+       /* Sysfs "dlevel" callback */
+       cb_attrib_access dlevel;
+       /* USB notifier callback */
+       cb_usbnotify usbnotify;
+       /* PM notifier callback */
+       cb_pmnotify pmnotify;
        bool valid;
 };
 
-#ifdef CONFIG_TEGRA_BB_M7400
-extern void *m7400_get_cblist(void);
-#define M7400_CB m7400_get_cblist
+#ifdef CONFIG_TEGRA_BB_OEM1
+extern void *bb_oem1_get_cblist(void);
+#define OEM1_CB bb_oem1_get_cblist
 #else
-#define M7400_CB NULL
+#define OEM1_CB NULL
 #endif