/* * arch/arm/mach-tegra/tegra11_edp.c * * Copyright (C) 2012 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 "clock.h" #include "fuse.h" #define CORE_MODULES_STATES 1 #define TEMPERATURE_RANGES 3 #define CAP_CLKS_NUM 2 #define TOTAL_CAPS (CORE_EDP_PROFILES_NUM * CORE_MODULES_STATES *\ TEMPERATURE_RANGES * CAP_CLKS_NUM) struct core_edp_entry { int sku; unsigned int cap_mA; int mult; unsigned long cap_scpu_on[CORE_EDP_PROFILES_NUM][ CORE_MODULES_STATES][TEMPERATURE_RANGES][CAP_CLKS_NUM]; unsigned long cap_scpu_off[CORE_EDP_PROFILES_NUM][ CORE_MODULES_STATES][TEMPERATURE_RANGES][CAP_CLKS_NUM]; }; static int temperatures[] = { 50, 60, 120 }; #ifdef CONFIG_TEGRA_DUAL_CBUS static char *cap_clks_names[] = { "edp.c2bus", "edp.emc" }; #else static char *cap_clks_names[] = { "edp.cbus", "edp.emc" }; #endif static struct clk *cap_clks[CAP_CLKS_NUM]; static struct core_edp_entry core_edp_table[] = { { .sku = 0, /* SKU = 0 */ .cap_mA = 4000, /* 4A cap */ .mult = 1000000, /* MHZ */ .cap_scpu_on = { /* balanced profile */ { /* core modules power state 0 (all ON) */ {{ 520, 800 }, { 456, 550 }, { 370, 350 }, }, }, /* favor gpu */ { /* core modules power state 0 (all ON) */ {{ 520, 800 }, { 520, 300 }, { 520, 150 }, }, }, /* favor emc */ { /* core modules power state 0 (all ON) */ {{ 520, 800 }, { 372, 800 }, { 100, 800 }, } }, }, .cap_scpu_off = { /* balanced profile */ { /* core modules power state 0 (all ON) */ {{ 520, 800 }, { 456, 600 }, { 450, 400 }, }, }, /* favor gpu */ { /* core modules power state 0 (all ON) */ {{ 520, 800 }, { 520, 400 }, { 520, 200 }, }, }, /* favor emc */ { /* core modules power state 0 (all ON) */ {{ 520, 800 }, { 450, 800 }, { 380, 800 }, }, }, }, }, }; static struct core_edp_entry *find_edp_entry(int sku, unsigned int regulator_mA) { int i; for (i = 0; i < ARRAY_SIZE(core_edp_table); i++) { struct core_edp_entry *entry = &core_edp_table[i]; if ((entry->sku == sku) && (entry->cap_mA == regulator_mA)) return entry; } return NULL; } static unsigned long clip_cap_rate(struct clk *cap_clk, unsigned long rate) { unsigned long floor, ceiling; struct clk *p = clk_get_parent(cap_clk); if (!p || !p->ops || !p->ops->shared_bus_update) { WARN(1, "%s: edp cap clk %s is not a shared bus user\n", __func__, cap_clk->name); return rate; } /* * Clip cap rate to shared bus possible rates (going up via shared * bus * ladder since bus clocks always rounds up with resolution of * at least 2kHz) */ ceiling = clk_round_rate(p, clk_get_min_rate(p)); do { floor = ceiling; ceiling = clk_round_rate(p, floor + 2000); if (IS_ERR_VALUE(ceiling)) { pr_err("%s: failed to clip %lu to %s possible rates\n", __func__, rate, p->name); return rate; } } while ((floor < ceiling) && (ceiling <= rate)); if (floor > rate) WARN(1, "%s: %s cap rate %lu is below %s floor %lu\n", __func__, cap_clk->name, rate, p->name, floor); return floor; } int __init tegra11x_select_core_edp_table(unsigned int regulator_mA, struct tegra_core_edp_limits *limits) { int i; int sku = tegra_sku_id; unsigned long *cap_rates; struct core_edp_entry *edp_entry; BUG_ON(ARRAY_SIZE(temperatures) != TEMPERATURE_RANGES); BUG_ON(ARRAY_SIZE(cap_clks_names) != CAP_CLKS_NUM); for (i = 0; i < CAP_CLKS_NUM; i++) { struct clk *c = tegra_get_clock_by_name(cap_clks_names[i]); if (!c) { pr_err("%s: failed to find edp cap clock %s\n", __func__, cap_clks_names[i]); return -ENODEV; } cap_clks[i] = c; } edp_entry = find_edp_entry(sku, regulator_mA); if (!edp_entry) { pr_err("%s: failed to find edp entry for sku %d cap mA %d\n", __func__, sku, regulator_mA); return -ENODATA; } limits->sku = sku; limits->cap_clocks = cap_clks; limits->cap_clocks_num = CAP_CLKS_NUM; limits->temperatures = temperatures; limits->temperature_ranges = TEMPERATURE_RANGES; limits->core_modules_states = CORE_MODULES_STATES; cap_rates = &edp_entry->cap_scpu_on[0][0][0][0]; limits->cap_rates_scpu_on = cap_rates; for (i = 0; i < TOTAL_CAPS; i++, cap_rates++) { unsigned long rate = *cap_rates * edp_entry->mult; *cap_rates = clip_cap_rate(cap_clks[i % CAP_CLKS_NUM], rate); } cap_rates = &edp_entry->cap_scpu_off[0][0][0][0]; limits->cap_rates_scpu_off = cap_rates; for (i = 0; i < TOTAL_CAPS; i++, cap_rates++) { unsigned long rate = *cap_rates * edp_entry->mult; *cap_rates = clip_cap_rate(cap_clks[i % CAP_CLKS_NUM], rate); } return 0; }