#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; }