summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/tegra_cl_dvfs.c
diff options
context:
space:
mode:
authorAlex Frid <afrid@nvidia.com>2014-01-04 21:33:35 -0800
committerYu-Huan Hsu <yhsu@nvidia.com>2014-01-17 14:44:52 -0800
commit5bf5a77113c0451e1cc3036945babe0c00e4b3ad (patch)
treed80e4811ebd19ff2add1bd7eaa6e0451755eb19b /arch/arm/mach-tegra/tegra_cl_dvfs.c
parent5e9a1ba85961b092b4fcd0d27831991ed1ef1e9f (diff)
ARM: tegra: dvfs: Build DFLL voltage selection map
Added an option to build DFLL voltage selection map dynamically, if static map is not provided in platform data. Two building algorithms: - Use regulator interface to match regulator selector to voltage level, and linear conversion of selector to register values. Applied when vdd supply with I2C interface and internal voltage selection register is connected. - Directly map PWM duty cycle to voltage level linearly, and record number of high PWM steps as register setting. Applied when vdd supply driven by DFLL PWM data output is connected. Conversion coefficients are added to DFLL platform data, and in both cases above can be specified instead of static voltage map. Change-Id: I1c41e7d13df564affb5cb02441d57a90128c14a4 Signed-off-by: Alex Frid <afrid@nvidia.com> Reviewed-on: http://git-master/r/356954 GVS: Gerrit_Virtual_Submit Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com>
Diffstat (limited to 'arch/arm/mach-tegra/tegra_cl_dvfs.c')
-rw-r--r--arch/arm/mach-tegra/tegra_cl_dvfs.c106
1 files changed, 98 insertions, 8 deletions
diff --git a/arch/arm/mach-tegra/tegra_cl_dvfs.c b/arch/arm/mach-tegra/tegra_cl_dvfs.c
index 86138f001f9b..fec1e443e366 100644
--- a/arch/arm/mach-tegra/tegra_cl_dvfs.c
+++ b/arch/arm/mach-tegra/tegra_cl_dvfs.c
@@ -1,7 +1,7 @@
/*
* arch/arm/mach-tegra/tegra_cl_dvfs.c
*
- * Copyright (c) 2012-2013 NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2012-2014 NVIDIA Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -30,6 +30,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
#include <linux/regulator/tegra-dfll-bypass-regulator.h>
#include <linux/tegra-soc.h>
@@ -1799,12 +1800,80 @@ static void tegra_cl_dvfs_register_simon_notifier(struct tegra_cl_dvfs *cld)
return;
}
+/*
+ * Two mechanisms to build vdd_map dynamically:
+ *
+ * 1. Use regulator interface to match voltage selector to voltage level,
+ * and platform data coefficients to convert selector to register values.
+ * Applied when vdd supply with I2C inteface and internal voltage selection
+ * register is connected.
+ *
+ * 2. Directly map PWM duty cycle selector to voltage level using platform data
+ * coefficients. Applied when vdd supply driven by PWM data output is connected.
+ */
+static int build_regulator_vdd_map(struct tegra_cl_dvfs_platform_data *p_data,
+ struct regulator *reg, struct voltage_reg_map **p_vdd_map)
+{
+ int n;
+ u32 sel, i;
+ struct voltage_reg_map *vdd_map;
+
+ if (!reg)
+ return -ENOSYS;
+
+ n = regulator_count_voltages(reg);
+ if (n <= 0)
+ return -ENODATA;
+
+ vdd_map = kzalloc(sizeof(*vdd_map) * n, GFP_KERNEL);
+ if (!vdd_map)
+ return -ENOMEM;
+
+ for (i = 0, sel = 0; sel < n; sel++) {
+ int v = regulator_list_voltage(reg, sel);
+ if (v > 0) {
+ vdd_map[i].reg_uV = v;
+ vdd_map[i].reg_value = sel * p_data->u.pmu_i2c.sel_mul +
+ p_data->u.pmu_i2c.sel_offs;
+ i++;
+ }
+ }
+
+ p_data->vdd_map_size = i;
+ p_data->vdd_map = vdd_map;
+ *p_vdd_map = vdd_map;
+ return i ? 0 : -EINVAL;
+}
+
+static int build_direct_vdd_map(struct tegra_cl_dvfs_platform_data *p_data,
+ struct voltage_reg_map **p_vdd_map)
+{
+ int i;
+ struct voltage_reg_map *vdd_map =
+ kzalloc(sizeof(*vdd_map) * MAX_CL_DVFS_VOLTAGES, GFP_KERNEL);
+
+ if (!vdd_map)
+ return -ENOMEM;
+
+ for (i = 0; i < MAX_CL_DVFS_VOLTAGES; i++) {
+ vdd_map[i].reg_uV = i * p_data->u.pmu_pwm.step_uV +
+ p_data->u.pmu_pwm.min_uV;
+ vdd_map[i].reg_value = i;
+ }
+
+ p_data->vdd_map_size = i;
+ p_data->vdd_map = vdd_map;
+ *p_vdd_map = vdd_map;
+ return 0;
+}
+
static int __init tegra_cl_dvfs_probe(struct platform_device *pdev)
{
int ret;
struct tegra_cl_dvfs_platform_data *p_data;
struct resource *res, *res_i2c = NULL;
- struct tegra_cl_dvfs *cld;
+ struct voltage_reg_map *p_vdd_map = NULL;
+ struct tegra_cl_dvfs *cld = NULL;
struct clk *ref_clk, *soc_clk, *i2c_clk, *safe_dvfs_clk, *dfll_clk;
/* Get resources */
@@ -1823,7 +1892,7 @@ static int __init tegra_cl_dvfs_probe(struct platform_device *pdev)
}
p_data = pdev->dev.platform_data;
- if (!p_data || !p_data->cfg_param || !p_data->vdd_map) {
+ if (!p_data || !p_data->cfg_param) {
dev_err(&pdev->dev, "missing platform data\n");
return -ENODATA;
}
@@ -1850,11 +1919,26 @@ static int __init tegra_cl_dvfs_probe(struct platform_device *pdev)
return -EINVAL;
}
+ /* Build vdd_map if not specified by platform data */
+ if (!p_data->vdd_map || !p_data->vdd_map_size) {
+ struct regulator *reg = safe_dvfs_clk->dvfs->dvfs_rail->reg;
+ if (p_data->pmu_if == TEGRA_CL_DVFS_PMU_PWM)
+ ret = build_direct_vdd_map(p_data, &p_vdd_map);
+ else
+ ret = build_regulator_vdd_map(p_data, reg, &p_vdd_map);
+
+ if (ret) {
+ dev_err(&pdev->dev, "missing vdd_map (%d)\n", ret);
+ goto err_out;
+ }
+ }
+
/* Allocate cl_dvfs object and populate resource accessors */
cld = kzalloc(sizeof(*cld), GFP_KERNEL);
if (!cld) {
dev_err(&pdev->dev, "failed to allocate cl_dvfs object\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err_out;
}
cld->cl_base = IO_ADDRESS(res->start);
@@ -1870,11 +1954,10 @@ static int __init tegra_cl_dvfs_probe(struct platform_device *pdev)
#endif
/* Initialize cl_dvfs */
ret = cl_dvfs_init(cld);
- if (ret) {
- kfree(cld);
- return ret;
- }
+ if (ret)
+ goto err_out;
+ /* From now on probe would not fail */
platform_set_drvdata(pdev, cld);
/*
@@ -1904,6 +1987,13 @@ static int __init tegra_cl_dvfs_probe(struct platform_device *pdev)
cld->safe_dvfs->dvfs_rail->vmax_cdev)
schedule_work(&cld->init_cdev_work);
return 0;
+
+err_out:
+ if (p_data && p_vdd_map)
+ p_data->vdd_map = NULL;
+ kfree(p_vdd_map);
+ kfree(cld);
+ return ret;
}
static struct platform_driver tegra_cl_dvfs_driver = {