| /* |
| * NDA AND NEED-TO-KNOW REQUIRED |
| * |
| * Copyright © 2013-2018 Synaptics Incorporated. All rights reserved. |
| * |
| * This file contains information that is proprietary to Synaptics |
| * Incorporated ("Synaptics"). The holder of this file shall treat all |
| * information contained herein as confidential, shall use the |
| * information only for its intended purpose, and shall not duplicate, |
| * disclose, or disseminate any of this information in any manner |
| * unless Synaptics has otherwise provided express, written |
| * permission. |
| * |
| * Use of the materials may require a license of intellectual property |
| * from a third party or from Synaptics. This file conveys no express |
| * or implied licenses to any intellectual property rights belonging |
| * to Synaptics. |
| * |
| * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND |
| * SYNAPTICS EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, |
| * INCLUDING ANY IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE, AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY |
| * INTELLECTUAL PROPERTY RIGHTS. IN NO EVENT SHALL SYNAPTICS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, PUNITIVE, OR |
| * CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION WITH THE USE |
| * OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED AND |
| * BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
| * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS |
| * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF |
| * COMPETENT JURISDICTION DOES NOT PERMIT THE DISCLAIMER OF DIRECT |
| * DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' TOTAL CUMULATIVE LIABILITY |
| * TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. DOLLARS. |
| */ |
| #include <io.h> |
| #include "util.h" |
| #include "debug.h" |
| #include "chip_voltage_info.h" |
| #include "pmic_select.h" |
| |
| #include "lgpl_printf.h" |
| #include "pv_comp.h" |
| |
| #define UNUSED(var) do { (void)(var); } while(0) |
| |
| const dvfs_ops_t * dvfs_ops = NULL; |
| |
| static int find_dvfs_group(struct freq_leakage_table *flt, int pll, int leakage) |
| { |
| struct leakage_table *p = NULL; |
| |
| while(flt->freq != 0) { |
| if(flt->freq >= pll) { |
| p = flt->lt; |
| if(p == NULL) { |
| break; |
| } |
| while(1) { |
| if(leakage <= p->info) |
| break; |
| p++; |
| } |
| return p->volt; |
| } |
| flt++; |
| } |
| |
| return -1; |
| } |
| |
| |
| static int get_vcpu_volt_from_dvfs(int cpu_pll, int leakage) |
| { |
| struct freq_leakage_table *flt = get_vcpu_leakage_table(); |
| int ret = 0; |
| |
| if(flt == NULL) { |
| lgpl_printf("No vcpu PV table found!!!!\n"); |
| return -1; |
| } |
| |
| ret = find_dvfs_group(flt, cpu_pll, leakage); |
| |
| if(ret == -1) |
| lgpl_printf("No leakage group in vcpu PV table found!!!!\n"); |
| |
| return ret; |
| } |
| |
| #ifdef CORE_VOLT_VALUE |
| int get_vcore_volt_from_dvfs(int gpu_pll, int leakage) |
| { |
| struct freq_leakage_table *flt = get_vcore_leakage_table(); |
| int ret = 0; |
| |
| if(flt == NULL) { |
| lgpl_printf("No vcore PV table found!!!!\n"); |
| return -1; |
| } |
| |
| ret = find_dvfs_group(flt, gpu_pll, leakage); |
| |
| if(ret == -1) |
| lgpl_printf("No leakage group in vcore PV table found!!!!\n"); |
| |
| return ret; |
| } |
| #endif |
| |
| void get_vcpu_vh_vl(unsigned int *opp) |
| { |
| int leakage = 0; |
| int volt = 0; |
| unsigned int count = 0; |
| struct freq_leakage_table *flt = NULL; |
| |
| leakage = get_leakage_info(); |
| flt = get_vcpu_leakage_table(); |
| |
| if(flt == NULL) { |
| lgpl_printf("No vcpu PV table found!!!!\n"); |
| goto error; |
| } |
| |
| //vl |
| if(flt->freq != 0) { |
| volt = get_vcpu_volt_from_dvfs(flt->freq, leakage); |
| if(volt > 0) { |
| opp[count++] = volt * 1000; |
| lgpl_printf("vl = %d\n", (volt * 1000)); |
| } |
| flt++; |
| |
| //vh |
| if(flt->freq != 0) { |
| volt = get_vcpu_volt_from_dvfs(flt->freq, leakage); |
| if(volt > 0) { |
| opp[count++] = volt * 1000; |
| lgpl_printf("vh = %d\n", (volt * 1000)); |
| } |
| } |
| } |
| |
| if(count > 1) { |
| lgpl_printf("Got %d value for vcpu vl and vh\n", count); |
| return; |
| } |
| |
| error: |
| lgpl_printf("###### wrong value for vcpu vl and vh!!!!!!!!\n"); |
| return; |
| } |
| |
| #ifdef CORE_VOLT_VALUE |
| void get_vcore_vh_vl(unsigned int *opp) |
| { |
| int leakage = 0; |
| int volt = 0; |
| unsigned int count = 0; |
| struct freq_leakage_table *flt = NULL; |
| |
| leakage = get_leakage_info(); |
| flt = get_vcore_leakage_table(); |
| |
| if(NULL == flt) { |
| lgpl_printf("No vcore PV table found!!!!\n"); |
| goto error; |
| } |
| |
| //vl |
| if(0 != flt->freq) { |
| volt = get_vcore_volt_from_dvfs(flt->freq, leakage); |
| if(volt > 0) { |
| opp[count++] = volt * 1000; |
| lgpl_printf("vcore vl = %d\n", (volt * 1000)); |
| } |
| flt++; |
| |
| //vh |
| if(0 != flt->freq) { |
| volt = get_vcore_volt_from_dvfs(flt->freq, leakage); |
| if(volt > 0) { |
| opp[count++] = volt * 1000; |
| lgpl_printf("vcore vh = %d\n", (volt * 1000)); |
| } |
| } |
| } |
| |
| if(count > 1) { |
| lgpl_printf("Got %d value for vcore vl and vh\n", count); |
| return; |
| } |
| |
| error: |
| lgpl_printf("###### wrong value for vcore vl and vh!!!!!!!!\n"); |
| return; |
| } |
| #endif |
| |
| extern const dvfs_ops_t m88pg86x_ops; |
| extern const dvfs_ops_t sy8824b_ops; |
| extern const dvfs_ops_t ncp6335d_ops; |
| extern const dvfs_ops_t sy20276_ops; |
| extern const dvfs_ops_t sy20278_ops; |
| |
| void pv_comp(int cpu_pll, int gpu_pll) |
| { |
| int leakage_info = 0; |
| int volt = 0, final_volt = 0, default_cpu_volt = 0;; |
| int ret = 0; |
| unsigned int pmic_id = get_pmic_id(); |
| |
| if(M88PG86X == pmic_id) { |
| lgpl_printf("PMIC: Marvell 88PG86X selected!\n"); |
| dvfs_ops = &m88pg86x_ops; |
| } |
| else if(SY8824B == pmic_id) { |
| lgpl_printf("PMIC: SY8824B selected!\n"); |
| dvfs_ops = &sy8824b_ops; |
| } |
| else if(SY20276 == pmic_id) { |
| lgpl_printf("PMIC: SY20276 is selected!\n"); |
| dvfs_ops = &sy20276_ops; |
| } |
| else if(SY20278 == pmic_id) { |
| lgpl_printf("PMIC: SY20278 is selected!\n"); |
| dvfs_ops = &sy20278_ops; |
| } |
| else if(NCP6335D == pmic_id) { |
| lgpl_printf("PMIC: NCP6335D selected!\n"); |
| dvfs_ops = &ncp6335d_ops; |
| } |
| |
| if(NULL == dvfs_ops) |
| lgpl_printf("WARN: unsupported PMIC for pv_comp!\n"); |
| |
| leakage_info = get_leakage_info(); |
| |
| if(!leakage_info) { |
| lgpl_printf("leakage is not present!!!!\n"); |
| return; |
| } |
| lgpl_printf("leakage info %d.\n", leakage_info); |
| |
| // start to set vcpu |
| volt = get_vcpu_volt_from_dvfs(cpu_pll, leakage_info); |
| if(volt != -1) |
| final_volt = volt * 1000; //change to uv to handle 12.5mv case |
| else { |
| lgpl_printf("Failed to parse valid vcpu from opp table !\n"); |
| #ifdef CORE_VOLT_VALUE |
| goto set_vcore; |
| #else |
| UNUSED(gpu_pll); |
| return ; |
| #endif |
| } |
| |
| default_cpu_volt = dvfs_ops->get_vcpu_volt(); |
| if(default_cpu_volt < 0) { |
| lgpl_printf("Failed to get current Vcpu! \n"); |
| return ; |
| } |
| |
| volt = default_cpu_volt; |
| if(volt == final_volt) { |
| lgpl_printf("Vcpu is %duV, default setting by hardware. \n",volt); |
| } else { |
| ret = dvfs_ops->set_vcpu_volt(default_cpu_volt, final_volt); |
| if(ret) |
| lgpl_printf("Failed to set Vcpu from %duv to %duv\n", default_cpu_volt, final_volt); |
| else |
| lgpl_printf("set Vcpu from %duv to %duv\n", default_cpu_volt, final_volt); |
| } |
| |
| #ifdef CORE_VOLT_VALUE |
| set_vcore: |
| { |
| int default_core_volt = 0; |
| |
| // start to set vcore if has |
| volt = get_vcore_volt_from_dvfs(gpu_pll, leakage_info); |
| if(volt != -1) |
| final_volt = volt * 1000; //change to uv to handle 12.5mv case |
| else { |
| lgpl_printf("Failed to parse valid vcore from opp table !\n"); |
| return; |
| } |
| |
| default_core_volt = dvfs_ops->get_vcore_volt(); |
| if(default_core_volt < 0) { |
| lgpl_printf("Failed to get current Vcore! \n"); |
| return; |
| } |
| volt = default_core_volt; |
| if(volt == final_volt) { |
| lgpl_printf("Vcore is %duV, default setting by hardware. \n",volt); |
| } else { |
| ret = dvfs_ops->set_vcore_volt(final_volt, default_core_volt); |
| if(ret) { |
| lgpl_printf("Failed to set Vcore from %duv to %duv\n", default_core_volt, final_volt); |
| } else { |
| lgpl_printf("set Vcore from %duv to %duv\n", default_cpu_volt, final_volt); |
| } |
| } |
| } |
| #endif |
| return ; |
| } |