diff options
author | Prashant Gaikwad <pgaikwad@nvidia.com> | 2012-08-16 19:11:32 +0530 |
---|---|---|
committer | Rohan Somvanshi <rsomvanshi@nvidia.com> | 2012-09-18 07:14:57 -0700 |
commit | 149d6f2e5a5972bb0d785ff548534ea5809dfd7a (patch) | |
tree | a4a9db0742adf8549e2217309daec3421670bbb6 /arch/arm | |
parent | 7d02b8307bda47bb917b8da84e9300685e91eb6d (diff) |
ARM: Tegra: Add support for T30 DVFS DT bindings
This patch implements functions required to parse
DVFS tabled defined in device tree.
Bug 906383
Change-Id: I13cf76b3a8c1284349d6684f91f62f011501c84d
Signed-off-by: Prashant Gaikwad <pgaikwad@nvidia.com>
Reviewed-on: http://git-master/r/130671
Reviewed-by: Automatic_Commit_Validation_User
GVS: Gerrit_Virtual_Submit
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
Diffstat (limited to 'arch/arm')
-rw-r--r-- | arch/arm/mach-tegra/dvfs.c | 20 | ||||
-rw-r--r-- | arch/arm/mach-tegra/dvfs.h | 19 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra3_dvfs.c | 265 |
3 files changed, 275 insertions, 29 deletions
diff --git a/arch/arm/mach-tegra/dvfs.c b/arch/arm/mach-tegra/dvfs.c index 4565ca0cd7db..b0180ed17d68 100644 --- a/arch/arm/mach-tegra/dvfs.c +++ b/arch/arm/mach-tegra/dvfs.c @@ -718,6 +718,26 @@ bool tegra_dvfs_rail_updating(struct clk *clk) (clk->dvfs->dvfs_rail->updating)))); } +#ifdef CONFIG_OF +int __init of_tegra_dvfs_init(const struct of_device_id *matches) +{ + int ret; + struct device_node *np; + + for_each_matching_node(np, matches) { + const struct of_device_id *match = of_match_node(matches, np); + of_tegra_dvfs_init_cb_t dvfs_init_cb = match->data; + ret = dvfs_init_cb(np); + if (ret) { + pr_err("dt: Failed to read %s tables from DT\n", + match->compatible); + return ret; + } + } + return 0; +} +#endif + /* * Iterate through all the dvfs regulators, finding the regulator exported * by the regulator api for each one. Must be called in late init, after diff --git a/arch/arm/mach-tegra/dvfs.h b/arch/arm/mach-tegra/dvfs.h index eeb88a238409..4bf976b1133e 100644 --- a/arch/arm/mach-tegra/dvfs.h +++ b/arch/arm/mach-tegra/dvfs.h @@ -21,7 +21,10 @@ #ifndef _TEGRA_DVFS_H_ #define _TEGRA_DVFS_H_ +#include <linux/of.h> + #define MAX_DVFS_FREQS 40 +#define MAX_DVFS_TABLES 80 #define DVFS_RAIL_STATS_TOP_BIN 42 struct clk; @@ -122,6 +125,22 @@ struct cpu_cvb_dvfs { extern struct dvfs_rail *tegra_cpu_rail; extern struct dvfs_rail *tegra_core_rail; +struct dvfs_data { + struct dvfs_rail *rail; + struct dvfs *tables; + int *millivolts; + unsigned int num_tables; + unsigned int num_voltages; +}; + +#ifdef CONFIG_OF +typedef int (*of_tegra_dvfs_init_cb_t)(struct device_node *); +int of_tegra_dvfs_init(const struct of_device_id *matches); +#else +static inline int of_tegra_dvfs_init(const struct of_device_id *matches) +{ return -ENODATA; } +#endif + void tegra2_init_dvfs(void); void tegra3_init_dvfs(void); void tegra11x_init_dvfs(void); diff --git a/arch/arm/mach-tegra/tegra3_dvfs.c b/arch/arm/mach-tegra/tegra3_dvfs.c index 16356835055d..53f49aacc481 100644 --- a/arch/arm/mach-tegra/tegra3_dvfs.c +++ b/arch/arm/mach-tegra/tegra3_dvfs.c @@ -38,12 +38,16 @@ static struct dvfs *cpu_dvfs; static int cpu_millivolts[MAX_DVFS_FREQS] = CPU_MILLIVOLTS; -static const int cpu_millivolts_aged[MAX_DVFS_FREQS] = CPU_MILLIVOLTS; +static int cpu_millivolts_aged[MAX_DVFS_FREQS]; -static const unsigned int cpu_cold_offs_mhz[MAX_DVFS_FREQS] = { +static struct dvfs_data *cpu_dvfs_data; +static struct dvfs_data *cpu_0_dvfs_data; +static struct dvfs_data *core_dvfs_data; + +static unsigned int cpu_cold_offs_mhz[MAX_DVFS_FREQS] = { 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50}; -static const int core_millivolts[MAX_DVFS_FREQS] = { +static int core_millivolts[MAX_DVFS_FREQS] = { 950, 1000, 1050, 1100, 1150, 1200, 1250, 1300, 1350}; #define KHZ 1000 @@ -76,6 +80,33 @@ static struct dvfs_rail *tegra3_dvfs_rails[] = { &tegra3_dvfs_rail_vdd_core, }; +#ifdef CONFIG_OF +static int of_cpu_millivolts[MAX_DVFS_FREQS]; +static int of_core_millivolts[MAX_DVFS_FREQS]; + +static struct dvfs of_cpu_dvfs[MAX_DVFS_TABLES]; +static struct dvfs of_cpu_0_dvfs[MAX_DVFS_TABLES]; +static struct dvfs of_core_dvfs[MAX_DVFS_TABLES]; + +static struct dvfs_data of_cpu_data = { + .tables = of_cpu_dvfs, + .millivolts = of_cpu_millivolts, + .rail = &tegra3_dvfs_rail_vdd_cpu, +}; + +static struct dvfs_data of_cpu_0_data = { + .tables = of_cpu_0_dvfs, + .millivolts = of_cpu_millivolts, + .rail = &tegra3_dvfs_rail_vdd_cpu, +}; + +static struct dvfs_data of_core_data = { + .tables = of_core_dvfs, + .millivolts = of_core_millivolts, + .rail = &tegra3_dvfs_rail_vdd_core, +}; +#endif + static int tegra3_get_core_floor_mv(int cpu_mv) { if (cpu_mv < 800) @@ -371,6 +402,30 @@ static unsigned long cpu_cold_freqs[MAX_DVFS_FREQS]; /* CPU alternative DVFS table for single G CPU core 0 */ static unsigned long *cpu_0_freqs; +static struct dvfs_data tegra30_cpu_data = { + .tables = cpu_dvfs_table, + .num_tables = ARRAY_SIZE(cpu_dvfs_table), + .millivolts = cpu_millivolts, + .num_voltages = ARRAY_SIZE(cpu_millivolts), + .rail = &tegra3_dvfs_rail_vdd_cpu, +}; + +static struct dvfs_data tegra30_cpu_0_data = { + .tables = cpu_0_dvfs_table, + .num_tables = ARRAY_SIZE(cpu_0_dvfs_table), + .millivolts = cpu_millivolts, + .num_voltages = ARRAY_SIZE(cpu_millivolts), + .rail = &tegra3_dvfs_rail_vdd_cpu, +}; + +static struct dvfs_data tegra30_core_data = { + .tables = core_dvfs_table, + .num_tables = ARRAY_SIZE(core_dvfs_table), + .millivolts = core_millivolts, + .num_voltages = ARRAY_SIZE(core_millivolts), + .rail = &tegra3_dvfs_rail_vdd_core, +}; + int tegra_dvfs_disable_core_set(const char *arg, const struct kernel_param *kp) { int ret; @@ -524,10 +579,10 @@ static void __init init_cpu_0_dvfs(struct dvfs *cpud) /* Init single G CPU core 0 dvfs if this particular SKU/bin has it. Max rates in multi-core and single-core tables must be the same */ - for (i = 0; i < ARRAY_SIZE(cpu_0_dvfs_table); i++) { - if (match_dvfs_one(&cpu_0_dvfs_table[i], + for (i = 0; i < cpu_0_dvfs_data->num_tables; i++) { + if (match_dvfs_one(&cpu_0_dvfs_data->tables[i], cpud->speedo_id, cpud->process_id)) { - d = &cpu_0_dvfs_table[i]; + d = &cpu_0_dvfs_data->tables[i]; break; } } @@ -550,7 +605,7 @@ static int __init get_cpu_nominal_mv_index( int speedo_id, int process_id, struct dvfs **cpu_dvfs) { int i, j, mv; - struct dvfs *d; + struct dvfs *d = NULL; struct clk *c; /* @@ -559,13 +614,13 @@ static int __init get_cpu_nominal_mv_index( * result to the nominal cpu level for the chips with this speedo_id. */ mv = tegra3_dvfs_rail_vdd_core.nominal_millivolts; - for (i = 0; i < MAX_DVFS_FREQS; i++) { - if ((cpu_millivolts[i] == 0) || - tegra3_get_core_floor_mv(cpu_millivolts[i]) > mv) + for (i = 0; i < cpu_dvfs_data->num_voltages; i++) { + if ((cpu_dvfs_data->millivolts[i] == 0) || + tegra3_get_core_floor_mv(cpu_dvfs_data->millivolts[i]) > mv) break; } BUG_ON(i == 0); - mv = cpu_millivolts[i - 1]; + mv = cpu_dvfs_data->millivolts[i - 1]; BUG_ON(mv < tegra3_dvfs_rail_vdd_cpu.min_millivolts); mv = min(mv, tegra_cpu_speedo_mv()); @@ -576,16 +631,16 @@ static int __init get_cpu_nominal_mv_index( * the dvfs entry and clock tree * - does not violate cpu_to_core dependency as determined above */ - for (i = 0, j = 0; j < ARRAY_SIZE(cpu_dvfs_table); j++) { - d = &cpu_dvfs_table[j]; + for (i = 0, j = 0; j < cpu_dvfs_data->num_tables; j++) { + d = &cpu_dvfs_data->tables[j]; if (match_dvfs_one(d, speedo_id, process_id)) { c = tegra_get_clock_by_name(d->clk_name); BUG_ON(!c); for (; i < MAX_DVFS_FREQS; i++) { if ((d->freqs[i] == 0) || - (cpu_millivolts[i] == 0) || - (mv < cpu_millivolts[i])) + (cpu_dvfs_data->millivolts[i] == 0) || + (mv < cpu_dvfs_data->millivolts[i])) break; if (c->max_rate <= d->freqs[i]*d->freqs_mult) { @@ -598,7 +653,7 @@ static int __init get_cpu_nominal_mv_index( } BUG_ON(i == 0); - if (j == (ARRAY_SIZE(cpu_dvfs_table) - 1)) + if (j == (cpu_dvfs_data->num_tables - 1)) pr_err("tegra3_dvfs: WARNING!!!\n" "tegra3_dvfs: no cpu dvfs table found for chip speedo_id" " %d and process_id %d: set CPU rate limit at %lu\n" @@ -624,8 +679,9 @@ static int __init get_core_nominal_mv_index(int speedo_id) mv = min(mv, core_edp_limit); /* Round nominal level down to the nearest core scaling step */ - for (i = 0; i < MAX_DVFS_FREQS; i++) { - if ((core_millivolts[i] == 0) || (mv < core_millivolts[i])) + for (i = 0; i < core_dvfs_data->num_voltages; i++) { + if ((core_dvfs_data->millivolts[i] == 0) || + (mv < core_dvfs_data->millivolts[i])) break; } @@ -643,8 +699,8 @@ static void tegra_adjust_cpu_mvs(int mvs) BUG_ON(ARRAY_SIZE(cpu_millivolts) != ARRAY_SIZE(cpu_millivolts_aged)); - for (i = 0; i < ARRAY_SIZE(cpu_millivolts); i++) - cpu_millivolts[i] = cpu_millivolts_aged[i] - mvs; + for (i = 0; i < cpu_dvfs_data->num_voltages; i++) + cpu_dvfs_data->millivolts[i] = cpu_millivolts_aged[i] - mvs; } /** @@ -672,6 +728,142 @@ void tegra_dvfs_age_cpu(int cur_linear_age) } } +#ifdef CONFIG_OF +static int __init of_read_dvfs_properties(struct device_node *np, + struct dvfs *d, struct dvfs_data *data) +{ + bool auto_dvfs = !of_property_read_bool(np, "nvidia,manual-dvfs"); + const char *clk_name; + int speedo_id; + int process_id; + + if (of_property_read_string(np, "nvidia,clock-name", &clk_name)) + return -ENODATA; + + if (of_property_read_u32(np, "nvidia,speedo-id", &speedo_id)) + speedo_id = -1; + + if (of_property_read_u32(np, "nvidia,process-id", &process_id)) + process_id = -1; + + d->clk_name = clk_name; + d->speedo_id = speedo_id; + d->process_id = process_id; + d->freqs_mult = KHZ; + d->dvfs_rail = data->rail; + d->millivolts = data->millivolts; + d->auto_dvfs = auto_dvfs; + + return 0; +} + +static int __init of_read_dvfs_data(struct device_node *np, + struct dvfs_data *data) +{ + struct device_node *next = NULL; + struct property *prop; + int ret = 0; + int count = 0; + const __be32 *p; + u32 u; + + if (!data->tables || !data->millivolts) + return -ENODEV; + + of_property_for_each_u32(np, "nvidia,voltage-table", prop, p, u) { + if (count == MAX_DVFS_FREQS) + return -EOVERFLOW; + data->millivolts[count] = u; + count++; + } + + if (!count) + return -ENODATA; + + data->num_voltages = count; + + while (data->num_tables < MAX_DVFS_TABLES) { + struct dvfs *d = NULL; + int i = 0; + + next = of_get_next_child(np, next); + if (!next) + break; + + d = &data->tables[data->num_tables]; + ret = of_read_dvfs_properties(next, d, data); + if (ret) { + pr_warn("Failed to read DVFS table properties\n"); + return ret; + } + + prop = of_find_property(next, "nvidia,frequencies", NULL); + if (!prop) { + pr_debug("Frequencies property not found in DT\n"); + return -ENODATA; + } + + p = of_prop_next_u32(prop, NULL, &u); + while (p && i < data->num_voltages) { + d->freqs[i] = u; + p = of_prop_next_u32(prop, p, &u); + i++; + } + + d->num_freqs = i; + data->num_tables++; + }; + + return ret; +} +#endif /* CONFIG_OF */ + +static int __init of_read_cpu_g_dvfs_data(struct device_node *np) +{ + int ret; + int i; + struct dvfs *d; + int cpu_speedo_id = tegra_cpu_speedo_id(); + int cpu_process_id = tegra_cpu_process_id(); + + ret = of_read_dvfs_data(np, &of_cpu_data); + if (ret) { + pr_debug("Failed to read CPU DVFS tables from DT\n"); + return ret; + } + + for (i = 0; i < of_cpu_data.num_tables; i++) { + d = &of_cpu_data.tables[i]; + if (match_dvfs_one(d, cpu_speedo_id, cpu_process_id)) + break; + } + + if (i >= of_cpu_data.num_tables) + return -ENODATA; + + return 0; +} + +static int __init of_read_cpu_0_dvfs_data(struct device_node *np) +{ + return of_read_dvfs_data(np, &of_cpu_0_data); +} + +static int __init of_read_core_dvfs_data(struct device_node *np) +{ + return of_read_dvfs_data(np, &of_core_data); +} + +static const __initconst struct of_device_id dvfs_match[] = { + { .compatible = "nvidia,tegra30-cpu-dvfs", + .data = of_read_cpu_g_dvfs_data, }, + { .compatible = "nvidia,tegra30-cpu0-dvfs", + .data = of_read_cpu_0_dvfs_data, }, + { .compatible = "nvidia,tegra30-core-dvfs", + .data = of_read_core_dvfs_data, }, + {} +}; + void __init tegra3_init_dvfs(void) { int cpu_speedo_id = tegra_cpu_speedo_id(); @@ -690,6 +882,20 @@ void __init tegra3_init_dvfs(void) tegra_dvfs_cpu_disabled = true; #endif + if (!of_tegra_dvfs_init(dvfs_match) && of_cpu_data.num_tables > 0 && + of_core_data.num_tables > 0) { + cpu_dvfs_data = &of_cpu_data; + cpu_0_dvfs_data = &of_cpu_0_data; + core_dvfs_data = &of_core_data; + } else { + cpu_dvfs_data = &tegra30_cpu_data; + cpu_0_dvfs_data = &tegra30_cpu_0_data; + core_dvfs_data = &tegra30_core_data; + } + + for (i = 0; i < cpu_dvfs_data->num_voltages; i++) + cpu_millivolts_aged[i] = cpu_dvfs_data->millivolts[i]; + /* * Find nominal voltages for core (1st) and cpu rails before rail * init. Nominal voltage index in the scaling ladder will also be @@ -702,13 +908,14 @@ void __init tegra3_init_dvfs(void) core_nominal_mv_index = 0; } tegra3_dvfs_rail_vdd_core.nominal_millivolts = - core_millivolts[core_nominal_mv_index]; + core_dvfs_data->millivolts[core_nominal_mv_index]; cpu_nominal_mv_index = get_cpu_nominal_mv_index( cpu_speedo_id, cpu_process_id, &cpu_dvfs); + BUG_ON((cpu_nominal_mv_index < 0) || (!cpu_dvfs)); tegra3_dvfs_rail_vdd_cpu.nominal_millivolts = - cpu_millivolts[cpu_nominal_mv_index]; + cpu_dvfs->millivolts[cpu_nominal_mv_index]; /* Init rail structures and dependencies */ tegra_dvfs_init_rails(tegra3_dvfs_rails, ARRAY_SIZE(tegra3_dvfs_rails)); @@ -717,8 +924,8 @@ void __init tegra3_init_dvfs(void) /* Search core dvfs table for speedo/process matching entries and initialize dvfs-ed clocks */ - for (i = 0; i < ARRAY_SIZE(core_dvfs_table); i++) { - struct dvfs *d = &core_dvfs_table[i]; + for (i = 0; i < core_dvfs_data->num_tables; i++) { + struct dvfs *d = &core_dvfs_data->tables[i]; if (!match_dvfs_one(d, soc_speedo_id, core_process_id)) continue; init_dvfs_one(d, core_nominal_mv_index); @@ -840,13 +1047,13 @@ static void core_cap_level_set(int level) { int i, j; - for (j = 0; j < ARRAY_SIZE(core_millivolts); j++) { - int v = core_millivolts[j]; + for (j = 0; j < core_dvfs_data->num_voltages; j++) { + int v = core_dvfs_data->millivolts[j]; if ((v == 0) || (level < v)) break; } j = (j == 0) ? 0 : j - 1; - level = core_millivolts[j]; + level = core_dvfs_data->millivolts[j]; if (level < tegra3_core_cap.level) { for (i = 0; i < ARRAY_SIZE(core_cap_table); i++) @@ -1057,8 +1264,8 @@ static int __init init_core_cap_one(struct clk *c, unsigned long *freqs) int i, v, next_v = 0; unsigned long rate, next_rate = 0; - for (i = 0; i < ARRAY_SIZE(core_millivolts); i++) { - v = core_millivolts[i]; + for (i = 0; i < core_dvfs_data->num_voltages; i++) { + v = core_dvfs_data->millivolts[i]; if (v == 0) break; |