/* * (C) Copyright 2007-2013 * Allwinner Technology Co., Ltd. * Jerry Wang * * See file CREDITS for list of people who contributed to this * project. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include DECLARE_GLOBAL_DATA_PTR; extern int platform_axp_probe(sunxi_axp_dev_t* sunxi_axp_dev[], int max_dev); __attribute__((section(".data"))) static sunxi_axp_dev_t *sunxi_axp_dev[SUNXI_AXP_DEV_MAX] = {NULL}; __attribute__((section(".data"))) static ulong pmu_nodeoffset_in_config = 0; #ifdef CONFIG_CHARGER_PMU #define CHARGER_PMU 1 #else #define CHARGER_PMU 0 #endif void sunxi_axp_dummy_init(void) { int i; printf("probe axp is dummy\n"); memset(sunxi_axp_dev, 0, SUNXI_AXP_DEV_MAX * 4); for (i = 0; i < SUNXI_AXP_DEV_MAX; i++) { sunxi_axp_dev[i] = &sunxi_axp_null; } } int axp_probe(void) { int axp_num = 0; memset(sunxi_axp_dev, 0, SUNXI_AXP_DEV_MAX * 4); pmu_nodeoffset_in_config = script_parser_offset(PMU_SCRIPT_NAME); if(pmu_nodeoffset_in_config == 0 ) { printf("axp: get node[%s] error\n",PMU_SCRIPT_NAME); } axp_num = platform_axp_probe((sunxi_axp_dev_t* *)&sunxi_axp_dev, SUNXI_AXP_DEV_MAX); return axp_num; } int axp_reinit(void) { int i; for(i=0;ireloc_off); } } //pmu_nodeoffset = fdt_path_offset(working_fdt,PMU_SCRIPT_NAME); pmu_nodeoffset_in_config = script_parser_offset(PMU_SCRIPT_NAME); if(pmu_nodeoffset_in_config == 0) { printf("axp: get node[%s] error\n",PMU_SCRIPT_NAME); return 0; } return 0; } int axp_get_power_vol_level(void) { return gd->power_step_level; } int axp_probe_startup_cause(void) { return sunxi_axp_dev[CHARGER_PMU]->probe_this_poweron_cause(); } int axp_probe_factory_mode(void) { int buffer_value, status; int poweron_reason; buffer_value = sunxi_axp_dev[0]->probe_pre_sys_mode(); if(buffer_value == PMU_PRE_FACTORY_MODE) //factory mode: need the power key and dc or vbus { printf("factory mode detect\n"); status = sunxi_axp_dev[CHARGER_PMU]->probe_power_status(); if(status > 0) //has the dc or vbus { poweron_reason = sunxi_axp_dev[CHARGER_PMU]->probe_this_poweron_cause(); if(poweron_reason == AXP_POWER_ON_BY_POWER_KEY) { //set the system next powerom status as 0x0e(the system mode) printf("factory mode release\n"); sunxi_axp_dev[0]->set_next_sys_mode(PMU_PRE_SYS_MODE); } else { printf("factory mode: try to poweroff without power key\n"); axp_set_hardware_poweron_vol(); //poweroff axp_set_power_off(); for(;;); } } else { printf("factory mode: try to poweroff without power in\n"); axp_set_hardware_poweroff_vol(); //poweroff axp_set_power_off(); for(;;); } } return 0; } int axp_set_hardware_poweron_vol(void) { int vol_value = 0; if(script_parser_fetch_by_offset(pmu_nodeoffset_in_config,"pmu_pwron_vol", (uint32_t*)&vol_value)<0) { pr_msg("set power on vol to default\n"); } return sunxi_axp_dev[0]->set_power_onoff_vol(vol_value, 1); } int axp_set_hardware_poweroff_vol(void) { int vol_value = 0; if(script_parser_fetch_by_offset(pmu_nodeoffset_in_config,"pmu_pwroff_vol", (uint32_t*)&vol_value)<0) { puts("set power off vol to default\n"); } return sunxi_axp_dev[0]->set_power_onoff_vol(vol_value, 0); } int axp_set_power_off(void) { #ifdef CONFIG_SUNXI_AXP2585 if (sunxi_axp_dev[CHARGER_PMU]->set_power_off()) { puts("bmu power off err\n"); return -1; } #endif if (sunxi_axp_dev[0]->set_power_off()) { puts("pmu power off err\n"); return -1; } return 0; } int axp_set_next_poweron_status(int value) { return sunxi_axp_dev[0]->set_next_sys_mode(value); } int axp_probe_pre_sys_mode(void) { #ifdef CONFIG_SUNXI_AXP2585 return sunxi_axp_dev[0]->probe_pre_sys_mode(); #else return sunxi_axp_dev[CHARGER_PMU]->probe_pre_sys_mode(); #endif } int axp_power_get_dcin_battery_exist(int *dcin_exist, int *battery_exist) { *dcin_exist = sunxi_axp_dev[CHARGER_PMU]->probe_power_status(); *battery_exist = sunxi_axp_dev[CHARGER_PMU]->probe_battery_exist(); return 0; } int axp_probe_battery_exist(void) { return sunxi_axp_dev[CHARGER_PMU]->probe_battery_exist(); } int axp_probe_power_source(void) { int status = 0; status = sunxi_axp_dev[CHARGER_PMU]->probe_power_status(); return status; } int axp_probe_battery_vol(void) { return sunxi_axp_dev[CHARGER_PMU]->probe_battery_vol(); } int axp_probe_rest_battery_capacity(void) { return sunxi_axp_dev[CHARGER_PMU]->probe_battery_ratio(); } int axp_probe_key(void) { return sunxi_axp_dev[0]->probe_key(); } int axp_probe_charge_current(void) { return sunxi_axp_dev[CHARGER_PMU]->probe_charge_current(); } int axp_set_charge_current(int current) { return sunxi_axp_dev[CHARGER_PMU]->set_charge_current(current); } int axp_set_charge_control(void) { return sunxi_axp_dev[CHARGER_PMU]->set_charge_control(); } int axp_set_vbus_limit_dc(void) { script_parser_fetch_by_offset(pmu_nodeoffset_in_config, "pmu_ac_vol", (uint32_t *)&gd->limit_vol); script_parser_fetch_by_offset(pmu_nodeoffset_in_config, "pmu_ac_cur", (uint32_t *)&gd->limit_cur); sunxi_axp_dev[CHARGER_PMU]->set_vbus_vol_limit(gd->limit_vol); sunxi_axp_dev[CHARGER_PMU]->set_vbus_cur_limit(gd->limit_cur); return 0; } int axp_set_vbus_limit_pc(void) { script_parser_fetch_by_offset(pmu_nodeoffset_in_config, "pmu_usbpc_vol", (uint32_t *)&gd->limit_pcvol); script_parser_fetch_by_offset(pmu_nodeoffset_in_config, "pmu_usbpc_cur", (uint32_t *)&gd->limit_pccur); sunxi_axp_dev[CHARGER_PMU]->set_vbus_vol_limit(gd->limit_pcvol); sunxi_axp_dev[CHARGER_PMU]->set_vbus_cur_limit(gd->limit_pccur); return 0; } int axp_set_all_limit(void) { axp_set_vbus_limit_dc(); return 0; } int axp_set_suspend_chgcur(void) { return sunxi_axp_dev[CHARGER_PMU]->set_charge_current(gd->pmu_suspend_chgcur); } int axp_set_runtime_chgcur(void) { return sunxi_axp_dev[CHARGER_PMU]->set_charge_current(gd->pmu_runtime_chgcur); } int axp_set_charge_vol_limit(void) { int ret1; int ret2; ret1 = script_parser_fetch_by_offset(pmu_nodeoffset_in_config,"pmu_runtime_chgcur", (uint32_t*)&gd->pmu_runtime_chgcur); ret2 = script_parser_fetch_by_offset(pmu_nodeoffset_in_config,"pmu_suspend_chgcur", (uint32_t*)&gd->pmu_suspend_chgcur); if(ret1) { gd->pmu_runtime_chgcur = 600; } if(ret2) { gd->pmu_suspend_chgcur = 1500; } debug("pmu_runtime_chgcur=%ld\n", gd->pmu_runtime_chgcur); debug("pmu_suspend_chgcur=%ld\n", gd->pmu_suspend_chgcur); axp_set_suspend_chgcur(); return 0; } int axp_set_power_supply_output(void) { int onoff; int power_index1 = 0,power_index2 = 0, ret; char power_name[32]; int power_vol; int power_vol_d; do { memset(power_name, 0, 16); ret = script_parser_fetch_subkey_next("power_sply", power_name, &power_vol, &power_index1,&power_index2); if(ret < 0) { pr_msg("find power_sply to end\n"); return 0; } onoff = -1; power_vol_d = 0; if(power_vol > 10000) { onoff = 1; power_vol_d = power_vol%10000; } else if(power_vol >= 0) { onoff = 0; power_vol_d = power_vol; } pr_msg("%s = %d, onoff=%d\n", power_name, power_vol_d, onoff); if(sunxi_axp_dev[0]->set_supply_status_byname(power_name, power_vol_d, onoff)) { printf("axp set %s to %d failed\n", power_name, power_vol_d); return -1; } }while(1); return 0; } int axp_slave_set_power_supply_output(void) { int ret, onoff; char power_name[16]; int power_vol, power_index1 = 0,power_index2 = 0; int index = -1; int i; int power_vol_d; for(i=1;ipmu_name, sunxi_axp_dev[i]->pmu_name)) { index = i; break; } } } if(index == -1) { printf("unable to find slave pmu\n"); return -1; } printf("slave power\n"); do { memset(power_name, 0, 16); ret = script_parser_fetch_subkey_next("slave_power_sply", power_name, &power_vol, &power_index1,&power_index2); if(ret < 0) { printf("find slave power sply to end\n"); return 0; } onoff = -1; power_vol_d = 0; if(power_vol > 10000) { onoff = 1; power_vol_d = power_vol%10000; } #if defined(CONFIG_SUNXI_AXP_CONFIG_ONOFF) else if(power_vol > 0) { onoff = 0; power_vol_d = power_vol; } #endif else if(power_vol == 0) { onoff = 0; } #if defined(CONFIG_SUNXI_AXP_CONFIG_ONOFF) printf("%s = %d, onoff=%d\n", power_name, power_vol_d, onoff); #else printf("%s = %d\n", power_name, power_vol_d); #endif if(sunxi_axp_dev[index]->set_supply_status_byname(power_name, power_vol_d, onoff)) { printf("axp set %s to %d failed\n", power_name, power_vol_d); } }while(1); return 0; } int axp_probe_power_supply_condition(void) { int dcin_exist, bat_vol; int ratio; int safe_vol; dcin_exist = sunxi_axp_dev[CHARGER_PMU]->probe_power_status(); bat_vol = sunxi_axp_dev[CHARGER_PMU]->probe_battery_vol(); safe_vol = 0; script_parser_fetch_by_offset(pmu_nodeoffset_in_config,"pmu_safe_vol", (uint32_t*)&safe_vol); if(safe_vol < 3000) { safe_vol = 3500; } ratio = sunxi_axp_dev[CHARGER_PMU]->probe_battery_ratio(); pr_msg("bat_vol=%d, ratio=%d\n", bat_vol, ratio); if(ratio < 1) { if(dcin_exist) { if(bat_vol < safe_vol) { gd->power_step_level = BATTERY_RATIO_TOO_LOW_WITH_DCIN_VOL_TOO_LOW; } else { gd->power_step_level = BATTERY_RATIO_TOO_LOW_WITH_DCIN; } } else { gd->power_step_level = BATTERY_RATIO_TOO_LOW_WITHOUT_DCIN; } } else { gd->power_step_level = BATTERY_RATIO_ENOUGH; } return 0; } int axp_int_enable(__u8 *value) { return 0; } int axp_int_disable(void) { return 0; } int axp_int_query(__u8 *addr) { return 0; } /* pmu_type: 0- main pmu */ int axp_set_supply_status(int pmu_type, int vol_name, int vol_value, int onoff) { return sunxi_axp_dev[pmu_type]->set_supply_status(vol_name, vol_value, onoff); } int axp_set_supply_status_byname(char *pmu_type, char *vol_type, int vol_value, int onoff) { int axp_index = 0; if(0 == strcmp(AXP_SLAVE, pmu_type)) { axp_index = AXP_TYPE_SLAVE; } else { axp_index = AXP_TYPE_MAIN; } if(sunxi_axp_dev[axp_index] != NULL) return sunxi_axp_dev[axp_index]->set_supply_status_byname(vol_type, vol_value, onoff); return -1; } int axp_probe_supply_status(int pmu_type, int vol_name, int vol_value) { return sunxi_axp_dev[pmu_type]->probe_supply_status(vol_name, 0, 0); } int axp_probe_supply_status_byname(char *pmu_type, char *vol_type) { int i; for(i=0;ipmu_name, pmu_type)) { return sunxi_axp_dev[i]->probe_supply_status_byname(vol_type); } } } return -1; } //example string : "axp81x_dcdc6 vdd-sys vdd-usb0-09 vdd-hdmi-09" static int find_regulator_str(const char* src, const char* des) { int i,len_src, len_des ; int token_index; len_src = strlen(src); len_des = strlen(des); if(len_des > len_src) return 0; token_index = 0; for(i =0 ; i < len_src+1; i++) { if(src[i]==' ' || src[i] == '\t' || src[i] == '\0') { if(i-token_index == len_des) { if(memcmp(src+token_index,des,len_des) == 0) { return 1; } } token_index = i+1; } } return 0; } int axp_set_supply_status_byregulator(const char* id, int onoff) { char main_key[32]; char pmu_type[32]; char vol_type[32]; char sub_key_name[32]; char sub_key_value[256]; int main_hd = 0; unsigned int i = 0, j = 0, index = 0; int ldo_count = 0; int find_flag = 0; int ret = 0; do { strcpy(main_key,FDT_PATH_REGU); main_hd = script_parser_fetch(main_key,"regulator_count",&ldo_count, 1); if (main_hd != 0) { printf("unable to get ldo_count from [%s] \n",main_key); break; } for (index = 1; index <= ldo_count; index++) { sprintf(sub_key_name, "regulator%u", index); main_hd = script_parser_fetch(main_key,sub_key_name,(int*)(sub_key_value), sizeof(sub_key_value)/sizeof(int)); if (main_hd != 0) { printf("unable to get subkey %s from [%s]\n",sub_key_name, main_key); break; } if (find_regulator_str(sub_key_value, id)) { find_flag = 1; break; } } if (find_flag) break; } while(0); if (!find_flag) { printf("unable to find regulator %s from [pmu1_regu] or [pmu2_regu] \n",id); return -1; } //example : ldo6 = "axp81x_dcdc6 vdd-sys vdd-usb0-09 vdd-hdmi-09" memset(pmu_type, 0, sizeof(pmu_type)); memset(vol_type, 0, sizeof(vol_type)); //get pmu type for(j = 0,i =0; i < strlen(sub_key_value); i++) { if(sub_key_value[i] == '_') { i++; break; } pmu_type[j++] = sub_key_value[i]; } //get vol type j = 0; for(; i < strlen(sub_key_value); i++) { if(sub_key_value[i] == ' ') { break; } vol_type[j++]= sub_key_value[i]; } //para vol = 0 indcate not set vol,only open or close voltage switch ret = axp_set_supply_status_byname(pmu_type,vol_type,0, onoff); if(ret != 0) { printf("error: supply regelator %s=id, pmu_type=%s_%s onoff=%d fail\n", id,pmu_type,vol_type,onoff ); } return ret; } int axp_probe_supply_pmu_name(char *axpname) { static int current_pmu = -1; int i = 0; if(axpname == NULL) { return -1; } for(i=0;ipmu_name); return 0; } } return -1; } int axp_probe_vbus_cur_limit(void) { return sunxi_axp_dev[0]->probe_vbus_cur_limit(); } int axp_set_coulombmeter_onoff(int onoff ) { return sunxi_axp_dev[0]->set_coulombmeter_onoff(onoff); } __weak int axp_probe_vbus_type(void) { return SUNXI_VBUS_PC; } #ifdef CONFIG_SUNXI_PIO_POWER_MODE struct sunxi_bias_set { const char* name; u32 base; u32 shift; }; #define SUNXI_PIO_BIAS(_name, _shift) \ { \ .name = _name, \ .base = SUNXI_PIO_BASE, \ .shift = _shift, \ } #define SUNXI_R_PIO_BIAS(_name, _shift) \ { \ .name = _name, \ .base = SUNXI_RPIO_BASE,\ .shift = _shift, \ } static const struct sunxi_bias_set gpio_bias_tlb[] = { SUNXI_PIO_BIAS("pa_bias", 0), SUNXI_PIO_BIAS("pc_bias", 2), SUNXI_PIO_BIAS("pd_bias", 3), SUNXI_PIO_BIAS("pe_bias", 4), SUNXI_PIO_BIAS("pf_bias", 5), SUNXI_PIO_BIAS("pg_bias", 6), SUNXI_PIO_BIAS("pi_bias", 8), SUNXI_PIO_BIAS("pj_bias", 9), SUNXI_PIO_BIAS("vcc_bias", 12), SUNXI_R_PIO_BIAS("pl_bias", 0), SUNXI_R_PIO_BIAS("pm_bias", 1), }; static char* __parse_axp_name(char *gpio_bias, int *offset) { char *axp = gpio_bias + *offset; while ( (axp[*offset]!=':') && (axp[*offset]!='\0') ) (*offset)++; axp[(*offset)++]='\0'; return axp; } static char* __parse_supply_name(char *gpio_bias,int *offset) { char *supply = gpio_bias + *offset; while ( (gpio_bias[*offset]!=':') && (gpio_bias[*offset]!='\0') ) (*offset)++; gpio_bias[(*offset)++]='\0'; return supply; } static u32 __parse_setting_vol(char *vol, char *gpio_bias,int *offset) { vol = gpio_bias + *offset; while ( (gpio_bias[*offset]!=':') && (gpio_bias[*offset]!='\0') ) (*offset)++; return simple_strtoul(vol, NULL, 10); } static int __gpio_name_is_valid(char *gpio_name) { int i,ret = -1; for (i = 0; i 10000) { supply_set = 1; bias_vol_set = bias_vol_set%10000; } else if (bias_vol_set >= 0) { supply_set = 0; } printf("set %s(%d) bias:%d\n", gpio_name, port_index, bias_vol_set); __pio_power_mode_select(bias_vol_set, port_index); if (uboot_spare_head.boot_ext[0].data[0]) { if (supply_set) { printf("axp=%s, supply=%s, vol=%d\n", axp, supply, bias_vol_set); if (axp_set_supply_status_byname(axp, supply, bias_vol_set, 1) < 0) printf("pmu set fail!\n"); } } } while (1); return 0; } #endif /*CONFIG_SUNXI_PIO_POWER_MODE*/