blob: 414254943155cab8d429e2644ecb942731479dc7 [file] [log] [blame]
#include "pwrcal-env.h"
#include "pwrcal-pmu.h"
#include "pwrcal-clk.h"
#include "pwrcal-vclk.h"
#include "pwrcal-dfs.h"
unsigned int dfs_set_rate_switch(unsigned int rate_from,
unsigned int rate_to,
struct dfs_table *table)
{
unsigned int rate_max;
int i;
rate_max = (rate_from > rate_to) ? rate_from : rate_to;
for (i = 0; i < table->num_of_switches; i++) {
if (rate_max >= table->switches[i].switch_rate) {
if (is_div(table->switch_src_div))
if (pwrcal_div_set_ratio(
table->switch_src_div,
table->switches[i].div_value + 1))
goto errorout;
if (is_mux(table->switch_src_mux))
if (pwrcal_mux_set_src(
table->switch_src_mux,
table->switches[i].mux_value))
goto errorout;
return table->switches[i].switch_rate;
}
}
return table->switches[table->num_of_switches - 1].switch_rate;
errorout:
return 0;
}
int dfs_enable_switch(struct dfs_table *table)
{
if (is_gate(table->switch_src_gate))
if (pwrcal_gate_enable(table->switch_src_gate))
return -1;
if (is_mux(table->switch_src_usermux))
if (pwrcal_mux_set_src(table->switch_src_usermux, 1))
return -1;
return 0;
}
int dfs_disable_switch(struct dfs_table *table)
{
if (is_mux(table->switch_src_usermux))
if (pwrcal_mux_set_src(table->switch_src_usermux, 0))
return -1;
if (is_div(table->switch_src_div))
if (pwrcal_div_set_ratio(table->switch_src_div, 1))
return -1;
if (is_gate(table->switch_src_gate))
if (pwrcal_gate_disable(table->switch_src_gate))
return -1;
return 0;
}
int dfs_use_switch(struct dfs_table *table)
{
if (is_mux(table->switch_mux))
if (pwrcal_mux_set_src(table->switch_mux,
table->switch_use))
return -1;
return 0;
}
int dfs_not_use_switch(struct dfs_table *table)
{
if (is_mux(table->switch_mux))
if (pwrcal_mux_set_src(table->switch_mux, table->switch_notuse))
return -1;
return 0;
}
int dfs_trans_div(int lv_from, int lv_to, struct dfs_table *table, int opt)
{
unsigned int from;
unsigned int to;
int trans;
int i;
struct pwrcal_clk *clk;
for (i = 1; i < table->num_of_members; i++) {
clk = table->members[i];
if (is_div(clk)) {
if (lv_from >= 0)
from = get_value(table, lv_from, i);
else
from = pwrcal_div_get_ratio(clk) - 1;
to = get_value(table, lv_to, i);
trans = 0;
switch (opt) {
case TRANS_HIGH:
if (from < to)
trans = 1;
break;
case TRANS_LOW:
if (from > to)
trans = 1;
break;
case TRANS_DIFF:
if (from != to)
trans = 1;
break;
case TRANS_FORCE:
trans = 1;
break;
default:
break;
}
if (trans == 0)
continue;
if (pwrcal_div_set_ratio(clk, to + 1))
goto errorout;
}
}
return 0;
errorout:
pr_err("%s %s %d\n", __func__, clk->name, to + 1);
return -1;
}
int dfs_trans_pll(int lv_from, int lv_to, struct dfs_table *table, int opt)
{
unsigned long long rate;
unsigned int from;
unsigned int to;
int trans;
int i;
struct pwrcal_clk *clk;
for (i = 1; i < table->num_of_members; i++) {
clk = table->members[i];
if (is_pll(clk)) {
if (lv_from >= 0) {
from = get_value(table, lv_from, i);
} else {
rate = pwrcal_pll_get_rate(clk);
do_div(rate, 1000);
from = (unsigned int)rate;
}
to = get_value(table, lv_to, i);
trans = 0;
switch (opt) {
case TRANS_HIGH:
if (from < to)
trans = 1;
if (to == 0)
trans = 0;
break;
case TRANS_LOW:
if (from > to)
trans = 1;
if (from == 0)
trans = 0;
break;
case TRANS_DIFF:
if (from != to)
trans = 1;
break;
case TRANS_FORCE:
trans = 1;
break;
default:
break;
}
if (trans == 0)
continue;
rate = (unsigned long long)to * 1000;
if (rate != 0) {
if (pwrcal_pll_set_rate(clk, rate))
goto errorout;
if (pwrcal_pll_is_enabled(clk) != 1)
if (pwrcal_pll_enable(clk))
goto errorout;
} else {
if (pwrcal_pll_is_enabled(clk) != 0)
if (pwrcal_pll_disable(clk))
goto errorout;
}
}
}
return 0;
errorout:
pr_err("%s %s %d\n", __func__, clk->name, to);
return -1;
}
int dfs_trans_mux(int lv_from, int lv_to, struct dfs_table *table, int opt)
{
unsigned int from;
unsigned int to;
int trans;
int i;
struct pwrcal_clk *clk;
for (i = 1; i < table->num_of_members; i++) {
clk = table->members[i];
if (is_mux(clk)) {
if (lv_from >= 0)
from = get_value(table, lv_from, i);
else
from = pwrcal_mux_get_src(clk);
to = get_value(table, lv_to, i);
trans = 0;
switch (opt) {
case TRANS_HIGH:
if (from < to)
trans = 1;
break;
case TRANS_LOW:
if (from > to)
trans = 1;
break;
case TRANS_DIFF:
if (from != to)
trans = 1;
break;
case TRANS_FORCE:
trans = 1;
break;
default:
break;
}
if (trans == 0)
continue;
if (pwrcal_mux_set_src(clk, to) != 0)
goto errorout;
}
}
return 0;
errorout:
pr_err("%s %s %d\n", __func__, clk->name, to);
return -1;
}
int dfs_trans_gate(int lv_from, int lv_to, struct dfs_table *table, int opt)
{
unsigned int from;
unsigned int to;
int trans;
int i;
struct pwrcal_clk *clk;
for (i = 1; i < table->num_of_members; i++) {
clk = table->members[i];
if (is_gate(clk)) {
if (lv_from >= 0)
from = get_value(table, lv_from, i);
else
from = pwrcal_gate_is_enabled(clk);
to = get_value(table, lv_to, i);
trans = 0;
switch (opt) {
case TRANS_HIGH:
if (from < to)
trans = 1;
break;
case TRANS_LOW:
if (from > to)
trans = 1;
break;
case TRANS_DIFF:
if (from != to)
trans = 1;
break;
default:
break;
}
if (trans == 0)
continue;
if (to)
pwrcal_gate_enable(clk);
else
pwrcal_gate_disable(clk);
}
}
return 0;
}
int dfs_get_lv(unsigned int rate, struct dfs_table *table)
{
int i;
unsigned int rep;
if (rate == 0)
return -1;
for (i = 0; i < table->num_of_lv; i++) {
rep = *(table->rate_table + (table->num_of_members * i));
if (0 != rep && rate >= rep)
return i;
}
return i;
}
static int transition(unsigned int rate_from,
unsigned int rate_to,
struct dfs_table *table)
{
int lv_from, lv_to, lv_switch;
unsigned int rate_switch;
lv_from = dfs_get_lv(rate_from, table);
lv_to = dfs_get_lv(rate_to, table);
if (lv_from == lv_to)
return 0;
if (lv_from >= table->num_of_lv || lv_to >= table->num_of_lv)
goto errorout;
if (table->trans_pre)
table->trans_pre(rate_from, rate_to);
if (table->num_of_switches != 0) {
rate_switch = dfs_set_rate_switch(rate_from, rate_to, table);
lv_switch = dfs_get_lv(rate_switch, table);
if (dfs_enable_switch(table))
goto errorout;
if (dfs_trans_div(lv_from, lv_switch, table, TRANS_HIGH))
goto errorout;
if (table->switch_pre)
table->switch_pre(rate_from, rate_switch);
if (dfs_use_switch(table))
goto errorout;
if (table->switch_post)
table->switch_post(rate_from, rate_switch);
if (dfs_trans_mux(lv_from, lv_switch, table, TRANS_DIFF))
goto errorout;
if (dfs_trans_div(lv_from, lv_switch, table, TRANS_LOW))
goto errorout;
if (dfs_trans_pll(lv_from, lv_to, table, TRANS_DIFF))
goto errorout;
if (dfs_trans_div(lv_switch, lv_to, table, TRANS_HIGH))
goto errorout;
if (table->switch_pre)
table->switch_pre(rate_switch, rate_to);
if (dfs_not_use_switch(table))
goto errorout;
if (table->switch_post)
table->switch_post(rate_switch, rate_to);
if (dfs_trans_mux(lv_switch, lv_to, table, TRANS_DIFF))
goto errorout;
if (dfs_trans_div(lv_switch, lv_to, table, TRANS_LOW))
goto errorout;
if (dfs_disable_switch(table))
goto errorout;
} else {
if (dfs_trans_gate(lv_from, lv_to, table, TRANS_HIGH))
goto errorout;
if (dfs_trans_div(lv_from, lv_to, table, TRANS_HIGH))
goto errorout;
if (dfs_trans_pll(lv_from, lv_to, table, TRANS_LOW))
goto errorout;
if (dfs_trans_mux(lv_from, lv_to, table, TRANS_DIFF))
goto errorout;
if (dfs_trans_pll(lv_from, lv_to, table, TRANS_HIGH))
goto errorout;
if (dfs_trans_div(lv_from, lv_to, table, TRANS_LOW))
goto errorout;
if (dfs_trans_gate(lv_from, lv_to, table, TRANS_LOW))
goto errorout;
}
if (table->trans_post)
table->trans_post(rate_from, rate_to);
return 0;
errorout:
return -1;
}
static unsigned int get_rate(struct dfs_table *table)
{
int l, m;
unsigned int cur[128] = {0, };
unsigned long long rate;
struct pwrcal_clk *clk;
for (m = 1; m < table->num_of_members; m++) {
clk = table->members[m];
if (is_pll(clk)) {
rate = pwrcal_pll_get_rate(clk);
do_div(rate, 1000);
cur[m] = (unsigned int)rate;
}
if (is_mux(clk))
cur[m] = pwrcal_mux_get_src(clk);
if (is_div(clk))
cur[m] = pwrcal_div_get_ratio(clk) - 1;
if (is_gate(clk))
cur[m] = pwrcal_gate_is_enabled(clk);
}
for (l = 0; l < table->num_of_lv; l++) {
for (m = 1; m < table->num_of_members; m++)
if (cur[m] != get_value(table, l, m))
break;
if (m == table->num_of_members)
return get_value(table, l, 0);
}
if (is_pll(table->members[1])) {
for (l = 0; l < table->num_of_lv; l++)
if (cur[1] == get_value(table, l, 1))
return get_value(table, l, 0);
}
for (m = 1; m < table->num_of_members; m++) {
clk = table->members[m];
pr_err("dfs_get_rate mid : %s : %d\n", clk->name, cur[m]);
}
return 0;
}
int dfs_get_rate_table(struct dfs_table *dfs, unsigned long *table)
{
int i;
for (i = 0; i < dfs->num_of_lv; i++)
table[i] = get_value(dfs, i, 0);
return dfs->num_of_lv;
}
int dfs_get_target_rate_table(struct dfs_table *dfs,
unsigned int mux,
unsigned int div,
unsigned long *table)
{
int m, d, i;
int num_of_parent;
struct pwrcal_clk *parents[32];
unsigned int parents_rate[32];
unsigned long long rate;
unsigned int src, ratio;
for (m = 0; m < dfs->num_of_members; m++)
if (dfs->members[m]->id == mux)
break;
for (d = 0; d < dfs->num_of_members; d++)
if (dfs->members[d]->id == div)
break;
if (m != dfs->num_of_members) {
num_of_parent = pwrcal_mux_get_parents(dfs->members[m],
parents);
for (i = 0; i < num_of_parent; i++) {
rate = pwrcal_clk_get_rate(parents[i]);
do_div(rate, 1000);
parents_rate[i] = (unsigned int)rate;
}
} else {
parents[0] = pwrcal_clk_get_parent(dfs->members[d]);
rate = pwrcal_clk_get_rate(parents[0]);
do_div(rate, 1000);
parents_rate[0] = (unsigned int)rate;
}
for (i = 0; i < dfs->num_of_lv; i++) {
src = get_value(dfs, i, m);
ratio = get_value(dfs, i, d) + 1;
if (m == dfs->num_of_members) {
table[i] = parents_rate[0] / ratio;
continue;
}
if (d == dfs->num_of_members) {
table[i] = parents_rate[src];
continue;
}
table[i] = parents_rate[src] / ratio;
}
return i;
}
int dfs_set_rate(struct vclk *vclk, unsigned long to)
{
struct pwrcal_vclk_dfs *dfs;
unsigned long ret = -1;
dfs = to_dfs(vclk);
/* pr_info("(%s) set from (%ldHz) to (%ldHz) start\n",
vclk->name, vclk->vfreq, to); */
if (transition((unsigned int)(vclk->vfreq),
(unsigned int)to,
dfs->table))
goto out;
/* pr_info("(%s) set from (%ldHz) to (%ldHz) success\n",
vclk->name, vclk->vfreq, to); */
ret = 0;
out:
if (ret)
pr_err("dfs_set_rate error (%s) from(%ld) to(%ld)\n",
vclk->name, vclk->vfreq, to);
return ret;
}
unsigned long dfs_get_rate(struct vclk *vclk)
{
struct pwrcal_vclk_dfs *dfs;
unsigned long ret = 0;
dfs = to_dfs(vclk);
ret = (unsigned long)get_rate(dfs->table);
return ret;
}
int dfs_enable(struct vclk *vclk)
{
struct pwrcal_vclk_dfs *dfs;
int ret = -1;
dfs = to_dfs(vclk);
if (dfs->en_clks)
if (set_config(dfs->en_clks, 0))
goto out;
ret = 0;
out:
if (ret)
pr_err("p1_enable error (%s)\n", vclk->name);
return ret;
}
int dfs_disable(struct vclk *vclk)
{
struct pwrcal_vclk_dfs *dfs;
int ret = -1;
dfs = to_dfs(vclk);
if (dfs->en_clks != 0)
if (set_config(dfs->en_clks, 1))
goto out;
ret = 0;
out:
if (ret)
pr_err("dfs_disable error (%s)\n", vclk->name);
return ret;
}
int dfs_is_enabled(struct vclk *vclk)
{
if (vclk->ref_count)
return 1;
return 0;
}
struct vclk_ops dfs_ops = {
.enable = dfs_enable,
.disable = dfs_disable,
.is_enabled = dfs_is_enabled,
.get_rate = dfs_get_rate,
.set_rate = dfs_set_rate,
};
unsigned long dfs_get_max_freq(struct vclk *vclk)
{
struct pwrcal_vclk_dfs *dfs = to_dfs(vclk);
return dfs->table->max_freq;
}
unsigned long dfs_get_min_freq(struct vclk *vclk)
{
struct pwrcal_vclk_dfs *dfs = to_dfs(vclk);
return dfs->table->min_freq;
}