// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2020 MediaTek Inc. * Author: Samuel Hsieh */ #include #include #include #include #include #include #include #include "mtk_pbm.h" #include "mtk_mdpm.h" #include "mtk_mdpm_platform.h" #include "mtk_mdpm_platform_data.h" #include "mtk_mdpm_platform_table.h" #include "mtk_ccci_common.h" static u32 cnted_share_mem[SHARE_MEM_SIZE]; static u32 rfhw_sel; static int mt_mdpm_debug; static struct md_power_status mdpm_power_sta; #if IS_ENABLED(CONFIG_MTK_ECCCI_DRIVER) static u32 *dbm_share_mem; static bool md1_ccci_ready; #endif static int section_shift[DBM_SECTION_NUM] = { DBM_SECTION_1, DBM_SECTION_2, DBM_SECTION_3, DBM_SECTION_4, DBM_SECTION_5, DBM_SECTION_6, DBM_SECTION_7, DBM_SECTION_8, DBM_SECTION_9, DBM_SECTION_10, DBM_SECTION_11, DBM_SECTION_12}; static struct tx_power *mdpm_tx_pwr; static struct mdpm_scenario *mdpm_scen; static int *scen_priority; void init_md_section_level(enum pbm_kicker kicker, u32 *share_mem) { if (!mdpm_tx_pwr || !mdpm_scen || !scen_priority) { pr_notice("MD power table is empty\n"); return; } #if IS_ENABLED(CONFIG_MTK_ECCCI_DRIVER) if (share_mem == NULL) { pr_info_ratelimited("can't get dbm share memory\n"); return; } #ifdef DBM_RESERVE_OFFSET share_mem += DBM_RESERVE_OFFSET; #endif dbm_share_mem = share_mem; if (kicker == KR_MD1) { init_md1_section_level(dbm_share_mem); init_version_check(dbm_share_mem); md1_ccci_ready = 1; } else pr_notice("unknown MD kicker: %d\n", kicker); #else return; #endif } EXPORT_SYMBOL(init_md_section_level); int get_md1_power(enum mdpm_power_type power_type, bool need_update) { #if IS_ENABLED(CONFIG_MTK_ECCCI_DRIVER) u32 share_reg; enum md_scenario scenario; int scenario_power, tx_power; if (!mdpm_tx_pwr || !mdpm_scen || !scen_priority) { pr_notice("[md1_power] MD power table is empty\n"); return 0; } if (power_type >= POWER_TYPE_NUM || power_type < 0) { pr_notice("[md1_power] invalid power_type=%d\n", power_type); return 0; } if (need_update == false) return mdpm_power_sta.total_power; memset((void *)&mdpm_power_sta, 0, sizeof(struct md_power_status)); if (!md1_ccci_ready) { pr_info_ratelimited("%s: md1_ccci_ready=%d\n", __func__, md1_ccci_ready); return MAX_MD1_POWER; } share_reg = get_md1_status_reg(); if (dbm_share_mem == NULL) return MAX_MD1_POWER; #ifdef GET_MD_SCEANRIO_BY_SHARE_MEMORY scenario = get_md1_scenario_by_shm(dbm_share_mem); #else scenario = get_md1_scenario(share_reg, power_type); #endif scenario_power = get_md1_scenario_power(scenario, power_type, &mdpm_power_sta); tx_power = get_md1_tx_power(scenario, dbm_share_mem, power_type, &mdpm_power_sta); if (mt_mdpm_debug) pr_info("[md1_power] scenario_power=%d tx_power=%d total=%d\n", scenario_power, tx_power, scenario_power + tx_power); return scenario_power + tx_power; #else return 0; #endif /* CONFIG_MTK_ECCCI_DRIVER */ } EXPORT_SYMBOL(get_md1_power); static int mt_mdpm_debug_proc_show(struct seq_file *m, void *v) { if (mt_mdpm_debug) seq_printf(m, "mdpm debug enabled mt_mdpm_debug=%d\n", mt_mdpm_debug); else seq_puts(m, "mdpm debug disabled\n"); return 0; } /* * enable debug message */ static ssize_t mt_mdpm_debug_proc_write (struct file *file, const char __user *buffer, size_t count, loff_t *data) { char desc[32]; int len = 0; int debug = 0; len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1); if (copy_from_user(desc, buffer, len)) return 0; len = (len < 0) ? 0 : len; desc[len] = '\0'; /* if (sscanf(desc, "%d", &debug) == 1) { */ if (kstrtoint(desc, 10, &debug) == 0) { if (debug >= 0 && debug <= 2) mt_mdpm_debug = debug; else pr_notice("should be [0:disable, 1,2:enable level]\n"); } else pr_notice("should be [0:disable, 1,2:enable level]\n"); return count; } static int mt_mdpm_power_proc_show(struct seq_file *m, void *v) { seq_printf(m, "MAX power: scenario=%dmW dbm=%dmW total=%dmW\n scenario name=%s(%d) section=%d, rat=%d, power_type=%d\n", mdpm_power_sta.scanario_power, mdpm_power_sta.tx_power, mdpm_power_sta.total_power, mdpm_power_sta.scenario_name, mdpm_power_sta.scenario_id, mdpm_power_sta.dbm_section, mdpm_power_sta.rat, mdpm_power_sta.power_type); return 0; } #define PROC_FOPS_RW(name) \ static int mt_ ## name ## _proc_open(struct inode *inode, struct file *file)\ { \ return single_open(file, mt_ ## name ## _proc_show, PDE_DATA(inode));\ } \ static const struct proc_ops mt_ ## name ## _proc_fops = { \ .proc_open = mt_ ## name ## _proc_open, \ .proc_read = seq_read, \ .proc_lseek = seq_lseek, \ .proc_release = single_release, \ .proc_write = mt_ ## name ## _proc_write, \ } #define PROC_FOPS_RO(name) \ static int mt_ ## name ## _proc_open(struct inode *inode, struct file *file)\ { \ return single_open(file, mt_ ## name ## _proc_show, PDE_DATA(inode));\ } \ static const struct proc_ops mt_ ## name ## _proc_fops = { \ .proc_open = mt_ ## name ## _proc_open, \ .proc_read = seq_read, \ .proc_lseek = seq_lseek, \ .proc_release = single_release, \ } #define PROC_ENTRY(name) {__stringify(name), &mt_ ## name ## _proc_fops} PROC_FOPS_RW(mdpm_debug); PROC_FOPS_RO(mdpm_power); static int mt_mdpm_create_procfs(void) { struct proc_dir_entry *dir = NULL; int i; struct pentry { const char *name; const struct proc_ops *fops; }; const struct pentry entries[] = { PROC_ENTRY(mdpm_debug), PROC_ENTRY(mdpm_power), }; dir = proc_mkdir("mdpm", NULL); if (!dir) { pr_notice("fail to create /proc/mdpm @ %s()\n", __func__); return -ENOMEM; } for (i = 0; i < ARRAY_SIZE(entries); i++) { if (!proc_create (entries[i].name, 0660, dir, entries[i].fops)) pr_notice("@%s: create /proc/mdpm/%s failed\n", __func__, entries[i].name); } return 0; } static int get_md1_scenario_internal(u64 share_reg) { int hit = -1; int i = 0; #ifdef GET_MD_SCEANRIO_BY_SHARE_MEMORY for (i = 0; i < SCENARIO_NUM; i++) { if ((share_reg & mdpm_scen[scen_priority[i]].scenario_reg) == mdpm_scen[scen_priority[i]].scenario_reg) { hit = scen_priority[i]; break; } } if (hit == -1) hit = S_STANDBY; #else for (i = 0; i < SCENARIO_NUM; i++) { if (mdpm_scen[i].scenario_reg == share_reg) { hit = i; break; } } if (hit == -1) pr_notice("[%s] ERROR, unknown scenario [%d]\n", __func__, share_reg); #endif return hit; } static int get_shm_idx(enum tx_power_table tx_dbm, int sec_shift, bool get_dbm) { int idx = 0, mem_idx = 0; idx = sec_shift / 32; if (idx >= DBM_TABLE_SIZE || idx < 0 || tx_dbm >= TX_DBM_NUM || tx_dbm < 0) { pr_notice("[%s] ERROR, exceed index %d %d %d\n", __func__, idx, sec_shift, tx_dbm); WARN_ON_ONCE(1); return mem_idx; } if (get_dbm) { /* get dbm index */ mem_idx = mdpm_tx_pwr[tx_dbm].shm_dbm_idx[idx]; if (mem_idx > DBM_TABLE_END || mem_idx < DBM_TABLE_START) { pr_notice("[%s] ERROR, not in dbm table %d %d %d\n", __func__, idx, tx_dbm, mem_idx); WARN_ON_ONCE(1); } } else { /* get section index */ mem_idx = mdpm_tx_pwr[tx_dbm].shm_sec_idx[idx]; if (!(mem_idx <= SECTION_LEVEL_END && mem_idx >= SECTION_LEVEL_START) && !(mem_idx <= SECTION_LEVEL_2_END && mem_idx >= SECTION_LEVEL_2_START)) { pr_notice("[%s] ERROR, not in section level %d %d %d\n", __func__, idx, tx_dbm, mem_idx); WARN_ON_ONCE(1); } } return mem_idx; } static int mdpm_shm_write(u32 *share_mem, enum share_mem_mapping mem_idx, u32 value, u32 mask, u32 shift) { if (mem_idx < 0 || mem_idx >= SHARE_MEM_SIZE) return -EINVAL; share_mem[mem_idx] = (share_mem[mem_idx] & (~(mask << shift))) | ((value & mask) << shift); return 0; } static int mdpm_shm_read(u32 *share_mem, enum share_mem_mapping mem_idx, u32 *value, u32 mask, u32 shift) { if (mem_idx < 0 || mem_idx >= SHARE_MEM_SIZE) return -EINVAL; *value = (share_mem[mem_idx] >> shift) & mask; return 0; } static u32 check_shm_version(u32 *share_mem) { static u32 mdpm_version_check; if (mdpm_version_check == VERSION_INIT) { mdpm_shm_read(share_mem, M_VERSION_CHECK, &mdpm_version_check, VERSION_CHECK_VALID_MASK, VERSION_CHECK_VALID_SHIFT); } switch (mdpm_version_check) { case VERSION_INIT: if (mt_mdpm_debug) pr_info_ratelimited("mdpm share memory: MD not init\n"); break; case VERSION_INVALID: pr_info("dpm share memory: MD check version ERROR\n"); WARN_ON_ONCE(1); break; case VERSION_VALID: default: break; } return mdpm_version_check; } static u32 get_rfhw(u32 *share_mem) { u32 rfhw_version; static u32 rfhw_check; static bool rfhw_updated; if (rfhw_updated == 1) return rfhw_sel; else if (rfhw_check == RF_HW_INIT) { mdpm_shm_read(share_mem, M_RF_HW, &rfhw_check, RF_HW_VALID_MASK, RF_HW_VALID_SHIFT); } if (rfhw_check == RF_HW_VALID) { mdpm_shm_read(share_mem, M_RF_HW, &rfhw_version, RF_HW_VERSION_MASK, RF_HW_VERSION_SHIFT); if (rfhw_version >= 0 && rfhw_version < RF_HW_NUM) { rfhw_updated = 1; return rfhw_version; } else if (1) pr_notice("wrong rfhw_version %d\n", rfhw_version); } return rfhw_sel; } void init_version_check(u32 *share_mem) { mdpm_shm_write(share_mem, M_VERSION_CHECK, VERSION_INIT, VERSION_CHECK_VALID_MASK, VERSION_CHECK_VALID_SHIFT); mdpm_shm_write(share_mem, M_VERSION_CHECK, AP_MD_MDPM_VERSION, VERSION_CHECK_VERSION_MASK, VERSION_CHECK_VERSION_SHIFT); } unsigned int get_md1_status_reg(void) { #ifdef GET_MD_SCEANRIO_BY_SHARE_MEMORY return 0; #else return vcorefs_get_md_scenario(); #endif } void init_md1_section_level(u32 *share_mem) { u32 mem[SHARE_MEM_SIZE]; int i, j, index, offset; memset(&mem[0], 0, sizeof(u32) * SHARE_MEM_SIZE); for (i = 0; i < DBM_SECTION_NUM; i++) { for (j = 0; j < TX_DBM_NUM; j++) { if (!mdpm_tx_pwr[j].rfhw) continue; if (mdpm_tx_pwr[j].rfhw->section[i] > DBM_SECTION_MASK) { pr_notice("[%s] md1_section_level too large i:%d s:%d !\n", __func__, j, i); WARN_ON_ONCE(1); } index = get_shm_idx(j, section_shift[i], false); offset = section_shift[i] % 32; if (index >= SHARE_MEM_SIZE) { pr_notice("%s out of bounds, index:%d\n", __func__, index); return; } mem[index] |= mdpm_tx_pwr[j].rfhw->section[i] << offset; } } memcpy(&share_mem[SECTION_LEVEL_START], &mem[SECTION_LEVEL_START], sizeof(u32) * (SECTION_LEVEL_END - SECTION_LEVEL_START + 1)); memcpy(&share_mem[SECTION_LEVEL_2_START], &mem[SECTION_LEVEL_2_START], sizeof(u32) * (SECTION_LEVEL_2_END - SECTION_LEVEL_2_START + 1)); pr_info("AP2MD1 section, 2G: 0x%08x%08x(0x%08x%08x), 3G: 0x%08x%08x(0x%08x %08x)\n", mem[M_2G_SECTION_LEVEL], mem[M_2G_SECTION_1_LEVEL], share_mem[M_2G_SECTION_LEVEL], share_mem[M_2G_SECTION_1_LEVEL], mem[M_3G_SECTION_LEVEL], mem[M_3G_SECTION_1_LEVEL], share_mem[M_3G_SECTION_LEVEL], share_mem[M_3G_SECTION_1_LEVEL]); pr_info("4G_CC0:0x%08x%08x(0x%08x%08x),4G_CC1:0x%08x%08x(0x%08x%08x)\n", mem[M_4G_SECTION_LEVEL], mem[M_4G_SECTION_9_LEVEL], share_mem[M_4G_SECTION_LEVEL], share_mem[M_4G_SECTION_9_LEVEL], mem[M_4G_SECTION_LEVEL], mem[M_4G_SECTION_9_LEVEL], share_mem[M_4G_SECTION_LEVEL], share_mem[M_4G_SECTION_9_LEVEL]); pr_info("3GTDD: 0x%08x%08x(0x%08x%08x)\n", mem[M_TDD_SECTION_LEVEL], mem[M_TDD_SECTION_1_LEVEL], share_mem[M_TDD_SECTION_LEVEL], share_mem[M_TDD_SECTION_1_LEVEL]); pr_info("C2K: 0x%08x%08x(0x%08x%08x), addr: 0x%p\n", mem[M_C2K_SECTION_1_LEVEL], mem[M_C2K_SECTION_2_LEVEL], share_mem[M_C2K_SECTION_1_LEVEL], share_mem[M_C2K_SECTION_2_LEVEL], share_mem); pr_info("NR_CC0:0x%08x%08x(0x%08x%08x),NR_CC1:0x%08x%08x(0x%08x%08x)\n", mem[M_NR_SECTION_LEVEL], mem[M_NR_SECTION_1_LEVEL], share_mem[M_NR_SECTION_LEVEL], share_mem[M_NR_SECTION_1_LEVEL], mem[M_NR_SECTION_2_LEVEL], mem[M_NR_SECTION_3_LEVEL], share_mem[M_NR_SECTION_2_LEVEL], share_mem[M_NR_SECTION_3_LEVEL]); pr_info("MMW_TX1:0x%08x%08x(0x%08x%08x),MMW_TX2:0x%08x%08x(0x%08x%08x)\n", mem[M_MMW_SECTION_LEVEL], mem[M_MMW_SECTION_1_LEVEL], share_mem[M_MMW_SECTION_LEVEL], share_mem[M_MMW_SECTION_1_LEVEL], mem[M_MMW_SECTION_2_LEVEL], mem[M_MMW_SECTION_3_LEVEL], share_mem[M_MMW_SECTION_2_LEVEL], share_mem[M_MMW_SECTION_3_LEVEL]); } #ifdef GET_MD_SCEANRIO_BY_SHARE_MEMORY enum md_scenario get_md1_scenario_by_shm(u32 *share_mem) { int scenario = S_STANDBY; u32 scen_status1 = -1; u32 scen_status2 = -1; u64 scen_status = -1; mdpm_shm_read(share_mem, M_MD_SCENARIO_1, &scen_status1, MDPM_SHARE_MEMORY_MASK, MDPM_SHARE_MEMORY_SHIFT); mdpm_shm_read(share_mem, M_MD_SCENARIO_2, &scen_status2, MDPM_SHARE_MEMORY_MASK, MDPM_SHARE_MEMORY_SHIFT); scen_status = ((u64)scen_status2 << 32 | scen_status1); scenario = get_md1_scenario_internal(scen_status); scenario = (scenario < 0) ? S_STANDBY : scenario; if (mt_mdpm_debug) pr_info("scen_status: 0x%lx, scen_status1: 0x%x, scen_status2: 0x%x\n", scen_status, scen_status1, scen_status2); pr_info("MD1 scenario: %d(%s), scen_status: 0x%x\n", scenario, mdpm_scen[scenario].scenario_name, scen_status); return scenario; } #else enum md_scenario get_md1_scenario(u32 share_reg, enum mdpm_power_type power_type) { int scenario = S_STANDBY; share_reg = share_reg & SHARE_REG_MASK; scenario = get_md1_scenario_internal(share_reg); scenario = (scenario < 0) ? S_STANDBY : scenario; if (mt_mdpm_debug) pr_info("MD1 scenario: %d(%s), reg: 0x%x\n", scenario, mdpm_scen[scenario].scenario_name, share_reg); return scenario; } #endif int get_md1_scenario_power(enum md_scenario scenario, enum mdpm_power_type power_type, struct md_power_status *mdpm_pwr_sta) { int ret; int s_power = 0; switch (power_type) { case MAX_POWER: s_power = mdpm_scen[scenario].scenario_power->max; break; case AVG_POWER: s_power = mdpm_scen[scenario].scenario_power->avg; break; default: pr_notice("%s error power_type=%d\n", __func__, power_type); break; } mdpm_pwr_sta->scenario_id = scenario; ret = snprintf(mdpm_pwr_sta->scenario_name, sizeof(mdpm_pwr_sta->scenario_name), "%s", mdpm_scen[scenario].scenario_name); if (ret < 0 || ret >= sizeof(mdpm_pwr_sta->scenario_name)) pr_notice("%s snprintf failed, ret =%d\n", __func__, ret); mdpm_pwr_sta->scanario_power = s_power; mdpm_pwr_sta->power_type = power_type; return s_power; } static int get_md1_tx_power_by_table(u32 *dbm_mem, u32 *old_dbm_mem, struct tx_power *tx_pwr, enum tx_power_table dbm_type, enum tx_rat_type rat, enum mdpm_power_type power_type, int *sec_shift, struct md_power_status *md_power_s) { int pa_power = 0, rf_power = 0; int section, index, offset, i; bool cmp = true; if (dbm_type >= TX_DBM_NUM || dbm_type < 0 || power_type >= POWER_TYPE_NUM || power_type < 0) { pr_notice("error argument dbm_type=%d power_type=%d\n", dbm_type, power_type); return 0; } if (tx_pwr == NULL) { pr_notice("no data for tx_power\n"); return 0; } for (i = 0; i < DBM_TABLE_SIZE; i++) { if (dbm_mem[tx_pwr->shm_dbm_idx[i]] != old_dbm_mem[tx_pwr->shm_dbm_idx[i]]) { cmp = false; break; } } if (cmp) { if (mt_mdpm_debug == 2) pr_info("%s dBm no TX power, reg: 0x%08x%08x(0x%08x%08x) return 0\n", tx_pwr->dbm_name, dbm_mem[tx_pwr->shm_dbm_idx[0]], dbm_mem[tx_pwr->shm_dbm_idx[1]], old_dbm_mem[tx_pwr->shm_dbm_idx[0]], old_dbm_mem[tx_pwr->shm_dbm_idx[1]]); return 0; } for (section = 0; section < DBM_SECTION_NUM; section++) { index = get_shm_idx(dbm_type, sec_shift[section], true); offset = sec_shift[section] % 32; if (((dbm_mem[index] >> offset) & DBM_SECTION_MASK) != ((old_dbm_mem[index] >> offset) & DBM_SECTION_MASK)) { switch (power_type) { case MAX_POWER: pa_power = tx_pwr->rfhw->pa_power.max[section]; rf_power = tx_pwr->rfhw->rf_power.max[section]; break; case AVG_POWER: pa_power = tx_pwr->rfhw->pa_power.avg[section]; rf_power = tx_pwr->rfhw->rf_power.avg[section]; break; default: pr_notice("%s error power_type=%d\n", __func__, power_type); break; } if (mt_mdpm_debug) pr_info("%s dBm: reg:0x%08x%08x(0x%08x%08x),pa:%d,rf:%d,s:%d\n", tx_pwr->dbm_name, dbm_mem[tx_pwr->shm_dbm_idx[0]], dbm_mem[tx_pwr->shm_dbm_idx[1]], old_dbm_mem[tx_pwr->shm_dbm_idx[0]], old_dbm_mem[tx_pwr->shm_dbm_idx[1]], pa_power, rf_power, section+1); for (i = 0; i < DBM_TABLE_SIZE; i++) { memcpy( &old_dbm_mem[tx_pwr->shm_dbm_idx[i]], &dbm_mem[tx_pwr->shm_dbm_idx[i]], sizeof(u32)); } md_power_s->rat = rat; md_power_s->power_type = power_type; md_power_s->dbm_section = section + 1; md_power_s->pa_power += pa_power; md_power_s->rf_power += rf_power; md_power_s->tx_power += pa_power + rf_power; break; } } return pa_power + rf_power; } static int get_md1_tx_power_by_rat(u32 *dbm_mem, u32 *old_dbm_mem, enum tx_rat_type rat, enum mdpm_power_type power_type, struct md_power_status *md_power_s) { int power; struct tx_power *tx_pwr = NULL; if (rat > RAT_NUM || rat <= 0 || power_type >= POWER_TYPE_NUM || power_type < 0) { pr_notice("error argument rat_type=%d power_type=%d\n", rat, power_type); return 0; } switch (rat) { case RAT_2G: tx_pwr = &mdpm_tx_pwr[TX_2G_DBM]; power = get_md1_tx_power_by_table(dbm_mem, old_dbm_mem, tx_pwr, TX_2G_DBM, rat, power_type, section_shift, md_power_s); break; case RAT_3G: tx_pwr = &mdpm_tx_pwr[TX_3G_DBM]; power = get_md1_tx_power_by_table(dbm_mem, old_dbm_mem, tx_pwr, TX_3G_DBM, rat, power_type, section_shift, md_power_s); break; case RAT_4G: tx_pwr = &mdpm_tx_pwr[TX_4G_CC0_DBM]; power = get_md1_tx_power_by_table(dbm_mem, old_dbm_mem, tx_pwr, TX_4G_CC0_DBM, rat, power_type, section_shift, md_power_s); tx_pwr = &mdpm_tx_pwr[TX_4G_CC1_DBM]; power = power + get_md1_tx_power_by_table(dbm_mem, old_dbm_mem, tx_pwr, TX_4G_CC1_DBM, rat, power_type, section_shift, md_power_s); break; case RAT_5G: tx_pwr = &mdpm_tx_pwr[TX_NR_CC0_DBM]; power = get_md1_tx_power_by_table(dbm_mem, old_dbm_mem, tx_pwr, TX_NR_CC0_DBM, rat, power_type, section_shift, md_power_s); tx_pwr = &mdpm_tx_pwr[TX_NR_CC1_DBM]; power = power + get_md1_tx_power_by_table(dbm_mem, old_dbm_mem, tx_pwr, TX_NR_CC1_DBM, rat, power_type, section_shift, md_power_s); break; case RAT_MMW: tx_pwr = &mdpm_tx_pwr[TX_MMW_TX2_DBM]; power = get_md1_tx_power_by_table(dbm_mem, old_dbm_mem, tx_pwr, TX_MMW_TX2_DBM, rat, power_type, section_shift, md_power_s); if (!power) { tx_pwr = &mdpm_tx_pwr[TX_MMW_TX1_DBM]; power = get_md1_tx_power_by_table(dbm_mem, old_dbm_mem, tx_pwr, TX_MMW_TX1_DBM, rat, power_type, section_shift, md_power_s); } break; case RAT_C2K: tx_pwr = &mdpm_tx_pwr[TX_C2K_DBM]; power = get_md1_tx_power_by_table(dbm_mem, old_dbm_mem, tx_pwr, TX_C2K_DBM, rat, power_type, section_shift, md_power_s); break; case RAT_3GTDD: tx_pwr = &mdpm_tx_pwr[TX_3GTDD_DBM]; power = get_md1_tx_power_by_table(dbm_mem, old_dbm_mem, tx_pwr, TX_3GTDD_DBM, rat, power_type, section_shift, md_power_s); break; default: pr_notice("error argument rat_type=%d power_type=%d\n", rat, power_type); break; } return power; } int get_md1_tx_power(enum md_scenario scenario, u32 *share_mem, enum mdpm_power_type power_type, struct md_power_status *mdpm_pwr_sta) { int i, rf_ret, tx_power, tx_power_max, usedBytes = 0; enum tx_rat_type rat; struct md_power_status mdpm_power_s_tmp; char log_buffer[128]; if (share_mem == NULL) { if (mt_mdpm_debug) pr_info("MD1 share_mem is NULL\n"); return 0; } if (check_shm_version(share_mem) == VERSION_VALID) rf_ret = get_rfhw(share_mem); else return 0; if (mt_mdpm_debug == 2) for (i = 0; i < SHARE_MEM_SIZE; i++) { usedBytes += sprintf(log_buffer + usedBytes, "0x%x ", share_mem[i]); if ((i + 1) % 10 == 0 || (i + 1) == SHARE_MEM_SIZE) { usedBytes = 0; pr_info("%s\n", log_buffer); } } memset((void *)&mdpm_power_s_tmp, 0, sizeof(struct md_power_status)); tx_power_max = 0; for (i = 0; i < MAX_DBM_FUNC_NUM; i++) { rat = mdpm_scen[scenario].tx_power_rat[i]; if (rat == 0) break; tx_power = get_md1_tx_power_by_rat(share_mem, cnted_share_mem, rat, power_type, &mdpm_power_s_tmp); if (mdpm_scen[scenario].tx_power_rat_sum == true) { /* select sum RAT Tx power */ tx_power_max += tx_power; mdpm_pwr_sta->rat |= mdpm_power_s_tmp.rat; mdpm_pwr_sta->power_type = mdpm_power_s_tmp.power_type; mdpm_pwr_sta->dbm_section = mdpm_power_s_tmp.dbm_section; mdpm_pwr_sta->pa_power += mdpm_power_s_tmp.pa_power; mdpm_pwr_sta->rf_power += mdpm_power_s_tmp.rf_power; mdpm_pwr_sta->tx_power += mdpm_power_s_tmp.tx_power; mdpm_pwr_sta->total_power += mdpm_pwr_sta->scanario_power + mdpm_pwr_sta->tx_power; } else { /* select largest RAT Tx power */ if (tx_power > tx_power_max) { tx_power_max = tx_power; mdpm_pwr_sta->rat = mdpm_power_s_tmp.rat; mdpm_pwr_sta->power_type = mdpm_power_s_tmp.power_type; mdpm_pwr_sta->dbm_section = mdpm_power_s_tmp.dbm_section; mdpm_pwr_sta->pa_power = mdpm_power_s_tmp.pa_power; mdpm_pwr_sta->rf_power = mdpm_power_s_tmp.rf_power; mdpm_pwr_sta->tx_power = mdpm_power_s_tmp.tx_power; mdpm_pwr_sta->total_power = mdpm_pwr_sta->scanario_power + mdpm_pwr_sta->tx_power; } } } return tx_power_max; } static int mdpm_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct mdpm_data *mdpm_data; mdpm_data = (struct mdpm_data *) of_device_get_match_data(dev); if (!mdpm_data) { dev_notice(dev, "Error: Failed to get mdpm platform data\n"); return -ENODATA; } mdpm_data->dev = &pdev->dev; platform_set_drvdata(pdev, mdpm_data); mdpm_tx_pwr = mdpm_data->tx_power_t; mdpm_scen = mdpm_data->scenario_power_t; scen_priority = mdpm_data->prority_t; mt_mdpm_create_procfs(); return 0; } enum mdpm_platform { MT6873_MDPM_DATA, MT6893_MDPM_DATA, MT6983_MDPM_DATA, MT6879_MDPM_DATA, MT6895_MDPM_DATA, MT6886_MDPM_DATA, MT6985_MDPM_DATA, MT6835_MDPM_DATA }; static struct mdpm_data mt6873_mdpm_data = { .platform = MT6873_MDPM_DATA, .scenario_power_t = mt6873_mdpm_scen, .tx_power_t = mt6873_mdpm_tx_pwr, .prority_t = (void *)&mt6873_scen_priority }; static struct mdpm_data mt6893_mdpm_data = { .platform = MT6893_MDPM_DATA, .scenario_power_t = mt6893_mdpm_scen, .tx_power_t = mt6893_mdpm_tx_pwr, .prority_t = (void *)&mt6893_scen_priority }; static struct mdpm_data mt6983_mdpm_data = { .platform = MT6983_MDPM_DATA, .scenario_power_t = mt6983_mdpm_scen, .tx_power_t = mt6983_mdpm_tx_pwr, .prority_t = (void *)&mt6983_scen_priority }; static struct mdpm_data mt6879_mdpm_data = { .platform = MT6879_MDPM_DATA, .scenario_power_t = mt6879_mdpm_scen, .tx_power_t = mt6879_mdpm_tx_pwr, .prority_t = (void *)&mt6879_scen_priority }; static struct mdpm_data mt6895_mdpm_data = { .platform = MT6895_MDPM_DATA, .scenario_power_t = mt6895_mdpm_scen, .tx_power_t = mt6895_mdpm_tx_pwr, .prority_t = (void *)&mt6895_scen_priority }; static struct mdpm_data mt6886_mdpm_data = { .platform = MT6886_MDPM_DATA, .scenario_power_t = mt6886_mdpm_scen, .tx_power_t = mt6886_mdpm_tx_pwr, .prority_t = (void *)&mt6886_scen_priority }; static struct mdpm_data mt6985_mdpm_data = { .platform = MT6985_MDPM_DATA, .scenario_power_t = mt6985_mdpm_scen, .tx_power_t = mt6985_mdpm_tx_pwr, .prority_t = (void *)&mt6985_scen_priority }; static struct mdpm_data mt6835_mdpm_data = { .platform = MT6835_MDPM_DATA, .scenario_power_t = mt6835_mdpm_scen, .tx_power_t = mt6835_mdpm_tx_pwr, .prority_t = (void *)&mt6835_scen_priority }; static const struct of_device_id mdpm_of_match[] = { { .compatible = "mediatek,mt6873-mdpm", .data = (void *)&mt6873_mdpm_data, }, { .compatible = "mediatek,mt6893-mdpm", .data = (void *)&mt6893_mdpm_data, }, { .compatible = "mediatek,mt6983-mdpm", .data = (void *)&mt6983_mdpm_data, }, { .compatible = "mediatek,mt6879-mdpm", .data = (void *)&mt6879_mdpm_data, }, { .compatible = "mediatek,mt6895-mdpm", .data = (void *)&mt6895_mdpm_data, }, { .compatible = "mediatek,mt6886-mdpm", .data = (void *)&mt6886_mdpm_data, }, { .compatible = "mediatek,mt6985-mdpm", .data = (void *)&mt6985_mdpm_data, }, { .compatible = "mediatek,mt6835-mdpm", .data = (void *)&mt6835_mdpm_data, }, { }, }; MODULE_DEVICE_TABLE(of, mdpm_of_match); static struct platform_driver mdpm_driver = { .driver = { .name = "mtk-modem_power_meter", .of_match_table = mdpm_of_match, }, .probe = mdpm_probe, }; module_platform_driver(mdpm_driver); MODULE_AUTHOR("Samuel Hsieh"); MODULE_DESCRIPTION("MTK modem power meter driver"); MODULE_LICENSE("GPL");