blob: b040fdcfb978cfb61da9e66743433d61839574ab [file] [log] [blame]
/*
* 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 <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/module.h>
#include <linux/clk.h>
#include <linux/kobject.h>
#include <linux/err.h>
#include <mach/edp.h>
#include "clock.h"
#include "fuse.h"
#define CORE_MODULES_STATES 1
#define TEMPERATURE_RANGES 4
#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;
int process_id;
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, 70, 90, 105 };
#ifdef CONFIG_TEGRA_DUAL_CBUS
static char *cap_clks_names[] = { "edp.emc", "edp.c2bus" };
#else
static char *cap_clks_names[] = { "edp.emc", "edp.cbus" };
#endif
static struct clk *cap_clks[CAP_CLKS_NUM];
static struct core_edp_entry core_edp_table[] = {
/* SKU 3 */
{
.sku = 0x3, /* SKU = 3 */
.process_id = -1, /* any process id */
.cap_mA = 6000, /* 6A cap */
.mult = 1000000, /* MHZ */
.cap_scpu_on = {
/* favor emc */
{ /* core modules power state 0 (all ON) */
{{ 924, 636 },
{ 924, 612 },
{ 924, 564 },
{ 924, 384 },
},
},
/* balanced profile */
{ /* core modules power state 0 (all ON) */
{{ 792, 636 },
{ 792, 636 },
{ 792, 636 },
{ 792, 384 },
},
},
/* favor gpu */
{ /* core modules power state 0 (all ON) */
{{ 624, 672 },
{ 624, 672 },
{ 528, 672 },
{ 528, 384 },
}
},
},
.cap_scpu_off = {
/* favor emc */
{ /* core modules power state 0 (all ON) */
{{ 924, 672 },
{ 924, 648 },
{ 924, 636 },
{ 924, 516 },
},
},
/* balanced profile */
{ /* core modules power state 0 (all ON) */
{{ 924, 672 },
{ 792, 672 },
{ 792, 672 },
{ 792, 516 },
},
},
/* favor gpu */
{ /* core modules power state 0 (all ON) */
{{ 924, 672 },
{ 792, 672 },
{ 792, 672 },
{ 792, 516 },
}
},
},
},
{
.sku = 0x3, /* SKU = 3 */
.process_id = -1, /* any process id */
.cap_mA = 8000, /* 8A cap */
.mult = 1000000, /* MHZ */
.cap_scpu_on = {
/* favor emc */
{ /* core modules power state 0 (all ON) */
{{ 924, 672 },
{ 924, 672 },
{ 924, 672 },
{ 924, 588 },
},
},
/* balanced profile */
{ /* core modules power state 0 (all ON) */
{{ 924, 672 },
{ 924, 672 },
{ 924, 672 },
{ 924, 588 },
},
},
/* favor gpu */
{ /* core modules power state 0 (all ON) */
{{ 924, 672 },
{ 924, 672 },
{ 924, 672 },
{ 924, 588 },
}
},
},
.cap_scpu_off = {
/* favor emc */
{ /* core modules power state 0 (all ON) */
{{ 924, 672 },
{ 924, 672 },
{ 924, 672 },
{ 924, 588 },
},
},
/* balanced profile */
{ /* core modules power state 0 (all ON) */
{{ 924, 672 },
{ 924, 672 },
{ 924, 672 },
{ 924, 588 },
},
},
/* favor gpu */
{ /* core modules power state 0 (all ON) */
{{ 924, 672 },
{ 924, 672 },
{ 924, 672 },
{ 924, 588 },
}
},
},
},
/* SKU 5 */
{
.sku = 0x5, /* SKU = 5 */
.process_id = 0, /* bin 0 */
.cap_mA = 4000, /* 4A cap */
.mult = 1000000, /* MHZ */
.cap_scpu_on = {
/* favor emc */
{ /* core modules power state 0 (all ON) */
{{ 792, 336 },
{ 792, 336 },
{ 792, 300 },
{ 792, 240 },
},
},
/* balanced profile */
{ /* core modules power state 0 (all ON) */
{{ 624, 396 },
{ 660, 372 },
{ 660, 324 },
{ 660, 288 },
},
},
/* favor gpu */
{ /* core modules power state 0 (all ON) */
{{ 408, 492 },
{ 408, 396 },
{ 408, 396 },
{ 408, 396 },
}
},
},
.cap_scpu_off = {
/* favor emc */
{ /* core modules power state 0 (all ON) */
{{ 792, 432 },
{ 792, 432 },
{ 792, 396 },
{ 792, 348 },
},
},
/* balanced profile */
{ /* core modules power state 0 (all ON) */
{{ 624, 492 },
{ 660, 492 },
{ 660, 444 },
{ 660, 384 },
},
},
/* favor gpu */
{ /* core modules power state 0 (all ON) */
{{ 408, 516 },
{ 408, 516 },
{ 408, 516 },
{ 408, 492 },
}
},
},
},
{
.sku = 0x5, /* SKU = 5 */
.process_id = 1, /* bin 1 */
.cap_mA = 4000, /* 4A cap */
.mult = 1000000, /* MHZ */
.cap_scpu_on = {
/* favor emc */
{ /* core modules power state 0 (all ON) */
{{ 792, 348 },
{ 792, 336 },
{ 792, 300 },
{ 792, 240 },
},
},
/* balanced profile */
{ /* core modules power state 0 (all ON) */
{{ 624, 420 },
{ 660, 372 },
{ 660, 324 },
{ 660, 288 },
},
},
/* favor gpu */
{ /* core modules power state 0 (all ON) */
{{ 408, 528 },
{ 408, 492 },
{ 408, 420 },
{ 408, 420 },
}
},
},
.cap_scpu_off = {
/* favor emc */
{ /* core modules power state 0 (all ON) */
{{ 792, 432 },
{ 792, 432 },
{ 792, 396 },
{ 792, 348 },
},
},
/* balanced profile */
{ /* core modules power state 0 (all ON) */
{{ 624, 528 },
{ 660, 492 },
{ 660, 444 },
{ 660, 384 },
},
},
/* favor gpu */
{ /* core modules power state 0 (all ON) */
{{ 408, 564 },
{ 408, 564 },
{ 408, 528 },
{ 408, 528 },
}
},
},
},
{
.sku = 0x5, /* SKU = 5 */
.process_id = -1, /* any process id */
.cap_mA = 6000, /* 6A cap */
.mult = 1000000, /* MHZ */
.cap_scpu_on = {
/* favor emc */
{ /* core modules power state 0 (all ON) */
{{ 792, 600 },
{ 792, 600 },
{ 792, 600 },
{ 792, 516 },
},
},
/* balanced profile */
{ /* core modules power state 0 (all ON) */
{{ 792, 600 },
{ 792, 600 },
{ 660, 600 },
{ 660, 564 },
},
},
/* favor gpu */
{ /* core modules power state 0 (all ON) */
{{ 792, 600 },
{ 792, 600 },
{ 660, 600 },
{ 528, 600 },
}
},
},
.cap_scpu_off = {
/* favor emc */
{ /* core modules power state 0 (all ON) */
{{ 792, 600 },
{ 792, 600 },
{ 792, 600 },
{ 792, 600 },
},
},
/* balanced profile */
{ /* core modules power state 0 (all ON) */
{{ 792, 600 },
{ 792, 600 },
{ 792, 600 },
{ 624, 600 },
},
},
/* favor gpu */
{ /* core modules power state 0 (all ON) */
{{ 792, 600 },
{ 792, 600 },
{ 792, 600 },
{ 624, 600 },
}
},
},
},
/* SKU 6 */
{
.sku = 0x6, /* SKU = 6 */
.process_id = 0, /* bin 0 */
.cap_mA = 4000, /* 4A cap */
.mult = 1000000, /* MHZ */
.cap_scpu_on = {
/* favor emc */
{ /* core modules power state 0 (all ON) */
{{ 792, 348 },
{ 792, 348 },
{ 792, 312 },
{ 792, 264 },
},
},
/* balanced profile */
{ /* core modules power state 0 (all ON) */
{{ 660, 372 },
{ 660, 372 },
{ 660, 336 },
{ 660, 300 },
},
},
/* favor gpu */
{ /* core modules power state 0 (all ON) */
{{ 408, 492 },
{ 408, 396 },
{ 408, 396 },
{ 408, 396 },
}
},
},
.cap_scpu_off = {
/* favor emc */
{ /* core modules power state 0 (all ON) */
{{ 792, 444 },
{ 792, 444 },
{ 792, 408 },
{ 792, 372 },
},
},
/* balanced profile */
{ /* core modules power state 0 (all ON) */
{{ 660, 492 },
{ 660, 492 },
{ 660, 456 },
{ 660, 408 },
},
},
/* favor gpu */
{ /* core modules power state 0 (all ON) */
{{ 408, 552 },
{ 408, 516 },
{ 408, 516 },
{ 408, 516 },
}
},
},
},
{
.sku = 0x6, /* SKU = 6 */
.process_id = 1, /* bin 1 */
.cap_mA = 4000, /* 4A cap */
.mult = 1000000, /* MHZ */
.cap_scpu_on = {
/* favor emc */
{ /* core modules power state 0 (all ON) */
{{ 792, 348 },
{ 792, 348 },
{ 792, 312 },
{ 792, 264 },
},
},
/* balanced profile */
{ /* core modules power state 0 (all ON) */
{{ 660, 420 },
{ 660, 372 },
{ 660, 336 },
{ 660, 300 },
},
},
/* favor gpu */
{ /* core modules power state 0 (all ON) */
{{ 408, 528 },
{ 408, 492 },
{ 408, 420 },
{ 408, 420 },
}
},
},
.cap_scpu_off = {
/* favor emc */
{ /* core modules power state 0 (all ON) */
{{ 792, 444 },
{ 792, 444 },
{ 792, 408 },
{ 792, 372 },
},
},
/* balanced profile */
{ /* core modules power state 0 (all ON) */
{{ 660, 492 },
{ 660, 492 },
{ 660, 456 },
{ 660, 408 },
},
},
/* favor gpu */
{ /* core modules power state 0 (all ON) */
{{ 408, 564 },
{ 408, 564 },
{ 408, 564 },
{ 408, 528 },
}
},
},
},
{
.sku = 0x6, /* SKU = 6 */
.process_id = -1, /* any process id */
.cap_mA = 6000, /* 6A cap */
.mult = 1000000, /* MHZ */
.cap_scpu_on = {
/* favor emc */
{ /* core modules power state 0 (all ON) */
{{ 792, 600 },
{ 792, 600 },
{ 792, 600 },
{ 792, 516 },
},
},
/* balanced profile */
{ /* core modules power state 0 (all ON) */
{{ 792, 600 },
{ 792, 600 },
{ 792, 600 },
{ 660, 600 },
},
},
/* favor gpu */
{ /* core modules power state 0 (all ON) */
{{ 792, 600 },
{ 792, 600 },
{ 792, 600 },
{ 660, 600 },
}
},
},
.cap_scpu_off = {
/* favor emc */
{ /* core modules power state 0 (all ON) */
{{ 792, 600 },
{ 792, 600 },
{ 792, 600 },
{ 792, 600 },
},
},
/* balanced profile */
{ /* core modules power state 0 (all ON) */
{{ 792, 600 },
{ 792, 600 },
{ 792, 600 },
{ 792, 600 },
},
},
/* favor gpu */
{ /* core modules power state 0 (all ON) */
{{ 792, 600 },
{ 792, 600 },
{ 792, 600 },
{ 792, 600 },
}
},
},
},
};
static struct core_edp_entry *find_edp_entry(int sku, unsigned int regulator_mA)
{
int i;
int pid = tegra_core_process_id();
if ((sku == 0x5) || (sku == 0x6)) {
if (regulator_mA >= 8000)
return NULL; /* no edp limits above 8A */
} else if (sku == 0x3) {
if (regulator_mA >= 8000)
regulator_mA = 8000; /* apply 8A table above 8A */
} else {
return NULL; /* no edp limits at all */
}
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) &&
((entry->process_id == -1) || (entry->process_id == pid)))
return entry;
}
return ERR_PTR(-ENOENT);
}
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_info("%s: no core edp table for sku %d, %d mA\n",
__func__, sku, regulator_mA);
return -ENODATA;
} else if (IS_ERR(edp_entry)) {
WARN(1, "%s: missing core edp table for sku %d, %d mA\n",
__func__, sku, regulator_mA);
return PTR_ERR(edp_entry);
}
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;
}