diff options
author | Michael Hsu <mhsu@nvidia.com> | 2011-10-14 17:26:07 -0700 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2011-11-30 21:52:39 -0800 |
commit | 645f91c68b19172da28779e86c2d60045fc64705 (patch) | |
tree | bc570f012238e1578d528f004048a477ad361a92 /arch/arm/mach-tegra | |
parent | 7a6f2363adc93e15bc2b1f96a3b4db9d75a43552 (diff) |
arm: tegra: comms: GPIO changes for XMM modem ver 1130 or later
XMM modem version 1130 (or later) changes the GPIO power up
sequence. Add module variable to support pre-1130 and post-1130
modem versions.
BUG 828389
Reviewed-on: http://git-master/r/58240
(cherry picked from commit 0639c200face90d6dd0144acc7362c02909fa66c)
Change-Id: If3a3486741d72d1251deeca3759af6b39c2031f1
Reviewed-on: http://git-master/r/62750
Reviewed-by: Varun Colbert <vcolbert@nvidia.com>
Tested-by: Varun Colbert <vcolbert@nvidia.com>
Rebase-Id: Rf848b30671a8739c11a376011165db6165f0a259
Diffstat (limited to 'arch/arm/mach-tegra')
-rw-r--r-- | arch/arm/mach-tegra/baseband-xmm-power.c | 484 | ||||
-rw-r--r-- | arch/arm/mach-tegra/baseband-xmm-power.h | 50 | ||||
-rw-r--r-- | arch/arm/mach-tegra/baseband-xmm-power2.c | 540 |
3 files changed, 822 insertions, 252 deletions
diff --git a/arch/arm/mach-tegra/baseband-xmm-power.c b/arch/arm/mach-tegra/baseband-xmm-power.c index dc8f8191a95d..fc4e957c964e 100644 --- a/arch/arm/mach-tegra/baseband-xmm-power.c +++ b/arch/arm/mach-tegra/baseband-xmm-power.c @@ -22,6 +22,7 @@ #include <linux/gpio.h> #include <linux/interrupt.h> #include <linux/workqueue.h> +#include <linux/slab.h> #include <linux/delay.h> #include <linux/fs.h> #include <linux/uaccess.h> @@ -32,15 +33,34 @@ #include "gpio-names.h" #include "baseband-xmm-power.h" +#define VENDOR_ID 0x1519 +#define PRODUCT_ID 0x0020 + MODULE_LICENSE("GPL"); -unsigned long enum_delay_ms = 1000; +unsigned long modem_ver = XMM_MODEM_VER_1121; +EXPORT_SYMBOL(modem_ver); -module_param(enum_delay_ms, ulong, 0644); -MODULE_PARM_DESC(enum_delay_ms, "baseband xmm power" - " - delay in ms between modem on and enumeration"); +unsigned long modem_flash; +EXPORT_SYMBOL(modem_flash); + +unsigned long modem_pm = 1; +EXPORT_SYMBOL(modem_pm); -#define TEGRA_EHCI_DEVICE "/sys/devices/platform/tegra-ehci.1/ehci_power" +unsigned long enum_delay_ms = 1000; /* ignored if !modem_flash */ + +module_param(modem_ver, ulong, 0644); +MODULE_PARM_DESC(modem_ver, + "baseband xmm power - modem software version"); +module_param(modem_flash, ulong, 0644); +MODULE_PARM_DESC(modem_flash, + "baseband xmm power - modem flash (1 = flash, 0 = flashless)"); +module_param(modem_pm, ulong, 0644); +MODULE_PARM_DESC(modem_pm, + "baseband xmm power - modem power management (1 = pm, 0 = no pm)"); +module_param(enum_delay_ms, ulong, 0644); +MODULE_PARM_DESC(enum_delay_ms, + "baseband xmm power - delay in ms between modem on and enumeration"); /* Currently no baseband initiated suspend */ #define BB_INITIATED_L2_SUSPEND 0 @@ -73,6 +93,8 @@ static enum { IPC_AP_WAKE_H, } ipc_ap_wake_state; +enum baseband_xmm_powerstate_t baseband_xmm_powerstate; + static struct workqueue_struct *workqueue; static struct work_struct init1_work; static struct work_struct init2_work; @@ -81,59 +103,54 @@ static struct baseband_power_platform_data *baseband_power_driver_data; static bool register_hsic_device; static struct wake_lock wakelock; -/* static functions */ -static int baseband_xmm_power_on(struct platform_device *device); -static int baseband_xmm_power_off(struct platform_device *device); - -static ssize_t baseband_xmm_onoff(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - int power_onoff; - struct platform_device *device = to_platform_device(dev); - - pr_debug("%s\n", __func__); - - if (sscanf(buf, "%d", &power_onoff) != 1) - return -EINVAL; - - if (power_onoff == 0) - baseband_xmm_power_off(device); - else if (power_onoff == 1) - baseband_xmm_power_on(device); - return count; -} - -static DEVICE_ATTR(xmm_onoff, S_IRUSR | S_IWUSR | S_IRGRP, - NULL, baseband_xmm_onoff); - static int baseband_xmm_power_on(struct platform_device *device) { - int value; - struct baseband_power_platform_data *data - = (struct baseband_power_platform_data *) device->dev.platform_data; + struct baseband_power_platform_data *data; - pr_debug("%s{\n", __func__); + pr_debug("%s {\n", __func__); - /* check for platform data */ - if (!baseband_power_driver_data) + /* check for device / platform data */ + if (!device) { + pr_err("%s: !device\n", __func__); return -EINVAL; - - /* check if IPC_HSIC_ACTIVE low */ - value = gpio_get_value(baseband_power_driver_data-> - modem.xmm.ipc_hsic_active); - if (value != 0) { - pr_err("%s - expected IPC_HSIC_ACTIVE low!\n", __func__); + } + data = (struct baseband_power_platform_data *) + device->dev.platform_data; + if (!data) { + pr_err("%s: !data\n", __func__); return -EINVAL; } + /* turn on usb host controller */ + if (!modem_flash) { + /* if already registered usb host controller, then turn on + * using ehci_power file + */ + if (!register_hsic_device) { + mm_segment_t oldfs; + struct file *filp; + pr_debug("%s(%d) register usb host controller echo on\n", + __func__, __LINE__); + oldfs = get_fs(); + set_fs(KERNEL_DS); + filp = filp_open(TEGRA_EHCI_DEVICE, O_RDWR, 0); + if (IS_ERR(filp) || (filp == NULL)) { + pr_err("open ehci_power failed\n"); + } else { + filp->f_op->write(filp, "1", 1, &filp->f_pos); + filp_close(filp, NULL); + } + set_fs(oldfs); + } + /* else if usb host controller not registered yet, then turn + * on will occur later in this function (when usb host + * controller is registered for first time) + */ + } + /* reset the state machine */ baseband_xmm_powerstate = BBXMM_PS_INIT; - ipc_ap_wake_state = IPC_AP_WAKE_IRQ_READY; - - /* set IPC_HSIC_ACTIVE high */ - gpio_set_value(baseband_power_driver_data-> - modem.xmm.ipc_hsic_active, 1); + ipc_ap_wake_state = IPC_AP_WAKE_INIT2; /* wait 20 ms */ mdelay(20); @@ -146,23 +163,15 @@ static int baseband_xmm_power_on(struct platform_device *device) udelay(40); gpio_set_value(data->modem.xmm.bb_on, 0); - if (enum_delay_ms) - mdelay(enum_delay_ms); - - /* turn on usb host controller */ - { - mm_segment_t oldfs; - struct file *filp; - oldfs = get_fs(); - set_fs(KERNEL_DS); - filp = filp_open(TEGRA_EHCI_DEVICE, O_RDWR, 0); - if (!filp) { - pr_err("open ehci_power failed\n"); - } else { - filp->f_op->write(filp, "1", 1, &filp->f_pos); - filp_close(filp, NULL); + /* register usb host controller */ + if (!modem_flash) { + /* register usb host controller only once */ + if (register_hsic_device) { + pr_debug("%s(%d) register usb host controller\n", + __func__, __LINE__); + platform_device_register(data->modem.xmm.hsic_device); + register_hsic_device = false; } - set_fs(oldfs); } pr_debug("%s }\n", __func__); @@ -172,10 +181,21 @@ static int baseband_xmm_power_on(struct platform_device *device) static int baseband_xmm_power_off(struct platform_device *device) { - struct baseband_power_platform_data *data - = (struct baseband_power_platform_data *) device->dev.platform_data; + struct baseband_power_platform_data *data; - pr_debug("%s\n", __func__); + pr_debug("%s {\n", __func__); + + /* check for device / platform data */ + if (!device) { + pr_err("%s: !device\n", __func__); + return -EINVAL; + } + data = (struct baseband_power_platform_data *) + device->dev.platform_data; + if (!data) { + pr_err("%s: !data\n", __func__); + return -EINVAL; + } /* turn off usb host controller */ { @@ -184,7 +204,7 @@ static int baseband_xmm_power_off(struct platform_device *device) oldfs = get_fs(); set_fs(KERNEL_DS); filp = filp_open(TEGRA_EHCI_DEVICE, O_RDWR, 0); - if (!filp) { + if (IS_ERR(filp) || (filp == NULL)) { pr_err("open ehci_power failed\n"); } else { filp->f_op->write(filp, "0", 1, &filp->f_pos); @@ -196,6 +216,7 @@ static int baseband_xmm_power_off(struct platform_device *device) /* set IPC_HSIC_ACTIVE low */ gpio_set_value(baseband_power_driver_data-> modem.xmm.ipc_hsic_active, 0); + /* wait 20 ms */ mdelay(20); @@ -203,24 +224,48 @@ static int baseband_xmm_power_off(struct platform_device *device) gpio_set_value(data->modem.xmm.bb_rst, 0); mdelay(1); + pr_debug("%s }\n", __func__); + return 0; } +static ssize_t baseband_xmm_onoff(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int power_onoff; + struct platform_device *device = to_platform_device(dev); + + pr_debug("%s\n", __func__); + + if (sscanf(buf, "%d", &power_onoff) != 1) + return -EINVAL; + + if (power_onoff == 0) + baseband_xmm_power_off(device); + else if (power_onoff == 1) + baseband_xmm_power_on(device); + return count; +} + +static DEVICE_ATTR(xmm_onoff, S_IRUSR | S_IWUSR | S_IRGRP, + NULL, baseband_xmm_onoff); + void baseband_xmm_set_power_status(unsigned int status) { switch (status) { case BBXMM_PS_L0: wake_lock(&wakelock); - break; + break; case BBXMM_PS_L2: wake_unlock(&wakelock); - break; + break; case BBXMM_PS_L2TOL0: /* do this only from L2 state */ if (baseband_xmm_powerstate == BBXMM_PS_L2) queue_work(workqueue, &init3_work); default: - break; + break; } baseband_xmm_powerstate = status; pr_debug("BB XMM POWER STATE = %d\n", status); @@ -228,7 +273,8 @@ void baseband_xmm_set_power_status(unsigned int status) EXPORT_SYMBOL_GPL(baseband_xmm_set_power_status); #if BB_INITIATED_L2_SUSPEND -static irqreturn_t ipc_hsic_sus_req_irq(int irq, void *dev_id) +static irqreturn_t baseband_xmm_power_ipc_hsic_sus_req_irq + (int irq, void *dev_id) { int value; @@ -250,10 +296,9 @@ static irqreturn_t ipc_hsic_sus_req_irq(int irq, void *dev_id) return IRQ_HANDLED; } - #endif -static irqreturn_t ipc_ap_wake_irq(int irq, void *dev_id) +irqreturn_t baseband_xmm_power_ipc_ap_wake_irq(int irq, void *dev_id) { int value; @@ -265,24 +310,28 @@ static irqreturn_t ipc_ap_wake_irq(int irq, void *dev_id) value = gpio_get_value(baseband_power_driver_data-> modem.xmm.ipc_ap_wake); if (!value) { - pr_debug("%s - IPC_AP_WAKE_INIT1 - got falling edge\n", + pr_debug("%s - IPC_AP_WAKE_INIT1" + " - got falling edge\n", __func__); /* go to IPC_AP_WAKE_INIT1 state */ ipc_ap_wake_state = IPC_AP_WAKE_INIT1; /* queue work */ queue_work(workqueue, &init1_work); } else { - pr_debug("%s - IPC_AP_WAKE_INIT1 - wait for falling edge\n", + pr_debug("%s - IPC_AP_WAKE_INIT1" + " - wait for falling edge\n", __func__); } } else if (ipc_ap_wake_state == IPC_AP_WAKE_INIT1) { value = gpio_get_value(baseband_power_driver_data-> modem.xmm.ipc_ap_wake); if (!value) { - pr_debug("%s - IPC_AP_WAKE_INIT2 - wait for rising edge\n", + pr_debug("%s - IPC_AP_WAKE_INIT2" + " - wait for rising edge\n", __func__); } else { - pr_debug("%s - IPC_AP_WAKE_INIT2 - got rising edge\n", + pr_debug("%s - IPC_AP_WAKE_INIT2" + " - got rising edge\n", __func__); /* go to IPC_AP_WAKE_INIT2 state */ ipc_ap_wake_state = IPC_AP_WAKE_INIT2; @@ -294,21 +343,32 @@ static irqreturn_t ipc_ap_wake_irq(int irq, void *dev_id) modem.xmm.ipc_ap_wake); if (!value) { pr_debug("%s - falling\n", __func__); - pr_debug("gpio host wakeup done <-\n"); - /* Set the slave wakeup request */ - gpio_set_value(baseband_power_driver_data-> - modem.xmm.ipc_bb_wake, 0); - pr_debug("gpio slave wakeup done ->\n"); + /* save gpio state */ ipc_ap_wake_state = IPC_AP_WAKE_L; - baseband_xmm_set_power_status(BBXMM_PS_L0); } else { pr_debug("%s - rising\n", __func__); + /* [ver >= 1130] gpio protocol rising edge */ + if (modem_ver >= XMM_MODEM_VER_1130) { + pr_debug("gpio host wakeup done <-\n"); + value = gpio_get_value + (baseband_power_driver_data-> + modem.xmm.ipc_bb_wake); + if (value) { + /* Clear the slave wakeup request */ + gpio_set_value + (baseband_power_driver_data-> + modem.xmm.ipc_bb_wake, 0); + pr_debug("gpio slave wakeup done ->\n"); + } + } + /* save gpio state */ ipc_ap_wake_state = IPC_AP_WAKE_H; } } return IRQ_HANDLED; } +EXPORT_SYMBOL(baseband_xmm_power_ipc_ap_wake_irq); static void baseband_xmm_power_init1_work(struct work_struct *work) { @@ -368,49 +428,162 @@ static void baseband_xmm_power_init3_work(struct work_struct *work) modem.xmm.ipc_bb_wake, 1); } +static void baseband_xmm_power_reset_on(void) +{ + /* reset / power on sequence */ + mdelay(40); + gpio_set_value(baseband_power_driver_data->modem.xmm.bb_rst, 1); + mdelay(1); + gpio_set_value(baseband_power_driver_data->modem.xmm.bb_on, 1); + udelay(40); + gpio_set_value(baseband_power_driver_data->modem.xmm.bb_on, 0); +} + +static struct baseband_xmm_power_work_t *baseband_xmm_power_work; + +static void baseband_xmm_power_work_func(struct work_struct *work) +{ + struct baseband_xmm_power_work_t *bbxmm_work + = (struct baseband_xmm_power_work_t *) work; + + pr_debug("%s\n", __func__); + + switch (bbxmm_work->state) { + case BBXMM_WORK_UNINIT: + pr_debug("BBXMM_WORK_UNINIT\n"); + break; + case BBXMM_WORK_INIT: + pr_debug("BBXMM_WORK_INIT\n"); + /* go to next state */ + bbxmm_work->state = (modem_flash && !modem_pm) + ? BBXMM_WORK_INIT_FLASH_STEP1 + : (modem_flash && modem_pm) + ? BBXMM_WORK_INIT_FLASH_PM_STEP1 + : (!modem_flash && modem_pm) + ? BBXMM_WORK_INIT_FLASHLESS_PM_STEP1 + : BBXMM_WORK_UNINIT; + queue_work(workqueue, work); + break; + case BBXMM_WORK_INIT_FLASH_STEP1: + pr_debug("BBXMM_WORK_INIT_FLASH_STEP1\n"); + /* register usb host controller */ + platform_device_register(baseband_power_driver_data->modem + .xmm.hsic_device); + break; + case BBXMM_WORK_INIT_FLASH_PM_STEP1: + pr_debug("BBXMM_WORK_INIT_FLASH_PM_STEP1\n"); + /* [modem ver >= 1130] start with IPC_HSIC_ACTIVE low */ + if (modem_ver >= XMM_MODEM_VER_1130) { + pr_debug("%s: ver > 1130:" + " ipc_hsic_active -> 0\n", __func__); + gpio_set_value(baseband_power_driver_data-> + modem.xmm.ipc_hsic_active, 0); + } + /* reset / power on sequence */ + baseband_xmm_power_reset_on(); + /* optional delay + * 0 = flashless + * ==> causes next step to enumerate modem boot rom + * (058b / 0041) + * some delay > boot rom timeout + * ==> causes next step to enumerate modem software + * (1519 / 0020) + * (requires modem to be flash version, not flashless + * version) + */ + if (enum_delay_ms) + mdelay(enum_delay_ms); + /* register usb host controller */ + platform_device_register(baseband_power_driver_data->modem + .xmm.hsic_device); + /* go to next state */ + bbxmm_work->state = (modem_ver < XMM_MODEM_VER_1130) + ? BBXMM_WORK_INIT_FLASH_PM_VER_LT_1130_STEP1 + : BBXMM_WORK_INIT_FLASH_PM_VER_GE_1130_STEP1; + queue_work(workqueue, work); + break; + case BBXMM_WORK_INIT_FLASH_PM_VER_LT_1130_STEP1: + pr_debug("BBXMM_WORK_INIT_FLASH_PM_VER_LT_1130_STEP1\n"); + break; + case BBXMM_WORK_INIT_FLASH_PM_VER_GE_1130_STEP1: + pr_debug("BBXMM_WORK_INIT_FLASH_PM_VER_GE_1130_STEP1\n"); + break; + case BBXMM_WORK_INIT_FLASHLESS_PM_STEP1: + pr_debug("BBXMM_WORK_INIT_FLASHLESS_PM_STEP1\n"); + /* go to next state */ + bbxmm_work->state = (modem_ver < XMM_MODEM_VER_1130) + ? BBXMM_WORK_INIT_FLASHLESS_PM_VER_LT_1130_WAIT_IRQ + : BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP1; + queue_work(workqueue, work); + break; + case BBXMM_WORK_INIT_FLASHLESS_PM_VER_LT_1130_STEP1: + pr_debug("BBXMM_WORK_INIT_FLASHLESS_PM_VER_LT_1130_STEP1\n"); + break; + case BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP1: + pr_debug("BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP1\n"); + break; + default: + break; + } + +} + static int baseband_xmm_power_driver_probe(struct platform_device *device) { - struct device *dev = &device->dev; struct baseband_power_platform_data *data - = (struct baseband_power_platform_data *) device->dev.platform_data; + = (struct baseband_power_platform_data *) + device->dev.platform_data; + struct device *dev = &device->dev; int err; pr_debug("%s\n", __func__); - register_hsic_device = true; - baseband_xmm_powerstate = BBXMM_PS_UNINIT; + /* check for platform data */ + if (!data) + return -ENODEV; + /* check if supported modem */ if (data->baseband_type != BASEBAND_XMM) { pr_err("unsuppported modem\n"); return -ENODEV; } + /* save platform data */ + baseband_power_driver_data = data; + + /* create device file */ err = device_create_file(dev, &dev_attr_xmm_onoff); if (err < 0) { pr_err("%s - device_create_file failed\n", __func__); return -ENODEV; } - /* save platform data */ - baseband_power_driver_data = data; + /* init wake lock */ + wake_lock_init(&wakelock, WAKE_LOCK_SUSPEND, "baseband_xmm_power"); /* request baseband gpio(s) */ - tegra_baseband_gpios[0].gpio = data->modem.xmm.bb_rst; - tegra_baseband_gpios[1].gpio = data->modem.xmm.bb_on; - tegra_baseband_gpios[2].gpio = data->modem.xmm.ipc_bb_wake; - tegra_baseband_gpios[3].gpio = data->modem.xmm.ipc_ap_wake; - tegra_baseband_gpios[4].gpio = data->modem.xmm.ipc_hsic_active; - tegra_baseband_gpios[5].gpio = data->modem.xmm.ipc_hsic_sus_req; + tegra_baseband_gpios[0].gpio = baseband_power_driver_data + ->modem.xmm.bb_rst; + tegra_baseband_gpios[1].gpio = baseband_power_driver_data + ->modem.xmm.bb_on; + tegra_baseband_gpios[2].gpio = baseband_power_driver_data + ->modem.xmm.ipc_bb_wake; + tegra_baseband_gpios[3].gpio = baseband_power_driver_data + ->modem.xmm.ipc_ap_wake; + tegra_baseband_gpios[4].gpio = baseband_power_driver_data + ->modem.xmm.ipc_hsic_active; + tegra_baseband_gpios[5].gpio = baseband_power_driver_data + ->modem.xmm.ipc_hsic_sus_req; err = gpio_request_array(tegra_baseband_gpios, ARRAY_SIZE(tegra_baseband_gpios)); if (err < 0) { pr_err("%s - request gpio(s) failed\n", __func__); - return err; + return -ENODEV; } /* request baseband irq(s) */ #if BB_INITIATED_L2_SUSPEND - if (enum_delay_ms) { + if (modem_pm) { ipc_hsic_sus_req_state = IPC_HSIC_SUS_REQ_UNINIT; err = request_irq(gpio_to_irq(data->modem.xmm.ipc_hsic_sus_req), ipc_hsic_sus_req_irq, @@ -425,10 +598,11 @@ static int baseband_xmm_power_driver_probe(struct platform_device *device) ipc_hsic_sus_req_state = IPC_HSIC_SUS_REQ_IRQ_READY; } #endif - if (enum_delay_ms) { + if (modem_flash && modem_pm) { + pr_info("%s: request_irq IPC_AP_WAKE_IRQ\n", __func__); ipc_ap_wake_state = IPC_AP_WAKE_UNINIT; err = request_irq(gpio_to_irq(data->modem.xmm.ipc_ap_wake), - ipc_ap_wake_irq, + baseband_xmm_power_ipc_ap_wake_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "IPC_AP_WAKE_IRQ", NULL); @@ -438,6 +612,11 @@ static int baseband_xmm_power_driver_probe(struct platform_device *device) return err; } ipc_ap_wake_state = IPC_AP_WAKE_IRQ_READY; + if (modem_ver >= XMM_MODEM_VER_1130) { + pr_debug("%s: ver > 1130: AP_WAKE_INIT1\n", __func__); + /* ver 1130 or later starts in INIT1 state */ + ipc_ap_wake_state = IPC_AP_WAKE_INIT1; + } } /* init work queue */ @@ -447,52 +626,56 @@ static int baseband_xmm_power_driver_probe(struct platform_device *device) pr_err("cannot create workqueue\n"); return -1; } + baseband_xmm_power_work = (struct baseband_xmm_power_work_t *) + kmalloc(sizeof(struct baseband_xmm_power_work_t), GFP_KERNEL); + if (!baseband_xmm_power_work) { + pr_err("cannot allocate baseband_xmm_power_work\n"); + return -1; + } + INIT_WORK((struct work_struct *) baseband_xmm_power_work, + baseband_xmm_power_work_func); + baseband_xmm_power_work->state = BBXMM_WORK_INIT; + queue_work(workqueue, + (struct work_struct *) baseband_xmm_power_work); - wake_lock_init(&wakelock, WAKE_LOCK_SUSPEND, "baseband_xmm_power"); - - baseband_xmm_powerstate = BBXMM_PS_INIT; + /* init work objects */ INIT_WORK(&init1_work, baseband_xmm_power_init1_work); INIT_WORK(&init2_work, baseband_xmm_power_init2_work); INIT_WORK(&init3_work, baseband_xmm_power_init3_work); - /* reset / power on sequence */ - mdelay(40); - gpio_set_value(data->modem.xmm.bb_rst, 1); - mdelay(1); - gpio_set_value(data->modem.xmm.bb_on, 1); - udelay(40); - gpio_set_value(data->modem.xmm.bb_on, 0); - - /* optional delay - * 0 = flashless - * ==> causes next step to enumerate modem boot rom (058b / 0041) - * some delay > boot rom timeout - * ==> causes next step to enumerate modem software (1519 / 0020) - * (requires modem to be flash version, not flashless version) - */ - if (enum_delay_ms) - mdelay(enum_delay_ms); - - /* register usb host controller */ - if (!enum_delay_ms) - platform_device_register(data->modem.xmm.hsic_device); + /* init state variables */ + register_hsic_device = true; + baseband_xmm_powerstate = BBXMM_PS_UNINIT; return 0; } static int baseband_xmm_power_driver_remove(struct platform_device *device) { - struct device *dev = &device->dev; struct baseband_power_platform_data *data - = (struct baseband_power_platform_data *) device->dev.platform_data; + = (struct baseband_power_platform_data *) + device->dev.platform_data; + struct device *dev = &device->dev; pr_debug("%s\n", __func__); + /* check for platform data */ + if (!data) + return 0; + + /* free work structure */ + kfree(baseband_xmm_power_work); + baseband_xmm_power_work = (struct baseband_xmm_power_work_t *) 0; + /* free baseband irq(s) */ - if (enum_delay_ms) { - free_irq(gpio_to_irq(data->modem.xmm.ipc_ap_wake), NULL); + if (modem_flash && modem_pm) { + free_irq(gpio_to_irq(baseband_power_driver_data + ->modem.xmm.ipc_ap_wake), NULL); + } + if (modem_pm) { #if BB_INITIATED_L2_SUSPEND - free_irq(gpio_to_irq(data->modem.xmm.ipc_hsic_sus_req), NULL); + free_irq(gpio_to_irq(baseband_power_driver_data + ->modem.xmm.ipc_hsic_sus_req), NULL); #endif } @@ -500,9 +683,12 @@ static int baseband_xmm_power_driver_remove(struct platform_device *device) gpio_free_array(tegra_baseband_gpios, ARRAY_SIZE(tegra_baseband_gpios)); - device_remove_file(dev, &dev_attr_xmm_onoff); + /* destroy wake lock */ wake_lock_destroy(&wakelock); + /* delete device file */ + device_remove_file(dev, &dev_attr_xmm_onoff); + /* unregister usb host controller */ platform_device_unregister(baseband_power_driver_data-> modem.xmm.hsic_device); @@ -513,54 +699,44 @@ static int baseband_xmm_power_driver_remove(struct platform_device *device) static int baseband_xmm_power_driver_suspend(struct platform_device *device, pm_message_t state) { - struct baseband_power_platform_data *data - = (struct baseband_power_platform_data *) device->dev.platform_data; - pr_debug("%s\n", __func__); - - /* Indiate host active low to CP*/ - gpio_set_value(data->modem.xmm.ipc_hsic_active, 0); - pr_debug("gpio host active low->\n"); - - baseband_xmm_set_power_status(BBXMM_PS_L3); - return 0; } static int baseband_xmm_power_driver_resume(struct platform_device *device) { struct baseband_power_platform_data *data - = (struct baseband_power_platform_data *) device->dev.platform_data; + = (struct baseband_power_platform_data *) + device->dev.platform_data; int value; int delay = 10000; /* maxmum delay in msec */ pr_debug("%s\n", __func__); + /* check for platform data */ + if (!baseband_power_driver_data) + return 0; + + baseband_xmm_set_power_status(BBXMM_PS_L3TOL0); + value = gpio_get_value(data->modem.xmm.ipc_ap_wake); - if (!value) { - pr_info("AP Initiated L3 -> L0\n"); + if (value) { + pr_info("AP L3 -> L0\n"); /* wake bb */ gpio_set_value(data->modem.xmm.ipc_bb_wake, 1); - pr_debug("waiting for host wakeup\n"); + pr_debug("waiting for host wakeup...\n"); do { mdelay(1); value = gpio_get_value(data->modem.xmm.ipc_ap_wake); delay--; - } while ((!value) && (delay)); + } while ((value) && (delay)); if (delay) - pr_debug("gpio host wakeup high <-\n"); - else - pr_err("%s host wakeup not happened\n", __func__); + pr_debug("gpio host wakeup low <-\n"); } else { - pr_info("CP Initiated L3 -> L0\n"); + pr_info("CP L3 -> L0\n"); } - /* signal bb to resume hsic */ - gpio_set_value(data->modem.xmm.ipc_hsic_active, 1); - - baseband_xmm_set_power_status(BBXMM_PS_L3TOL0); - return 0; } diff --git a/arch/arm/mach-tegra/baseband-xmm-power.h b/arch/arm/mach-tegra/baseband-xmm-power.h index 8025c9a956ce..53e71bf7b32a 100644 --- a/arch/arm/mach-tegra/baseband-xmm-power.h +++ b/arch/arm/mach-tegra/baseband-xmm-power.h @@ -14,9 +14,24 @@ * */ +#ifndef BASEBAND_XMM_POWER_H +#define BASREBAND_XMM_POWER_H + #include <linux/pm.h> #include <linux/suspend.h> +#define TEGRA_EHCI_DEVICE "/sys/devices/platform/tegra-ehci.1/ehci_power" + +#define XMM_MODEM_VER_1121 0x1121 +#define XMM_MODEM_VER_1130 0x1130 + +/* shared between baseband-xmm-* modules so they can agree on same + * modem configuration + */ +extern unsigned long modem_ver; +extern unsigned long modem_flash; +extern unsigned long modem_pm; + enum baseband_type { BASEBAND_XMM, }; @@ -45,7 +60,34 @@ struct baseband_power_platform_data { } modem; }; -static enum { +enum baseband_xmm_power_work_state_t { + BBXMM_WORK_UNINIT, + BBXMM_WORK_INIT, + /* initialize flash modem */ + BBXMM_WORK_INIT_FLASH_STEP1, + /* initialize flash (with power management support) modem */ + BBXMM_WORK_INIT_FLASH_PM_STEP1, + BBXMM_WORK_INIT_FLASH_PM_VER_LT_1130_STEP1, + BBXMM_WORK_INIT_FLASH_PM_VER_GE_1130_STEP1, + /* initialize flashless (with power management support) modem */ + BBXMM_WORK_INIT_FLASHLESS_PM_STEP1, + BBXMM_WORK_INIT_FLASHLESS_PM_VER_LT_1130_WAIT_IRQ, + BBXMM_WORK_INIT_FLASHLESS_PM_VER_LT_1130_STEP1, + BBXMM_WORK_INIT_FLASHLESS_PM_VER_LT_1130_STEP2, + BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP1, + BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP2, + BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP3, + BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP4, +}; + +struct baseband_xmm_power_work_t { + /* work structure must be first structure member */ + struct work_struct work; + /* xmm modem state */ + enum baseband_xmm_power_work_state_t state; +}; + +enum baseband_xmm_powerstate_t { BBXMM_PS_UNINIT = 0, BBXMM_PS_INIT = 1, BBXMM_PS_L0 = 2, @@ -56,6 +98,10 @@ static enum { BBXMM_PS_L3 = 7, BBXMM_PS_L3TOL0 = 8, BBXMM_PS_LAST = -1, -} baseband_xmm_powerstate; +}; + +irqreturn_t baseband_xmm_power_ipc_ap_wake_irq(int irq, void *dev_id); void baseband_xmm_set_power_status(unsigned int status); + +#endif /* BASREBAND_XMM_POWER_H */ diff --git a/arch/arm/mach-tegra/baseband-xmm-power2.c b/arch/arm/mach-tegra/baseband-xmm-power2.c index 9601ed49e6a1..acb2ac344266 100644 --- a/arch/arm/mach-tegra/baseband-xmm-power2.c +++ b/arch/arm/mach-tegra/baseband-xmm-power2.c @@ -22,6 +22,7 @@ #include <linux/gpio.h> #include <linux/interrupt.h> #include <linux/workqueue.h> +#include <linux/slab.h> #include <linux/delay.h> #include <linux/fs.h> #include <linux/uaccess.h> @@ -32,15 +33,24 @@ MODULE_LICENSE("GPL"); -static struct baseband_power_platform_data *baseband_power2_driver_data; +static unsigned long XYZ = 500 * 1000000 + 30 * 1000 + 50; + +module_param(modem_ver, ulong, 0644); +MODULE_PARM_DESC(modem_ver, + "baseband xmm power2 - modem software version"); +module_param(modem_flash, ulong, 0644); +MODULE_PARM_DESC(modem_flash, + "baseband xmm power2 - modem flash (1 = flash, 0 = flashless)"); +module_param(modem_pm, ulong, 0644); +MODULE_PARM_DESC(modem_pm, + "baseband xmm power2 - modem power management (1 = pm, 0 = no pm)"); +module_param(XYZ, ulong, 0644); +MODULE_PARM_DESC(XYZ, + "baseband xmm power2 - timing parameters X/Y/Z delay in ms"); -static enum { - IPC_HSIC_SUS_REQ_UNINIT, - IPC_HSIC_SUS_REQ_IRQ_READY, - IPC_HSIC_SUS_REQ_INIT, - IPC_HSIC_SUS_REQ_L, - IPC_HSIC_SUS_REQ_H, -} ipc_hsic_sus_req_state; +static struct baseband_power_platform_data *baseband_power2_driver_data; +static struct workqueue_struct *workqueue; +static struct baseband_xmm_power_work_t *baseband_xmm_power2_work; static enum { IPC_AP_WAKE_UNINIT, @@ -51,11 +61,8 @@ static enum { IPC_AP_WAKE_H, } ipc_ap_wake_state; -static struct workqueue_struct *workqueue; -static struct work_struct init1_work; -static struct work_struct init2_work; - -static irqreturn_t ipc_hsic_sus_req_irq(int irq, void *dev_id) +static irqreturn_t baseband_xmm_power2_ver_lt_1130_ipc_ap_wake_irq2 + (int irq, void *dev_id) { int value; @@ -65,25 +72,65 @@ static irqreturn_t ipc_hsic_sus_req_irq(int irq, void *dev_id) if (!baseband_power2_driver_data) return IRQ_HANDLED; - /* IPC_HSIC_SUS_REQ state machine */ - if (ipc_hsic_sus_req_state < IPC_HSIC_SUS_REQ_IRQ_READY) { + /* IPC_AP_WAKE state machine */ + if (ipc_ap_wake_state < IPC_AP_WAKE_IRQ_READY) { pr_err("%s - spurious irq\n", __func__); + } else if (ipc_ap_wake_state == IPC_AP_WAKE_IRQ_READY) { + value = gpio_get_value(baseband_power2_driver_data-> + modem.xmm.ipc_ap_wake); + if (!value) { + pr_debug("%s - IPC_AP_WAKE_INIT1" + " - got falling edge\n", + __func__); + /* go to IPC_AP_WAKE_INIT1 state */ + ipc_ap_wake_state = IPC_AP_WAKE_INIT1; + /* queue work */ + baseband_xmm_power2_work->state = + BBXMM_WORK_INIT_FLASHLESS_PM_VER_LT_1130_STEP1; + queue_work(workqueue, (struct work_struct *) + baseband_xmm_power2_work); + } else { + pr_debug("%s - IPC_AP_WAKE_INIT1" + " - wait for falling edge\n", + __func__); + } + } else if (ipc_ap_wake_state == IPC_AP_WAKE_INIT1) { + value = gpio_get_value(baseband_power2_driver_data-> + modem.xmm.ipc_ap_wake); + if (!value) { + pr_debug("%s - IPC_AP_WAKE_INIT2" + " - wait for rising edge\n", + __func__); + } else { + pr_debug("%s - IPC_AP_WAKE_INIT2" + " - got rising edge\n", + __func__); + /* go to IPC_AP_WAKE_INIT2 state */ + ipc_ap_wake_state = IPC_AP_WAKE_INIT2; + /* queue work */ + baseband_xmm_power2_work->state = + BBXMM_WORK_INIT_FLASHLESS_PM_VER_LT_1130_STEP2; + queue_work(workqueue, (struct work_struct *) + baseband_xmm_power2_work); + } } else { value = gpio_get_value(baseband_power2_driver_data-> - modem.xmm.ipc_hsic_sus_req); + modem.xmm.ipc_ap_wake); if (!value) { pr_debug("%s - falling\n", __func__); - ipc_hsic_sus_req_state = IPC_HSIC_SUS_REQ_L; + ipc_ap_wake_state = IPC_AP_WAKE_L; } else { pr_debug("%s - rising\n", __func__); - ipc_hsic_sus_req_state = IPC_HSIC_SUS_REQ_H; + ipc_ap_wake_state = IPC_AP_WAKE_H; } + return baseband_xmm_power_ipc_ap_wake_irq(irq, dev_id); } return IRQ_HANDLED; } -static irqreturn_t ipc_ap_wake_irq(int irq, void *dev_id) +static irqreturn_t baseband_xmm_power2_ver_ge_1130_ipc_ap_wake_irq2 + (int irq, void *dev_id) { int value; @@ -100,29 +147,20 @@ static irqreturn_t ipc_ap_wake_irq(int irq, void *dev_id) value = gpio_get_value(baseband_power2_driver_data-> modem.xmm.ipc_ap_wake); if (!value) { - pr_debug("%s - IPC_AP_WAKE_INIT1 - got falling edge\n", - __func__); - /* go to IPC_AP_WAKE_INIT1 state */ - ipc_ap_wake_state = IPC_AP_WAKE_INIT1; - /* queue work */ - queue_work(workqueue, &init1_work); - } else { - pr_debug("%s - IPC_AP_WAKE_INIT1 - wait for falling edge\n", - __func__); - } - } else if (ipc_ap_wake_state == IPC_AP_WAKE_INIT1) { - value = gpio_get_value(baseband_power2_driver_data-> - modem.xmm.ipc_ap_wake); - if (!value) { - pr_debug("%s - IPC_AP_WAKE_INIT2 - wait for rising edge\n", - __func__); - } else { - pr_debug("%s - IPC_AP_WAKE_INIT2 - got rising edge\n", + pr_debug("%s - IPC_AP_WAKE_INIT1" + " - got falling edge\n", __func__); /* go to IPC_AP_WAKE_INIT2 state */ ipc_ap_wake_state = IPC_AP_WAKE_INIT2; /* queue work */ - queue_work(workqueue, &init2_work); + baseband_xmm_power2_work->state = + BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP2; + queue_work(workqueue, (struct work_struct *) + baseband_xmm_power2_work); + } else { + pr_debug("%s - IPC_AP_WAKE_INIT1" + " - wait for falling edge\n", + __func__); } } else { value = gpio_get_value(baseband_power2_driver_data-> @@ -134,12 +172,14 @@ static irqreturn_t ipc_ap_wake_irq(int irq, void *dev_id) pr_debug("%s - rising\n", __func__); ipc_ap_wake_state = IPC_AP_WAKE_H; } + return baseband_xmm_power_ipc_ap_wake_irq(irq, dev_id); } return IRQ_HANDLED; } -static void baseband_xmm_power2_init1_work(struct work_struct *work) +static void baseband_xmm_power2_flashless_pm_ver_lt_1130_step1 + (struct work_struct *work) { int value; @@ -167,7 +207,8 @@ static void baseband_xmm_power2_init1_work(struct work_struct *work) pr_debug("%s }\n", __func__); } -static void baseband_xmm_power2_init2_work(struct work_struct *work) +static void baseband_xmm_power2_flashless_pm_ver_lt_1130_step2 + (struct work_struct *work) { int value; @@ -194,8 +235,8 @@ static void baseband_xmm_power2_init2_work(struct work_struct *work) struct file *filp; oldfs = get_fs(); set_fs(KERNEL_DS); - filp = filp_open("/sys/devices/platform/tegra-ehci.1/ehci_power", O_RDWR, 0); - if (!filp) { + filp = filp_open(TEGRA_EHCI_DEVICE, O_RDWR, 0); + if (IS_ERR(filp) || (filp == NULL)) { pr_err("open ehci_power failed\n"); } else { filp->f_op->write(filp, "1", 1, &filp->f_pos); @@ -225,69 +266,381 @@ static void baseband_xmm_power2_init2_work(struct work_struct *work) pr_debug("%s }\n", __func__); } +static void baseband_xmm_power2_flashless_pm_ver_ge_1130_step1 + (struct work_struct *work) +{ + int X = XYZ / 1000000; + int Y = XYZ / 1000 - X * 1000; + int Z = XYZ % 1000; + + pr_info("%s {\n", __func__); + + pr_info("XYZ=%ld X=%d Y=%d Z=%d\n", XYZ, X, Y, Z); + + /* check for platform data */ + if (!baseband_power2_driver_data) + return; + + /* turn off usb host controller */ + { + mm_segment_t oldfs; + struct file *filp; + oldfs = get_fs(); + set_fs(KERNEL_DS); + filp = filp_open(TEGRA_EHCI_DEVICE, O_RDWR, 0); + if (IS_ERR(filp) || (filp == NULL)) { + pr_err("open ehci_power failed\n"); + } else { + filp->f_op->write(filp, "0", 1, &filp->f_pos); + filp_close(filp, NULL); + } + set_fs(oldfs); + } + + /* wait X ms */ + mdelay(X); + + /* set IPC_HSIC_ACTIVE low */ + gpio_set_value(baseband_power2_driver_data-> + modem.xmm.ipc_hsic_active, 0); + + pr_info("%s }\n", __func__); +} + +static void baseband_xmm_power2_flashless_pm_ver_ge_1130_step2 + (struct work_struct *work) +{ + int X = XYZ / 1000000; + int Y = XYZ / 1000 - X * 1000; + int Z = XYZ % 1000; + + pr_info("%s {\n", __func__); + + pr_info("XYZ=%ld X=%d Y=%d Z=%d\n", XYZ, X, Y, Z); + + /* check for platform data */ + if (!baseband_power2_driver_data) + return; + + /* wait Y ms */ + mdelay(Y); + + /* turn on usb host controller */ + { + mm_segment_t oldfs; + struct file *filp; + oldfs = get_fs(); + set_fs(KERNEL_DS); + filp = filp_open(TEGRA_EHCI_DEVICE, O_RDWR, 0); + if (IS_ERR(filp) || (filp == NULL)) { + pr_err("open ehci_power failed\n"); + } else { + filp->f_op->write(filp, "1", 1, &filp->f_pos); + filp_close(filp, NULL); + } + set_fs(oldfs); + } + + /* wait Z ms */ + mdelay(Z); + + /* set IPC_HSIC_ACTIVE high */ + gpio_set_value(baseband_power2_driver_data-> + modem.xmm.ipc_hsic_active, 1); + + /* queue work function to check if enumeration succeeded */ + baseband_xmm_power2_work->state = + BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP3; + queue_work(workqueue, (struct work_struct *) + baseband_xmm_power2_work); + + pr_info("%s }\n", __func__); +} + +static void baseband_xmm_power2_flashless_pm_ver_ge_1130_step3 + (struct work_struct *work) +{ + int X = XYZ / 1000000; + int Y = XYZ / 1000 - X * 1000; + int Z = XYZ % 1000; + int enum_success = 0; + + pr_info("%s {\n", __func__); + + pr_info("XYZ=%ld X=%d Y=%d Z=%d\n", XYZ, X, Y, Z); + + /* check for platform data */ + if (!baseband_power2_driver_data) + return; + + /* wait 500 ms */ + mdelay(500); + + /* check if enumeration succeeded */ + { + mm_segment_t oldfs; + struct file *filp; + oldfs = get_fs(); + set_fs(KERNEL_DS); + filp = filp_open("/sys/bus/usb/devices/usb2/2-1/manufacturer", + O_RDONLY, 0); + if (IS_ERR(filp) || (filp == NULL)) { + pr_err("open /sys/bus/usb/devices" + "/usb2/2-1/manufacturer failed %ld\n", + PTR_ERR(filp)); + } else { + filp_close(filp, NULL); + enum_success = 1; + } + set_fs(oldfs); + } + + /* if enumeration failed, attempt recovery pulse */ + if (!enum_success) { + pr_info("attempting recovery pulse...\n"); + /* wait 20 ms */ + mdelay(20); + /* set IPC_HSIC_ACTIVE low */ + gpio_set_value(baseband_power2_driver_data-> + modem.xmm.ipc_hsic_active, 0); + /* wait 20 ms */ + mdelay(20); + /* set IPC_HSIC_ACTIVE high */ + gpio_set_value(baseband_power2_driver_data-> + modem.xmm.ipc_hsic_active, 1); + /* check if recovery pulse worked */ + baseband_xmm_power2_work->state = + BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP4; + queue_work(workqueue, (struct work_struct *) + baseband_xmm_power2_work); + } + + pr_info("%s }\n", __func__); +} + +static void baseband_xmm_power2_flashless_pm_ver_ge_1130_step4 + (struct work_struct *work) +{ + int X = XYZ / 1000000; + int Y = XYZ / 1000 - X * 1000; + int Z = XYZ % 1000; + int enum_success = 0; + + pr_info("%s {\n", __func__); + + pr_info("XYZ=%ld X=%d Y=%d Z=%d\n", XYZ, X, Y, Z); + + /* check for platform data */ + if (!baseband_power2_driver_data) + return; + + /* wait 500 ms */ + mdelay(500); + + /* check if enumeration succeeded */ + { + mm_segment_t oldfs; + struct file *filp; + oldfs = get_fs(); + set_fs(KERNEL_DS); + filp = filp_open("/sys/bus/usb/devices/usb2/2-1/manufacturer", + O_RDONLY, 0); + if (IS_ERR(filp) || (filp == NULL)) { + pr_err("open /sys/bus/usb/devices" + "/usb2/2-1/manufacturer failed %ld\n", + PTR_ERR(filp)); + } else { + filp_close(filp, NULL); + enum_success = 1; + } + set_fs(oldfs); + } + + /* if recovery pulse did not fix enumeration, retry from beginning */ + if (!enum_success) { + static int retry = 3; + if (!retry) { + pr_info("failed to enumerate modem software" + " - too many retry attempts\n"); + } else { + pr_info("recovery pulse failed to fix modem" + " enumeration..." + " restarting from beginning" + " - attempt #%d\n", + retry); + --retry; + ipc_ap_wake_state = IPC_AP_WAKE_IRQ_READY; + baseband_xmm_power2_work->state = + BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP1; + queue_work(workqueue, (struct work_struct *) + baseband_xmm_power2_work); + } + } + + pr_info("%s }\n", __func__); +} + +static int free_ipc_ap_wake_irq; + +static void baseband_xmm_power2_work_func(struct work_struct *work) +{ + struct baseband_xmm_power_work_t *bbxmm_work + = (struct baseband_xmm_power_work_t *) work; + int err; + + pr_debug("%s\n", __func__); + + switch (bbxmm_work->state) { + case BBXMM_WORK_UNINIT: + pr_debug("BBXMM_WORK_UNINIT\n"); + /* free baseband irq(s) */ + if (free_ipc_ap_wake_irq) { + free_irq(gpio_to_irq(baseband_power2_driver_data + ->modem.xmm.ipc_ap_wake), NULL); + free_ipc_ap_wake_irq = 0; + } + break; + case BBXMM_WORK_INIT: + pr_debug("BBXMM_WORK_INIT\n"); + /* request baseband irq(s) */ + ipc_ap_wake_state = IPC_AP_WAKE_UNINIT; + err = request_irq(gpio_to_irq(baseband_power2_driver_data + ->modem.xmm.ipc_ap_wake), + (modem_ver < XMM_MODEM_VER_1130) + ? baseband_xmm_power2_ver_lt_1130_ipc_ap_wake_irq2 + : baseband_xmm_power2_ver_ge_1130_ipc_ap_wake_irq2, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "BBXMM_POWER2_IPC_AP_WAKE_IRQ", + NULL); + if (err < 0) { + pr_err("%s - request irq IPC_AP_WAKE_IRQ failed\n", + __func__); + return; + } + free_ipc_ap_wake_irq = 1; + ipc_ap_wake_state = IPC_AP_WAKE_IRQ_READY; + /* go to next state */ + bbxmm_work->state = (modem_flash && !modem_pm) + ? BBXMM_WORK_INIT_FLASH_STEP1 + : (modem_flash && modem_pm) + ? BBXMM_WORK_INIT_FLASH_PM_STEP1 + : (!modem_flash && modem_pm) + ? BBXMM_WORK_INIT_FLASHLESS_PM_STEP1 + : BBXMM_WORK_UNINIT; + queue_work(workqueue, work); + break; + case BBXMM_WORK_INIT_FLASH_STEP1: + pr_debug("BBXMM_WORK_INIT_FLASH_STEP1\n"); + break; + case BBXMM_WORK_INIT_FLASH_PM_STEP1: + pr_debug("BBXMM_WORK_INIT_FLASH_PM_STEP1\n"); + /* go to next state */ + bbxmm_work->state = (modem_ver < XMM_MODEM_VER_1130) + ? BBXMM_WORK_INIT_FLASH_PM_VER_LT_1130_STEP1 + : BBXMM_WORK_INIT_FLASH_PM_VER_GE_1130_STEP1; + queue_work(workqueue, work); + break; + case BBXMM_WORK_INIT_FLASH_PM_VER_LT_1130_STEP1: + pr_debug("BBXMM_WORK_INIT_FLASH_PM_VER_LT_1130_STEP1\n"); + break; + case BBXMM_WORK_INIT_FLASH_PM_VER_GE_1130_STEP1: + pr_debug("BBXMM_WORK_INIT_FLASH_PM_VER_GE_1130_STEP1\n"); + break; + case BBXMM_WORK_INIT_FLASHLESS_PM_STEP1: + pr_debug("BBXMM_WORK_INIT_FLASHLESS_PM_STEP1\n"); + /* go to next state */ + bbxmm_work->state = (modem_ver < XMM_MODEM_VER_1130) + ? BBXMM_WORK_INIT_FLASHLESS_PM_VER_LT_1130_WAIT_IRQ + : BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP1; + queue_work(workqueue, work); + break; + case BBXMM_WORK_INIT_FLASHLESS_PM_VER_LT_1130_WAIT_IRQ: + pr_debug("BBXMM_WORK_INIT_FLASHLESS_PM_VER_LT_1130_WAIT_IRQ" + " - waiting for IPC_AP_WAKE_IRQ to trigger step1\n"); + break; + case BBXMM_WORK_INIT_FLASHLESS_PM_VER_LT_1130_STEP1: + pr_debug("BBXMM_WORK_INIT_FLASHLESS_PM_VER_LT_1130_STEP1\n"); + baseband_xmm_power2_flashless_pm_ver_lt_1130_step1(work); + break; + case BBXMM_WORK_INIT_FLASHLESS_PM_VER_LT_1130_STEP2: + pr_debug("BBXMM_WORK_INIT_FLASHLESS_PM_VER_LT_1130_STEP2\n"); + baseband_xmm_power2_flashless_pm_ver_lt_1130_step2(work); + break; + case BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP1: + pr_debug("BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP1\n"); + baseband_xmm_power2_flashless_pm_ver_ge_1130_step1(work); + break; + case BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP2: + pr_debug("BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP2\n"); + baseband_xmm_power2_flashless_pm_ver_ge_1130_step2(work); + break; + case BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP3: + pr_debug("BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP3\n"); + baseband_xmm_power2_flashless_pm_ver_ge_1130_step3(work); + break; + case BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP4: + pr_debug("BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP4\n"); + baseband_xmm_power2_flashless_pm_ver_ge_1130_step4(work); + break; + } + +} + static int baseband_xmm_power2_driver_probe(struct platform_device *device) { struct baseband_power_platform_data *data - = (struct baseband_power_platform_data *) device->dev.platform_data; - int err; + = (struct baseband_power_platform_data *) + device->dev.platform_data; pr_debug("%s\n", __func__); /* save platform data */ baseband_power2_driver_data = data; - /* request baseband irq(s) */ - ipc_hsic_sus_req_state = IPC_HSIC_SUS_REQ_UNINIT; - err = request_irq(gpio_to_irq(data->modem.xmm.ipc_hsic_sus_req), - ipc_hsic_sus_req_irq, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - "IPC_HSIC_SUS_REQ_IRQ", - NULL); - if (err < 0) { - pr_err("%s - request irq IPC_HSIC_SUS_REQ_IRQ failed\n", - __func__); - return err; - } - ipc_hsic_sus_req_state = IPC_HSIC_SUS_REQ_IRQ_READY; - ipc_ap_wake_state = IPC_AP_WAKE_UNINIT; - err = request_irq(gpio_to_irq(data->modem.xmm.ipc_ap_wake), - ipc_ap_wake_irq, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - "IPC_AP_WAKE_IRQ", - NULL); - if (err < 0) { - pr_err("%s - request irq IPC_AP_WAKE_IRQ failed\n", - __func__); - return err; - } - ipc_ap_wake_state = IPC_AP_WAKE_IRQ_READY; - /* init work queue */ workqueue = create_singlethread_workqueue - ("baseband_power_workqueue"); + ("baseband_xmm_power2_workqueue"); if (!workqueue) { pr_err("cannot create workqueue\n"); return -1; } - INIT_WORK(&init1_work, baseband_xmm_power2_init1_work); - INIT_WORK(&init2_work, baseband_xmm_power2_init2_work); + baseband_xmm_power2_work = (struct baseband_xmm_power_work_t *) + kmalloc(sizeof(struct baseband_xmm_power_work_t), GFP_KERNEL); + if (!baseband_xmm_power2_work) { + pr_err("cannot allocate baseband_xmm_power2_work\n"); + return -1; + } + INIT_WORK((struct work_struct *) baseband_xmm_power2_work, + baseband_xmm_power2_work_func); + baseband_xmm_power2_work->state = BBXMM_WORK_INIT; + queue_work(workqueue, + (struct work_struct *) baseband_xmm_power2_work); return 0; } static int baseband_xmm_power2_driver_remove(struct platform_device *device) { + struct baseband_power_platform_data *data + = (struct baseband_power_platform_data *) + device->dev.platform_data; + pr_debug("%s\n", __func__); /* check for platform data */ - if (!baseband_power2_driver_data) + if (!data) return 0; - /* free baseband irq(s) */ - free_irq(gpio_to_irq(baseband_power2_driver_data - ->modem.xmm.ipc_ap_wake), NULL); - free_irq(gpio_to_irq(baseband_power2_driver_data - ->modem.xmm.ipc_hsic_sus_req), NULL); + /* free irq */ + if (free_ipc_ap_wake_irq) { + free_irq(gpio_to_irq(data->modem.xmm.ipc_ap_wake), NULL); + free_ipc_ap_wake_irq = 0; + } + + /* free work structure */ + destroy_workqueue(workqueue); + kfree(baseband_xmm_power2_work); + baseband_xmm_power2_work = (struct baseband_xmm_power_work_t *) 0; return 0; } @@ -296,35 +649,31 @@ static int baseband_xmm_power2_driver_remove(struct platform_device *device) static int baseband_xmm_power2_driver_suspend(struct platform_device *device, pm_message_t state) { - pr_debug("%s\n", __func__); + struct baseband_power_platform_data *data + = (struct baseband_power_platform_data *) + device->dev.platform_data; + + pr_debug("%s - nop\n", __func__); /* check for platform data */ - if (!baseband_power2_driver_data) + if (!data) return 0; - /* signal bb to suspend hsic */ - gpio_set_value(baseband_power2_driver_data - ->modem.xmm.ipc_hsic_active, 0); - return 0; } static int baseband_xmm_power2_driver_resume(struct platform_device *device) { - pr_debug("%s\n", __func__); + struct baseband_power_platform_data *data + = (struct baseband_power_platform_data *) + device->dev.platform_data; + + pr_debug("%s - nop\n", __func__); /* check for platform data */ - if (!baseband_power2_driver_data) + if (!data) return 0; - /* wake bb */ - gpio_set_value(baseband_power2_driver_data - ->modem.xmm.ipc_bb_wake, 1); - - /* signal bb to resume hsic */ - gpio_set_value(baseband_power2_driver_data - ->modem.xmm.ipc_hsic_active, 1); - return 0; } #endif @@ -355,4 +704,3 @@ static void __exit baseband_xmm_power2_exit(void) module_init(baseband_xmm_power2_init) module_exit(baseband_xmm_power2_exit) - |