From fdca632da6249827e54fa18bba4528d693e1fabb Mon Sep 17 00:00:00 2001 From: Raj Jayaraman Date: Fri, 14 Oct 2011 11:59:40 -0700 Subject: misc: tegra-baseband: Add power management driver. Bug 886459 (cherry picked from commit 5bf5402a2c966197b03845852171ae310e2e69e7) Change-Id: I1bbbeaa2abce338655b4ac3e5bce90b8ba316282 Signed-off-by: Raj Jayaraman Reviewed-on: http://git-master/r/66963 Reviewed-by: Steve Lin --- drivers/misc/Kconfig | 1 + drivers/misc/Makefile | 1 + drivers/misc/tegra-baseband/Kconfig | 25 +++ drivers/misc/tegra-baseband/Makefile | 5 + drivers/misc/tegra-baseband/bb-power.c | 302 +++++++++++++++++++++++++++++++++ drivers/misc/tegra-baseband/bb-power.h | 46 +++++ 6 files changed, 380 insertions(+) create mode 100644 drivers/misc/tegra-baseband/Kconfig create mode 100644 drivers/misc/tegra-baseband/Makefile create mode 100644 drivers/misc/tegra-baseband/bb-power.c create mode 100644 drivers/misc/tegra-baseband/bb-power.h (limited to 'drivers/misc') diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 274dfc1c9507..958577a840dd 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -575,5 +575,6 @@ source "drivers/misc/lis3lv02d/Kconfig" source "drivers/misc/carma/Kconfig" source "drivers/misc/mpu3050/Kconfig" source "drivers/misc/inv_mpu/Kconfig" +source "drivers/misc/tegra-baseband/Kconfig" endif # MISC_DEVICES diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index bde30eec62c9..6c38d32eff22 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -57,4 +57,5 @@ obj-$(CONFIG_SENSORS_NCT1008) += nct1008.o obj-$(CONFIG_BCM4329_RFKILL) += bcm4329_rfkill.o obj-$(CONFIG_MPU_SENSORS_MPU3050) += inv_mpu/ obj-$(CONFIG_TEGRA_CRYPTO_DEV) += tegra-cryptodev.o +obj-$(CONFIG_TEGRA_BB_SUPPORT) += tegra-baseband/ obj-$(CONFIG_MAX1749_VIBRATOR) += max1749.o diff --git a/drivers/misc/tegra-baseband/Kconfig b/drivers/misc/tegra-baseband/Kconfig new file mode 100644 index 000000000000..53147ec3c9fe --- /dev/null +++ b/drivers/misc/tegra-baseband/Kconfig @@ -0,0 +1,25 @@ +menuconfig TEGRA_BB_SUPPORT + bool "Tegra baseband support" + depends on ARCH_TEGRA + ---help--- + Say Y here to get to see options for tegra baseband support. + This option alone does not add any kernel code. + + If you say N, all options in this submenu will be skipped and disabled. + +if TEGRA_BB_SUPPORT + +config TEGRA_BB_POWER + bool "Enable tegra baseband power driver" + ---help--- + Adds power management driver for managing different baseband + modems with tegra processor. + + This driver should work with at least the following devices: + + * STE M7400 + * ... + + Disabled by default. Choose Y here if you want to build the driver. + +endif # TEGRA_BB_SUPPORT diff --git a/drivers/misc/tegra-baseband/Makefile b/drivers/misc/tegra-baseband/Makefile new file mode 100644 index 000000000000..54414d6b2016 --- /dev/null +++ b/drivers/misc/tegra-baseband/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for tegra baseband support. +# + +obj-$(CONFIG_TEGRA_BB_POWER) += bb-power.o diff --git a/drivers/misc/tegra-baseband/bb-power.c b/drivers/misc/tegra-baseband/bb-power.c new file mode 100644 index 000000000000..cc1d0de7ebcf --- /dev/null +++ b/drivers/misc/tegra-baseband/bb-power.c @@ -0,0 +1,302 @@ +/* + * drivers/misc/tegra-baseband/bb-power.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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bb-power.h" + +static int bb_id; +static bool bb_registered; + +static bb_init_cb init_cb_list[] = { + NULL, + NULL, + NULL, + NULL, +}; + +static bb_power_cb power_cb_list[] = { + NULL, + NULL, + NULL, + NULL, +}; + +static int tegra_bb_power_gpio_init(struct tegra_bb_power_gdata *gdata) +{ + int ret; + int irq; + unsigned gpio_id; + const char *gpio_label; + unsigned long gpio_flags; + struct tegra_bb_gpio_data *gpiolist; + struct tegra_bb_gpio_irqdata *gpioirq; + + gpiolist = gdata->gpio; + for (; gpiolist->data.gpio != GPIO_INVALID; ++gpiolist) { + gpio_id = (gpiolist->data.gpio); + gpio_label = (gpiolist->data.label); + gpio_flags = (gpiolist->data.flags); + + /* Request the gpio */ + ret = gpio_request(gpio_id, gpio_label); + if (ret) { + pr_err("%s: gpio_request for gpio %d failed.\n", + __func__, gpio_id); + return ret; + } + + /* Set gpio direction, as requested */ + if (gpio_flags == GPIOF_IN) + gpio_direction_input(gpio_id); + else + gpio_direction_output(gpio_id, (!gpio_flags ? 0 : 1)); + + /* Enable the gpio */ + tegra_gpio_enable(gpio_id); + + /* Create a sysfs node, if requested */ + if (gpiolist->doexport) + gpio_export(gpio_id, false); + } + + gpioirq = gdata->gpioirq; + for (; gpioirq->id != GPIO_INVALID; ++gpioirq) { + + /* 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) { + pr_err("%s: request_threaded_irq error\n", + __func__); + return ret; + } + ret = enable_irq_wake(irq); + if (ret) { + pr_err("%s: enable_irq_wake error\n", __func__); + return ret; + } + } + } + return 0; +} + +static int tegra_bb_power_gpio_deinit(struct tegra_bb_power_gdata *gdata) +{ + struct tegra_bb_gpio_data *gpiolist; + struct tegra_bb_gpio_irqdata *gpioirq; + + gpiolist = gdata->gpio; + for (; gpiolist->data.gpio != GPIO_INVALID; ++gpiolist) { + + /* Free the gpio */ + gpio_free(gpiolist->data.gpio); + } + + gpioirq = gdata->gpioirq; + for (; gpioirq->id != GPIO_INVALID; ++gpioirq) { + + /* Free the irq */ + free_irq(gpio_to_irq(gpioirq->id), gpioirq->cookie); + } + return 0; +} + +static int baseband_l2_suspend(void) +{ + /* BB specific callback */ + if (power_cb_list[bb_id] != NULL) + power_cb_list[bb_id](CB_CODE_L0L2); + return 0; +} + +static int baseband_l2_resume(void) +{ + /* BB specific callback */ + if (power_cb_list[bb_id] != NULL) + power_cb_list[bb_id](CB_CODE_L2L0); + return 0; +} + +static ssize_t tegra_bb_attr_write(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct tegra_bb_pdata *pdata; + struct tegra_ehci_platform_data *ehci_data; + struct tegra_uhsic_config *hsic_config; + int load; + + if (sscanf(buf, "%d", &load) != 1) + return -EINVAL; + + if (load == 1 && !bb_registered) { + pdata = (struct tegra_bb_pdata *) dev->platform_data; + ehci_data = (struct tegra_ehci_platform_data *) + pdata->device->dev.platform_data; + hsic_config = (struct tegra_uhsic_config *) + ehci_data->phy_config; + + /* Register PHY callbacks */ + hsic_config->postsuspend = baseband_l2_suspend; + hsic_config->preresume = baseband_l2_resume; + + /* Override required settings */ + ehci_data->power_down_on_bus_suspend = 0; + + /* Register the ehci device. */ + platform_device_register(pdata->device); + bb_registered = true; + } + + return count; +} + +static ssize_t tegra_bb_attr_read(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret = 0; + return sprintf(buf, "%d", ret); +} + +static DEVICE_ATTR(load, S_IRUSR | S_IWUSR | S_IRGRP, + tegra_bb_attr_read, tegra_bb_attr_write); + +static int tegra_bb_power_probe(struct platform_device *device) +{ + struct device *dev = &device->dev; + struct tegra_bb_pdata *pdata; + struct tegra_bb_power_gdata *gdata; + int err; + + pdata = (struct tegra_bb_pdata *) dev->platform_data; + if (!pdata) { + pr_err("%s - Error: platform data is empty.\n", __func__); + return -ENODEV; + } + + /* BB specific callback */ + bb_id = pdata->bb_id; + if (init_cb_list[bb_id] != NULL) { + gdata = init_cb_list[pdata->bb_id](pdata, CB_CODE_INIT); + + if (!gdata) { + pr_err("%s - Error: Gpio data is empty.\n", __func__); + return -ENODEV; + } + + /* Initialize gpio as required */ + tegra_bb_power_gpio_init(gdata); + } + + bb_registered = false; + + /* Create the control sysfs node */ + err = device_create_file(dev, &dev_attr_load); + if (err < 0) { + pr_err("%s - device_create_file failed\n", __func__); + return -ENODEV; + } + + return 0; +} + +static int tegra_bb_power_remove(struct platform_device *device) +{ + struct device *dev = &device->dev; + struct tegra_bb_pdata *pdata; + struct tegra_bb_power_gdata *gdata; + + /* BB specific callback */ + if (init_cb_list[bb_id] != NULL) { + pdata = (struct tegra_bb_pdata *) dev->platform_data; + gdata = init_cb_list[bb_id](pdata, CB_CODE_DEINIT); + + /* Deinitialize gpios */ + if (gdata) + tegra_bb_power_gpio_deinit(gdata); + } + + /* Remove the control sysfs node */ + device_remove_file(dev, &dev_attr_load); + + return 0; +} + +#ifdef CONFIG_PM +static int tegra_bb_power_suspend(struct platform_device *device, + pm_message_t state) +{ + /* BB specific callback */ + if (power_cb_list[bb_id] != NULL) + power_cb_list[bb_id](CB_CODE_L2L3); + + return 0; +} + +static int tegra_bb_power_resume(struct platform_device *device) +{ + /* BB specific callback */ + if (power_cb_list[bb_id] != NULL) + power_cb_list[bb_id](CB_CODE_L3L0); + + return 0; +} +#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", + }, +}; + +static int __init tegra_baseband_power_init(void) +{ + pr_debug("%s\n", __func__); + return platform_driver_register(&tegra_bb_power_driver); +} + +static void __exit tegra_baseband_power_exit(void) +{ + pr_debug("%s\n", __func__); + platform_driver_unregister(&tegra_bb_power_driver); +} + +module_init(tegra_baseband_power_init) +module_exit(tegra_baseband_power_exit) +MODULE_AUTHOR("NVIDIA Corporation"); +MODULE_DESCRIPTION("Tegra modem power management driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/tegra-baseband/bb-power.h b/drivers/misc/tegra-baseband/bb-power.h new file mode 100644 index 000000000000..dfa7496219de --- /dev/null +++ b/drivers/misc/tegra-baseband/bb-power.h @@ -0,0 +1,46 @@ +/* + * drivers/misc/tegra-baseband/bb-power.h + * + * 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. + * + */ + +enum tegra_bb_callback_code { + CB_CODE_INIT = 1, + CB_CODE_DEINIT, + CB_CODE_L0L2, + CB_CODE_L2L0, + CB_CODE_L2L3, + CB_CODE_L3L0, + CB_CODE_INVALID, +}; + +struct tegra_bb_gpio_data { + struct gpio data; + bool doexport; +}; + +struct tegra_bb_gpio_irqdata { + int id; + const char *name; + irq_handler_t handler; + int flags; + void *cookie; +}; + +struct tegra_bb_power_gdata { + struct tegra_bb_gpio_data *gpio; + struct tegra_bb_gpio_irqdata *gpioirq; +}; + +typedef void* (*bb_init_cb)(void *pdata, int code); +typedef int (*bb_power_cb)(int code); -- cgit v1.2.3