diff options
-rw-r--r-- | arch/arm/mach-tegra/Makefile | 3 | ||||
-rw-r--r-- | arch/arm/mach-tegra/baseband-xmm-power.c | 7 | ||||
-rw-r--r-- | arch/arm/mach-tegra/baseband-xmm-power2.c | 359 | ||||
-rw-r--r-- | arch/arm/mach-tegra/board-cardhu.c | 11 |
4 files changed, 373 insertions, 7 deletions
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index ba9705104902..37b5bb6e002f 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -142,7 +142,8 @@ obj-${CONFIG_MACH_CARDHU} += board-cardhu-sensors.o obj-${CONFIG_MACH_CARDHU} += board-touch.o obj-${CONFIG_MACH_CARDHU} += board-cardhu-memory.o obj-${CONFIG_MACH_CARDHU} += board-cardhu-powermon.o -obj-${CONFIG_MACH_CARDHU} += baseband-xmm-power.o +obj-m += baseband-xmm-power.o +obj-m += baseband-xmm-power2.o obj-${CONFIG_MACH_TEGRA_ENTERPRISE} += board-enterprise.o obj-${CONFIG_MACH_TEGRA_ENTERPRISE} += board-enterprise-panel.o diff --git a/arch/arm/mach-tegra/baseband-xmm-power.c b/arch/arm/mach-tegra/baseband-xmm-power.c index 83959485b281..a8df78bf20b2 100644 --- a/arch/arm/mach-tegra/baseband-xmm-power.c +++ b/arch/arm/mach-tegra/baseband-xmm-power.c @@ -46,9 +46,7 @@ static struct gpio tegra_baseband_gpios[] = { { -1, GPIOF_OUT_INIT_LOW, "IPC_BB_WAKE" }, { -1, GPIOF_IN, "IPC_AP_WAKE" }, { -1, GPIOF_OUT_INIT_HIGH, "IPC_HSIC_ACTIVE" }, -#if BB_INITIATED_L2_SUSPEND { -1, GPIOF_IN, "IPC_HSIC_SUS_REQ" }, -#endif }; #if BB_INITIATED_L2_SUSPEND @@ -247,9 +245,7 @@ static int baseband_xmm_power_driver_probe(struct platform_device *device) 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; -#if BB_INITIATED_L2_SUSPEND tegra_baseband_gpios[5].gpio = data->modem.xmm.ipc_hsic_sus_req; -#endif err = gpio_request_array(tegra_baseband_gpios, ARRAY_SIZE(tegra_baseband_gpios)); if (err < 0) { @@ -346,7 +342,8 @@ static int baseband_xmm_power_driver_remove(struct platform_device *device) ARRAY_SIZE(tegra_baseband_gpios)); /* unregister usb host controller */ - platform_device_unregister(&tegra_ehci2_device); + platform_device_unregister(baseband_power_driver_data-> + modem.xmm.hsic_device); return 0; } diff --git a/arch/arm/mach-tegra/baseband-xmm-power2.c b/arch/arm/mach-tegra/baseband-xmm-power2.c new file mode 100644 index 000000000000..dd79986813a8 --- /dev/null +++ b/arch/arm/mach-tegra/baseband-xmm-power2.c @@ -0,0 +1,359 @@ +/* + * arch/arm/mach-tegra/baseband-xmm-power2.c + * + * Copyright (C) 2011 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. + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/platform_device.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/workqueue.h> +#include <linux/delay.h> +#include <linux/fs.h> +#include <linux/uaccess.h> +#include <mach/usb_phy.h> +#include "baseband-xmm-power.h" +#include "board.h" +#include "devices.h" + +MODULE_LICENSE("GPL"); + +static struct baseband_power_platform_data *baseband_power2_driver_data; + +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 enum { + IPC_AP_WAKE_UNINIT, + IPC_AP_WAKE_IRQ_READY, + IPC_AP_WAKE_INIT1, + IPC_AP_WAKE_INIT2, + IPC_AP_WAKE_L, + 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) +{ + int value; + + pr_debug("%s\n", __func__); + + /* check for platform data */ + 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) { + pr_err("%s - spurious irq\n", __func__); + } else { + value = gpio_get_value(baseband_power2_driver_data-> + modem.xmm.ipc_hsic_sus_req); + if (!value) { + pr_debug("%s - falling\n", __func__); + ipc_hsic_sus_req_state = IPC_HSIC_SUS_REQ_L; + } else { + pr_debug("%s - rising\n", __func__); + ipc_hsic_sus_req_state = IPC_HSIC_SUS_REQ_H; + } + } + + return IRQ_HANDLED; +} + +static irqreturn_t ipc_ap_wake_irq(int irq, void *dev_id) +{ + int value; + + pr_debug("%s\n", __func__); + + /* check for platform data */ + if (!baseband_power2_driver_data) + return IRQ_HANDLED; + + /* 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 */ + 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", + __func__); + /* go to IPC_AP_WAKE_INIT2 state */ + ipc_ap_wake_state = IPC_AP_WAKE_INIT2; + /* queue work */ + queue_work(workqueue, &init2_work); + } + } else { + value = gpio_get_value(baseband_power2_driver_data-> + modem.xmm.ipc_ap_wake); + if (!value) { + pr_debug("%s - falling\n", __func__); + ipc_ap_wake_state = IPC_AP_WAKE_L; + } else { + pr_debug("%s - rising\n", __func__); + ipc_ap_wake_state = IPC_AP_WAKE_H; + } + } + + return IRQ_HANDLED; +} + +static void baseband_xmm_power2_init1_work(struct work_struct *work) +{ + int value; + + pr_debug("%s {\n", __func__); + + /* check for platform data */ + if (!baseband_power2_driver_data) + return; + + /* check if IPC_HSIC_ACTIVE high */ + value = gpio_get_value(baseband_power2_driver_data-> + modem.xmm.ipc_hsic_active); + if (value != 1) { + pr_err("%s - expected IPC_HSIC_ACTIVE high!\n", __func__); + return; + } + + /* wait 30 ms */ + mdelay(30); + + /* set IPC_HSIC_ACTIVE low */ + gpio_set_value(baseband_power2_driver_data-> + modem.xmm.ipc_hsic_active, 0); + + pr_debug("%s }\n", __func__); +} + +static void baseband_xmm_power2_init2_work(struct work_struct *work) +{ + int value; + + pr_debug("%s {\n", __func__); + + /* check for platform data */ + if (!baseband_power2_driver_data) + return; + + /* check if IPC_HSIC_ACTIVE low */ + value = gpio_get_value(baseband_power2_driver_data-> + modem.xmm.ipc_hsic_active); + if (value != 0) { + pr_err("%s - expected IPC_HSIC_ACTIVE low!\n", __func__); + return; + } + + /* wait 1 ms */ + mdelay(1); + + /* turn on usb host controller */ + { + mm_segment_t oldfs; + 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) { + 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); + } + + /* set IPC_HSIC_ACTIVE high */ + gpio_set_value(baseband_power2_driver_data-> + modem.xmm.ipc_hsic_active, 1); + + /* 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); + + pr_debug("%s }\n", __func__); +} + +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; + + 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"); + 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); + + return 0; +} + +static int baseband_xmm_power2_driver_remove(struct platform_device *device) +{ + pr_debug("%s\n", __func__); + + /* check for platform data */ + if (!baseband_power2_driver_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); + + return 0; +} + +#ifdef CONFIG_PM +static int baseband_xmm_power2_driver_suspend(struct platform_device *device, + pm_message_t state) +{ + pr_debug("%s\n", __func__); + + /* check for platform data */ + if (!baseband_power2_driver_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__); + + /* check for platform data */ + if (!baseband_power2_driver_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 + +static struct platform_driver baseband_power2_driver = { + .probe = baseband_xmm_power2_driver_probe, + .remove = baseband_xmm_power2_driver_remove, +#ifdef CONFIG_PM + .suspend = baseband_xmm_power2_driver_suspend, + .resume = baseband_xmm_power2_driver_resume, +#endif + .driver = { + .name = "baseband_xmm_power2", + }, +}; + +static int __init baseband_xmm_power2_init(void) +{ + pr_debug("%s\n", __func__); + return platform_driver_register(&baseband_power2_driver); +} + +static int __exit baseband_xmm_power2_exit(void) +{ + pr_debug("%s\n", __func__); + platform_driver_unregister(&baseband_power2_driver); + return 0; +} + +module_init(baseband_xmm_power2_init) +module_exit(baseband_xmm_power2_exit) + diff --git a/arch/arm/mach-tegra/board-cardhu.c b/arch/arm/mach-tegra/board-cardhu.c index c0536c9c5bdb..eeca9c6a9159 100644 --- a/arch/arm/mach-tegra/board-cardhu.c +++ b/arch/arm/mach-tegra/board-cardhu.c @@ -866,7 +866,7 @@ static void cardhu_usb_init(void) platform_device_register(&tegra_ehci2_device); } else if (bi.board_id == BOARD_E1186) { tegra_ehci2_device.dev.platform_data = &tegra_ehci_uhsic_pdata; - /* baseband registartion happens in baseband-xmm-power */ + /* baseband registration happens in baseband-xmm-power */ } else { tegra_ehci2_device.dev.platform_data = &tegra_ehci_pdata[1]; platform_device_register(&tegra_ehci2_device); @@ -923,6 +923,14 @@ static struct platform_device tegra_baseband_power_device = { }, }; +static struct platform_device tegra_baseband_power2_device = { + .name = "baseband_xmm_power2", + .id = -1, + .dev = { + .platform_data = &tegra_baseband_power_data, + }, +}; + static void cardhu_modem_init(void) { struct board_info board_info; @@ -958,6 +966,7 @@ static void cardhu_modem_init(void) tegra_gpio_enable( tegra_baseband_power_data.modem.xmm.ipc_hsic_sus_req); platform_device_register(&tegra_baseband_power_device); + platform_device_register(&tegra_baseband_power2_device); break; default: break; |