blob: 70ec525b926047ddcb80cbf339984183fa03677f [file] [log] [blame]
/* linux/drivers/soc/samsung/cal-if/pmucal_asv.c
*
* Copyright (c) 2016 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*
* ASV common driver for Exynos
* Author: Hyunju Kang <hjtop.kang@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "fvmap_asv.h"
#include "pwrcal.h"
#include "vclk.h"
#include <soc/samsung/ect_parser.h>
#include "cmucal.h"
static void asv_set_grp(unsigned int id, unsigned int asvgrp)
{
if (exynos_cal_asv_ops.set_grp)
exynos_cal_asv_ops.set_grp(id, asvgrp);
}
static void asv_set_tablever(unsigned int version)
{
asv_table_ver = version;
}
static void asv_set_ssa0(unsigned int id, unsigned int ssa0)
{
if (exynos_cal_asv_ops.set_ssa0)
exynos_cal_asv_ops.set_ssa0(id, ssa0);
}
static void asv_get_asvinfo(void)
{
if (exynos_cal_asv_ops.asv_get_asvinfo)
exynos_cal_asv_ops.asv_get_asvinfo();
}
static int get_asv_table(unsigned int *table, unsigned int id)
{
int max_lv = 0;
if (exynos_cal_asv_ops.get_asv_table)
max_lv = exynos_cal_asv_ops.get_asv_table(table, id);
return max_lv;
}
static int asv_rcc_set_table(void)
{
int result = 0;
if (exynos_cal_asv_ops.set_rcc_table)
result = exynos_cal_asv_ops.set_rcc_table();
return result;
}
static void asv_voltage_init_table(struct asv_table_list **asv_table, char *name)
{
struct ect_voltage_domain *domain = NULL;
struct ect_voltage_table *table = NULL;
struct asv_table_entry *asv_entry = NULL;
struct ect_margin_domain *margin_domain = NULL;
void *asv_block = NULL, *margin_block = NULL;
int i =0, j = 0, k = 0;
asv_block = ect_get_block("ASV");
if (asv_block == NULL)
return;
margin_block = ect_get_block("MARGIN");
domain = ect_asv_get_domain(asv_block, name);
if (domain == NULL)
return;
if (margin_block)
margin_domain = ect_margin_get_domain(margin_block, name);
*asv_table = kzalloc(sizeof(struct asv_table_list) * domain->num_of_table, GFP_KERNEL);
if (*asv_table == NULL)
return;
for (i = 0; i < domain->num_of_table; ++i) {
table = &domain->table_list[i];
(*asv_table)[i].table_size = domain->num_of_table;
(*asv_table)[i].table = kzalloc(sizeof(struct asv_table_entry) * domain->num_of_level, GFP_KERNEL);
if ((*asv_table)[i].table == NULL)
return;
for (j = 0; j < domain->num_of_level; ++j) {
asv_entry = &(*asv_table)[i].table[j];
asv_entry->index = domain->level_list[j];
asv_entry->voltage = kzalloc(sizeof(unsigned int) * domain->num_of_group, GFP_KERNEL);
for (k = 0; k < domain->num_of_group; ++k) {
if (table->voltages != NULL)
asv_entry->voltage[k] = table->voltages[j * domain->num_of_group + k];
else if (table->voltages_step != NULL)
asv_entry->voltage[k] = table->voltages_step[j * domain->num_of_group + k] * table->volt_step;
if (margin_domain != NULL) {
if (margin_domain->offset != NULL)
asv_entry->voltage[k] += margin_domain->offset[j * margin_domain->num_of_group + k];
else
asv_entry->voltage[k] += margin_domain->offset_compact[j * margin_domain->num_of_group + k] * margin_domain->volt_step;
}
}
}
}
}
static void asv_rcc_init_table(struct asv_table_list **rcc_table, char *name)
{
struct ect_rcc_domain *domain = NULL;
struct ect_rcc_table *table = NULL;
struct asv_table_entry *rcc_entry = NULL;
void *rcc_block = NULL;
int i = 0, j = 0, k = 0;
rcc_block = ect_get_block("RCC");
if (rcc_block == NULL)
return;
domain = ect_rcc_get_domain(rcc_block, name);
if (domain == NULL)
return;
*rcc_table = kzalloc(sizeof(struct asv_table_list) * domain->num_of_table, GFP_KERNEL);
if (*rcc_table == NULL)
return;
for (i = 0; i < domain->num_of_table; ++i) {
table = &domain->table_list[i];
(*rcc_table)[i].table_size = domain->num_of_table;
(*rcc_table)[i].table = kzalloc(sizeof(struct asv_table_entry) * domain->num_of_level, GFP_KERNEL);
if ((*rcc_table)[i].table == NULL)
return;
for (j = 0; j < domain->num_of_level; ++j) {
rcc_entry = &(*rcc_table)[i].table[j];
rcc_entry->index = domain->level_list[j];
rcc_entry->voltage = kzalloc(sizeof(unsigned int) * domain->num_of_group, GFP_KERNEL);
for (k = 0; k < domain->num_of_group; ++k) {
if (table->rcc != NULL)
rcc_entry->voltage[k] = table->rcc[j * domain->num_of_group + k];
else
rcc_entry->voltage[k] = table->rcc_compact[j * domain->num_of_group + k];
}
}
}
}
static void asv_voltage_table_init(void)
{
int i = 0;
for (i = 0; i < NUM_OF_DVFS; i++)
asv_voltage_init_table(&(asv_table[i]), dvfs_names[i]);
}
static void asv_rcc_table_init(void)
{
int i = 0;
for (i = 0; i < NUM_OF_DVFS; i++)
asv_rcc_init_table(&(rcc_table[i]), dvfs_names[i]);
}
static void asv_ssa_init(void)
{
void *gen_block = 0;
struct ect_gen_param_table *param = NULL;
unsigned int asv_table_version = asv_table_ver;
int i = 0, j = 0;
gen_block = ect_get_block("GEN");
if (gen_block == NULL)
return;
for (i = 0; i < NUM_OF_DVFS; i++) {
param = ect_gen_param_get_table(gen_block, ssa_names[i]);
if (param) {
subgrp_table[i] = param->parameter[asv_table_version * param->num_of_col + SUB_GROUP_INDEX];
ssa_info_table[i].ssa0_base = param->parameter[asv_table_version * param->num_of_col + SSA0_BASE_INDEX];
ssa_info_table[i].ssa0_offset = param->parameter[asv_table_version * param->num_of_col + SSA0_OFFSET_INDEX];
for (j = 0; j < size_of_ssa1_table; j++)
ssa_info_table[i].ssa1_table[j] = param->parameter[asv_table_version * param->num_of_col + 4 + j];
}
}
}
static void asv_ema_init(void)
{
}
static void asv_print_info(void)
{
int i = 0;
pr_info("asv_table_ver : %d\n", asv_table_ver);
pr_info("fused_grp : %d\n", fused_grp);
for (i = 0; i < NUM_OF_DVFS; i++)
pr_info("%s_asv_group : %u\n", dvfs_names[i], fused_table[i].asv_group);
}
static void rcc_print_info(void)
{
if (exynos_cal_asv_ops.print_rcc_info)
exynos_cal_asv_ops.print_rcc_info();
}
static int asv_set_ema(unsigned int id, unsigned int volt)
{
int result = 0;
if(exynos_cal_asv_ops.set_ema)
result = exynos_cal_asv_ops.set_ema(id, volt);
return result;
}
static int asv_get_grp(unsigned int id, unsigned int lv)
{
int result = 0;
if (exynos_cal_asv_ops.get_grp)
result = exynos_cal_asv_ops.get_grp(id, lv);
return result;
}
static int asv_get_tablever(void)
{
return (int)(asv_table_ver);
}
int cal_asv_init(void)
{
int i = 0;
for (i = 0; i < NUM_OF_DVFS; i++) {
asv_table[i] = NULL;
rcc_table[i] = NULL;
}
asv_get_asvinfo();
asv_voltage_table_init();
asv_rcc_table_init();
asv_ssa_init();
asv_ema_init();
if (exynos_cal_asv_ops.asv_init)
exynos_cal_asv_ops.asv_init();
return 0;
}
struct cal_asv_ops cal_asv_ops = {
.print_asv_info = asv_print_info,
.print_rcc_info = rcc_print_info,
.asv_init = cal_asv_init,
.set_grp = asv_set_grp,
.get_grp = asv_get_grp,
.get_asv_table = get_asv_table,
.set_tablever = asv_set_tablever,
.get_tablever = asv_get_tablever,
.set_rcc_table = asv_rcc_set_table,
.set_ssa0 = asv_set_ssa0,
.set_ema = asv_set_ema,
};