/* 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 * * 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 #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, };