summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorGary King <gking@nvidia.com>2010-05-23 22:14:31 -0700
committerGary King <gking@nvidia.com>2010-05-23 22:14:31 -0700
commit50c9c5c39223a2b59809b61b87ee8351f0bbea9c (patch)
treea1414bcfcd2b5951033557b300af9e29d07b6781 /drivers
parentf6d39d63e6334db394b4459398f228f640e318cd (diff)
regulator: add tegra SoC regulator driver
enables platform code to register ODM queriable GUIDs as named regulators, and to specify the list of consumers for each registered regulator. uses NvRm PMU API to program the PMU Change-Id: I0cd04ad4bebc610d945daaf575f5d09e8d838385
Diffstat (limited to 'drivers')
-rw-r--r--drivers/regulator/Kconfig6
-rw-r--r--drivers/regulator/Makefile1
-rw-r--r--drivers/regulator/tegra-regulator.c274
3 files changed, 281 insertions, 0 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index bcbb161bde0b..e07f41f348cd 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -69,6 +69,12 @@ config REGULATOR_MAX1586
regulator via I2C bus. The provided regulator is suitable
for PXA27x chips to control VCC_CORE and VCC_USIM voltages.
+config REGULATOR_TEGRA
+ boolean "Regulator support on NVIDIA Tegra SoCs"
+ depends on ARCH_TEGRA && TEGRA_NVRM
+ help
+ This enables the regulator interfaces in NVIDIA Tegra SoCs.
+
config REGULATOR_TWL4030
bool "TI TWL4030/TWL5030/TPS695x0 PMIC"
depends on TWL4030_CORE
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 4257a8683778..c39716593676 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o
obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o
obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o
obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o
+obj-$(CONFIG_REGULATOR_TEGRA) += tegra-regulator.o
obj-$(CONFIG_REGULATOR_TWL4030) += twl4030-regulator.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o
diff --git a/drivers/regulator/tegra-regulator.c b/drivers/regulator/tegra-regulator.c
new file mode 100644
index 000000000000..4d6fd25e8060
--- /dev/null
+++ b/drivers/regulator/tegra-regulator.c
@@ -0,0 +1,274 @@
+/*
+ * drivers/regulator/tegra-regulator.c
+ *
+ * Regulator driver for NVIDIA Tegra NvRm PMU APIs
+ *
+ * Copyright (C) 2010 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.
+ */
+
+#define NV_DEBUG 0
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/err.h>
+#include <linux/regulator/machine.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+
+#include <mach/nvrm_linux.h>
+#include <mach/regulator.h>
+
+#include <nvassert.h>
+#include <nvrm_pmu.h>
+#include <nvrm_power.h>
+#include <nvodm_query_discovery.h>
+
+struct tegra_vdd {
+ NvU32 id;
+ unsigned long request_uV;
+};
+
+struct tegra_regulator {
+ struct regulator_desc rdesc;
+ struct regulator_init_data init;
+ struct regulator_dev *rdev;
+ struct device *dev;
+ struct list_head node;
+ bool enable;
+ int nr_vdd;
+ struct tegra_vdd vdd_list[1];
+};
+
+struct tegra_regulator_drv {
+ struct list_head list;
+};
+
+static int tegra_regulator_enable(struct regulator_dev *rdev)
+{
+ struct tegra_regulator *data = rdev_get_drvdata(rdev);
+ NvU32 settle;
+ int i;
+
+ dev_dbg(data->dev, "%s: %s->%luuV enabled:%s\n", __func__,
+ data->rdesc.name, data->vdd_list[0].request_uV,
+ data->enable ? "true" : "false");
+
+ if (!data->enable) {
+ for (i=0; i<data->nr_vdd; i++) {
+ const struct tegra_vdd *vdd = &data->vdd_list[i];
+ NvRmPmuSetVoltage(s_hRmGlobal, vdd->id,
+ vdd->request_uV / 1000, &settle);
+ udelay(settle);
+ }
+ }
+
+ data->enable = true;
+ return 0;
+}
+
+static int tegra_regulator_disable(struct regulator_dev *rdev)
+{
+ struct tegra_regulator *data = rdev_get_drvdata(rdev);
+ int i;
+
+ dev_dbg(data->dev, "%s: %s enabled:%s\n", __func__,
+ data->rdesc.name, data->enable ? "true" : "false");
+
+ if (data->enable) {
+ for (i=0; i<data->nr_vdd; i++) {
+ NvRmPmuSetVoltage(s_hRmGlobal, data->vdd_list[i].id,
+ NVODM_VOLTAGE_OFF, NULL);
+ }
+ }
+ data->enable = false;
+ return 0;
+}
+
+static int tegra_regulator_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ struct tegra_regulator *data = rdev_get_drvdata(rdev);
+
+ BUG_ON(data->nr_vdd > 1);
+
+ dev_dbg(data->dev, "%s: %s->[%d..%d]uV enabled:%s\n", __func__,
+ data->rdesc.name, min_uV, max_uV,
+ data->enable ? "true" : "false");
+
+ if (data->enable) {
+ NvU32 settle;
+ NvRmPmuSetVoltage(s_hRmGlobal, data->vdd_list[0].id,
+ min_uV, &settle);
+ udelay(settle);
+ }
+ data->vdd_list[0].request_uV = min_uV;
+ return 0;
+}
+
+static struct regulator_ops tegra_soc_dynamic_vdd_ops = {
+ .enable = tegra_regulator_enable,
+ .disable = tegra_regulator_disable,
+ .set_voltage = tegra_regulator_set_voltage,
+};
+
+static struct regulator_ops tegra_soc_fixed_vdd_ops = {
+ .enable = tegra_regulator_enable,
+ .disable = tegra_regulator_disable,
+};
+
+static int tegra_regulator_register(struct platform_device *pdev,
+ struct tegra_regulator_entry *entry)
+{
+ const NvOdmPeripheralConnectivity *con;
+ struct tegra_regulator *reg;
+ struct tegra_regulator_drv *drv;
+ struct regulator_dev *rdev;
+ NvRmPmuVddRailCapabilities cap;
+ unsigned int i;
+ unsigned int cnt;
+
+ drv = platform_get_drvdata(pdev);
+
+ con = NvOdmPeripheralGetGuid(entry->guid);
+ if (!con)
+ return -ENODEV;
+
+ for (i=0, cnt=0; i<con->NumAddress; i++) {
+ if (con->AddressList[i].Interface == NvOdmIoModule_Vdd)
+ cnt++;
+ }
+
+ if (!cnt) {
+ char guid_str[9];
+ memcpy(guid_str, &entry->guid, 8);
+ guid_str[8] = 0;
+ dev_err(&pdev->dev, "(%s): no VDDs defined for %s\n",
+ entry->name, guid_str);
+ return -ENOSYS;
+ }
+
+ reg = kzalloc(sizeof(*reg) + sizeof(struct tegra_vdd)*(cnt-1), GFP_KERNEL);
+ if (!reg) {
+ dev_err(&pdev->dev, "out of memory\n");
+ return -ENOMEM;
+ }
+ reg->nr_vdd = cnt;
+ for (i=0, cnt=0; i<con->NumAddress; i++) {
+ NvU32 vdd;
+ if (con->AddressList[i].Interface != NvOdmIoModule_Vdd)
+ continue;
+
+ vdd = con->AddressList[i].Address;
+ NvRmPmuGetCapabilities(s_hRmGlobal, vdd, &cap);
+ reg->vdd_list[cnt].id = vdd;
+ reg->vdd_list[cnt].request_uV = cap.requestMilliVolts * 1000;
+ cnt++;
+ }
+
+ reg->rdesc.type = REGULATOR_VOLTAGE;
+ reg->rdesc.name = kstrdup(entry->name, GFP_KERNEL);
+ reg->rdesc.owner = THIS_MODULE;
+ reg->enable = false;
+ reg->dev = &pdev->dev;
+ reg->init.consumer_supplies = entry->consumers;
+ reg->init.num_consumer_supplies = entry->nr_consumers;
+ reg->rdesc.id = entry->id;
+ if (cnt>1 || cap.RmProtected) {
+ reg->rdesc.ops = &tegra_soc_fixed_vdd_ops;
+ reg->init.constraints.min_uV = reg->vdd_list[0].request_uV;
+ reg->init.constraints.max_uV = reg->vdd_list[0].request_uV;
+ reg->init.constraints.valid_ops_mask = REGULATOR_CHANGE_STATUS;
+ } else {
+ reg->rdesc.ops = &tegra_soc_dynamic_vdd_ops;
+ reg->init.constraints.min_uV = cap.MinMilliVolts * 1000;
+ reg->init.constraints.max_uV = cap.MaxMilliVolts * 1000;
+ reg->init.constraints.valid_ops_mask = REGULATOR_CHANGE_STATUS |
+ REGULATOR_CHANGE_VOLTAGE;
+ }
+
+ rdev = regulator_register(&reg->rdesc, &pdev->dev, &reg->init, reg);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "unable to register %s\n", entry->name);
+ kfree(reg);
+ return PTR_ERR(reg);
+ }
+ reg->rdev = rdev;
+
+ list_add_tail(&reg->node, &drv->list);
+
+ return 0;
+}
+
+static int tegra_regulator_probe(struct platform_device *pdev)
+{
+ struct tegra_regulator_platform_data *plat = pdev->dev.platform_data;
+ struct tegra_regulator_drv *drv;
+ int i;
+
+ drv = kzalloc(sizeof(*drv), GFP_KERNEL);
+ if (!drv)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&drv->list);
+ platform_set_drvdata(pdev, drv);
+
+ for (i=0; i<plat->nr_regs; i++)
+ tegra_regulator_register(pdev, &plat->regs[i]);
+
+ return 0;
+}
+
+static int tegra_regulator_remove(struct platform_device *pdev)
+{
+ struct tegra_regulator_drv *drv = platform_get_drvdata(pdev);
+
+ while (!list_empty(&drv->list)) {
+ struct tegra_regulator *reg;
+ reg = list_first_entry(&drv->list, struct tegra_regulator, node);
+ list_del_init(&drv->list);
+ regulator_unregister(reg->rdev);
+ kfree(reg);
+ }
+
+ kfree(drv);
+
+ return 0;
+}
+
+static struct platform_driver tegra_regulator_driver = {
+ .driver = {
+ .name = "tegra_regulator",
+ },
+ .probe = tegra_regulator_probe,
+ .remove = tegra_regulator_remove,
+};
+
+static int __init tegra_regulator_init(void)
+{
+ return platform_driver_register(&tegra_regulator_driver);
+}
+module_init(tegra_regulator_init);
+
+static void __exit tegra_regulator_exit(void)
+{
+ platform_driver_unregister(&tegra_regulator_driver);
+}
+module_exit(tegra_regulator_exit);
+
+MODULE_DESCRIPTION("Tegra regulator driver");
+MODULE_LICENSE("GPL");