// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2016 MediaTek Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#include #include #include #include /* local include */ #include "mtk_common_upower.h" /* * #ifdef UPOWER_NUM_LARGER * #include "mtk_eem.h" * #endif */ #if UPOWER_ENABLE_TINYSYS_SSPM #include #endif #ifndef EARLY_PORTING_SPOWER #include "mtk_common_spower.h" #endif #ifdef UPOWER_USE_QOS_IPI #if UPOWER_ENABLE_TINYSYS_SSPM //#include #include #ifdef CONFIG_MTK_QOS_FRAMEWORK #include #else #include #endif #endif #endif #if UPOWER_ENABLE unsigned char upower_enable = 1; #else unsigned char upower_enable; #endif #ifdef UPOWER_NUM_LARGER int iter; #endif /* for log print */ #define LOG_BUF_LEN 1024 #define LL_CORE_NUM 4 #define L_CORE_NUM 2 #define LKG_IDX 0 #define UPOWER_UT /* charles add */ #define EEM_DISABLE 1 struct mtk_upower_buf { char buf[LOG_BUF_LEN]; char *p_idx; }; #define reset_upower_buf(upower) ((upower).p_idx = (upower).buf) #define get_upower_buf(upower) ((upower).buf) #define upower_buf_append(upower, fmt, args...) \ ((upower).p_idx += snprintf((upower).p_idx, \ LOG_BUF_LEN - strlen((upower).buf), fmt, ##args)) /* reference to target upower tbl, ex: big upower tbl */ struct upower_tbl *upower_tbl_ref; /* Used for rcu lock, points to all the target tables list*/ struct upower_tbl_info *new_p_tbl_infos; /* reference to raw upower tbl lists */ struct upower_tbl_info *upower_tbl_infos; /* sspm reserved mem info for sspm upower */ phys_addr_t upower_data_phy_addr, upower_data_virt_addr; unsigned long long upower_data_size; #ifdef ENABLE_UPOWER_LOG static void print_tbl(void) { int i, j; /* --------------------print static orig table -------------------------*/ /* --------------------print sram table -------------------------*/ for (i = 0; i < NR_UPOWER_BANK; i++) { /* table size must be 512 bytes */ upower_debug("---Bank %d , tbl size %ld---\n", i, sizeof(struct upower_tbl)); for (j = 0; j < UPOWER_OPP_NUM; j++) { upower_debug(" cap = %llu, volt = %u, dyn = %u, lkg = {%u, %u, %u, %u, %u}\n", upower_tbl_ref[i].row[j].cap, upower_tbl_ref[i].row[j].volt, upower_tbl_ref[i].row[j].dyn_pwr, upower_tbl_ref[i].row[j].lkg_pwr[0], upower_tbl_ref[i].row[j].lkg_pwr[1], upower_tbl_ref[i].row[j].lkg_pwr[2], upower_tbl_ref[i].row[j].lkg_pwr[3], upower_tbl_ref[i].row[j].lkg_pwr[4]); } upower_debug(" lkg_idx, num_row: %d, %d\n", upower_tbl_ref[i].lkg_idx, upower_tbl_ref[i].row_num); upower_debug("-------------------------------------------------\n"); } } #endif #ifdef UPOWER_UT void upower_ut(void) { struct upower_tbl_info **addr_ptr_tbl_info; struct upower_tbl_info *ptr_tbl_info; struct upower_tbl *ptr_tbl; int i, j; upower_debug("----upower_get_tbl()----\n"); /* get addr of ptr which points to upower_tbl_infos[] */ addr_ptr_tbl_info = upower_get_tbl(); /* get ptr which points to upower_tbl_infos[] */ ptr_tbl_info = *addr_ptr_tbl_info; upower_debug("get upower tbl location = %p\n", ptr_tbl_info[0].p_upower_tbl); #ifdef ENABLE_UPOWER_LOG upower_debug("ptr_tbl_info --> %p --> tbl %p (p_upower_tbl_infos --> %p)\n", ptr_tbl_info, ptr_tbl_info[0].p_upower_tbl, p_upower_tbl_infos); #endif /* print all the tables that record in upower_tbl_infos[]*/ for (i = 0; i < NR_UPOWER_BANK; i++) { upower_debug("bank %d\n", i); ptr_tbl = ptr_tbl_info[i].p_upower_tbl; for (j = 0; j < UPOWER_OPP_NUM; j++) { upower_debug(" cap = %llu, volt = %u, dyn = %u, lkg = {%u, %u, %u, %u, %u, %u}\n", ptr_tbl->row[j].cap, ptr_tbl->row[j].volt, ptr_tbl->row[j].dyn_pwr, ptr_tbl->row[j].lkg_pwr[0], ptr_tbl->row[j].lkg_pwr[1], ptr_tbl->row[j].lkg_pwr[2], ptr_tbl->row[j].lkg_pwr[3], ptr_tbl->row[j].lkg_pwr[4], ptr_tbl->row[j].lkg_pwr[5]); } upower_debug(" lkg_idx, num_row, nr_idle_states: %d, %d ,%d\n", ptr_tbl->lkg_idx, ptr_tbl->row_num, ptr_tbl->nr_idle_states); for (i = 0; i < NR_UPOWER_DEGREE; i++) { upower_debug("(%d)C c0 = %lu, c1 = %lu\n", degree_set[i], ptr_tbl->idle_states[i][0].power, ptr_tbl->idle_states[i][1].power); } } upower_debug("@@turn_point= %d\n", upower_get_turn_point()); upower_debug("----upower_get_power()----\n"); for (i = 0; i < NR_UPOWER_BANK; i++) { upower_debug("bank %d\n", i); upower_debug("[dyn] %u, %u, %u, %u, %u, %u, %u, %u, %u\n", upower_get_power(i, 0, UPOWER_DYN), upower_get_power(i, 1, UPOWER_DYN), upower_get_power(i, 2, UPOWER_DYN), upower_get_power(i, 3, UPOWER_DYN), upower_get_power(i, 4, UPOWER_DYN), upower_get_power(i, 5, UPOWER_DYN), upower_get_power(i, 6, UPOWER_DYN), upower_get_power(i, 7, UPOWER_DYN), upower_get_power(i, 15, UPOWER_DYN)); upower_debug("[lkg] %u, %u, %u, %u, %u, %u, %u, %u, %u\n", upower_get_power(i, 0, UPOWER_LKG), upower_get_power(i, 1, UPOWER_LKG), upower_get_power(i, 2, UPOWER_LKG), upower_get_power(i, 3, UPOWER_LKG), upower_get_power(i, 4, UPOWER_LKG), upower_get_power(i, 5, UPOWER_LKG), upower_get_power(i, 6, UPOWER_LKG), upower_get_power(i, 7, UPOWER_LKG), upower_get_power(i, 15, UPOWER_LKG)); } } #endif static void upower_update_dyn_pwr(void) { unsigned long long refPower, newVolt, refVolt, newPower; unsigned long long temp1, temp2; int i, j; struct upower_tbl *tbl; for (i = 0; i < NR_UPOWER_BANK; i++) { tbl = upower_tbl_infos[i].p_upower_tbl; for (j = 0; j < UPOWER_OPP_NUM; j++) { refPower = (unsigned long long)tbl->row[j].dyn_pwr; refVolt = (unsigned long long)tbl->row[j].volt; newVolt = (unsigned long long) upower_tbl_ref[i].row[j].volt; temp1 = (refPower * newVolt * newVolt); temp2 = (refVolt * refVolt); #if defined(__LP64__) || defined(_LP64) newPower = temp1 / temp2; #else newPower = div64_u64(temp1, temp2); #endif upower_tbl_ref[i].row[j].dyn_pwr = newPower; } } } static void upower_get_p_state_lkg(unsigned int bank, unsigned int spower_bank_id) { int j, k; int degree; unsigned int volt; unsigned int temp; /* get p-state lkg */ for (j = 0; j < UPOWER_OPP_NUM; j++) { volt = (unsigned int)upower_tbl_ref[bank].row[j].volt; for (k = 0; k < NR_UPOWER_DEGREE; k++) { degree = degree_set[k]; /* get leakage and transfer mw to uw */ temp = mt_spower_get_leakage( spower_bank_id, (volt/100), degree); upower_tbl_ref[bank].row[j].lkg_pwr[k] = temp * 1000; } } } static void upower_get_c_state_lkg(unsigned int bank, unsigned int spower_bank_id, struct upower_tbl *tbl) { int j, k; int degree; unsigned int volt; unsigned int temp; /* get c-state lkg */ upower_tbl_ref[bank].nr_idle_states = NR_UPOWER_CSTATES; volt = UPOWER_C1_VOLT; for (j = 0; j < NR_UPOWER_DEGREE; j++) { for (k = 0; k < NR_UPOWER_CSTATES; k++) { /* if c1 state, query lkg from lkg driver */ if (k == UPOWER_C1_IDX) { degree = degree_set[j]; /* get leakage and transfer mw to uw */ temp = mt_spower_get_leakage( spower_bank_id, (volt/100), degree); upower_tbl_ref[bank].idle_states[j][k].power = (unsigned long)(temp * 1000); } else { upower_tbl_ref[bank].idle_states[j][k].power = tbl->idle_states[j][k].power; } } } } static void upower_update_lkg_pwr(void) { int i; struct upower_tbl *tbl; int j, k; unsigned int spower_bank_id; /* *#ifdef EARLY_PORTING_SPOWER * int j, k; *#else * * unsigned int spower_bank_id; *#endif */ for (i = 0; i < NR_UPOWER_BANK; i++) { tbl = upower_tbl_infos[i].p_upower_tbl; /* modify mt3967 */ #ifdef EARLY_PORTING_SPOWER /* get p-state lkg */ for (j = 0; j < UPOWER_OPP_NUM; j++) { for (k = 0; k < NR_UPOWER_DEGREE; k++) upower_tbl_ref[i].row[j].lkg_pwr[k] = tbl->row[j].lkg_pwr[k]; } /* get c-state lkg */ for (j = 0; j < NR_UPOWER_DEGREE; j++) { for (k = 0; k < NR_UPOWER_CSTATES; k++) upower_tbl_ref[i].idle_states[j][k].power = tbl->idle_states[j][k].power; } #else spower_bank_id = upower_bank_to_spower_bank(i); /* wrong bank or LL L CLUSTER set default lkg_pwr*/ if (spower_bank_id == -1) { for (j = 0; j < UPOWER_OPP_NUM; j++) { for (k = 0; k < NR_UPOWER_DEGREE; k++) upower_tbl_ref[i].row[j].lkg_pwr[k] = tbl->row[j].lkg_pwr[k]; } continue; } upower_get_p_state_lkg(i, spower_bank_id); upower_get_c_state_lkg(i, spower_bank_id, tbl); #endif } } static void upower_init_cap(void) { int i, j; struct upower_tbl *tbl; for (i = 0; i < NR_UPOWER_BANK; i++) { tbl = upower_tbl_infos[i].p_upower_tbl; for (j = 0; j < UPOWER_OPP_NUM; j++) upower_tbl_ref[i].row[j].cap = tbl->row[j].cap; } } static void upower_init_rownum(void) { int i; for (i = 0; i < NR_UPOWER_BANK; i++) upower_tbl_ref[i].row_num = UPOWER_OPP_NUM; } static unsigned int eem_is_enabled(void) { /* #ifndef EARLY_PORTING_EEM */ #ifndef EEM_DISABLE return mt_eem_is_enabled(); #else return 0; #endif } static void upower_wait_for_eem_volt_done(void) { #ifndef EEM_NOT_SET_VOLT unsigned char eem_volt_not_ready = 0; int i; udelay(100); while (1) { eem_volt_not_ready = 0; for (i = 0; i < NR_UPOWER_BANK; i++) { if (upower_tbl_ref[i].row[UPOWER_OPP_NUM - 1].volt == 0) eem_volt_not_ready = 1; } if (!eem_volt_not_ready) break; /* if eem volt not ready, wait 100us */ upower_debug("wait for eem update\n"); udelay(100); } #endif } #ifdef UPOWER_NUM_LARGER static void upower_wait_for_eem_volt_done_upn_larger(void) { unsigned char eem_volt_not_ready = 0; int i; /* ensure upower bank num does not larger than eem det num */ iter = (int)NR_EEM_DET < (int)NR_UPOWER_BANK ? NR_EEM_DET:NR_UPOWER_BANK; udelay(100); while (1) { eem_volt_not_ready = 0; for (i = 0; i < iter; i++) { upower_debug("tbl_ref = %d iter %d\n", upower_tbl_ref[i].row[UPOWER_OPP_NUM - 1].volt, iter); if (upower_tbl_ref[i].row[UPOWER_OPP_NUM - 1].volt == 0) eem_volt_not_ready = 1; } if (!eem_volt_not_ready) break; /* if eem volt not ready, wait 100us */ udelay(100); } } #endif static void upower_init_lkgidx(void) { int i; for (i = 0; i < NR_UPOWER_BANK; i++) upower_tbl_ref[i].lkg_idx = DEFAULT_LKG_IDX; } #ifdef UPOWER_USE_DEF_CCI_TBL static void upower_init_volt_cci(void) { int j; struct upower_tbl *tbl; tbl = upower_tbl_infos[UPOWER_BANK_CCI].p_upower_tbl; for (j = 0; j < UPOWER_OPP_NUM; j++) upower_tbl_ref[UPOWER_BANK_CCI].row[j].volt = tbl->row[j].volt; } #endif static void upower_init_volt(void) { int i, j; struct upower_tbl *tbl; for (i = 0; i < NR_UPOWER_BANK; i++) { tbl = upower_tbl_infos[i].p_upower_tbl; for (j = 0; j < UPOWER_OPP_NUM; j++) upower_tbl_ref[i].row[j].volt = tbl->row[j].volt; } } #ifdef UPOWER_NUM_LARGER static void confirm_volt(void) { int i, j; struct upower_tbl *tbl; for (i = iter; i < NR_UPOWER_BANK; i++) { tbl = upower_tbl_infos[i].p_upower_tbl; for (j = 0; j < UPOWER_OPP_NUM; j++) upower_tbl_ref[i].row[j].volt = tbl->row[j].volt; } } #endif static int upower_update_tbl_ref(void) { int i; int ret = 0; /* To disable upower, do not update upower ptr*/ if (!upower_enable) { upower_error("upower is disabled\n"); return 0; } #ifdef UPOWER_PROFILE_API_TIME upower_get_start_time_us(UPDATE_TBL_PTR); #endif new_p_tbl_infos = kzalloc(sizeof(*new_p_tbl_infos) * NR_UPOWER_BANK, GFP_KERNEL); if (!new_p_tbl_infos) { upower_error("Out of mem to create new_p_tbl_infos\n"); return -ENOMEM; } /* upower_tbl_ref is the ptr points to table in sram */ for (i = 0; i < NR_UPOWER_BANK; i++) { new_p_tbl_infos[i].p_upower_tbl = &upower_tbl_ref[i]; new_p_tbl_infos[i].name = upower_tbl_infos[i].name; /* upower_debug("new_p_tbl_infos[%d].name = %s\n", * i, new_p_tbl_infos[i].name); */ } #ifdef UPOWER_RCU_LOCK rcu_assign_pointer(p_upower_tbl_infos, new_p_tbl_infos); /* synchronize_rcu();*/ #else p_upower_tbl_infos = new_p_tbl_infos; #endif #ifdef UPOWER_PROFILE_API_TIME upower_get_diff_time_us(UPDATE_TBL_PTR); print_diff_results(UPDATE_TBL_PTR); #endif return ret; } static void get_L_pwr_efficiency(void) { int i; unsigned int max = 0; unsigned int min = ~0U; unsigned long long sum; struct upower_tbl *tbl; for (i = 0; i < UPOWER_OPP_NUM; i++) { tbl = &upower_tbl_ref[UPOWER_BANK_L]; sum = (unsigned long long)(tbl->row[i].lkg_pwr[LKG_IDX] + tbl->row[i].dyn_pwr); #if defined(__LP64__) || defined(_LP64) tbl->row[i].pwr_efficiency = sum / (unsigned long long)tbl->row[i].cap; #else tbl->row[i].pwr_efficiency = div64_u64(sum, (unsigned long long)tbl->row[i].cap); #endif upower_debug("L[%d] eff = %d dyn = %d lkg = %d cap = %d\n", i, tbl->row[i].pwr_efficiency, tbl->row[i].dyn_pwr, tbl->row[i].lkg_pwr[LKG_IDX], tbl->row[i].cap ); if (tbl->row[i].pwr_efficiency > max) max = tbl->row[i].pwr_efficiency; if (tbl->row[i].pwr_efficiency < min) min = tbl->row[i].pwr_efficiency; } tbl->max_efficiency = max; tbl->min_efficiency = min; } static void get_LL_pwr_efficiency(void) { int i; unsigned int max = 0; unsigned int min = ~0U; unsigned long long LL_pwr, CCI_pwr; unsigned long long sum; struct upower_tbl *tbl, *ctbl; tbl = &upower_tbl_ref[UPOWER_BANK_LL]; ctbl = &upower_tbl_ref[UPOWER_BANK_CCI]; for (i = 0; i < UPOWER_OPP_NUM; i++) { LL_pwr = (unsigned long long)(tbl->row[i].lkg_pwr[LKG_IDX] + tbl->row[i].dyn_pwr); CCI_pwr = (unsigned long long)(ctbl->row[i].lkg_pwr[LKG_IDX] + ctbl->row[i].dyn_pwr); sum = (unsigned long long)LL_CORE_NUM * LL_pwr + CCI_pwr; #if defined(__LP64__) || defined(_LP64) tbl->row[i].pwr_efficiency = sum / (unsigned long long)(LL_CORE_NUM * tbl->row[i].cap); #else tbl->row[i].pwr_efficiency = div64_u64(LL_CORE_NUM * LL_pwr + CCI_pwr, (unsigned long long)(LL_CORE_NUM * tbl->row[i].cap)) #endif upower_debug("LL[%d] eff = %d dyn = %d lkg = %d cap = %d\n", i, tbl->row[i].pwr_efficiency, tbl->row[i].dyn_pwr, tbl->row[i].lkg_pwr[LKG_IDX], tbl->row[i].cap ); if (tbl->row[i].pwr_efficiency > max) max = tbl->row[i].pwr_efficiency; if (tbl->row[i].pwr_efficiency < min) min = tbl->row[i].pwr_efficiency; } tbl->max_efficiency = max; tbl->min_efficiency = min; } static int upower_cal_turn_point(void) { int i; struct upower_tbl *L_tbl, *LL_tbl; int tempLL; int find_flag = 0; L_tbl = &upower_tbl_ref[UPOWER_BANK_L]; LL_tbl = &upower_tbl_ref[UPOWER_BANK_LL]; /* calculate turn point */ for (i = UPOWER_OPP_NUM - 1; i >= 0 ; i--) { tempLL = LL_tbl->row[i].pwr_efficiency; upower_debug("@@LL_effi[%d] = %d , L_min_effi = %d\n", i, tempLL, L_tbl->min_efficiency); if (tempLL <= L_tbl->min_efficiency) { L_tbl->turn_point = i + 1; LL_tbl->turn_point = i + 1; find_flag = 1; break; } } if (!find_flag) { L_tbl->turn_point = UPOWER_OPP_NUM; LL_tbl->turn_point = UPOWER_OPP_NUM; i = UPOWER_OPP_NUM; } L_tbl->turn_point = 13; LL_tbl->turn_point = 13; return i; } #ifdef UPOWER_USE_QOS_IPI #if UPOWER_ENABLE_TINYSYS_SSPM void upower_send_data_ipi(phys_addr_t phy_addr, unsigned long long size) { struct qos_ipi_data qos_d; qos_d.cmd = QOS_IPI_UPOWER_DATA_TRANSFER; qos_d.u.upower_data.arg[0] = phy_addr; qos_d.u.upower_data.arg[1] = size; qos_ipi_to_sspm_command(&qos_d, 3); } void upower_dump_data_ipi(void) { struct qos_ipi_data qos_d; qos_d.cmd = QOS_IPI_UPOWER_DUMP_TABLE; qos_ipi_to_sspm_command(&qos_d, 1); } #endif #endif static int __init upower_get_tbl_ref(void) { #if UPOWER_ENABLE_TINYSYS_SSPM int i; unsigned char *ptr; #endif #ifdef UPOWER_NOT_READY return 0; #endif /* get raw upower table and target upower table location */ get_original_table(); #if UPOWER_ENABLE_TINYSYS_SSPM /* get sspm reserved mem */ upower_data_phy_addr = sspm_reserve_mem_get_phys(UPD_MEM_ID); upower_data_virt_addr = sspm_reserve_mem_get_virt(UPD_MEM_ID); upower_data_size = sspm_reserve_mem_get_size(UPD_MEM_ID); upower_debug("phy_addr = 0x%llx, virt_addr=0x%llx\n", (unsigned long long)upower_data_phy_addr, (unsigned long long)upower_data_virt_addr); /* clear */ ptr = (unsigned char *)(uintptr_t)upower_data_virt_addr; for (i = 0; i < upower_data_size; i++) ptr[i] = 0x0; upower_tbl_ref = (struct upower_tbl *)(uintptr_t)upower_data_virt_addr; #ifdef UPOWER_USE_QOS_IPI upower_send_data_ipi(upower_data_phy_addr, upower_data_size); #else /* send sspm reserved mem into sspm through eem's ipi (need fix) */ mt_eem_send_upower_table_ref(upower_data_phy_addr, upower_data_size); #endif #endif /* upower_tbl_ref is assigned in get_original_table() if no sspm */ upower_debug("upower tbl orig location([0](%p)= %p\n", upower_tbl_infos, upower_tbl_infos[0].p_upower_tbl); upower_debug("upower tbl new location([0](%p)\n", upower_tbl_ref); return 0; } #ifdef UPOWER_PROFILE_API_TIME static void profile_api(void) { int i, j; upower_debug("----profile upower_get_power()----\n"); /* do 56*2 times */ for (i = 0; i < NR_UPOWER_BANK; i++) { for (j = 0; j < UPOWER_OPP_NUM; j++) { upower_get_power(i, j, UPOWER_DYN); upower_get_power(i, j, UPOWER_LKG); } } upower_debug("----profile upower_update_tbl_ref()----\n"); for (i = 0; i < 10; i++) upower_update_tbl_ref(); } #endif static int upower_debug_proc_show(struct seq_file *m, void *v) { struct upower_tbl_info **addr_ptr_tbl_info; struct upower_tbl_info *ptr_tbl_info; struct upower_tbl *ptr_tbl; int i, j; /* get addr of ptr which points to upower_tbl_infos[] */ addr_ptr_tbl_info = upower_get_tbl(); /* get ptr which points to upower_tbl_infos[] */ ptr_tbl_info = *addr_ptr_tbl_info; seq_printf(m, "ptr_tbl_info --> %p --> tbl %p (p_upower_tbl_infos --> %p)\n", ptr_tbl_info, ptr_tbl_info[0].p_upower_tbl, p_upower_tbl_infos); /* print all the tables that record in upower_tbl_infos[]*/ for (i = 0; i < NR_UPOWER_BANK; i++) { seq_printf(m, "%s\n", upower_tbl_infos[i].name); ptr_tbl = ptr_tbl_info[i].p_upower_tbl; for (j = 0; j < UPOWER_OPP_NUM; j++) { seq_printf(m, " cap = %lu, volt = %u, dyn = %u,", ptr_tbl->row[j].cap, ptr_tbl->row[j].volt, ptr_tbl->row[j].dyn_pwr); seq_printf(m, " lkg = {%u, %u, %u, %u, %u, %u} pwr_efficiency = %u\n", ptr_tbl->row[j].lkg_pwr[0], ptr_tbl->row[j].lkg_pwr[1], ptr_tbl->row[j].lkg_pwr[2], ptr_tbl->row[j].lkg_pwr[3], ptr_tbl->row[j].lkg_pwr[4], ptr_tbl->row[j].lkg_pwr[5], ptr_tbl->row[j].pwr_efficiency); } seq_printf(m, " lkg_idx, num_row, turn_point: %d, %d, %d\n\n", ptr_tbl->lkg_idx, ptr_tbl->row_num, ptr_tbl->turn_point); } #ifdef UPOWER_USE_QOS_IPI #if UPOWER_ENABLE_TINYSYS_SSPM upower_dump_data_ipi(); #endif #endif return 0; } #define PROC_FOPS_RW(name) \ static int name ## _proc_open(struct inode *inode, \ struct file *file) \ { \ return single_open(file, name ## _proc_show, \ PDE_DATA(inode)); \ } \ static const struct file_operations name ## _proc_fops = { \ .owner = THIS_MODULE, \ .open = name ## _proc_open, \ .read = seq_read, \ .llseek = seq_lseek, \ .release = single_release, \ .write = name ## _proc_write, \ } #define PROC_FOPS_RO(name) \ static int name ## _proc_open(struct inode *inode, \ struct file *file) \ { \ return single_open(file, name ## _proc_show, \ PDE_DATA(inode)); \ } \ static const struct file_operations name ## _proc_fops = { \ .owner = THIS_MODULE, \ .open = name ## _proc_open, \ .read = seq_read, \ .llseek = seq_lseek, \ .release = single_release, \ } #define PROC_ENTRY(name) {__stringify(name), &name ## _proc_fops} /* create fops */ PROC_FOPS_RO(upower_debug); static int create_procfs(void) { struct proc_dir_entry *upower_dir = NULL; int i = 0; struct pentry { const char *name; const struct file_operations *fops; }; struct pentry upower_entries[] = { /* {__stringify(name), &name ## _proc_fops} */ PROC_ENTRY(upower_debug), }; /* To disable upower, do not create procfs*/ if (!upower_enable) return 0; /* create proc/upower node */ upower_dir = proc_mkdir("upower", NULL); if (!upower_dir) { upower_error("[%s] mkdir /proc/upower failed\n", __func__); return -1; } for (i = 0; i < ARRAY_SIZE(upower_entries); i++) { if (!proc_create(upower_entries[i].name, 0644, upower_dir, upower_entries[i].fops)) { upower_error("[%s]: create /proc/upower/%s failed\n", __func__, upower_entries[i].name); return -3; } } return 0; } void upower_register_perf_domain(void) { int retv; int cpu = 0; struct cpufreq_policy *policy; struct em_data_callback em_cb = EM_DATA_CB(of_dev_pm_opp_get_cpu_power); for_each_possible_cpu(cpu) { policy = cpufreq_cpu_get(cpu); if (!policy) { pr_notice("cpu %d get policy fail\n", cpu); return; } retv = em_register_perf_domain(policy->cpus, UPOWER_OPP_NUM, &em_cb); cpufreq_cpu_put(policy); } } static int __init upower_init(void) { int turn; /* charles modify */ /* int cpu; */ struct upower_tbl_info **addr_ptr_tbl_info; struct upower_tbl_info *ptr_tbl_info; #ifdef UPOWER_NOT_READY return 0; #endif /* PTP has no efuse, so volt will be set to orig data */ /* before upower_init_volt(), PTP has called * upower_update_volt_by_eem() */ #ifdef UPOWER_UT upower_debug("--------- (UT)before tbl ready--------------\n"); upower_ut(); #endif /* init rownum to UPOWER_OPP_NUM*/ upower_init_rownum(); upower_init_cap(); /* apply orig volt and lkgidx, if eem is not enabled*/ if (!eem_is_enabled()) { upower_debug("eem is not enabled\n"); upower_init_lkgidx(); upower_init_volt(); } else { #ifdef UPOWER_USE_DEF_CCI_TBL upower_init_volt_cci(); #endif #ifdef UPOWER_NUM_LARGER upower_wait_for_eem_volt_done_upn_larger(); confirm_volt(); #endif upower_wait_for_eem_volt_done(); } upower_update_dyn_pwr(); upower_update_lkg_pwr(); get_L_pwr_efficiency(); get_LL_pwr_efficiency(); turn = upower_cal_turn_point(); upower_register_perf_domain(); upower_debug("@@~turn point is %d\n", turn); #ifdef UPOWER_L_PLUS upower_update_L_plus_cap(); upower_update_L_plus_lkg_pwr(); #endif upower_update_tbl_ref(); upower_debug("----upower_get_tbl()----\n"); /* get addr of ptr which points to upower_tbl_infos[] */ addr_ptr_tbl_info = upower_get_tbl(); /* get ptr which points to upower_tbl_infos[] */ ptr_tbl_info = *addr_ptr_tbl_info; /* * for_each_possible_cpu(cpu) * init_cpu_capacity(cpu); */ #ifdef UPOWER_UT upower_debug("--------- (UT)tbl ready--------------\n"); upower_ut(); #endif #ifdef UPOWER_PROFILE_API_TIME profile_api(); #endif create_procfs(); /* print_tbl(); */ return 0; } #ifdef __KERNEL__ subsys_initcall(upower_get_tbl_ref); late_initcall(upower_init); #endif MODULE_DESCRIPTION("MediaTek Unified Power Driver v1.0"); MODULE_LICENSE("GPL");