blob: 4f59ccf6f47b2051992ef3778882d8b6ec638711 [file] [log] [blame]
#include "pwrcal-env.h"
#include "pwrcal-vclk.h"
#include "pwrcal-pmu.h"
#include "pwrcal-clk.h"
#include "pwrcal-rae.h"
#include <linux/exynos-ss.h>
#define is_vclk(id) ((id & 0x0F000000) == 0x0A000000)
unsigned int _cal_vclk_get(char *name)
{
unsigned int id;
for (id = 0; id < vclk_grpgate_list_size; id++)
if (!strcmp(vclk_grpgate_list[id]->vclk.name, name))
return vclk_group_grpgate + id;
for (id = 0; id < vclk_m1d1g1_list_size; id++)
if (!strcmp(vclk_m1d1g1_list[id]->vclk.name, name))
return vclk_group_m1d1g1 + id;
for (id = 0; id < vclk_p1_list_size; id++)
if (!strcmp(vclk_p1_list[id]->vclk.name, name))
return vclk_group_p1 + id;
for (id = 0; id < vclk_m1_list_size; id++)
if (!strcmp(vclk_m1_list[id]->vclk.name, name))
return vclk_group_m1 + id;
for (id = 0; id < vclk_d1_list_size; id++)
if (!strcmp(vclk_d1_list[id]->vclk.name, name))
return vclk_group_d1 + id;
for (id = 0; id < vclk_pxmxdx_list_size; id++)
if (!strcmp(vclk_pxmxdx_list[id]->vclk.name, name))
return vclk_group_pxmxdx + id;
for (id = 0; id < vclk_umux_list_size; id++)
if (!strcmp(vclk_umux_list[id]->vclk.name, name))
return vclk_group_umux + id;
for (id = 0; id < vclk_dfs_list_size; id++)
if (!strcmp(vclk_dfs_list[id]->vclk.name, name))
return vclk_group_dfs + id;
return 0;
}
struct vclk *cal_get_vclk(unsigned int id)
{
struct vclk *ret = NULL;
if (!is_vclk(id))
return ret;
switch (id & vclk_group_mask) {
case vclk_group_grpgate:
ret = &(vclk_grpgate_list[id & 0x0000FFFF]->vclk);
break;
case vclk_group_m1d1g1:
ret = &(vclk_m1d1g1_list[id & 0x0000FFFF]->vclk);
break;
case vclk_group_p1:
ret = &(vclk_p1_list[id & 0x0000FFFF]->vclk);
break;
case vclk_group_m1:
ret = &(vclk_m1_list[id & 0x0000FFFF]->vclk);
break;
case vclk_group_d1:
ret = &(vclk_d1_list[id & 0x0000FFFF]->vclk);
break;
case vclk_group_pxmxdx:
ret = &(vclk_pxmxdx_list[id & 0x0000FFFF]->vclk);
break;
case vclk_group_umux:
ret = &(vclk_umux_list[id & 0x0000FFFF]->vclk);
break;
case vclk_group_dfs:
ret = &(vclk_dfs_list[id & 0x0000FFFF]->vclk);
break;
default:
break;
}
return ret;
}
static int vclk_is_enabled(struct vclk *vclk)
{
if (vclk->ref_count)
return 1;
return 0;
}
/*
* grpgate functions list
*/
static unsigned long grpgate_get(struct vclk *vclk)
{
struct pwrcal_vclk_grpgate *grpgate;
struct pwrcal_clk *first;
unsigned long ret = 0;
grpgate = to_grpgate(vclk);
first = grpgate->gates[0];
if (first != CLK_NONE)
ret = (unsigned long)pwrcal_clk_get_rate(first);
return ret;
}
static int grpgate_enable(struct vclk *vclk)
{
struct pwrcal_vclk_grpgate *grpgate;
struct pwrcal_clk *cur;
struct pwrcal_clk **gates_list;
void *addr;
unsigned int value, dirty;
int i;
grpgate = to_grpgate(vclk);
gates_list = grpgate->gates;
addr = (void *)0xFFFFFFFF;
value = 0;
dirty = 0;
for (i = 0; gates_list[i] != CLK_NONE; i++) {
cur = gates_list[i];
if (!is_gate(cur))
continue;
if (addr != cur->offset) {
if (dirty == 1) {
pwrcal_writel(addr, value);
dirty = 0;
}
addr = cur->offset;
value = pwrcal_readl(addr);
}
if (value & (1 << cur->shift))
continue;
value |= (1 << cur->shift);
dirty = 1;
}
if (dirty == 1)
pwrcal_writel(addr, value);
return 0;
}
static int grpgate_disable(struct vclk *vclk)
{
struct pwrcal_vclk_grpgate *grpgate;
struct pwrcal_clk *cur;
struct pwrcal_clk **gates_list;
void *addr;
unsigned int value, dirty;
int i;
grpgate = to_grpgate(vclk);
gates_list = grpgate->gates;
addr = (void *)0xFFFFFFFF;
value = 0;
dirty = 0;
for (i = 0; gates_list[i] != CLK_NONE; i++) {
cur = gates_list[i];
if (!is_gate(cur))
continue;
if (addr != cur->offset) {
if (dirty == 1) {
pwrcal_writel(addr, value);
dirty = 0;
}
addr = cur->offset;
value = pwrcal_readl(addr);
}
if (value & (1 << cur->shift)) {
value &= ~(1 << cur->shift);
dirty = 1;
}
}
if (dirty == 1)
pwrcal_writel(addr, value);
return 0;
}
struct vclk_ops grpgate_ops = {
.enable = grpgate_enable,
.disable = grpgate_disable,
.is_enabled = vclk_is_enabled,
.get_rate = grpgate_get,
};
/*
* config related functions list
*/
int is_config(struct pwrcal_clk_set *table, int config)
{
int lidx;
unsigned int ratio, src, enable;
unsigned long long rate;
int target;
int diff = 0;
struct pwrcal_clk *cur;
for (lidx = 0; table[lidx].clk != CLK_NONE; lidx++) {
cur = table[lidx].clk;
target = config ? table[lidx].config1 : table[lidx].config0;
if (target == -1)
continue;
switch (cur->id & mask_of_type) {
case div_type:
ratio = pwrcal_div_get_ratio(cur);
if (ratio != target + 1) {
if (table[lidx].config0 == -1
|| table[lidx].config1 == -1) {
pr_warn("check div (%s)[%d][%d]",
cur->name,
target + 1,
ratio);
}
diff = 1;
}
break;
case pll_type:
rate = pwrcal_pll_get_rate(cur);
if (rate != ((unsigned long long)target) * 1000) {
if (table[lidx].config0 == -1
|| table[lidx].config1 == -1) {
do_div(rate, 1000);
pr_warn("check pll. (%s)[%dKhz][%lldKhz]",
cur->name,
target,
rate);
}
diff = 1;
}
break;
case mux_type:
src = pwrcal_mux_get_src(cur);
if (src != target) {
if (table[lidx].config0 == -1
|| table[lidx].config1 == -1) {
pr_warn("check mux. (%s)[%d][%d]",
cur->name,
target,
src);
}
diff = 1;
}
break;
case gate_type:
enable = pwrcal_gate_is_enabled(cur);
if (enable != target) {
if (table[lidx].config0 == -1
|| table[lidx].config1 == -1) {
pr_warn("check gate. (%s)[%d][%d]",
cur->name,
target,
enable);
}
diff = 1;
}
break;
default:
break;
}
if (diff)
return 0;
}
return 1;
}
int set_config(struct pwrcal_clk_set *table, int config)
{
int lidx;
int max;
unsigned long long rate;
int to;
struct pwrcal_clk *cur;
for (lidx = 0; table[lidx].clk != CLK_NONE; lidx++) {
cur = table[lidx].clk;
if (is_gate(cur)) {
to = config ? table[lidx].config1 : table[lidx].config0;
if (to != 1)
continue;
pwrcal_gate_enable(cur);
}
}
for (lidx = 0; table[lidx].clk != CLK_NONE; lidx++) {
cur = table[lidx].clk;
if (is_div(cur)) {
to = config ? table[lidx].config1 : table[lidx].config0;
if (to == -1)
continue;
if (pwrcal_div_get_ratio(cur) < to + 1)
if (pwrcal_div_set_ratio(cur, to + 1))
return -1;
}
}
for (lidx = 0; table[lidx].clk != CLK_NONE; lidx++) {
cur = table[lidx].clk;
if (is_pll(cur)) {
to = config ? table[lidx].config1 : table[lidx].config0;
if (to == -1)
continue;
if (to == 0) {
if (pwrcal_pll_disable(cur) != 0)
return -1;
continue;
}
rate = ((unsigned long long)to) * 1000;
if (pwrcal_pll_get_rate(cur) > rate) {
if (pwrcal_pll_set_rate(cur, rate))
return -1;
if (pwrcal_pll_is_enabled(cur) == 0)
if (pwrcal_pll_enable(cur))
return -1;
}
}
}
for (lidx = 0; table[lidx].clk != CLK_NONE; lidx++) {
cur = table[lidx].clk;
if (is_mux(cur)) {
to = config ? table[lidx].config1 : table[lidx].config0;
if (to == -1)
continue;
if (pwrcal_mux_get_src(cur) != to)
if (pwrcal_mux_set_src(cur, to))
return -1;
}
}
max = lidx;
for (lidx = max - 1; lidx >= 0; lidx--) {
cur = table[lidx].clk;
if (is_pll(cur)) {
to = config ? table[lidx].config1 : table[lidx].config0;
if (to == -1)
continue;
if (to == 0) {
if (pwrcal_pll_disable(cur))
return -1;
continue;
}
rate = ((unsigned long long)to) * 1000;
if (pwrcal_pll_get_rate(cur) < rate) {
if (pwrcal_pll_set_rate(cur, rate))
return -1;
if (pwrcal_pll_is_enabled(cur) == 0)
if (pwrcal_pll_enable(cur))
return -1;
}
}
}
for (lidx = max - 1; lidx >= 0; lidx--) {
cur = table[lidx].clk;
if (is_div(cur)) {
to = config ? table[lidx].config1 : table[lidx].config0;
if (to == -1)
continue;
if (pwrcal_div_get_ratio(cur) > to + 1)
if (pwrcal_div_set_ratio(cur, to + 1))
return -1;
}
}
for (lidx = max - 1; lidx >= 0; lidx--) {
cur = table[lidx].clk;
if (is_gate(cur)) {
to = config ? table[lidx].config1 : table[lidx].config0;
if (to != 0)
continue;
pwrcal_gate_disable(cur);
}
}
return 0;
}
/*
* pxmxdx functions list
*/
static unsigned long pxmxdx_get(struct vclk *vclk)
{
struct pwrcal_vclk_pxmxdx *pxmxdx;
struct pwrcal_clk *first;
unsigned long ret = 0;
pxmxdx = to_pxmxdx(vclk);
first = pxmxdx->clk_list[0].clk;
if (first != CLK_NONE)
ret = (unsigned long)pwrcal_clk_get_rate(first);
return ret;
}
static int pxmxdx_enable(struct vclk *vclk)
{
struct pwrcal_vclk_pxmxdx *pxmxdx;
int ret;
pxmxdx = to_pxmxdx(vclk);
ret = set_config(pxmxdx->clk_list, 0);
if (ret)
pr_err("pxmxdx_enable error (%s)", vclk->name);
return ret;
}
static int pxmxdx_disable(struct vclk *vclk)
{
struct pwrcal_vclk_pxmxdx *pxmxdx;
int ret;
pxmxdx = to_pxmxdx(vclk);
ret = set_config(pxmxdx->clk_list, 1);
if (ret)
pr_err("pxmxdx_disable error (%s)\n", vclk->name);
return ret;
}
struct vclk_ops pxmxdx_ops = {
.enable = pxmxdx_enable,
.disable = pxmxdx_disable,
.is_enabled = vclk_is_enabled,
.get_rate = pxmxdx_get,
};
/*
* umux functions list
*/
static unsigned long umux_get(struct vclk *vclk)
{
struct pwrcal_vclk_umux *umux;
unsigned long ret = 0;
umux = to_umux(vclk);
if (umux->umux != CLK_NONE)
ret = (unsigned long)pwrcal_clk_get_rate(umux->umux);
return ret;
}
static int umux_enable(struct vclk *vclk)
{
struct pwrcal_vclk_umux *umux;
umux = to_umux(vclk);
if (umux->umux != CLK_NONE)
pwrcal_mux_set_src(umux->umux, 1);
return 0;
}
static int umux_disable(struct vclk *vclk)
{
struct pwrcal_vclk_umux *umux;
umux = to_umux(vclk);
if (umux->umux != CLK_NONE)
pwrcal_mux_set_src(umux->umux, 0);
return 0;
}
struct vclk_ops umux_ops = {
.enable = umux_enable,
.disable = umux_disable,
.is_enabled = vclk_is_enabled,
.get_rate = umux_get,
};
/*
* m1d1g1 functions list
*/
static unsigned long m1d1g1_get(struct vclk *vclk)
{
struct pwrcal_vclk_m1d1g1 *m1d1g1;
unsigned long ret = -1;
m1d1g1 = to_m1d1g1(vclk);
if (m1d1g1->gate != CLK_NONE)
ret = (unsigned long)pwrcal_clk_get_rate(m1d1g1->gate);
else if (m1d1g1->div != CLK_NONE)
ret = (unsigned long)pwrcal_clk_get_rate(m1d1g1->div);
else if (m1d1g1->mux != CLK_NONE)
ret = (unsigned long)pwrcal_clk_get_rate(m1d1g1->mux);
return ret;
}
static int m1d1g1_set(struct vclk *vclk, unsigned long freq_to)
{
struct pwrcal_vclk_m1d1g1 *m1d1g1;
int pidx, didx;
struct pwrcal_clk *parents[32];
unsigned long parent_freq[32];
unsigned long long temp_freq;
int min_diff = 0x7FFFFFFF;
int min_diff_mux = 0x7FFFFFFF;
int min_diff_div = 0x7FFFFFFF;
unsigned long cur_freq;
unsigned int num_of_parents;
unsigned int max_ratio;
int ret = -1;
m1d1g1 = to_m1d1g1(vclk);
if (freq_to == 0 && m1d1g1->extramux != CLK_NONE)
if (pwrcal_mux_get_src(m1d1g1->extramux) != 0)
if (pwrcal_mux_set_src(m1d1g1->extramux, 0))
goto out;
if (freq_to == 0 && m1d1g1->gate != CLK_NONE) {
if (pwrcal_gate_is_enabled(m1d1g1->gate) != 0)
if (pwrcal_gate_disable(m1d1g1->gate))
goto out;
goto success;
}
num_of_parents = pwrcal_mux_get_parents(m1d1g1->mux, parents);
max_ratio = pwrcal_div_get_max_ratio(m1d1g1->div);
for (pidx = 0; pidx < num_of_parents; pidx++) {
temp_freq = pwrcal_clk_get_rate(parents[pidx]);
parent_freq[pidx] = (unsigned long)temp_freq;
}
if (num_of_parents == 0) {
temp_freq = pwrcal_clk_get_rate(m1d1g1->div->parent);
parent_freq[0] = temp_freq;
num_of_parents = 1;
}
if (freq_to == 0) {
freq_to = parent_freq[0];
for (pidx = 0; pidx < num_of_parents; pidx++)
if (freq_to > parent_freq[pidx])
freq_to = parent_freq[pidx];
if (freq_to != 0)
freq_to /= (max_ratio - 1);
}
for (pidx = 0; pidx < num_of_parents && min_diff != 0; pidx++) {
for (didx = 1; didx < max_ratio + 1 && min_diff != 0; didx++) {
cur_freq = parent_freq[pidx] / didx;
if (freq_to >= cur_freq) {
if (min_diff > freq_to - cur_freq) {
min_diff = freq_to - cur_freq;
min_diff_mux = pidx;
min_diff_div = didx;
if (min_diff == 0)
break;
}
}
}
}
if (min_diff == 0x7FFFFFFF) {
if (freq_to == 0)
goto success;
goto out;
}
if (m1d1g1->div != CLK_NONE)
if (pwrcal_div_get_ratio(m1d1g1->div) < min_diff_div)
if (pwrcal_div_set_ratio(m1d1g1->div, min_diff_div))
goto out;
if (m1d1g1->mux != CLK_NONE)
if (pwrcal_mux_get_src(m1d1g1->mux) != min_diff_mux)
if (pwrcal_mux_set_src(m1d1g1->mux, min_diff_mux))
goto out;
if (m1d1g1->div != CLK_NONE)
if (pwrcal_div_get_ratio(m1d1g1->div) > min_diff_div)
if (pwrcal_div_set_ratio(m1d1g1->div, min_diff_div))
goto out;
if (freq_to != 0 && m1d1g1->gate != CLK_NONE)
if (pwrcal_gate_is_enabled(m1d1g1->gate) != 1)
if (pwrcal_gate_enable(m1d1g1->gate))
goto out;
if (freq_to != 0 && m1d1g1->extramux != CLK_NONE)
if (pwrcal_mux_get_src(m1d1g1->extramux) != 1)
if (pwrcal_mux_set_src(m1d1g1->extramux, 1))
goto out;
success:
ret = 0;
out:
if (ret)
pr_err("m1d1g1_set error(%s) (%ld)\n", vclk->name, freq_to);
return ret;
}
static int m1d1g1_enable(struct vclk *vclk)
{
struct pwrcal_vclk_m1d1g1 *m1d1g1;
int ret = -1;
m1d1g1 = to_m1d1g1(vclk);
if (m1d1g1->mux != CLK_NONE) {
if (pwrcal_mux_is_enabled(m1d1g1->mux) != 1) {
ret = pwrcal_mux_enable(m1d1g1->mux);
if (ret)
goto out;
}
}
if (m1d1g1->div != CLK_NONE) {
if (pwrcal_div_is_enabled(m1d1g1->div) != 1) {
ret = pwrcal_div_enable(m1d1g1->div);
if (ret)
goto out;
}
}
if (m1d1g1->gate != CLK_NONE) {
if (pwrcal_gate_is_enabled(m1d1g1->gate) != 1) {
ret = pwrcal_gate_enable(m1d1g1->gate);
if (ret)
goto out;
}
}
if (m1d1g1->extramux != CLK_NONE) {
if (pwrcal_mux_get_src(m1d1g1->extramux) != 1) {
ret = pwrcal_mux_set_src(m1d1g1->extramux, 1);
if (ret)
goto out;
}
}
ret = 0;
out:
if (ret)
pr_err("m1d1g1_enable error (%s)", vclk->name);
return ret;
}
static int m1d1g1_disable(struct vclk *vclk)
{
struct pwrcal_vclk_m1d1g1 *m1d1g1;
int ret = 0;
m1d1g1 = to_m1d1g1(vclk);
if (m1d1g1->extramux != CLK_NONE) {
if (pwrcal_mux_get_src(m1d1g1->extramux) != 0) {
ret = pwrcal_mux_set_src(m1d1g1->extramux, 0);
if (ret)
goto out;
}
}
if (m1d1g1->gate != CLK_NONE) {
if (pwrcal_gate_is_enabled(m1d1g1->gate) != 0) {
ret = pwrcal_gate_disable(m1d1g1->gate);
if (ret)
goto out;
}
}
if (m1d1g1->mux != CLK_NONE) {
if (pwrcal_mux_is_enabled(m1d1g1->mux) != 0) {
ret = pwrcal_mux_disable(m1d1g1->mux);
if (ret)
goto out;
}
}
out:
if (ret)
pr_err("pxmxdx_disable error (%s)\n", vclk->name);
return ret;
}
struct vclk_ops m1d1g1_ops = {
.enable = m1d1g1_enable,
.disable = m1d1g1_disable,
.is_enabled = vclk_is_enabled,
.get_rate = m1d1g1_get,
.set_rate = m1d1g1_set,
};
/*
* p1 functions list
*/
static unsigned long p1_get(struct vclk *vclk)
{
struct pwrcal_vclk_p1 *p1;
unsigned long ret = 0;
p1 = to_p1(vclk);
ret = (unsigned long)pwrcal_pll_get_rate(p1->pll);
return ret;
}
static int p1_set(struct vclk *vclk, unsigned long freq_to)
{
struct pwrcal_vclk_p1 *p1;
unsigned long long temp_freq;
unsigned long ret = -1;
p1 = to_p1(vclk);
temp_freq = (unsigned long long)freq_to;
if (pwrcal_pll_get_rate(p1->pll) != temp_freq)
if (pwrcal_pll_set_rate(p1->pll, temp_freq))
goto out;
ret = 0;
out:
if (ret)
pr_err("p1_set error(%s) (%ld)\n", vclk->name, freq_to);
return ret;
}
static int p1_enable(struct vclk *vclk)
{
struct pwrcal_vclk_p1 *p1;
int ret = -1;
p1 = to_p1(vclk);
ret = pwrcal_pll_enable(p1->pll);
if (ret)
goto out;
ret = 0;
out:
if (ret)
pr_err("p1_enable error (%s)\n", vclk->name);
return ret;
}
static int p1_disable(struct vclk *vclk)
{
struct pwrcal_vclk_p1 *p1;
int ret = -1;
p1 = to_p1(vclk);
ret = pwrcal_pll_disable(p1->pll);
if (ret)
goto out;
ret = 0;
out:
if (ret)
pr_err("pxmxdx_disable error (%s)\n", vclk->name);
return ret;
}
struct vclk_ops p1_ops = {
.enable = p1_enable,
.disable = p1_disable,
.is_enabled = vclk_is_enabled,
.get_rate = p1_get,
.set_rate = p1_set,
};
/*
* m1 functions list
*/
static unsigned long m1_get(struct vclk *vclk)
{
struct pwrcal_vclk_m1 *m1;
unsigned long ret = 0;
m1 = to_m1(vclk);
ret = (unsigned long)pwrcal_pll_get_rate(m1->mux);
return ret;
}
static int m1_set(struct vclk *vclk, unsigned long freq_to)
{
struct pwrcal_vclk_m1 *m1;
unsigned long ret = -1;
m1 = to_m1(vclk);
if (pwrcal_mux_get_src(m1->mux) != freq_to)
if (pwrcal_mux_set_src(m1->mux, freq_to))
goto out;
ret = 0;
out:
if (ret)
pr_err("m1_set error(%s) (%ld)\n", vclk->name, freq_to);
return ret;
}
static int m1_enable(struct vclk *vclk)
{
struct pwrcal_vclk_m1 *m1;
int ret = -1;
m1 = to_m1(vclk);
ret = pwrcal_mux_enable(m1->mux);
if (ret)
goto out;
ret = 0;
out:
if (ret)
pr_err("m1_enable error (%s)\n", vclk->name);
return ret;
}
static int m1_disable(struct vclk *vclk)
{
struct pwrcal_vclk_m1 *m1 = to_m1(vclk);
int ret;
ret = pwrcal_mux_disable(m1->mux);
if (ret)
pr_err("m1_disable error (%s)\n", vclk->name);
return ret;
}
struct vclk_ops m1_ops = {
.enable = m1_enable,
.disable = m1_disable,
.is_enabled = vclk_is_enabled,
.get_rate = m1_get,
.set_rate = m1_set,
};
/*
* d1 functions list
*/
static unsigned long d1_get(struct vclk *vclk)
{
struct pwrcal_vclk_d1 *d1;
unsigned long ret = 0;
d1 = to_d1(vclk);
ret = (unsigned long)pwrcal_clk_get_rate(d1->div);
return ret;
}
static int d1_set(struct vclk *vclk, unsigned long freq_to)
{
struct pwrcal_vclk_d1 *d1;
unsigned long long temp_freq;
unsigned long long parent_freq;
unsigned long long parent_temp;
unsigned int ratio;
struct pwrcal_clk *parent;
unsigned long ret = -1;
d1 = to_d1(vclk);
temp_freq = (unsigned long long)freq_to;
if (pwrcal_clk_get_rate(d1->div) != temp_freq) {
parent = pwrcal_clk_get_parent(d1->div);
parent_freq = pwrcal_clk_get_rate(parent);
if (parent_freq < temp_freq)
goto out;
parent_temp = parent_freq;
do_div(parent_temp, temp_freq);
if (parent_freq > parent_temp * temp_freq)
parent_temp += 1;
ratio = (unsigned int)parent_temp;
if (pwrcal_div_set_ratio(d1->div, ratio))
goto out;
}
ret = 0;
out:
if (ret)
pr_err("d1_set error(%s) (%ld)\n", vclk->name, freq_to);
return ret;
}
static int d1_enable(struct vclk *vclk)
{
struct pwrcal_vclk_d1 *d1;
int ret = -1;
d1 = to_d1(vclk);
ret = pwrcal_div_enable(d1->div);
if (ret)
goto out;
ret = 0;
out:
if (ret)
pr_err("d1_enable error (%s)\n", vclk->name);
return ret;
}
static int d1_disable(struct vclk *vclk)
{
struct pwrcal_vclk_d1 *d1;
int ret = -1;
d1 = to_d1(vclk);
ret = pwrcal_div_disable(d1->div);
if (ret)
goto out;
ret = 0;
out:
if (ret)
pr_err("d1_disable error (%s)\n", vclk->name);
return ret;
}
struct vclk_ops d1_ops = {
.enable = d1_enable,
.disable = d1_disable,
.is_enabled = vclk_is_enabled,
.get_rate = d1_get,
.set_rate = d1_set,
};
struct pwrcal_vclk_none vclk_0;
int vclk_setrate(struct vclk *vclk, unsigned long rate)
{
int ret = 0;
#ifdef CONFIG_EXYNOS_SNAPSHOT_CLK
const char *name = "vclk_setrate";
#endif
if (!vclk->ref_count && vclk->ops->set_rate) {
vclk->vfreq = rate;
goto out;
}
exynos_ss_clk(vclk, name, ESS_FLAG_IN);
if (vclk->ops->set_rate)
ret = vclk->ops->set_rate(vclk, rate);
else
ret = -1;
if (!ret) {
vclk->vfreq = rate;
exynos_ss_clk(vclk, name, ESS_FLAG_OUT);
} else
exynos_ss_clk(vclk, name, ESS_FLAG_ON);
out:
return ret;
}
unsigned long vclk_getrate(struct vclk *vclk)
{
int ret = 0;
#ifdef CONFIG_EXYNOS_SNAPSHOT_CLK
const char *name = "vclk_getrate";
#endif
exynos_ss_clk(vclk, name, ESS_FLAG_IN);
if (!vclk->ref_count) {
exynos_ss_clk(vclk, name, ESS_FLAG_ON);
goto out;
}
ret = vclk->ops->get_rate(vclk);
if (ret > 0) {
vclk->vfreq = (unsigned long)ret;
exynos_ss_clk(vclk, name, ESS_FLAG_OUT);
} else
exynos_ss_clk(vclk, name, ESS_FLAG_ON);
out:
return ret;
}
int vclk_enable(struct vclk *vclk)
{
int ret = 0;
unsigned int tmp;
#ifdef CONFIG_EXYNOS_SNAPSHOT_CLK
const char *name = "vclk_enable";
#endif
if (vclk->ref_count++)
goto out;
if (vclk->parent != VCLK_NONE)
ret = vclk_enable(vclk->parent);
exynos_ss_clk(vclk, name, ESS_FLAG_IN);
if (ret)
goto out;
ret = vclk->ops->enable(vclk);
if (ret || !vclk->vfreq || !vclk->ops->set_rate)
goto out;
tmp = vclk->vfreq;
if (vclk->type == vclk_group_dfs)
vclk->vfreq = 0;
if (vclk->ops->set_rate(vclk, tmp))
pr_warn("vclk retenction fail (%s) (%ld)\n",
vclk->name, vclk->vfreq);
out:
if (!ret) {
exynos_ss_clk(vclk, name, ESS_FLAG_OUT);
} else
exynos_ss_clk(vclk, name, ESS_FLAG_ON);
return ret;
}
int vclk_disable(struct vclk *vclk)
{
int ret = 0;
int parent_disable = 0;
#ifdef CONFIG_EXYNOS_SNAPSHOT_CLK
const char *name = "vclk_disable";
#endif
if (vclk->ref_count)
parent_disable = 1;
if (vclk->ref_count)
vclk->ref_count--;
if (vclk->ref_count)
goto out;
exynos_ss_clk(vclk, name, ESS_FLAG_IN);
ret = vclk->ops->disable(vclk);
if (ret) {
exynos_ss_clk(vclk, name, ESS_FLAG_ON);
goto out;
} else
exynos_ss_clk(vclk, name, ESS_FLAG_OUT);
if (parent_disable && vclk->parent != VCLK_NONE)
ret = vclk_disable(vclk->parent);
out:
return ret;
}