android_kernel_samsung_on5x.../drivers/soc/samsung/pwrcal/pwrcal-cmu.c
2018-06-19 23:16:04 +02:00

473 lines
10 KiB
C

#include "pwrcal-env.h"
#include "pwrcal-clk.h"
#include "pwrcal-rae.h"
struct pwrcal_clk_none clk_0;
struct pwrcal_pll clk_pll_start, clk_pll_end;
struct pwrcal_clk_fixed_rate clk_fixed_rate_start, clk_fixed_rate_end;
struct pwrcal_clk_fixed_factor clk_fixed_factor_start, clk_fixed_factor_end;
struct pwrcal_mux clk_mux_start, clk_mux_end;
struct pwrcal_div clk_div_start, clk_div_end;
struct pwrcal_gate clk_gate_start, clk_gate_end;
unsigned int _cal_clk_get(char *name)
{
int id;
struct pwrcal_pll *pll;
struct pwrcal_clk_fixed_rate *fixed_rate;
struct pwrcal_clk_fixed_factor *fixed_factor;
struct pwrcal_mux *mux;
struct pwrcal_div *div;
struct pwrcal_gate *gate;
for (pll = &clk_pll_start, id = 0; pll < &clk_pll_end; pll++, id++)
if (!strcmp(pll->clk.name, name))
return pll_type + id;
for (fixed_rate = &clk_fixed_rate_start, id = 0; fixed_rate < &clk_fixed_rate_end; fixed_rate++, id++)
if (!strcmp(fixed_rate->clk.name, name))
return fixed_rate_type + id;
for (fixed_factor = &clk_fixed_factor_start, id = 0; fixed_factor < &clk_fixed_factor_end; fixed_factor++, id++)
if (!strcmp(fixed_factor->clk.name, name))
return fixed_factor_type + id;
for (mux = &clk_mux_start, id = 0; mux < &clk_mux_end; mux++, id++)
if (!strcmp(mux->clk.name, name))
return mux_type + id;
for (div = &clk_div_start, id = 0; div < &clk_div_end; div++, id++)
if (!strcmp(div->clk.name, name))
return div_type + id;
for (gate = &clk_gate_start, id = 0; gate < &clk_gate_end; gate++, id++)
if (!strcmp(gate->clk.name, name))
return gate_type + id;
return 0;
}
struct pwrcal_clk *cal_get_clk(unsigned int id)
{
struct pwrcal_clk *ret = NULL;
switch (id & mask_of_type) {
case pll_type:
ret = &((&clk_pll_start)[id & 0x00000FFF].clk);
break;
case fixed_rate_type:
ret = &((&clk_fixed_rate_start)[id & 0x00000FFF].clk);
break;
case fixed_factor_type:
ret = &((&clk_fixed_factor_start)[id & 0x00000FFF].clk);
break;
case mux_type:
ret = &((&clk_mux_start)[id & 0x00000FFF].clk);
break;
case div_type:
ret = &((&clk_div_start)[id & 0x00000FFF].clk);
break;
case gate_type:
ret = &((&clk_gate_start)[id & 0x00000FFF].clk);
break;
default:
break;
}
return ret;
}
int pwrcal_gate_is_enabled(struct pwrcal_clk *clk)
{
return pwrcal_getbit(clk->offset, clk->shift);
}
int pwrcal_gate_enable(struct pwrcal_clk *clk)
{
pwrcal_setbit(clk->offset, clk->shift, 1);
return 0;
}
int pwrcal_gate_disable(struct pwrcal_clk *clk)
{
pwrcal_setbit(clk->offset, clk->shift, 0);
return 0;
}
int pwrcal_mux_is_enabled(struct pwrcal_clk *clk)
{
if (clk->enable)
return pwrcal_getbit(clk->enable, clk->e_shift);
return 1;
}
int pwrcal_mux_enable(struct pwrcal_clk *clk)
{
if (clk->enable)
pwrcal_setbit(clk->enable, clk->e_shift, 1);
return 0;
}
int pwrcal_mux_disable(struct pwrcal_clk *clk)
{
if (clk->enable)
pwrcal_setbit(clk->enable, clk->e_shift, 0);
return 0;
}
int pwrcal_mux_get_src(struct pwrcal_clk *clk)
{
return pwrcal_getf(clk->offset, clk->shift, TO_MASK(clk->width));
}
int pwrcal_mux_set_src(struct pwrcal_clk *clk, unsigned int src)
{
struct pwrcal_mux *mux = to_mux(clk);
int timeout;
unsigned int mux_stat;
int muxgate = 1;
if (src >= (unsigned int)(mux->num_parents))
return -1;
if (_pwrcal_is_private_mux_set_src(clk))
return _pwrcal_private_mux_set_src(clk, src);
if (mux->gate != CLK_NONE) {
muxgate = pwrcal_gate_is_enabled(mux->gate);
if (!muxgate)
pwrcal_gate_enable(mux->gate);
}
if ((clk->id & user_mux_type) == user_mux_type && src == 1)
pwrcal_setbit(clk->offset, 27, 0);
pwrcal_setf(clk->offset, clk->shift, TO_MASK(clk->width), src);
if ((clk->id & user_mux_type) == user_mux_type && src == 0)
pwrcal_setbit(clk->offset, 27, 1);
if ((clk->id & user_mux_type) == user_mux_type)
return 0;
if (clk->status) {
for (timeout = 0;; timeout++) {
mux_stat = pwrcal_getf(clk->status, clk->s_shift, TO_MASK(clk->s_width));
if (mux_stat == (1 << src))
break;
if (timeout > CLK_WAIT_CNT)
goto timeout_error;
cpu_relax();
}
}
if (!muxgate)
pwrcal_gate_disable(mux->gate);
return 0;
timeout_error:
pr_err("stat(=%d) check time out, \'%s\', src_num(=%d)",
mux_stat, clk->name, src);
return -1;
}
int pwrcal_mux_get_parents(struct pwrcal_clk *clk, struct pwrcal_clk **parents)
{
struct pwrcal_mux *mux = to_mux(clk);
int i;
for (i = 0; i < mux->num_parents; i++)
parents[i] = mux->parents[i];
return mux->num_parents;
}
struct pwrcal_clk *pwrcal_mux_get_parent(struct pwrcal_clk *clk)
{
struct pwrcal_mux *mux = to_mux(clk);
int src;
src = pwrcal_mux_get_src(clk);
return mux->parents[src];
}
int pwrcal_div_is_enabled(struct pwrcal_clk *clk)
{
return 1;
}
int pwrcal_div_enable(struct pwrcal_clk *clk)
{
return 0;
}
int pwrcal_div_disable(struct pwrcal_clk *clk)
{
return 0;
}
unsigned int pwrcal_div_get_ratio(struct pwrcal_clk *clk)
{
return pwrcal_getf(clk->offset, clk->shift, TO_MASK(clk->width)) + 1;
}
int pwrcal_div_set_ratio(struct pwrcal_clk *clk, unsigned int ratio)
{
int timeout;
unsigned int div_stat_val;
struct pwrcal_div *div = to_div(clk);
int divgate = 1;
if (ratio == 0) {
pr_err("ratio is 0. \'%s\'", clk->name);
return -1;
}
if (ratio > (TO_MASK(clk->width) + 1)) {
pr_err("ratio is bigger than max. (%d) > of (%d) \'%s\'",
ratio,
TO_MASK(clk->width) + 1,
clk->name);
return -1;
}
if (div->gate != CLK_NONE) {
divgate = pwrcal_gate_is_enabled(div->gate);
if (!divgate)
pwrcal_gate_enable(div->gate);
}
pwrcal_setf(clk->offset, clk->shift, TO_MASK(clk->width), ratio - 1);
if (clk->status != 0) {
for (timeout = 0;; timeout++) {
div_stat_val = pwrcal_getf(clk->status,
clk->s_shift,
TO_MASK(clk->s_width));
if (0 == div_stat_val)
break;
if (timeout > CLK_WAIT_CNT)
goto timeout_error;
cpu_relax();
}
}
if (!divgate)
pwrcal_gate_disable(div->gate);
return 0;
timeout_error:
pr_err("stat(=%d) check time out, \'%s\'", div_stat_val, clk->name);
return -1;
}
unsigned int pwrcal_div_get_max_ratio(struct pwrcal_clk *clk)
{
return TO_MASK(clk->width) + 1;
}
struct pwrcal_clk *pwrcal_clk_get_parent(struct pwrcal_clk *clk)
{
struct pwrcal_mux *mux;
unsigned int src;
switch (clk->id & mask_of_type) {
case fixed_rate_type:
case fixed_factor_type:
case pll_type:
case div_type:
case gate_type:
return clk->parent;
break;
case mux_type:
mux = to_mux(clk);
src = pwrcal_mux_get_src(clk);
return mux->parents[src];
break;
default:
break;
}
return CLK_NONE;
}
unsigned long long pwrcal_clk_get_rate(struct pwrcal_clk *clk)
{
struct pwrcal_clk *cur = clk;
struct pwrcal_clk_fixed_rate *frate;
struct pwrcal_clk_fixed_factor *ffacor;
struct pwrcal_mux *mux;
unsigned int src;
struct pwrcal_clk *clk_stack[32];
unsigned int p = 0;
unsigned long long rate = 0;
unsigned int ratio;
while (cur != CLK_NONE) {
clk_stack[p++] = cur;
switch (cur->id & mask_of_type) {
case fixed_rate_type:
case fixed_factor_type:
case pll_type:
case div_type:
case gate_type:
cur = cur->parent;
break;
case mux_type:
mux = to_mux(cur);
src = pwrcal_mux_get_src(cur);
cur = mux->parents[src];
break;
default:
return 0;
}
}
/* calc rate */
while (p != 0) {
cur = clk_stack[--p];
switch (cur->id & mask_of_type) {
case fixed_rate_type:
frate = to_frate(cur);
rate = frate->fixed_rate;
break;
case fixed_factor_type:
ffacor = to_ffactor(cur);
ratio = (unsigned int)(ffacor->ratio);
do_div(rate, ratio);
break;
case pll_type:
if (pwrcal_pll_is_enabled(cur) == 0)
return 0;
rate = pwrcal_pll_get_rate(cur);
break;
case mux_type:
if (pwrcal_mux_is_enabled(cur) == 0)
return 0;
break;
case div_type:
if (pwrcal_div_is_enabled(cur) == 0)
return 0;
ratio = pwrcal_div_get_ratio(cur);
do_div(rate, ratio);
break;
case gate_type:
if (pwrcal_gate_is_enabled(cur) == 0)
return 0;
break;
default:
return 0;
}
}
return rate;
}
int pwrcal_mux_set_rate(struct pwrcal_clk *clk, unsigned long long rate)
{
struct pwrcal_clk *parents[48];
int num_of_parents, i, min_diff_parent = -1;
unsigned long long parents_rate;
unsigned long long diff = 0xFFFFFFFFFFFFFFFF;
num_of_parents = pwrcal_mux_get_parents(clk, parents);
for (i = 0; i < num_of_parents; i++) {
parents_rate = pwrcal_clk_get_rate(parents[i]);
if (parents_rate == rate) {
min_diff_parent = i;
break;
}
if (rate > parents_rate && diff > rate - parents_rate) {
diff = rate - parents_rate;
min_diff_parent = i;
}
}
if (min_diff_parent == -1)
return -1;
return pwrcal_mux_set_src(clk, min_diff_parent);
}
int pwrcal_div_set_rate(struct pwrcal_clk *clk, unsigned long long rate)
{
unsigned long long parents_rate;
unsigned long long ratio;
if (clk->parent == CLK_NONE)
return -1;
parents_rate = pwrcal_clk_get_rate(clk->parent);
ratio = parents_rate / rate;
if (rate < parents_rate / ratio)
ratio += 1;
return pwrcal_div_set_ratio(clk, ratio);
}
int pwrcal_clk_set_rate(struct pwrcal_clk *clk, unsigned long long rate)
{
int ret = -1;
switch (clk->id & mask_of_type) {
case pll_type:
ret = pwrcal_pll_set_rate(clk, rate);
break;
case mux_type:
ret = pwrcal_mux_set_rate(clk, rate);
break;
case div_type:
ret = pwrcal_div_set_rate(clk, rate);
break;
default:
break;
}
return ret;
}
int pwrcal_clk_enable(struct pwrcal_clk *clk)
{
int ret = -1;
switch (clk->id & mask_of_type) {
case pll_type:
ret = pwrcal_pll_enable(clk);
break;
case mux_type:
ret = pwrcal_mux_enable(clk);
break;
case div_type:
ret = pwrcal_div_enable(clk);
break;
case gate_type:
ret = pwrcal_gate_enable(clk);
break;
default:
break;
}
return ret;
}
int pwrcal_clk_disable(struct pwrcal_clk *clk)
{
int ret = -1;
switch (clk->id & mask_of_type) {
case pll_type:
ret = pwrcal_pll_disable(clk);
break;
case mux_type:
ret = pwrcal_mux_disable(clk);
break;
case div_type:
ret = pwrcal_div_disable(clk);
break;
case gate_type:
ret = pwrcal_gate_disable(clk);
break;
default:
break;
}
return ret;
}