mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-09 01:28:05 -04:00
1132 lines
22 KiB
C
1132 lines
22 KiB
C
#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>
|
|
#include <trace/events/exynos.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) {
|
|
if (pwrcal_div_set_ratio(d1->div, 1))
|
|
goto out;
|
|
} else {
|
|
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);
|
|
trace_exynos_clk_in(vclk, __func__);
|
|
|
|
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);
|
|
trace_exynos_clk_out(vclk, __func__);
|
|
} else {
|
|
exynos_ss_clk(vclk, name, ESS_FLAG_ON);
|
|
trace_exynos_clk_on(vclk, __func__);
|
|
}
|
|
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);
|
|
trace_exynos_clk_in(vclk, __func__);
|
|
|
|
if (!vclk->ref_count) {
|
|
exynos_ss_clk(vclk, name, ESS_FLAG_ON);
|
|
trace_exynos_clk_on(vclk, __func__);
|
|
goto out;
|
|
}
|
|
|
|
ret = vclk->ops->get_rate(vclk);
|
|
|
|
if (ret > 0) {
|
|
vclk->vfreq = (unsigned long)ret;
|
|
exynos_ss_clk(vclk, name, ESS_FLAG_OUT);
|
|
trace_exynos_clk_out(vclk, __func__);
|
|
} else {
|
|
exynos_ss_clk(vclk, name, ESS_FLAG_ON);
|
|
trace_exynos_clk_on(vclk, __func__);
|
|
}
|
|
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);
|
|
trace_exynos_clk_in(vclk, __func__);
|
|
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);
|
|
|
|
if (vclk->type == vclk_group_dfs)
|
|
vclk->vfreq = tmp;
|
|
|
|
out:
|
|
if (!ret) {
|
|
exynos_ss_clk(vclk, name, ESS_FLAG_OUT);
|
|
trace_exynos_clk_out(vclk, __func__);
|
|
} else {
|
|
exynos_ss_clk(vclk, name, ESS_FLAG_ON);
|
|
trace_exynos_clk_on(vclk, __func__);
|
|
}
|
|
|
|
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);
|
|
trace_exynos_clk_in(vclk, __func__);
|
|
ret = vclk->ops->disable(vclk);
|
|
|
|
if (ret) {
|
|
exynos_ss_clk(vclk, name, ESS_FLAG_ON);
|
|
trace_exynos_clk_on(vclk, __func__);
|
|
goto out;
|
|
} else {
|
|
exynos_ss_clk(vclk, name, ESS_FLAG_OUT);
|
|
trace_exynos_clk_out(vclk, __func__);
|
|
}
|
|
|
|
if (parent_disable && vclk->parent != VCLK_NONE)
|
|
ret = vclk_disable(vclk->parent);
|
|
|
|
out:
|
|
return ret;
|
|
}
|