kernel_samsung_a53x/drivers/soc/samsung/cal-if/exynos_debug_freq.c
2024-06-15 16:02:09 -03:00

557 lines
14 KiB
C
Executable file

/*
* Copyright (c) 2014-2019 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Samsung debugging code
*
* 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 <linux/kernel.h>
#include <linux/list.h>
#include <linux/of.h>
#include <soc/samsung/exynos_debug_freq.h>
#include <soc/samsung/ect_parser.h>
#include <soc/samsung/exynos-dm.h>
#include "cmucal.h"
extern struct vclk acpm_vclk_list[];
#define MAX_FREQ_DOMAIN (30)
struct table_domain {
unsigned int main_freq;
unsigned int sub_freq;
};
static int secdbg_dm_to_calid[MAX_FREQ_DOMAIN]; /* index: dm-id */
#define CAL_ID(id) (secdbg_dm_to_calid[(id)])
static unsigned int secdbg_freq_cur[MAX_FREQ_DOMAIN]; /* index: dss-id */
#define get_cur_freq(type) (secdbg_freq_cur[(type)])
#define get_freq_name(type) (acpm_vclk_list[type].name)
struct secdbg_skew_cache {
unsigned int freq;
struct table_domain *freq_table;
};
struct secdbg_skew_domain {
struct list_head list;
unsigned int master_dm_id;
unsigned int slave_dm_id;
unsigned int constraint_type;
int master_cal_id;
int slave_cal_id;
int num_freq;
const char *ect_name;
struct secdbg_skew_cache search_cache;
struct table_domain *freq_table;
};
static LIST_HEAD(secdbg_skew_domains);
struct secdbg_skew_ref {
int count; /* 0: no refs */
struct secdbg_skew_domain *refs[MAX_FREQ_DOMAIN];
};
static struct secdbg_skew_ref *secdbg_skew_ref_table[MAX_FREQ_DOMAIN]; /* index: dss-id */
static bool secdbg_freq_initialized;
static void secdbg_print_skew_domain(struct secdbg_skew_domain *dm)
{
struct table_domain *ft = dm->freq_table;
pr_info("[%s]%s-%s: %d entries\n",
dm->ect_name, get_freq_name(dm->master_cal_id),
get_freq_name(dm->slave_cal_id), dm->num_freq);
while (ft && ft->main_freq) {
pr_debug(" %u %u\n", ft->main_freq, ft->sub_freq);
ft++;
}
}
static void secdbg_print_all_skew_domain_info(void)
{
struct secdbg_skew_domain *dm;
list_for_each_entry(dm, &secdbg_skew_domains, list)
secdbg_print_skew_domain(dm);
}
static void secdbg_freq_set(int type, unsigned int freq)
{
secdbg_freq_cur[type] = freq;
pr_debug("%s: type: %d, freq: %d\n", __func__, type, freq);
}
static void secdbg_freq_ac_print(unsigned int type, unsigned int main_val,
unsigned int sub_val, unsigned int table_sub,
struct secdbg_skew_domain *skew_domain)
{
const char *freq_type = get_freq_name(type);
const char *main_type = get_freq_name(skew_domain->master_cal_id);
const char *sub_type = get_freq_name(skew_domain->slave_cal_id);
/* e.g. SKEW (mif): big 2106 mif 1014 < 1539 @ BIG-MIF table */
pr_info("SKEW (%s): %s %d %s %d < %d @ %s-%s table\n",
freq_type, main_type, main_val/1000,
sub_type, sub_val/1000, table_sub,
get_freq_name(skew_domain->master_cal_id),
get_freq_name(skew_domain->slave_cal_id));
BUG();
}
static struct table_domain *secdbg_get_suitable_freq_lb(unsigned int freq, int tsize, struct table_domain *table)
{
struct table_domain *islot;
struct table_domain *eslot = table + tsize;
for (islot = table; islot < eslot; islot++) {
pr_debug("%s: [%d] freq: %u, table_main: %d, sub_val: %d\n",
__func__, islot - table, freq, islot->main_freq, islot->sub_freq);
/* table must be descending order */
if (freq >= islot->main_freq)
return islot;
}
return NULL;
}
static void secdbg_freq_main_sub(int type, struct secdbg_skew_domain *domain)
{
unsigned int master_freq = get_cur_freq(domain->master_cal_id);
unsigned int slave_freq = get_cur_freq(domain->slave_cal_id);
struct table_domain *freq_constraint;
if (!master_freq || !slave_freq) {
pr_debug("%s: no cur freq: m_%d, s_%d\n", __func__, master_freq, slave_freq);
return;
}
if (master_freq == domain->search_cache.freq) {
freq_constraint = domain->search_cache.freq_table;
pr_debug("%s: (%d)%u cache found\n", __func__, domain->master_cal_id, master_freq);
} else {
freq_constraint = secdbg_get_suitable_freq_lb(master_freq, domain->num_freq, domain->freq_table);
if (!freq_constraint) {
pr_err("%s: no constraint: (%d)%uKhz-(%d)\n", __func__, domain->master_cal_id, master_freq, domain->slave_cal_id);
return;
} else {
domain->search_cache.freq = master_freq;
domain->search_cache.freq_table = freq_constraint;
}
}
pr_debug("%s: %d: main_val: %u, table_main: %u, sub_val: %u, table_sub: %u\n",
__func__, type, master_freq, freq_constraint->main_freq, slave_freq, freq_constraint->sub_freq);
if (slave_freq < freq_constraint->sub_freq)
secdbg_freq_ac_print(type, master_freq, slave_freq, freq_constraint->sub_freq, domain);
}
static void secdbg_freq_judge_skew(int type)
{
int i;
int count = secdbg_skew_ref_table[type]->count;
struct secdbg_skew_domain *domain;
for (i = 0; i < count; i++) {
domain = secdbg_skew_ref_table[type]->refs[i];
secdbg_freq_main_sub(type, domain);
}
}
void secdbg_freq_check(int type, unsigned long freq)
{
if (!secdbg_freq_initialized) {
pr_err_once("%s: not initialized, type(%d)\n", type);
return;
}
if (type < 0 || type >= MAX_FREQ_DOMAIN) {
pr_err("%s: type(%d) not in range\n", type);
return;
}
/* skip uninterested freqs */
if (!secdbg_skew_ref_table[type])
return;
pr_debug("%s: start\n", __func__);
/* set freq. domain and target freq */
secdbg_freq_set(type, (unsigned int)freq);
/* judge skew and ac print */
secdbg_freq_judge_skew(type);
}
static void secdbg_set_domain_to_ref(struct secdbg_skew_domain *domain, struct secdbg_skew_ref **skew_ref)
{
struct secdbg_skew_ref *skref;
if (!*skew_ref) {
skref = kzalloc(sizeof(struct secdbg_skew_ref), GFP_KERNEL);
if (!skref)
return;
skref->count = 0;
*skew_ref = skref;
}
pr_err("%s %d\n", __func__, __LINE__);
pr_err("%s %d, %d\n", __func__, __LINE__, (*skew_ref)->count);
(*skew_ref)->refs[(*skew_ref)->count] = domain;
pr_err("%s %d\n", __func__, __LINE__);
(*skew_ref)->count++;
}
static void secdbg_freq_create_ref_table(void)
{
struct secdbg_skew_domain *domain;
list_for_each_entry(domain, &secdbg_skew_domains, list) {
if (!domain->num_freq) {
pr_err("%s: null table %s", __func__, domain->ect_name);
continue;
}
pr_err("%s %d\n", __func__, __LINE__);
pr_err("%s %d, %u\n", __func__, __LINE__, domain->master_cal_id);
secdbg_set_domain_to_ref(domain, &secdbg_skew_ref_table[domain->master_cal_id]);
pr_err("%s %d\n", __func__, __LINE__);
secdbg_set_domain_to_ref(domain, &secdbg_skew_ref_table[domain->slave_cal_id]);
pr_err("%s %d\n", __func__, __LINE__);
}
}
static void secdbg_update_cal_id(struct secdbg_skew_domain *domain)
{
domain->master_cal_id = CAL_ID(domain->master_dm_id);
domain->slave_cal_id = CAL_ID(domain->slave_dm_id);
}
static int secdbg_devfreq_init_domain(struct secdbg_skew_domain *domain,
struct device_node *dn,
struct device_node *child)
{
int ret;
ret = of_property_read_u32(dn, "dm-index", &domain->master_dm_id);
if (ret) {
pr_err("%s: no dm-index\n", __func__, ret);
return ret;
}
if (of_property_read_string(child, "master_dm_name", &domain->ect_name)) {
ret = of_property_read_string(dn, "devfreq_domain_name", &domain->ect_name);
if (ret) {
pr_err("%s: no devfreq_domain_name\n", __func__, ret);
return ret;
}
}
ret = of_property_read_u32(child, "constraint_dm_type", &domain->slave_dm_id);
if (ret) {
pr_err("%s: no constraint_dm_type\n", __func__, ret);
return ret;
}
ret = of_property_read_u32(child, "constraint_type", &domain->constraint_type);
if (ret) {
pr_err("%s: no constraint_type\n", __func__, ret);
return ret;
}
return 0;
}
static int secdbg_cpufreq_init_domain(struct secdbg_skew_domain *domain,
struct device_node *dn)
{
int ret;
ret = of_property_read_u32(dn, "const-type", &domain->constraint_type);
if (ret) {
pr_err("%s: [%u] no const-type(%d)\n", __func__, domain->master_dm_id, ret);
return ret;
}
ret = of_property_read_u32(dn, "dm-type", &domain->slave_dm_id);
if (ret) {
pr_err("%s: [%u] no dm-type(%d)\n", __func__, domain->master_dm_id, ret);
return ret;
}
ret = of_property_read_string(dn, "ect-name", &domain->ect_name);
if (ret) {
pr_err("%s: [%u] no ect-name(%d)\n", __func__, domain->master_dm_id, ret);
return ret;
}
return 0;
}
static void secdbg_free_skew_domain(struct secdbg_skew_domain *domain)
{
kfree(domain);
}
static struct secdbg_skew_domain * secdbg_alloc_skew_domain(void)
{
struct secdbg_skew_domain *domain;
domain = kzalloc(sizeof(struct secdbg_skew_domain), GFP_KERNEL);
return domain;
}
static bool __cpufreq_has_ect_constraint(struct device_node *dn)
{
if (of_property_read_bool(dn, "guidance"))
return true;
else
return false;
}
static int secdbg_parse_id(struct device_node *np)
{
int ret;
unsigned int dm_index, cal_id;
ret = of_property_read_u32(np, "dm-index", &dm_index);
if (ret) {
pr_err("%s: no dm-index property (%d)\n", __func__, ret);
return ret;
}
if (dm_index >= MAX_FREQ_DOMAIN) {
pr_err("%s: too big id (%u)\n", __func__, dm_index);
return -1;
}
ret = of_property_read_u32(np, "cal_id", &cal_id);
if (ret) {
pr_err("%s: no dm-type property (%d)\n", __func__, ret);
return ret;
}
cal_id = GET_IDX(cal_id);
if (cal_id >= MAX_FREQ_DOMAIN) {
pr_err("%s: too big id (%u)\n", __func__, cal_id);
return -1;
}
secdbg_dm_to_calid[dm_index] = cal_id;
return 0;
}
static void secdbg_freq_dt_init(void)
{
struct device_node *np, *child_np, *domain_np = NULL;
struct device_node *dn, *dp, *root, *child;
struct secdbg_skew_domain *domain;
unsigned int dm_id;
int ret;
int cnt;
np = of_find_compatible_node(NULL, NULL, "samsung,exynos-dvfs-manager");
domain_np = of_get_child_by_name(np, "dm_domains");
if (!domain_np) {
pr_err("[%s] can not found dm_domains\n", __func__);
return;
}
cnt = of_get_child_count(domain_np);
if (!cnt) {
pr_err("[%s] dm_domains child node count is 0\n", __func__);
return;
}
for_each_child_of_node(domain_np, child_np)
secdbg_parse_id(child_np);
/* cpufreq */
for_each_node_by_type(dn, "cpufreq-domain") {
ret = of_property_read_u32(dn, "dm-type", &dm_id);
if (ret)
continue;
root = of_get_child_by_name(dn, "dm-constraints");
for_each_child_of_node(root, child) {
if (!__cpufreq_has_ect_constraint(child))
continue;
domain = secdbg_alloc_skew_domain();
if (!domain) {
pr_err("%s: failed to allocate skew domain[%s]\n", __func__, child->name);
continue;
}
//domain->dn = dn;
domain->master_dm_id = dm_id;
ret = secdbg_cpufreq_init_domain(domain, child);
if (ret) {
pr_err("%s: failed to initialize skew domain[%s]\n", __func__, child->name);
secdbg_free_skew_domain(domain);
continue;
}
list_add_tail(&domain->list, &secdbg_skew_domains);
//secdbg_print_domain_info(domain);
}
of_node_put(root);
}
/* devfreq */
dp = of_find_compatible_node(NULL, NULL, "samsung,exynos-devfreq-root");
if (!dp) {
pr_notice("%s: no devfreq dt\n");
goto out;
}
for_each_child_of_node(dp, dn) {
if (!of_device_is_compatible(dn, "samsung,exynos-devfreq"))
continue;
root = of_get_child_by_name(dn, "skew");
if (!root)
continue;
for_each_available_child_of_node(root, child) {
domain = secdbg_alloc_skew_domain();
if (!domain) {
pr_err("%s: failed to allocate skew domain[%s]\n", __func__, child->name);
continue;
}
//
//domain->dn = dn;
domain->master_dm_id = dm_id;
ret = secdbg_devfreq_init_domain(domain, dn, child);
if (ret) {
pr_err("%s: failed to initialize skew domain[%s]\n", __func__, child->name);
secdbg_free_skew_domain(domain);
continue;
}
list_add_tail(&domain->list, &secdbg_skew_domains);
//secdbg_print_domain_info(domain);
}
of_node_put(root);
}
of_node_put(dp);
out:
list_for_each_entry(domain, &secdbg_skew_domains, list)
secdbg_update_cal_id(domain);
}
static int secdbg_freq_mini_table(struct ect_minlock_domain *ect_domain, struct table_domain table[])
{
unsigned int i, j, k;
for (i = 0, j = 0; j < ect_domain->num_of_level - 1; j++) {
pr_debug("secdbg_freq: %s: i:%d, %d %d\n", ect_domain->domain_name, i, ect_domain->level[j].main_frequencies, ect_domain->level[j].sub_frequencies);
if (ect_domain->level[j].sub_frequencies != ect_domain->level[j+1].sub_frequencies) {
table[i].main_freq = ect_domain->level[j].main_frequencies;
table[i].sub_freq = ect_domain->level[j].sub_frequencies;
i++;
}
}
pr_debug("secdbg_freq: %s: i:%d, %d %d\n", ect_domain->domain_name, i, ect_domain->level[j].main_frequencies, ect_domain->level[j].sub_frequencies);
table[i].main_freq = ect_domain->level[ect_domain->num_of_level - 1].main_frequencies;
table[i].sub_freq = ect_domain->level[ect_domain->num_of_level - 1].sub_frequencies;
i++;
for (k = 0; k < i; k++)
pr_info("secdbg: %s: %d %d\n", ect_domain->domain_name, table[k].main_freq, table[k].sub_freq);
pr_err("%s %d\n", __func__, __LINE__);
return i;
}
static void secdbg_freq_gen_table(struct secdbg_skew_domain *skew_domain, struct ect_minlock_domain *ect_domain)
{
pr_err("%s %d\n", __func__, __LINE__);
skew_domain->freq_table = kcalloc(ect_domain->num_of_level + 1,
sizeof(struct table_domain),
GFP_KERNEL);
pr_err("%s %d\n", __func__, __LINE__);
if (!skew_domain->freq_table) {
pr_err("%s: [%d] failed to alloc table\n", __func__, skew_domain->master_dm_id);
return;
}
pr_err("%s %d\n", __func__, __LINE__);
skew_domain->num_freq = secdbg_freq_mini_table(ect_domain, skew_domain->freq_table);
}
static int secdbg_init_constraint_ect(struct secdbg_skew_domain *skew_domain)
{
void *min_block;
struct ect_minlock_domain *ect_domain;
/* get ect domain1, domain2 */
min_block = ect_get_block(BLOCK_MINLOCK);
if (!min_block) {
pr_err("%s: no BLOCK_MINLOCK\n", __func__);
return -ENODEV;
}
/* Actually, ect_minlock_get_domain() doesn't change a domain_name. */
ect_domain = ect_minlock_get_domain(min_block, (char *)skew_domain->ect_name);
if (!ect_domain) {
pr_err("%s: no ect domain for %s\n", __func__, skew_domain->ect_name);
return -ENODEV;
}
secdbg_freq_gen_table(skew_domain, ect_domain);
return 0;
}
static void secdbg_freq_ect_data_init(void)
{
struct secdbg_skew_domain *domain, *tmp;
list_for_each_entry_safe(domain, tmp, &secdbg_skew_domains, list) {
pr_err("%s %d\n", __func__, __LINE__);
if (secdbg_init_constraint_ect(domain)) {
/* we don't remove domain failed for debug */
}
}
pr_err("%s %d\n", __func__, __LINE__);
}
int secdbg_freq_init(void)
{
secdbg_freq_dt_init();
secdbg_freq_ect_data_init();
secdbg_freq_create_ref_table();
secdbg_freq_initialized = true;
secdbg_print_all_skew_domain_info();
pr_info("%s: initialized\n", __func__);
return 0;
}
MODULE_LICENSE("GPL");