695 lines
14 KiB
C
Executable File
695 lines
14 KiB
C
Executable File
#include <linux/types.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/io.h>
|
|
#include <soc/samsung/ect_parser.h>
|
|
|
|
#include "cmucal.h"
|
|
#include "vclk.h"
|
|
#include "ra.h"
|
|
#include "acpm_dvfs.h"
|
|
#include "asv.h"
|
|
|
|
#define ECT_DUMMY_SFR (0xFFFFFFFF)
|
|
unsigned int asv_table_ver = 0;
|
|
|
|
static struct vclk_lut *get_lut(struct vclk *vclk, unsigned int rate)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < vclk->num_rates; i++)
|
|
if (rate >= vclk->lut[i].rate)
|
|
break;
|
|
|
|
if (i == vclk->num_rates)
|
|
return NULL;
|
|
|
|
return &vclk->lut[i];
|
|
}
|
|
|
|
static unsigned int get_max_rate(unsigned int from, unsigned int to)
|
|
{
|
|
unsigned int max_rate;
|
|
|
|
if (from)
|
|
max_rate = (from > to) ? from : to;
|
|
else
|
|
max_rate = to;
|
|
|
|
return max_rate;
|
|
}
|
|
|
|
static void __select_switch_pll(struct vclk *vclk,
|
|
unsigned int rate,
|
|
unsigned int select)
|
|
{
|
|
if (vclk->ops && vclk->ops->switch_pre)
|
|
vclk->ops->switch_pre(vclk->vrate, rate);
|
|
|
|
if (vclk->ops && vclk->ops->switch_trans && select)
|
|
vclk->ops->switch_trans(vclk->vrate, rate);
|
|
else if (vclk->ops && vclk->ops->restore_trans && !select)
|
|
vclk->ops->restore_trans(vclk->vrate, rate);
|
|
else
|
|
ra_select_switch_pll(vclk->switch_info, select);
|
|
|
|
if (vclk->ops && vclk->ops->switch_post)
|
|
vclk->ops->switch_post(vclk->vrate, rate);
|
|
}
|
|
|
|
static int transition_switch(struct vclk *vclk, struct vclk_lut *lut,
|
|
unsigned int switch_rate)
|
|
{
|
|
unsigned int *list = vclk->list;
|
|
unsigned int num_list = vclk->num_list;
|
|
|
|
/* Change to swithing PLL */
|
|
if (vclk->ops && vclk->ops->trans_pre)
|
|
vclk->ops->trans_pre(vclk->vrate, lut->rate);
|
|
|
|
ra_set_clk_by_type(list, lut, num_list, DIV_TYPE, TRANS_HIGH);
|
|
|
|
__select_switch_pll(vclk, switch_rate, 1);
|
|
|
|
ra_set_clk_by_type(list, lut, num_list, MUX_TYPE, TRANS_FORCE);
|
|
ra_set_clk_by_type(list, lut, num_list, DIV_TYPE, TRANS_LOW);
|
|
|
|
vclk->vrate = switch_rate;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int transition_restore(struct vclk *vclk, struct vclk_lut *lut)
|
|
{
|
|
unsigned int *list = vclk->list;
|
|
unsigned int num_list = vclk->num_list;
|
|
|
|
/* PLL setting */
|
|
ra_set_pll_ops(list, lut, num_list, vclk->ops);
|
|
|
|
ra_set_clk_by_type(list, lut, num_list, DIV_TYPE, TRANS_HIGH);
|
|
|
|
__select_switch_pll(vclk, lut->rate, 0);
|
|
|
|
ra_set_clk_by_type(list, lut, num_list, MUX_TYPE, TRANS_FORCE);
|
|
ra_set_clk_by_type(list, lut, num_list, DIV_TYPE, TRANS_LOW);
|
|
if (vclk->ops && vclk->ops->trans_post)
|
|
vclk->ops->trans_post(vclk->vrate, lut->rate);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int transition(struct vclk *vclk,
|
|
struct vclk_lut *lut)
|
|
{
|
|
unsigned int *list = vclk->list;
|
|
unsigned int num_list = vclk->num_list;
|
|
|
|
ra_set_clk_by_type(list, lut, num_list, DIV_TYPE, TRANS_HIGH);
|
|
ra_set_clk_by_type(list, lut, num_list, PLL_TYPE, TRANS_LOW);
|
|
ra_set_clk_by_type(list, lut, num_list, MUX_TYPE, TRANS_FORCE);
|
|
ra_set_clk_by_type(list, lut, num_list, PLL_TYPE, TRANS_HIGH);
|
|
ra_set_clk_by_type(list, lut, num_list, DIV_TYPE, TRANS_LOW);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool is_switching_pll_ops(struct vclk *vclk, int cmd)
|
|
{
|
|
int i;
|
|
|
|
if (!vclk->switch_info)
|
|
return false;
|
|
|
|
if (cmd != ONESHOT_TRANS)
|
|
return true;
|
|
|
|
for (i = 0; i < vclk->num_list; i++) {
|
|
if (IS_PLL(vclk->list[i]))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static int __vclk_set_rate(unsigned int id, unsigned int rate, int cmd)
|
|
{
|
|
struct vclk *vclk;
|
|
struct vclk_lut *new_lut, *switch_lut;
|
|
unsigned int switch_rate, max_rate;
|
|
|
|
if (!IS_VCLK(id))
|
|
return ra_set_rate(id, rate);
|
|
|
|
vclk = cmucal_get_node(id);
|
|
if (!vclk || !vclk->lut)
|
|
return -EVCLKINVAL;
|
|
|
|
if (IS_DFS_VCLK(id) || IS_COMMON_VCLK(id))
|
|
new_lut = get_lut(vclk, rate);
|
|
else
|
|
new_lut = get_lut(vclk, rate / 1000);
|
|
|
|
if (!new_lut)
|
|
return -EVCLKINVAL;
|
|
|
|
if (is_switching_pll_ops(vclk, cmd)) {
|
|
switch_lut = new_lut;
|
|
switch_rate = rate;
|
|
if (is_oneshot_trans(cmd)) {
|
|
max_rate = get_max_rate(vclk->vrate, rate);
|
|
switch_rate = ra_set_rate_switch(vclk->switch_info,
|
|
max_rate);
|
|
switch_lut = get_lut(vclk, switch_rate);
|
|
if (!switch_lut)
|
|
return -EVCLKINVAL;
|
|
}
|
|
if (is_switch_trans(cmd))
|
|
transition_switch(vclk, switch_lut, switch_rate);
|
|
if (is_restore_trans(cmd))
|
|
transition_restore(vclk, new_lut);
|
|
} else if (vclk->seq) {
|
|
ra_set_clk_by_seq(vclk->list,
|
|
new_lut,
|
|
vclk->seq,
|
|
vclk->num_list);
|
|
} else {
|
|
transition(vclk, new_lut);
|
|
}
|
|
|
|
vclk->vrate = rate;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int vclk_set_rate(unsigned int id, unsigned long rate)
|
|
{
|
|
int ret;
|
|
|
|
ret = __vclk_set_rate(id, rate, ONESHOT_TRANS);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int vclk_set_rate_switch(unsigned int id, unsigned long rate)
|
|
{
|
|
int ret;
|
|
|
|
ret = __vclk_set_rate(id, rate, SWITCH_TRANS);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int vclk_set_rate_restore(unsigned int id, unsigned long rate)
|
|
{
|
|
int ret;
|
|
|
|
ret = __vclk_set_rate(id, rate, RESTORE_TRANS);
|
|
|
|
return ret;
|
|
}
|
|
|
|
unsigned long vclk_recalc_rate(unsigned int id)
|
|
{
|
|
struct vclk *vclk;
|
|
int i, ret;
|
|
|
|
if (!IS_VCLK(id))
|
|
return ra_recalc_rate(id);
|
|
|
|
vclk = cmucal_get_node(id);
|
|
if (!vclk)
|
|
return 0;
|
|
|
|
if (IS_DFS_VCLK(vclk->id) ||
|
|
IS_COMMON_VCLK(vclk->id) ||
|
|
IS_ACPM_VCLK(vclk->id)) {
|
|
for (i = 0; i < vclk->num_rates; i++) {
|
|
ret = ra_compare_clk_list(vclk->lut[i].params,
|
|
vclk->list,
|
|
vclk->num_list);
|
|
if (!ret) {
|
|
vclk->vrate = vclk->lut[i].rate;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == vclk->num_rates) {
|
|
vclk->vrate = 0;
|
|
pr_debug("%s:%x failed\n", __func__, id);
|
|
}
|
|
} else {
|
|
vclk->vrate = ra_recalc_rate(vclk->list[0]);
|
|
}
|
|
|
|
return vclk->vrate;
|
|
}
|
|
|
|
unsigned long vclk_get_rate(unsigned int id)
|
|
{
|
|
struct vclk *vclk;
|
|
|
|
if (IS_VCLK(id)) {
|
|
vclk = cmucal_get_node(id);
|
|
if (vclk)
|
|
return vclk->vrate;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int vclk_set_enable(unsigned int id)
|
|
{
|
|
struct vclk *vclk;
|
|
int ret = -EVCLKINVAL;
|
|
|
|
if (IS_GATE_VCLK(id)) {
|
|
vclk = cmucal_get_node(id);
|
|
if (vclk)
|
|
ret = ra_set_list_enable(vclk->list, vclk->num_list);
|
|
} else if (IS_VCLK(id)){
|
|
ret = 0;
|
|
} else {
|
|
ret = ra_set_enable(id, 1);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int vclk_set_disable(unsigned int id)
|
|
{
|
|
struct vclk *vclk;
|
|
int ret = -EVCLKINVAL;
|
|
|
|
if (IS_GATE_VCLK(id)) {
|
|
vclk = cmucal_get_node(id);
|
|
if (vclk)
|
|
ret = ra_set_list_disable(vclk->list, vclk->num_list);
|
|
} else if (IS_VCLK(id)){
|
|
ret = 0;
|
|
} else {
|
|
ret = ra_set_enable(id, 0);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
unsigned int vclk_get_lv_num(unsigned int id)
|
|
{
|
|
struct vclk *vclk;
|
|
int lv_num = 0;
|
|
|
|
vclk = cmucal_get_node(id);
|
|
|
|
if (vclk && vclk->lut)
|
|
lv_num = vclk->num_rates;
|
|
|
|
return lv_num;
|
|
|
|
}
|
|
|
|
unsigned int vclk_get_max_freq(unsigned int id)
|
|
{
|
|
struct vclk *vclk;
|
|
int rate = 0;
|
|
|
|
vclk = cmucal_get_node(id);
|
|
|
|
if (vclk && vclk->lut)
|
|
rate = vclk->max_freq;
|
|
|
|
return rate;
|
|
}
|
|
|
|
unsigned int vclk_get_min_freq(unsigned int id)
|
|
{
|
|
struct vclk *vclk;
|
|
int rate = 0;
|
|
|
|
vclk = cmucal_get_node(id);
|
|
|
|
if (vclk && vclk->lut)
|
|
rate = vclk->min_freq;
|
|
|
|
return rate;
|
|
}
|
|
|
|
int vclk_get_rate_table(unsigned int id, unsigned long *table)
|
|
{
|
|
struct vclk *vclk;
|
|
int i;
|
|
unsigned int nums = 0;
|
|
|
|
vclk = cmucal_get_node(id);
|
|
if (!vclk || !IS_VCLK(vclk->id))
|
|
return 0;
|
|
if (vclk->lut) {
|
|
for (i = 0; i < vclk->num_rates; i++)
|
|
table[i] = vclk->lut[i].rate;
|
|
nums = vclk->num_rates;
|
|
}
|
|
|
|
return nums;
|
|
}
|
|
|
|
int vclk_get_bigturbo_table(unsigned int *table)
|
|
{
|
|
void *gen_block;
|
|
struct ect_gen_param_table *bigturbo;
|
|
int idx;
|
|
int i;
|
|
|
|
gen_block = ect_get_block("GEN");
|
|
if (gen_block == NULL)
|
|
return -EVCLKINVAL;
|
|
|
|
bigturbo = ect_gen_param_get_table(gen_block, "BIGTURBO");
|
|
if (bigturbo == NULL)
|
|
return -EVCLKINVAL;
|
|
|
|
if (bigturbo->num_of_row == 0)
|
|
return -EVCLKINVAL;
|
|
|
|
if (asv_table_ver >= bigturbo->num_of_row)
|
|
idx = bigturbo->num_of_row - 1;
|
|
else
|
|
idx = asv_table_ver;
|
|
|
|
for (i = 0; i < bigturbo->num_of_col; i++)
|
|
table[i] = bigturbo->parameter[idx * bigturbo->num_of_col + i];
|
|
|
|
return 0;
|
|
}
|
|
|
|
unsigned int vclk_get_boot_freq(unsigned int id)
|
|
{
|
|
struct vclk *vclk;
|
|
unsigned int rate = 0;
|
|
|
|
vclk = cmucal_get_node(id);
|
|
if (!vclk || !(IS_DFS_VCLK(vclk->id) || IS_ACPM_VCLK(vclk->id)))
|
|
return rate;
|
|
|
|
if (vclk->boot_freq)
|
|
rate = vclk->boot_freq;
|
|
else
|
|
rate = (unsigned int)vclk_recalc_rate(id);
|
|
|
|
return rate;
|
|
}
|
|
|
|
unsigned int vclk_get_resume_freq(unsigned int id)
|
|
{
|
|
struct vclk *vclk;
|
|
unsigned int rate = 0;
|
|
|
|
vclk = cmucal_get_node(id);
|
|
if (!vclk || !(IS_DFS_VCLK(vclk->id) || IS_ACPM_VCLK(vclk->id)))
|
|
return rate;
|
|
|
|
if (vclk->resume_freq)
|
|
rate = vclk->resume_freq;
|
|
else
|
|
rate = (unsigned int)vclk_recalc_rate(id);
|
|
|
|
return rate;
|
|
}
|
|
|
|
static int vclk_get_dfs_info(struct vclk *vclk)
|
|
{
|
|
int i, j;
|
|
void *dvfs_block;
|
|
struct ect_dvfs_domain *dvfs_domain;
|
|
void *gen_block;
|
|
struct ect_gen_param_table *minmax = NULL;
|
|
unsigned int *minmax_table = NULL;
|
|
int *params, idx;
|
|
int ret = 0;
|
|
char buf[32];
|
|
|
|
dvfs_block = ect_get_block("DVFS");
|
|
if (dvfs_block == NULL)
|
|
return -EVCLKNOENT;
|
|
|
|
dvfs_domain = ect_dvfs_get_domain(dvfs_block, vclk->name);
|
|
if (dvfs_domain == NULL)
|
|
return -EVCLKINVAL;
|
|
|
|
gen_block = ect_get_block("GEN");
|
|
if (gen_block) {
|
|
sprintf(buf, "MINMAX_%s", vclk->name);
|
|
minmax = ect_gen_param_get_table(gen_block, buf);
|
|
if (minmax != NULL) {
|
|
for (i = 0; i < minmax->num_of_row; i++) {
|
|
minmax_table = &minmax->parameter[minmax->num_of_col * i];
|
|
if (minmax_table[0] == asv_table_ver)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
vclk->num_rates = dvfs_domain->num_of_level;
|
|
vclk->num_list = dvfs_domain->num_of_clock;
|
|
vclk->max_freq = dvfs_domain->max_frequency;
|
|
vclk->min_freq = dvfs_domain->min_frequency;
|
|
|
|
if (minmax_table != NULL) {
|
|
vclk->min_freq = minmax_table[MINMAX_MIN_FREQ] * 1000;
|
|
vclk->max_freq = minmax_table[MINMAX_MAX_FREQ] * 1000;
|
|
}
|
|
pr_debug("ACPM_DVFS :%s\n", vclk->name);
|
|
|
|
vclk->list = kzalloc(sizeof(unsigned int) * vclk->num_list, GFP_KERNEL);
|
|
if (!vclk->list)
|
|
return -EVCLKNOMEM;
|
|
|
|
for (i = 0; i < dvfs_domain->num_of_clock; i++) {
|
|
if (dvfs_domain->list_sfr[i] == ECT_DUMMY_SFR) {
|
|
vclk->list[i] = INVALID_CLK_ID;
|
|
continue;
|
|
}
|
|
|
|
vclk->list[i] = cmucal_get_id_by_addr(dvfs_domain->list_sfr[i]);
|
|
if (vclk->list[i] == INVALID_CLK_ID) {
|
|
ret = -EVCLKINVAL;
|
|
goto err_nomem1;
|
|
}
|
|
}
|
|
|
|
vclk->lut = kzalloc(sizeof(struct vclk_lut) * vclk->num_rates,
|
|
GFP_KERNEL);
|
|
if (!vclk->lut) {
|
|
ret = -EVCLKNOMEM;
|
|
goto err_nomem1;
|
|
}
|
|
|
|
for (i = 0; i < vclk->num_rates; i++) {
|
|
vclk->lut[i].rate = dvfs_domain->list_level[i].level;
|
|
params = kcalloc(vclk->num_list, sizeof(int), GFP_KERNEL);
|
|
if (!params) {
|
|
ret = -EVCLKNOMEM;
|
|
if (i == 0)
|
|
goto err_nomem2;
|
|
for (i = i-1; i >= 0; i--)
|
|
kfree(vclk->lut[i].params);
|
|
goto err_nomem2;
|
|
}
|
|
|
|
for (j = 0; j < vclk->num_list; ++j) {
|
|
idx = i * vclk->num_list + j;
|
|
params[j] = dvfs_domain->list_dvfs_value[idx];
|
|
}
|
|
vclk->lut[i].params = params;
|
|
}
|
|
vclk->boot_freq = 0;
|
|
vclk->resume_freq = 0;
|
|
|
|
if (minmax_table != NULL) {
|
|
for (i = 0; i < vclk->num_rates; i++) {
|
|
if (vclk->lut[i].rate == minmax_table[MINMAX_BOOT_FREQ] * 1000)
|
|
vclk->boot_freq = vclk->lut[i].rate;
|
|
}
|
|
|
|
for (i = 0; i < vclk->num_rates; i++) {
|
|
if (vclk->lut[i].rate == minmax_table[MINMAX_RESUME_FREQ] * 1000)
|
|
vclk->resume_freq = vclk->lut[i].rate;
|
|
}
|
|
} else{
|
|
if (dvfs_domain->boot_level_idx != -1)
|
|
vclk->boot_freq = vclk->lut[dvfs_domain->boot_level_idx].rate;
|
|
|
|
if (dvfs_domain->resume_level_idx != -1)
|
|
vclk->resume_freq = vclk->lut[dvfs_domain->resume_level_idx].rate;
|
|
}
|
|
|
|
return ret;
|
|
err_nomem2:
|
|
kfree(vclk->lut);
|
|
err_nomem1:
|
|
kfree(vclk->list);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static struct ect_voltage_table *get_max_min_freq_lv(struct ect_voltage_domain *domain, unsigned int version, int *max_lv, int *min_lv)
|
|
{
|
|
int i;
|
|
unsigned int max_asv_version = 0;
|
|
struct ect_voltage_table *table = NULL;
|
|
|
|
for (i = 0; i < domain->num_of_table; i++) {
|
|
table = &domain->table_list[i];
|
|
if (version == table->table_version)
|
|
break;
|
|
|
|
if (table->table_version > max_asv_version)
|
|
max_asv_version = table->table_version;
|
|
}
|
|
|
|
if (i == domain->num_of_table) {
|
|
pr_err("There is no voltage table, force change %d to %d\n",
|
|
asv_table_ver, max_asv_version);
|
|
asv_table_ver = max_asv_version;
|
|
}
|
|
|
|
if (!table) {
|
|
*max_lv = -1;
|
|
*min_lv = -1;
|
|
return NULL;
|
|
}
|
|
|
|
*max_lv = -1;
|
|
*min_lv = domain->num_of_level - 1;
|
|
for (i = 0; i < domain->num_of_level; i++) {
|
|
if (*max_lv == -1 && table->level_en[i])
|
|
*max_lv = i;
|
|
if (*max_lv != -1 && !table->level_en[i]) {
|
|
*min_lv = i - 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return table;
|
|
}
|
|
|
|
static int vclk_get_asv_info(struct vclk *vclk)
|
|
{
|
|
void *asv_block;
|
|
struct ect_voltage_domain *domain;
|
|
struct ect_voltage_table *table = NULL;
|
|
int max_lv, min_lv;
|
|
int ret = 0;
|
|
char buf[32];
|
|
void *gen_block;
|
|
struct ect_gen_param_table *minmax = NULL;
|
|
|
|
asv_block = ect_get_block("ASV");
|
|
if (asv_block == NULL)
|
|
return -EVCLKNOENT;
|
|
|
|
domain = ect_asv_get_domain(asv_block, vclk->name);
|
|
if (domain == NULL)
|
|
return -EVCLKINVAL;
|
|
|
|
gen_block = ect_get_block("GEN");
|
|
if (gen_block) {
|
|
sprintf(buf, "MINMAX_%s", vclk->name);
|
|
minmax = ect_gen_param_get_table(gen_block, buf);
|
|
if (minmax != NULL)
|
|
goto minmax_skip;
|
|
}
|
|
|
|
table = get_max_min_freq_lv(domain, asv_table_ver, &max_lv, &min_lv);
|
|
if (table == NULL)
|
|
return -EVCLKFAULT;
|
|
|
|
if (max_lv >= 0)
|
|
vclk->max_freq = domain->level_list[max_lv] * 1000;
|
|
else
|
|
vclk->max_freq = -1;
|
|
|
|
if (min_lv >= 0)
|
|
vclk->min_freq = domain->level_list[min_lv] * 1000;
|
|
else
|
|
vclk->min_freq = -1;
|
|
|
|
if (table->boot_level_idx >= 0)
|
|
vclk->boot_freq = domain->level_list[table->boot_level_idx] * 1000;
|
|
else
|
|
vclk->boot_freq = -1;
|
|
|
|
if (table->resume_level_idx >= 0)
|
|
vclk->resume_freq = domain->level_list[table->resume_level_idx] * 1000;
|
|
else
|
|
vclk->resume_freq = -1;
|
|
|
|
minmax_skip:
|
|
pr_debug(" num_rates : %7d\n", vclk->num_rates);
|
|
pr_debug(" num_clk_list : %7d\n", vclk->num_list);
|
|
pr_debug(" max_freq : %7d\n", vclk->max_freq);
|
|
pr_debug(" min_freq : %7d\n", vclk->min_freq);
|
|
pr_debug(" boot_freq : %7d\n", vclk->boot_freq);
|
|
pr_debug(" resume_freq : %7d\n", vclk->resume_freq);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void vclk_bind(void)
|
|
{
|
|
struct vclk *vclk;
|
|
int i;
|
|
bool warn_on = 0;
|
|
int ret;
|
|
|
|
for (i = 0; i < cmucal_get_list_size(ACPM_VCLK_TYPE); i++) {
|
|
vclk = cmucal_get_node(ACPM_VCLK_TYPE | i);
|
|
if (!vclk) {
|
|
pr_err("cannot found vclk node %x\n", i);
|
|
continue;
|
|
}
|
|
|
|
ret = vclk_get_dfs_info(vclk);
|
|
if (ret == -EVCLKNOENT) {
|
|
if (!warn_on)
|
|
pr_warn("ECT DVFS not found\n");
|
|
warn_on = 1;
|
|
} else if (ret) {
|
|
pr_err("ECT DVFS [%s] not found %d\n",
|
|
vclk->name, ret);
|
|
} else {
|
|
ret = vclk_get_asv_info(vclk);
|
|
if (ret)
|
|
pr_err("ECT ASV [%s] not found %d\n",
|
|
vclk->name, ret);
|
|
}
|
|
}
|
|
}
|
|
|
|
int vclk_register_ops(unsigned int id, struct vclk_trans_ops *ops)
|
|
{
|
|
struct vclk *vclk;
|
|
|
|
if (IS_DFS_VCLK(id)) {
|
|
vclk = cmucal_get_node(id);
|
|
if (!vclk)
|
|
return -EVCLKINVAL;
|
|
vclk->ops = ops;
|
|
|
|
return 0;
|
|
}
|
|
|
|
return -EVCLKNOENT;
|
|
}
|
|
|
|
int __init vclk_initialize(void)
|
|
{
|
|
pr_info("vclk initialize for cmucal\n");
|
|
|
|
ra_init();
|
|
|
|
asv_table_ver = asv_table_init();
|
|
|
|
vclk_bind();
|
|
|
|
return 0;
|
|
}
|