summaryrefslogtreecommitdiff
path: root/drivers/misc
diff options
context:
space:
mode:
authorRaj Jayaraman <rjayaraman@nvidia.com>2011-10-14 11:59:40 -0700
committerVarun Wadekar <vwadekar@nvidia.com>2011-12-08 18:05:37 +0530
commitfdca632da6249827e54fa18bba4528d693e1fabb (patch)
treede8f5ba82eb0adf9b47bd37ce4055c93d541c828 /drivers/misc
parent0daea81c26b9952c98a35da8136864e029a452df (diff)
misc: tegra-baseband: Add power management driver.
Bug 886459 (cherry picked from commit 5bf5402a2c966197b03845852171ae310e2e69e7) Change-Id: I1bbbeaa2abce338655b4ac3e5bce90b8ba316282 Signed-off-by: Raj Jayaraman <rjayaraman@nvidia.com> Reviewed-on: http://git-master/r/66963 Reviewed-by: Steve Lin <stlin@nvidia.com>
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/Kconfig1
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/tegra-baseband/Kconfig25
-rw-r--r--drivers/misc/tegra-baseband/Makefile5
-rw-r--r--drivers/misc/tegra-baseband/bb-power.c302
-rw-r--r--drivers/misc/tegra-baseband/bb-power.h46
6 files changed, 380 insertions, 0 deletions
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 <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 <linux/platform_data/tegra_usb.h>
+#include <mach/usb_phy.h>
+#include <mach/tegra-bb-power.h>
+#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);