diff --git a/firmware/SOURCES b/firmware/SOURCES index 8868e4f21f..87db67d8fd 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -1947,8 +1947,8 @@ drivers/touchpad.c #ifdef HAVE_I2C_ASYNC drivers/i2c-async.c #endif -#if defined(HAVE_AXP_PMU) && HAVE_AXP_PMU == 192 -drivers/axp192.c +#ifdef HAVE_AXP_PMU +drivers/axp-pmu.c #endif #ifdef HAVE_FT6x06 drivers/ft6x06.c diff --git a/firmware/drivers/axp-pmu.c b/firmware/drivers/axp-pmu.c new file mode 100644 index 0000000000..fd1126dbbf --- /dev/null +++ b/firmware/drivers/axp-pmu.c @@ -0,0 +1,670 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021 Aidan MacDonald + * + * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "axp-pmu.h" +#include "power.h" +#include "system.h" +#include "i2c-async.h" +#include + +/* Headers for the debug menu */ +#ifndef BOOTLOADER +# include "action.h" +# include "list.h" +# include +#endif + +struct axp_adc_info { + uint8_t reg; + uint8_t en_reg; + uint8_t en_bit; +}; + +struct axp_supply_info { + uint8_t volt_reg; + uint8_t volt_reg_mask; + uint8_t en_reg; + uint8_t en_bit; + int min_mV; + int max_mV; + int step_mV; +}; + +static const struct axp_adc_info axp_adc_info[NUM_ADC_CHANNELS] = { + {0x56, AXP_REG_ADCENABLE1, 5}, /* ACIN_VOLTAGE */ + {0x58, AXP_REG_ADCENABLE1, 4}, /* ACIN_CURRENT */ + {0x5a, AXP_REG_ADCENABLE1, 3}, /* VBUS_VOLTAGE */ + {0x5c, AXP_REG_ADCENABLE1, 2}, /* VBUS_CURRENT */ + {0x5e, AXP_REG_ADCENABLE2, 7}, /* INTERNAL_TEMP */ + {0x62, AXP_REG_ADCENABLE1, 1}, /* TS_INPUT */ + {0x78, AXP_REG_ADCENABLE1, 7}, /* BATTERY_VOLTAGE */ + {0x7a, AXP_REG_ADCENABLE1, 6}, /* CHARGE_CURRENT */ + {0x7c, AXP_REG_ADCENABLE1, 6}, /* DISCHARGE_CURRENT */ + {0x7e, AXP_REG_ADCENABLE1, 1}, /* APS_VOLTAGE */ + {0x70, 0xff, 0}, /* BATTERY_POWER */ +}; + +static const struct axp_supply_info axp_supply_info[AXP_NUM_SUPPLIES] = { +#if HAVE_AXP_PMU == 192 + [AXP_SUPPLY_DCDC1] = { + .volt_reg = 0x26, + .volt_reg_mask = 0x7f, + .en_reg = 0x12, + .en_bit = 0, + .min_mV = 700, + .max_mV = 3500, + .step_mV = 25, + }, + [AXP_SUPPLY_DCDC2] = { + .volt_reg = 0x23, + .volt_reg_mask = 0x3f, + .en_reg = 0x10, + .en_bit = 0, + .min_mV = 700, + .max_mV = 2275, + .step_mV = 25, + }, + [AXP_SUPPLY_DCDC3] = { + .volt_reg = 0x27, + .volt_reg_mask = 0x7f, + .en_reg = 0x12, + .en_bit = 1, + .min_mV = 700, + .max_mV = 3500, + .step_mV = 25, + }, + /* + * NOTE: LDO1 is always on, and we can't query it or change voltages + */ + [AXP_SUPPLY_LDO2] = { + .volt_reg = 0x28, + .volt_reg_mask = 0xf0, + .en_reg = 0x12, + .en_bit = 2, + .min_mV = 1800, + .max_mV = 3300, + .step_mV = 100, + }, + [AXP_SUPPLY_LDO3] = { + .volt_reg = 0x28, + .volt_reg_mask = 0x0f, + .en_reg = 0x12, + .en_bit = 3, + .min_mV = 1800, + .max_mV = 3300, + .step_mV = 100, + }, + [AXP_SUPPLY_LDO_IO0] = { + .volt_reg = 0x91, + .volt_reg_mask = 0xf0, + .en_reg = 0x90, + .en_bit = 0xff, /* this one requires special handling */ + .min_mV = 1800, + .max_mV = 3300, + .step_mV = 100, + }, +#else +# error "Untested AXP chip" +#endif +}; + +static struct axp_driver { + int adc_enable; + int chargecurrent_setting; + int chip_id; +} axp; + +static void axp_init_enabled_adcs(void) +{ + axp.adc_enable = 0; + + /* Read chip ID, so we can display it on the debug screen. + * This is undocumented but there's Linux driver code floating around + * which suggests this should work for many AXP chips. */ + axp.chip_id = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_CHIP_ID); + + /* Read enabled ADCs from the hardware */ + uint8_t regs[2]; + int rc = i2c_reg_read(AXP_PMU_BUS, AXP_PMU_ADDR, + AXP_REG_ADCENABLE1, 2, ®s[0]); + if(rc != I2C_STATUS_OK) + return; + + /* Parse registers to set ADC enable bits */ + const struct axp_adc_info* info = axp_adc_info; + for(int i = 0; i < NUM_ADC_CHANNELS; ++i) { + if(info[i].en_reg == 0xff) + continue; + + if(regs[info[i].en_reg - AXP_REG_ADCENABLE1] & info[i].en_bit) + axp.adc_enable |= 1 << i; + } + + /* Handle battery power ADC */ + if((axp.adc_enable & (1 << ADC_BATTERY_VOLTAGE)) && + (axp.adc_enable & (1 << ADC_DISCHARGE_CURRENT))) { + axp.adc_enable |= (1 << ADC_BATTERY_POWER); + } +} + +void axp_init(void) +{ + axp_init_enabled_adcs(); + + /* We need discharge current ADC to reliably poll for a full battery */ + int bits = axp.adc_enable; + bits |= (1 << ADC_DISCHARGE_CURRENT); + axp_adc_set_enabled(bits); + + /* Read the maximum charging current */ + int value = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_CHARGECONTROL1); + axp.chargecurrent_setting = (value < 0) ? -1 : (value & 0xf); +} + +void axp_supply_set_voltage(int supply, int voltage) +{ + const struct axp_supply_info* info = &axp_supply_info[supply]; + if(info->volt_reg == 0 || info->volt_reg_mask == 0) + return; + + if(voltage > 0 && info->step_mV != 0) { + if(voltage < info->min_mV || voltage > info->max_mV) + return; + + int regval = (voltage - info->min_mV) / info->step_mV; + i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR, info->volt_reg, + info->volt_reg_mask, regval, NULL); + } + + if(info->en_bit != 0xff) { + i2c_reg_setbit1(AXP_PMU_BUS, AXP_PMU_ADDR, + info->en_reg, info->en_bit, + voltage > 0 ? 1 : 0, NULL); + } +} + +int axp_supply_get_voltage(int supply) +{ + const struct axp_supply_info* info = &axp_supply_info[supply]; + if(info->volt_reg == 0) + return AXP_SUPPLY_NOT_PRESENT; + + if(info->en_reg != 0) { + int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, info->en_reg); + if(r < 0) + return AXP_SUPPLY_DISABLED; + +#if HAVE_AXP_PMU == 192 + if(supply == AXP_SUPPLY_LDO_IO0) { + if((r & 7) != 2) + return AXP_SUPPLY_DISABLED; + } else +#endif + { + if(r & (1 << info->en_bit) == 0) + return AXP_SUPPLY_DISABLED; + } + } + + /* Hack, avoid undefined shift below. Can be useful too... */ + if(info->volt_reg_mask == 0) + return info->min_mV; + + int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, info->volt_reg); + if(r < 0) + return 0; + + int bit = find_first_set_bit(info->volt_reg_mask); + int val = (r & info->volt_reg_mask) >> bit; + return info->min_mV + (val * info->step_mV); +} + +/* TODO: this can STILL indicate some false positives! */ +int axp_battery_status(void) +{ + int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_POWERSTATUS); + if(r >= 0) { + /* Charging bit indicates we're currently charging */ + if((r & 0x04) != 0) + return AXP_BATT_CHARGING; + + /* Not plugged in means we're discharging */ + if((r & 0xf0) == 0) + return AXP_BATT_DISCHARGING; + } else { + /* Report discharging if we can't find out power status */ + return AXP_BATT_DISCHARGING; + } + + /* If the battery is full and not in use, the charging bit will be 0, + * there will be an external power source, AND the discharge current + * will be zero. Seems to rule out all false positives. */ + int d = axp_adc_read_raw(ADC_DISCHARGE_CURRENT); + if(d == 0) + return AXP_BATT_FULL; + + return AXP_BATT_DISCHARGING; +} + +int axp_input_status(void) +{ +#ifdef HAVE_BATTERY_SWITCH + int input_status = 0; +#else + int input_status = AXP_INPUT_BATTERY; +#endif + + int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_POWERSTATUS); + if(r < 0) + return input_status; + + /* Check for AC input */ + if(r & 0x80) + input_status |= AXP_INPUT_AC; + + /* Only report USB if ACIN and VBUS are not shorted */ + if((r & 0x20) != 0 && (r & 0x02) == 0) + input_status |= AXP_INPUT_USB; + +#ifdef HAVE_BATTERY_SWITCH + /* Check for battery presence if target defines it as removable */ + r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_CHARGESTATUS); + if(r >= 0 && (r & 0x20) != 0) + input_status |= AXP_INPUT_BATTERY; +#endif + + return input_status; +} + +int axp_adc_read(int adc) +{ + int value = axp_adc_read_raw(adc); + if(value == INT_MIN) + return INT_MIN; + + return axp_adc_conv_raw(adc, value); +} + +int axp_adc_read_raw(int adc) +{ + /* Don't give a reading if the ADC is not enabled */ + if((axp.adc_enable & (1 << adc)) == 0) + return INT_MIN; + + /* Read the ADC */ + uint8_t buf[3]; + int count = (adc == ADC_BATTERY_POWER) ? 3 : 2; + uint8_t reg = axp_adc_info[adc].reg; + int rc = i2c_reg_read(AXP_PMU_BUS, AXP_PMU_ADDR, reg, count, &buf[0]); + if(rc != I2C_STATUS_OK) + return INT_MIN; + + /* Parse the value */ + if(adc == ADC_BATTERY_POWER) + return (buf[0] << 16) | (buf[1] << 8) | buf[2]; + else if(adc == ADC_CHARGE_CURRENT || adc == ADC_DISCHARGE_CURRENT) + return (buf[0] << 5) | (buf[1] & 0x1f); + else + return (buf[0] << 4) | (buf[1] & 0xf); +} + +int axp_adc_conv_raw(int adc, int value) +{ + switch(adc) { + case ADC_ACIN_VOLTAGE: + case ADC_VBUS_VOLTAGE: + /* 0 mV ... 6.9615 mV, step 1.7 mV */ + return value * 17 / 10; + case ADC_ACIN_CURRENT: + /* 0 mA ... 2.5594 A, step 0.625 mA */ + return value * 5 / 8; + case ADC_VBUS_CURRENT: + /* 0 mA ... 1.5356 A, step 0.375 mA */ + return value * 3 / 8; + case ADC_INTERNAL_TEMP: + /* -144.7 C ... 264.8 C, step 0.1 C */ + return value - 1447; + case ADC_TS_INPUT: + /* 0 mV ... 3.276 V, step 0.8 mV */ + return value * 4 / 5; + case ADC_BATTERY_VOLTAGE: + /* 0 mV ... 4.5045 V, step 1.1 mV */ + return value * 11 / 10; + case ADC_CHARGE_CURRENT: + case ADC_DISCHARGE_CURRENT: + /* 0 mA to 4.095 A, step 0.5 mA */ + return value / 2; + case ADC_APS_VOLTAGE: + /* 0 mV to 5.733 V, step 1.4 mV */ + return value * 7 / 5; + case ADC_BATTERY_POWER: + /* 0 uW to 23.6404 W, step 0.55 uW */ + return value * 11 / 20; + default: + /* Shouldn't happen */ + return INT_MIN; + } +} + +int axp_adc_get_enabled(void) +{ + return axp.adc_enable; +} + +void axp_adc_set_enabled(int adc_bits) +{ + /* Ignore no-op */ + if(adc_bits == axp.adc_enable) + return; + + /* Compute the new register values */ + const struct axp_adc_info* info = axp_adc_info; + uint8_t regs[2] = {0, 0}; + for(int i = 0; i < NUM_ADC_CHANNELS; ++i) { + if(info[i].en_reg == 0xff) + continue; + + if(adc_bits & (1 << i)) + regs[info[i].en_reg - 0x82] |= 1 << info[i].en_bit; + } + + /* These ADCs share an enable bit */ + if(adc_bits & ((1 << ADC_CHARGE_CURRENT)|(1 << ADC_DISCHARGE_CURRENT))) { + adc_bits |= (1 << ADC_CHARGE_CURRENT); + adc_bits |= (1 << ADC_DISCHARGE_CURRENT); + } + + /* Enable required bits for battery power ADC */ + if(adc_bits & (1 << ADC_BATTERY_POWER)) { + regs[0] |= 1 << info[ADC_DISCHARGE_CURRENT].en_bit; + regs[0] |= 1 << info[ADC_BATTERY_VOLTAGE].en_bit; + } + + /* Update the configuration */ + i2c_reg_write(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_ADCENABLE1, 2, ®s[0]); + axp.adc_enable = adc_bits; +} + +int axp_adc_get_rate(void) +{ + int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_ADCSAMPLERATE); + if(r < 0) + return AXP_ADC_RATE_100HZ; /* an arbitrary value */ + + return (r >> 6) & 3; +} + +void axp_adc_set_rate(int rate) +{ + i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_ADCSAMPLERATE, + 0xc0, (rate & 3) << 6, NULL); +} + +static uint32_t axp_cc_parse(const uint8_t* buf) +{ + return ((uint32_t)buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; +} + +void axp_cc_read(uint32_t* charge, uint32_t* discharge) +{ + uint8_t buf[8]; + int rc = i2c_reg_read(AXP_PMU_BUS, AXP_PMU_ADDR, + AXP_REG_COULOMBCOUNTERBASE, 8, &buf[0]); + if(rc != I2C_STATUS_OK) { + if(charge) + *charge = 0; + if(discharge) + *discharge = 0; + return; + } + + if(charge) + *charge = axp_cc_parse(&buf[0]); + if(discharge) + *discharge = axp_cc_parse(&buf[4]); +} + +void axp_cc_clear(void) +{ + i2c_reg_setbit1(AXP_PMU_BUS, AXP_PMU_ADDR, + AXP_REG_COULOMBCOUNTERCTRL, 5, 1, NULL); +} + +void axp_cc_enable(bool en) +{ + i2c_reg_setbit1(AXP_PMU_BUS, AXP_PMU_ADDR, + AXP_REG_COULOMBCOUNTERCTRL, 7, en ? 1 : 0, NULL); +} + +bool axp_cc_is_enabled(void) +{ + int reg = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, + AXP_REG_COULOMBCOUNTERCTRL); + return reg >= 0 && (reg & 0x40) != 0; +} + +static const int chargecurrent_tbl[] = { + 100, 190, 280, 360, + 450, 550, 630, 700, + 780, 880, 960, 1000, + 1080, 1160, 1240, 1320, +}; + +static const int chargecurrent_tblsz = sizeof(chargecurrent_tbl)/sizeof(int); + +void axp_set_charge_current(int maxcurrent) +{ + /* Find the charge current just higher than maxcurrent */ + int value = 0; + while(value < chargecurrent_tblsz && + chargecurrent_tbl[value] <= maxcurrent) + ++value; + + /* Select the next lower current, the greatest current <= maxcurrent */ + if(value >= chargecurrent_tblsz) + value = chargecurrent_tblsz - 1; + else if(value > 0) + --value; + + /* Don't issue i2c write if desired setting is already in use */ + if(value == axp.chargecurrent_setting) + return; + + /* Update register */ + i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR, + AXP_REG_CHARGECONTROL1, 0x0f, value, NULL); + axp.chargecurrent_setting = value; +} + +int axp_get_charge_current(void) +{ + if(axp.chargecurrent_setting < 0) + return chargecurrent_tbl[0]; + else + return chargecurrent_tbl[axp.chargecurrent_setting]; +} + +void axp_power_off(void) +{ + /* Set the shutdown bit */ + i2c_reg_setbit1(AXP_PMU_BUS, AXP_PMU_ADDR, + AXP_REG_SHUTDOWNLEDCTRL, 7, 1, NULL); +} + +#ifndef BOOTLOADER +enum { + AXP_DEBUG_CHIP_ID, + AXP_DEBUG_BATTERY_STATUS, + AXP_DEBUG_INPUT_STATUS, + AXP_DEBUG_CHARGE_CURRENT, + AXP_DEBUG_COULOMB_COUNTERS, + AXP_DEBUG_ADC_RATE, + AXP_DEBUG_FIRST_ADC, + AXP_DEBUG_FIRST_SUPPLY = AXP_DEBUG_FIRST_ADC + NUM_ADC_CHANNELS, + AXP_DEBUG_NUM_ENTRIES = AXP_DEBUG_FIRST_SUPPLY + AXP_NUM_SUPPLIES, +}; + +static int axp_debug_menu_cb(int action, struct gui_synclist* lists) +{ + (void)lists; + + if(action == ACTION_NONE) + action = ACTION_REDRAW; + + return action; +} + +static const char* axp_debug_menu_get_name(int item, void* data, + char* buf, size_t buflen) +{ + (void)data; + + static const char* const adc_names[] = { + "V_acin", "I_acin", "V_vbus", "I_vbus", "T_int", + "V_ts", "V_batt", "I_chrg", "I_dchg", "V_aps", "P_batt" + }; + + static const char* const adc_units[] = { + "mV", "mA", "mV", "mA", "C", "mV", "mV", "mA", "mA", "mV", "uW", + }; + + static const char* const supply_names[] = { + "DCDC1", "DCDC2", "DCDC3", + "LDO1", "LDO2", "LDO3", "LDO_IO0", + }; + + int adc = item - AXP_DEBUG_FIRST_ADC; + if(item >= AXP_DEBUG_FIRST_ADC && adc < NUM_ADC_CHANNELS) { + int raw_value = axp_adc_read_raw(adc); + if(raw_value == INT_MIN) { + snprintf(buf, buflen, "%s: [Disabled]", adc_names[adc]); + return buf; + } + + int value = axp_adc_conv_raw(adc, raw_value); + if(adc == ADC_INTERNAL_TEMP) { + snprintf(buf, buflen, "%s: %d.%d %s", adc_names[adc], + value/10, value%10, adc_units[adc]); + } else { + snprintf(buf, buflen, "%s: %d %s", adc_names[adc], + value, adc_units[adc]); + } + + return buf; + } + + int supply = item - AXP_DEBUG_FIRST_SUPPLY; + if(item >= AXP_DEBUG_FIRST_SUPPLY && supply < AXP_NUM_SUPPLIES) { + int voltage = axp_supply_get_voltage(supply); + if(voltage == AXP_SUPPLY_NOT_PRESENT) + snprintf(buf, buflen, "%s: [Not Present]", supply_names[supply]); + else if(voltage == AXP_SUPPLY_DISABLED) + snprintf(buf, buflen, "%s: [Disabled]", supply_names[supply]); + else + snprintf(buf, buflen, "%s: %d mV", supply_names[supply], voltage); + + return buf; + } + + switch(item) { + case AXP_DEBUG_CHIP_ID: { + snprintf(buf, buflen, "Chip ID: %d (%02x) [Driver: AXP%d]", + axp.chip_id, axp.chip_id, HAVE_AXP_PMU); + return buf; + } break; + + case AXP_DEBUG_BATTERY_STATUS: { + switch(axp_battery_status()) { + case AXP_BATT_FULL: + return "Battery: Full"; + case AXP_BATT_CHARGING: + return "Battery: Charging"; + case AXP_BATT_DISCHARGING: + return "Battery: Discharging"; + default: + return "Battery: Unknown"; + } + } break; + + case AXP_DEBUG_INPUT_STATUS: { + int s = axp_input_status(); + const char* ac = (s & AXP_INPUT_AC) ? " AC" : ""; + const char* usb = (s & AXP_INPUT_USB) ? " USB" : ""; + const char* batt = (s & AXP_INPUT_BATTERY) ? " Battery" : ""; + snprintf(buf, buflen, "Inputs:%s%s%s", ac, usb, batt); + return buf; + } break; + + case AXP_DEBUG_CHARGE_CURRENT: { + int current = axp_get_charge_current(); + snprintf(buf, buflen, "Max charge current: %d mA", current); + return buf; + } break; + + case AXP_DEBUG_COULOMB_COUNTERS: { + uint32_t charge, discharge; + axp_cc_read(&charge, &discharge); + + snprintf(buf, buflen, "Coulomb counters: +%lu / -%lu", + (unsigned long)charge, (unsigned long)discharge); + return buf; + } break; + + case AXP_DEBUG_ADC_RATE: { + int rate = 25 << axp_adc_get_rate(); + snprintf(buf, buflen, "ADC sample rate: %d Hz", rate); + return buf; + } break; + + default: + return "---"; + } +} + +bool axp_debug_menu(void) +{ + struct simplelist_info info; + simplelist_info_init(&info, "AXP debug", AXP_DEBUG_NUM_ENTRIES, NULL); + info.action_callback = axp_debug_menu_cb; + info.get_name = axp_debug_menu_get_name; + return simplelist_show_list(&info); +} +#endif /* !BOOTLOADER */ + +/* This is basically the only valid implementation, so define it here */ +unsigned int power_input_status(void) +{ + unsigned int state = 0; + int input_status = axp_input_status(); + + if(input_status & AXP_INPUT_AC) + state |= POWER_INPUT_MAIN_CHARGER; + + if(input_status & AXP_INPUT_USB) + state |= POWER_INPUT_USB_CHARGER; + +#ifdef HAVE_BATTERY_SWITCH + if(input_status & AXP_INPUT_BATTERY) + state |= POWER_INPUT_BATTERY; +#endif + + return state; +} diff --git a/firmware/drivers/axp192.c b/firmware/drivers/axp192.c deleted file mode 100644 index 3c61d8c533..0000000000 --- a/firmware/drivers/axp192.c +++ /dev/null @@ -1,810 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2021 Aidan MacDonald - * - * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ****************************************************************************/ - -#include "axp192.h" -#include "system.h" -#include "power.h" -#include "i2c-async.h" -#include "logf.h" - -/* - * Direct register access - */ - -int axp_read(uint8_t reg) -{ - int ret = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, reg); - if(ret < 0) - logf("axp: read reg %02x err=%d", reg, ret); - - return ret; -} - -int axp_write(uint8_t reg, uint8_t value) -{ - int ret = i2c_reg_write1(AXP_PMU_BUS, AXP_PMU_ADDR, reg, value); - if(ret < 0) - logf("axp: write reg %02x err=%d", reg, ret); - - return ret; -} - -int axp_modify(uint8_t reg, uint8_t clr, uint8_t set) -{ - int ret = i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR, reg, clr, set, NULL); - if(ret < 0) - logf("axp: modify reg %02x err=%d", reg, ret); - - return ret; -} - -/* - * Power supplies: enable/disable, set voltage - */ - -struct axp_supplydata { - uint8_t en_reg; - uint8_t en_bit; - uint8_t volt_reg; - uint8_t volt_msb: 4; - uint8_t volt_lsb: 4; - short min_mV; - short step_mV; -}; - -static const struct axp_supplydata supplydata[] = { - [AXP_SUPPLY_EXTEN] = { - .en_reg = AXP_REG_PWRCTL1, - .en_bit = 1 << 2, - .volt_reg = 0xff, /* undefined */ - .volt_msb = 0xf, - .volt_lsb = 0xf, - .min_mV = 0, - .step_mV = 0, - }, - [AXP_SUPPLY_DCDC1] = { - .en_reg = AXP_REG_PWRCTL2, - .en_bit = 1 << 0, - .volt_reg = AXP_REG_DCDC1VOLT, - .volt_msb = 6, - .volt_lsb = 0, - .min_mV = 700, - .step_mV = 25, - }, - [AXP_SUPPLY_DCDC2] = { - .en_reg = AXP_REG_PWRCTL1, - .en_bit = 1 << 0, - .volt_reg = AXP_REG_DCDC2VOLT, - .volt_msb = 5, - .volt_lsb = 0, - .min_mV = 700, - .step_mV = 25, - }, - [AXP_SUPPLY_DCDC3] = { - .en_reg = AXP_REG_PWRCTL2, - .en_bit = 1 << 1, - .volt_reg = AXP_REG_DCDC3VOLT, - .volt_msb = 6, - .volt_lsb = 0, - .min_mV = 700, - .step_mV = 25, - }, - [AXP_SUPPLY_LDO2] = { - .en_reg = AXP_REG_PWRCTL2, - .en_bit = 1 << 2, - .volt_reg = AXP_REG_LDO2LDO3VOLT, - .volt_msb = 7, - .volt_lsb = 4, - .min_mV = 1800, - .step_mV = 100, - }, - [AXP_SUPPLY_LDO3] = { - .en_reg = AXP_REG_PWRCTL2, - .en_bit = 1 << 3, - .volt_reg = AXP_REG_LDO2LDO3VOLT, - .volt_msb = 3, - .volt_lsb = 0, - .min_mV = 1800, - .step_mV = 100, - }, - [AXP_SUPPLY_LDOIO0] = { - .en_reg = 0xff, /* undefined */ - .en_bit = 0, - .volt_reg = AXP_REG_GPIO0LDO, - .volt_msb = 7, - .volt_lsb = 4, - .min_mV = 1800, - .step_mV = 100, - }, -}; - -void axp_enable_supply(int supply, bool enable) -{ - const struct axp_supplydata* data = &supplydata[supply]; - axp_modify(data->en_reg, data->en_bit, enable ? data->en_bit : 0); -} - -void axp_set_enabled_supplies(unsigned int supply_mask) -{ - uint8_t xfer[3]; - xfer[0] = 0; - xfer[1] = AXP_REG_PWRCTL2; - xfer[2] = 0; - - for(int i = 0; i < AXP_NUM_SUPPLIES; ++i) { - if(!(supply_mask & (1 << i))) - continue; - - const struct axp_supplydata* data = &supplydata[i]; - if(data->en_reg == AXP_REG_PWRCTL1) { - xfer[0] |= data->en_bit; - xfer[2] |= data->en_bit << 4; /* HACK: work around AXP quirk */ - } else { - xfer[2] |= data->en_bit; - } - } - - i2c_reg_write(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_PWRCTL1, 3, xfer); -} - -void axp_set_supply_voltage(int supply, int output_mV) -{ - const struct axp_supplydata* data = &supplydata[supply]; - uint8_t mask = (1 << (data->volt_msb - data->volt_lsb + 1)) - 1; - uint8_t value = (output_mV - data->min_mV) / data->step_mV; - axp_modify(data->volt_reg, mask << data->volt_lsb, value << data->volt_lsb); -} - -/* - * ADC control: enable/disable, read - */ - -struct axp_adcdata { - uint8_t data_reg; - uint8_t en_reg; - uint8_t en_bit; - int8_t num; - int8_t den; -}; - -static const struct axp_adcdata adcdata[] = { - [AXP_ADC_ACIN_VOLTAGE] = {0x56, AXP_REG_ADCEN1, 1 << 5, 17, 10}, - [AXP_ADC_ACIN_CURRENT] = {0x58, AXP_REG_ADCEN1, 1 << 4, 5, 8}, - [AXP_ADC_VBUS_VOLTAGE] = {0x5a, AXP_REG_ADCEN1, 1 << 3, 17, 10}, - [AXP_ADC_VBUS_CURRENT] = {0x5c, AXP_REG_ADCEN1, 1 << 2, 3, 8}, - [AXP_ADC_INTERNAL_TEMP] = {0x5e, AXP_REG_ADCEN2, 1 << 7, 0, 0}, - [AXP_ADC_TS_INPUT] = {0x62, AXP_REG_ADCEN1, 1 << 0, 4, 5}, - [AXP_ADC_GPIO0] = {0x64, AXP_REG_ADCEN2, 1 << 3, 1, 2}, - [AXP_ADC_GPIO1] = {0x66, AXP_REG_ADCEN2, 1 << 2, 1, 2}, - [AXP_ADC_GPIO2] = {0x68, AXP_REG_ADCEN2, 1 << 1, 1, 2}, - [AXP_ADC_GPIO3] = {0x6a, AXP_REG_ADCEN2, 1 << 0, 1, 2}, - [AXP_ADC_BATTERY_VOLTAGE] = {0x78, AXP_REG_ADCEN1, 1 << 7, 11, 10}, - [AXP_ADC_CHARGE_CURRENT] = {0x7a, AXP_REG_ADCEN1, 1 << 6, 1, 2}, - [AXP_ADC_DISCHARGE_CURRENT] = {0x7c, AXP_REG_ADCEN1, 1 << 6, 1, 2}, - [AXP_ADC_APS_VOLTAGE] = {0x7e, AXP_REG_ADCEN1, 1 << 1, 7, 5}, -}; - -void axp_enable_adc(int adc, bool enable) -{ - const struct axp_adcdata* data = &adcdata[adc]; - axp_modify(data->en_reg, data->en_bit, enable ? data->en_bit : 0); -} - -void axp_set_enabled_adcs(unsigned int adc_mask) -{ - uint8_t xfer[3]; - xfer[0] = 0; - xfer[1] = AXP_REG_ADCEN2; - xfer[2] = 0; - - for(int i = 0; i < AXP_NUM_ADCS; ++i) { - if(!(adc_mask & (1 << i))) - continue; - - const struct axp_adcdata* data = &adcdata[i]; - if(data->en_reg == AXP_REG_ADCEN1) - xfer[0] |= data->en_bit; - else - xfer[2] |= data->en_bit; - } - - i2c_reg_write(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_ADCEN1, 3, xfer); -} - -int axp_read_adc_raw(int adc) -{ - uint8_t data[2]; - int ret = i2c_reg_read(AXP_PMU_BUS, AXP_PMU_ADDR, - adcdata[adc].data_reg, 2, data); - if(ret < 0) { - logf("axp: ADC read failed, err=%d", ret); - return INT_MIN; - } - - if(adc == AXP_ADC_CHARGE_CURRENT || adc == AXP_ADC_DISCHARGE_CURRENT) - return (data[0] << 5) | data[1]; - else - return (data[0] << 4) | data[1]; -} - -int axp_conv_adc(int adc, int value) -{ - const struct axp_adcdata* data = &adcdata[adc]; - if(adc == AXP_ADC_INTERNAL_TEMP) - return value - 1447; - else - return data->num * value / data->den; -} - -int axp_read_adc(int adc) -{ - int ret = axp_read_adc_raw(adc); - if(ret == INT_MIN) - return ret; - - return axp_conv_adc(adc, ret); -} - -/* - * GPIOs: set function, pull down control, get/set pin level - */ - -struct axp_gpiodata { - uint8_t func_reg; - uint8_t func_msb: 4; - uint8_t func_lsb: 4; - uint8_t level_reg; - uint8_t level_out: 4; - uint8_t level_in: 4; -}; - -static const struct axp_gpiodata gpiodata[] = { - {AXP_REG_GPIO0FUNC, 2, 0, AXP_REG_GPIOLEVEL1, 0, 4}, - {AXP_REG_GPIO1FUNC, 2, 0, AXP_REG_GPIOLEVEL1, 1, 5}, - {AXP_REG_GPIO2FUNC, 2, 0, AXP_REG_GPIOLEVEL1, 2, 6}, - {AXP_REG_GPIO3GPIO4FUNC, 1, 0, AXP_REG_GPIOLEVEL2, 0, 4}, - {AXP_REG_GPIO3GPIO4FUNC, 3, 2, AXP_REG_GPIOLEVEL2, 1, 5}, - {AXP_REG_NRSTO, 7, 6, AXP_REG_NRSTO, 4, 5}, -}; - -static const uint8_t gpio34funcmap[8] = { - [AXP_GPIO_SPECIAL] = 0x0, - [AXP_GPIO_OPEN_DRAIN_OUTPUT] = 0x1, - [AXP_GPIO_INPUT] = 0x2, - [AXP_GPIO_ADC_IN] = 0x3, -}; - -static const uint8_t nrstofuncmap[8] = { - [AXP_GPIO_SPECIAL] = 0x0, - [AXP_GPIO_OPEN_DRAIN_OUTPUT] = 0x2, - [AXP_GPIO_INPUT] = 0x3, -}; - -void axp_set_gpio_function(int gpio, int function) -{ - const struct axp_gpiodata* data = &gpiodata[gpio]; - int mask = (1 << (data->func_msb - data->func_lsb + 1)) - 1; - - if(gpio == 5) - function = nrstofuncmap[function]; - else if(gpio >= 3) - function = gpio34funcmap[function]; - - axp_modify(data->func_reg, mask << data->func_lsb, function << data->func_lsb); -} - -void axp_set_gpio_pulldown(int gpio, bool enable) -{ - int bit = 1 << gpio; - axp_modify(AXP_REG_GPIOPULL, bit, enable ? bit : 0); -} - -int axp_get_gpio(int gpio) -{ - const struct axp_gpiodata* data = &gpiodata[gpio]; - return axp_read(data->level_reg) & (1 << data->level_in); -} - -void axp_set_gpio(int gpio, bool enable) -{ - const struct axp_gpiodata* data = &gpiodata[gpio]; - uint8_t bit = 1 << data->level_out; - axp_modify(data->level_reg, bit, enable ? bit : 0); -} - -/* - * Charging: set charging current, query charging/input status - */ - -static const short chargecurrent_tbl[] = { - 100, 190, 280, 360, - 450, 550, 630, 700, - 780, 880, 960, 1000, - 1080, 1160, 1240, 1320, -}; - -void axp_set_charge_current(int current_mA) -{ - /* find greatest charging current not exceeding requested current */ - unsigned int index = 0; - while(index < ARRAYLEN(chargecurrent_tbl)-1 && - chargecurrent_tbl[index+1] <= current_mA) - ++index; - - axp_modify(AXP_REG_CHGCTL1, BM_AXP_CHGCTL1_CHARGE_CURRENT, - index << BP_AXP_CHGCTL1_CHARGE_CURRENT); -} - -int axp_get_charge_current(void) -{ - int value = axp_read(AXP_REG_CHGCTL1); - if(value < 0) - value = 0; - - value &= BM_AXP_CHGCTL1_CHARGE_CURRENT; - value >>= BP_AXP_CHGCTL1_CHARGE_CURRENT; - return chargecurrent_tbl[value]; -} - -void axp_set_vbus_limit(int mode) -{ - const int mask = BM_AXP_VBUSIPSOUT_VHOLD_LIM | - BM_AXP_VBUSIPSOUT_VBUS_LIM | - BM_AXP_VBUSIPSOUT_LIM_100mA; - - axp_modify(AXP_REG_VBUSIPSOUT, mask, mode); -} - -void axp_set_vhold_level(int vhold_mV) -{ - if(vhold_mV < 4000) - vhold_mV = 4000; - else if(vhold_mV > 4700) - vhold_mV = 4700; - - int level = (vhold_mV - 4000) / 100; - axp_modify(AXP_REG_VBUSIPSOUT, BM_AXP_VBUSIPSOUT_VHOLD_LEV, - level << BP_AXP_VBUSIPSOUT_VHOLD_LEV); -} - -bool axp_is_charging(void) -{ - int value = axp_read(AXP_REG_CHGSTS); - return (value >= 0) && (value & BM_AXP_CHGSTS_CHARGING); -} - -unsigned int axp_power_input_status(void) -{ - unsigned int state = 0; - int value = axp_read(AXP_REG_PWRSTS); - if(value >= 0) { - /* ACIN is the main charger. Includes USB */ - if(value & BM_AXP_PWRSTS_ACIN_VALID) - state |= POWER_INPUT_MAIN_CHARGER; - - /* Report USB separately if discernable from ACIN */ - if((value & BM_AXP_PWRSTS_VBUS_VALID) && - !(value & BM_AXP_PWRSTS_PCB_SHORTED)) - state |= POWER_INPUT_USB_CHARGER; - } - -#ifdef HAVE_BATTERY_SWITCH - /* If target allows switching batteries then report if the - * battery is present or not */ - value = axp_read(AXP_REG_CHGSTS); - if(value >= 0 && (value & BM_AXP_CHGSTS_BATT_PRESENT)) - state |= POWER_INPUT_BATTERY; -#endif - - return state; -} - -/* - * Misc. functions - */ - -void axp_power_off(void) -{ - axp_modify(AXP_REG_PWROFF, BM_AXP_PWROFF_SHUTDOWN, BM_AXP_PWROFF_SHUTDOWN); -} - -/* - * Debug menu - */ - -#ifndef BOOTLOADER -#include "action.h" -#include "list.h" -#include "splash.h" -#include - -/* enable extra debug menus which are only useful for development, - * allow potentially dangerous operations and increase code size - * significantly */ -/*#define AXP_EXTRA_DEBUG*/ - -enum { - MODE_ADC, -#ifdef AXP_EXTRA_DEBUG - MODE_SUPPLY, - MODE_REGISTER, -#endif - NUM_MODES, -}; - -static const char* const axp_modenames[NUM_MODES] = { - [MODE_ADC] = "ADCs", -#ifdef AXP_EXTRA_DEBUG - [MODE_SUPPLY] = "Power supplies", - [MODE_REGISTER] = "Register viewer", -#endif -}; - -struct axp_adcdebuginfo { - const char* name; - const char* unit; -}; - -static const struct axp_adcdebuginfo adc_debuginfo[AXP_NUM_ADCS] = { - [AXP_ADC_ACIN_VOLTAGE] = {"V_acin", "mV"}, - [AXP_ADC_ACIN_CURRENT] = {"I_acin", "mA"}, - [AXP_ADC_VBUS_VOLTAGE] = {"V_vbus", "mV"}, - [AXP_ADC_VBUS_CURRENT] = {"I_vbus", "mA"}, - [AXP_ADC_INTERNAL_TEMP] = {"T_int", "C"}, - [AXP_ADC_TS_INPUT] = {"V_ts", "mV"}, - [AXP_ADC_GPIO0] = {"V_gpio0", "mV"}, - [AXP_ADC_GPIO1] = {"V_gpio1", "mV"}, - [AXP_ADC_GPIO2] = {"V_gpio2", "mV"}, - [AXP_ADC_GPIO3] = {"V_gpio3", "mV"}, - [AXP_ADC_BATTERY_VOLTAGE] = {"V_batt", "mV"}, - [AXP_ADC_CHARGE_CURRENT] = {"I_chrg", "mA"}, - [AXP_ADC_DISCHARGE_CURRENT] = {"I_dchg", "mA"}, - [AXP_ADC_APS_VOLTAGE] = {"V_aps", "mV"}, -}; - -#ifdef AXP_EXTRA_DEBUG -static const char* supply_names[AXP_NUM_SUPPLIES] = { - [AXP_SUPPLY_EXTEN] = "EXTEN", - [AXP_SUPPLY_DCDC1] = "DCDC1", - [AXP_SUPPLY_DCDC2] = "DCDC2", - [AXP_SUPPLY_DCDC3] = "DCDC3", - [AXP_SUPPLY_LDO2] = "LDO2", - [AXP_SUPPLY_LDO3] = "LDO3", - [AXP_SUPPLY_LDOIO0] = "LDOIO0", -}; - -struct axp_fieldinfo { - uint8_t rnum; - uint8_t msb: 4; - uint8_t lsb: 4; -}; - -enum { -#define DEFREG(name, ...) AXP_RNUM_##name, -#include "axp192-defs.h" - AXP_NUM_REGS, -}; - -enum { -#define DEFFLD(regname, fldname, ...) AXP_FNUM_##regname##_##fldname, -#include "axp192-defs.h" - AXP_NUM_FIELDS, -}; - -static const uint8_t axp_regaddr[AXP_NUM_REGS] = { -#define DEFREG(name, addr) addr, -#include "axp192-defs.h" -}; - -static const struct axp_fieldinfo axp_fieldinfo[AXP_NUM_FIELDS] = { -#define DEFFLD(regname, fldname, _msb, _lsb, ...) \ - {.rnum = AXP_RNUM_##regname, .msb = _msb, .lsb = _lsb}, -#include "axp192-defs.h" -}; - -static const char* const axp_regnames[AXP_NUM_REGS] = { -#define DEFREG(name, ...) #name, -#include "axp192-defs.h" -}; - -static const char* const axp_fldnames[AXP_NUM_FIELDS] = { -#define DEFFLD(regname, fldname, ...) #fldname, -#include "axp192-defs.h" -}; -#endif /* AXP_EXTRA_DEBUG */ - -struct axp_debug_menu_state { - int mode; -#ifdef AXP_EXTRA_DEBUG - int reg_num; - int field_num; - int field_cnt; - uint8_t cache[AXP_NUM_REGS]; - uint8_t is_cached[AXP_NUM_REGS]; -#endif -}; - -#ifdef AXP_EXTRA_DEBUG -static void axp_debug_clear_cache(struct axp_debug_menu_state* state) -{ - memset(state->is_cached, 0, sizeof(state->is_cached)); -} - -static int axp_debug_get_rnum(uint8_t addr) -{ - for(int i = 0; i < AXP_NUM_REGS; ++i) - if(axp_regaddr[i] == addr) - return i; - - return -1; -} - -static uint8_t axp_debug_read(struct axp_debug_menu_state* state, int rnum) -{ - if(state->is_cached[rnum]) - return state->cache[rnum]; - - int value = axp_read(axp_regaddr[rnum]); - if(value < 0) - return 0; - - state->is_cached[rnum] = 1; - state->cache[rnum] = value; - return value; -} - -static void axp_debug_get_sel(const struct axp_debug_menu_state* state, - int item, int* rnum, int* fnum) -{ - if(state->reg_num >= 0 && state->field_num >= 0) { - int i = item - state->reg_num; - if(i <= 0) { - /* preceding register is selected */ - } else if(i <= state->field_cnt) { - /* field is selected */ - *rnum = state->reg_num; - *fnum = i + state->field_num - 1; - return; - } else { - /* subsequent regiser is selected */ - item -= state->field_cnt; - } - } - - /* register is selected */ - *rnum = item; - *fnum = -1; -} - -static int axp_debug_set_sel(struct axp_debug_menu_state* state, int rnum) -{ - state->reg_num = rnum; - state->field_num = -1; - state->field_cnt = 0; - - for(int i = 0; i < AXP_NUM_FIELDS; ++i) { - if(axp_fieldinfo[i].rnum != rnum) - continue; - - state->field_num = i; - do { - state->field_cnt++; - i++; - } while(axp_fieldinfo[i].rnum == rnum); - break; - } - - return rnum; -} -#endif /* AXP_EXTRA_DEBUG */ - -static const char* axp_debug_menu_get_name(int item, void* data, - char* buf, size_t buflen) -{ - struct axp_debug_menu_state* state = data; - int value; - - /* for safety */ - buf[0] = '\0'; - - if(state->mode == MODE_ADC && item < AXP_NUM_ADCS) - { - const struct axp_adcdebuginfo* info = &adc_debuginfo[item]; - value = axp_read_adc(item); - if(item == AXP_ADC_INTERNAL_TEMP) { - snprintf(buf, buflen, "%s: %d.%d %s", - info->name, value/10, value%10, info->unit); - } else { - snprintf(buf, buflen, "%s: %d %s", info->name, value, info->unit); - } - } -#ifdef AXP_EXTRA_DEBUG - else if(state->mode == MODE_SUPPLY && item < AXP_NUM_SUPPLIES) - { - const struct axp_supplydata* data = &supplydata[item]; - int en_rnum = axp_debug_get_rnum(data->en_reg); - int volt_rnum = axp_debug_get_rnum(data->volt_reg); - bool enabled = false; - int voltage = -1; - - if(en_rnum >= 0) { - value = axp_debug_read(state, en_rnum); - if(value & data->en_bit) - enabled = true; - else - enabled = false; - } else if(item == AXP_SUPPLY_LDOIO0) { - value = axp_debug_read(state, AXP_RNUM_GPIO0FUNC); - if((value & 0x7) == AXP_GPIO_SPECIAL) - enabled = true; - else - enabled = false; - } - - if(volt_rnum >= 0) { - voltage = axp_debug_read(state, volt_rnum); - voltage >>= data->volt_lsb; - voltage &= (1 << (data->volt_msb - data->volt_lsb + 1)) - 1; - - /* convert to mV */ - voltage = data->min_mV + voltage * data->step_mV; - } - - if(enabled && voltage >= 0) { - snprintf(buf, buflen, "%s: %d mV", - supply_names[item], voltage); - } else { - snprintf(buf, buflen, "%s: %sabled", - supply_names[item], enabled ? "en" : "dis"); - } - } - else if(state->mode == MODE_REGISTER) - { - int rnum, fnum; - axp_debug_get_sel(state, item, &rnum, &fnum); - - if(fnum >= 0) { - const struct axp_fieldinfo* info = &axp_fieldinfo[fnum]; - value = axp_debug_read(state, info->rnum); - value >>= info->lsb; - value &= (1 << (info->msb - info->lsb + 1)) - 1; - snprintf(buf, buflen, "\t%s: %d (0x%x)", - axp_fldnames[fnum], value, value); - } else if(rnum < AXP_NUM_REGS) { - value = axp_debug_read(state, rnum); - snprintf(buf, buflen, "%s: 0x%02x", axp_regnames[rnum], value); - } - } -#endif /* AXP_EXTRA_DEBUG */ - - return buf; -} - -static int axp_debug_menu_cb(int action, struct gui_synclist* lists) -{ - struct axp_debug_menu_state* state = lists->data; - - if(state->mode == MODE_ADC) - { - /* update continuously */ - if(action == ACTION_NONE) - action = ACTION_REDRAW; - } -#ifdef AXP_EXTRA_DEBUG - else if(state->mode == MODE_REGISTER) - { - if(action == ACTION_STD_OK) { - /* expand a register to show its fields */ - int rnum, fnum; - int sel_pos = gui_synclist_get_sel_pos(lists); - axp_debug_get_sel(state, sel_pos, &rnum, &fnum); - if(fnum < 0 && rnum < AXP_NUM_REGS) { - int delta_items = -state->field_cnt; - if(rnum != state->reg_num) { - if(rnum > state->reg_num) - sel_pos += delta_items; - - axp_debug_set_sel(state, rnum); - delta_items += state->field_cnt; - } else { - state->reg_num = -1; - state->field_num = -1; - state->field_cnt = 0; - } - - gui_synclist_set_nb_items(lists, lists->nb_items + delta_items); - gui_synclist_select_item(lists, sel_pos); - action = ACTION_REDRAW; - } - } - } - else if(state->mode == MODE_SUPPLY) - { - /* disable a supply... use with caution */ - if(action == ACTION_STD_CONTEXT) { - int sel_pos = gui_synclist_get_sel_pos(lists); - axp_enable_supply(sel_pos, false); - } - } -#endif - -#ifdef AXP_EXTRA_DEBUG - /* clear register cache to refresh values */ - if(state->mode != MODE_ADC && action == ACTION_STD_CONTEXT) { - splashf(HZ/2, "Refreshed"); - axp_debug_clear_cache(state); - action = ACTION_REDRAW; - } -#endif - - /* mode switching */ - if(action == ACTION_STD_MENU) { - state->mode = (state->mode + 1) % NUM_MODES; - gui_synclist_set_title(lists, (char*)axp_modenames[state->mode], Icon_NOICON); - action = ACTION_REDRAW; - - switch(state->mode) { - case MODE_ADC: - gui_synclist_set_nb_items(lists, AXP_NUM_ADCS); - gui_synclist_select_item(lists, 0); - break; - -#ifdef AXP_EXTRA_DEBUG - case MODE_SUPPLY: - axp_debug_clear_cache(state); - gui_synclist_set_nb_items(lists, AXP_NUM_SUPPLIES); - gui_synclist_select_item(lists, 0); - break; - - case MODE_REGISTER: - state->reg_num = -1; - state->field_num = -1; - state->field_cnt = 0; - axp_debug_clear_cache(state); - gui_synclist_set_nb_items(lists, AXP_NUM_REGS); - gui_synclist_select_item(lists, 0); - break; -#endif - } - } - - return action; -} - -bool axp_debug_menu(void) -{ - struct axp_debug_menu_state state; - state.mode = MODE_ADC; -#ifdef AXP_EXTRA_DEBUG - state.reg_num = -1; - state.field_num = -1; - state.field_cnt = 0; - axp_debug_clear_cache(&state); -#endif - - struct simplelist_info info; - simplelist_info_init(&info, (char*)axp_modenames[state.mode], - AXP_NUM_ADCS, &state); - info.get_name = axp_debug_menu_get_name; - info.action_callback = axp_debug_menu_cb; - return simplelist_show_list(&info); -} -#endif diff --git a/firmware/export/axp-pmu.h b/firmware/export/axp-pmu.h new file mode 100644 index 0000000000..457f746e8c --- /dev/null +++ b/firmware/export/axp-pmu.h @@ -0,0 +1,151 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021 Aidan MacDonald + * + * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#ifndef __AXP_PMU_H__ +#define __AXP_PMU_H__ + +#include "config.h" +#include +#include + +/* ADC channels */ +#define ADC_ACIN_VOLTAGE 0 +#define ADC_ACIN_CURRENT 1 +#define ADC_VBUS_VOLTAGE 2 +#define ADC_VBUS_CURRENT 3 +#define ADC_INTERNAL_TEMP 4 +#define ADC_TS_INPUT 5 +#define ADC_BATTERY_VOLTAGE 6 +#define ADC_CHARGE_CURRENT 7 +#define ADC_DISCHARGE_CURRENT 8 +#define ADC_APS_VOLTAGE 9 +#define ADC_BATTERY_POWER 10 +#define NUM_ADC_CHANNELS 11 + +/* ADC sampling rates */ +#define AXP_ADC_RATE_25HZ 0 +#define AXP_ADC_RATE_50HZ 1 +#define AXP_ADC_RATE_100HZ 2 +#define AXP_ADC_RATE_200HZ 3 + +/* Return values of axp_battery_status() */ +#define AXP_BATT_DISCHARGING 0 +#define AXP_BATT_CHARGING 1 +#define AXP_BATT_FULL 2 + +/* Bits returned by axp_input_status() */ +#define AXP_INPUT_AC (1 << 0) +#define AXP_INPUT_USB (1 << 1) +#define AXP_INPUT_BATTERY (1 << 2) +#define AXP_INPUT_EXTERNAL (AXP_INPUT_AC|AXP_INPUT_USB) + +/* Power supplies known by this driver. Not every chip has all supplies! */ +#define AXP_SUPPLY_DCDC1 0 +#define AXP_SUPPLY_DCDC2 1 +#define AXP_SUPPLY_DCDC3 2 +#define AXP_SUPPLY_LDO1 3 +#define AXP_SUPPLY_LDO2 4 +#define AXP_SUPPLY_LDO3 5 +#define AXP_SUPPLY_LDO_IO0 6 +#define AXP_NUM_SUPPLIES 7 + +/* Special values returned by axp_supply_get_voltage */ +#define AXP_SUPPLY_NOT_PRESENT INT_MIN +#define AXP_SUPPLY_DISABLED (-1) + +/* Registers -- common to AXP173 and AXP192 (incomplete listing) */ +#define AXP_REG_POWERSTATUS 0x00 +#define AXP_REG_CHARGESTATUS 0x01 +#define AXP_REG_CHIP_ID 0x03 +#define AXP_REG_PWROUTPUTCTRL1 0x10 +#define AXP_REG_PWROUTPUTCTRL2 0x12 +#define AXP_REG_SHUTDOWNLEDCTRL 0x32 +#define AXP_REG_CHARGECONTROL1 0x33 +#define AXP_REG_DCDCWORKINGMODE 0x80 +#define AXP_REG_ADCENABLE1 0x82 +#define AXP_REG_ADCENABLE2 0x83 +#define AXP_REG_ADCSAMPLERATE 0x84 +#define AXP_REG_COULOMBCOUNTERBASE 0xb0 +#define AXP_REG_COULOMBCOUNTERCTRL 0xb8 + +/* AXP192-only registers (incomplete listing) */ +#define AXP192_REG_GPIO0FUNCTION 0x90 +#define AXP192_REG_GPIO1FUNCTION 0x92 +#define AXP192_REG_GPIO2FUNCTION 0x93 +#define AXP192_REG_GPIOSTATE1 0x94 + +/* Must be called from power_init() to initialize the driver state */ +extern void axp_init(void); + +/* - axp_supply_set_voltage(): set a supply voltage to the given value + * in millivolts. Pass a voltage of AXP_SUPPLY_DISABLED to shut off + * the supply. Any invalid supply or voltage will make the call a no-op. + * + * - axp_supply_get_voltage() returns a supply voltage in millivolts. + * If the supply is powered off, returns AXP_SUPPLY_DISABLED. + * If the chip does not have the supply, returns AXP_SUPPLY_NOT_PRESENT. + */ +extern void axp_supply_set_voltage(int supply, int voltage); +extern int axp_supply_get_voltage(int supply); + +/* Basic battery and power supply status */ +extern int axp_battery_status(void); +extern int axp_input_status(void); + +/* ADC access -- ADCs which are not enabled will return INT_MIN if read. + * The output of axp_adc_read() is normalized to appropriate units: + * + * - for voltages, the scale is millivolts + * - for currents, the scale is milliamps + * - for temperatures, the scale is tenths of a degree Celsius + * - for power, the scale is microwatts + * + * See the comment in axp_adc_conv_raw() for raw value precision/scale. + */ +extern int axp_adc_read(int adc); +extern int axp_adc_read_raw(int adc); +extern int axp_adc_conv_raw(int adc, int value); +extern int axp_adc_get_enabled(void); +extern void axp_adc_set_enabled(int adc_bits); +extern int axp_adc_get_rate(void); +extern void axp_adc_set_rate(int rate); + +/* - axp_cc_read() reads the coulomb counters + * - axp_cc_clear() resets both counters to zero + * - axp_cc_enable() will stop/start the counters running + * - axp_cc_is_enabled() returns true if the counters are running + */ +extern void axp_cc_read(uint32_t* charge, uint32_t* discharge); +extern void axp_cc_clear(void); +extern void axp_cc_enable(bool en); +extern bool axp_cc_is_enabled(void); + +/* Set/get maximum charging current in milliamps */ +extern void axp_set_charge_current(int maxcurrent); +extern int axp_get_charge_current(void); + +/* Set the shutdown bit */ +extern void axp_power_off(void); + +/* Debug menu */ +extern bool axp_debug_menu(void); + +#endif /* __AXP_PMU_H__ */ diff --git a/firmware/export/axp192-defs.h b/firmware/export/axp192-defs.h deleted file mode 100644 index 13b465351b..0000000000 --- a/firmware/export/axp192-defs.h +++ /dev/null @@ -1,308 +0,0 @@ -/* Internal header for axp192 driver - not for general inclusion */ - -#ifndef DEFREG -# define DEFREG(...) -#endif -#ifndef DEFFLD -# define DEFFLD(...) -#endif - -#define DEFBIT(regname, fldname, bitpos, ...) \ - DEFFLD(regname, fldname, bitpos, bitpos, __VA_ARGS__) - -DEFREG(PWRSTS, 0x00) -DEFREG(CHGSTS, 0x01) -DEFREG(CHIPID, 0x03) -DEFREG(VBUSSTS, 0x04) -DEFREG(DATA0, 0x06) -DEFREG(DATA1, 0x07) -DEFREG(DATA2, 0x08) -DEFREG(DATA3, 0x09) -DEFREG(DATA4, 0x0a) -DEFREG(DATA5, 0x0b) -DEFREG(PWRCTL1, 0x10) -DEFREG(PWRCTL2, 0x12) -DEFREG(DCDC2VOLT, 0x23) -DEFREG(DCDC2RAMP, 0x25) -DEFREG(DCDC1VOLT, 0x26) -DEFREG(DCDC3VOLT, 0x27) -DEFREG(LDO2LDO3VOLT, 0x28) -DEFREG(VBUSIPSOUT, 0x30) -DEFREG(VOFF, 0x31) -DEFREG(PWROFF, 0x32) -DEFREG(CHGCTL1, 0x33) -DEFREG(CHGCTL2, 0x34) -DEFREG(BKPCHGCTL, 0x35) -DEFREG(PEKPARAM, 0x36) -DEFREG(DCDCFREQ, 0x37) -DEFREG(VLTFCHG, 0x38) -DEFREG(VHTFCHG, 0x39) -DEFREG(APSLOW1, 0x3a) -DEFREG(APSLOW2, 0x3b) -DEFREG(VLTFDCHG, 0x3c) -DEFREG(VHTFDCHG, 0x3d) -DEFREG(IRQEN1, 0x40) -DEFREG(IRQEN2, 0x41) -DEFREG(IRQEN3, 0x42) -DEFREG(IRQEN4, 0x43) -DEFREG(IRQSTS1, 0x44) -DEFREG(IRQSTS2, 0x45) -DEFREG(IRQSTS3, 0x46) -DEFREG(IRQSTS4, 0x47) -DEFREG(IRQEN5, 0x4a) -DEFREG(IRQSTS5, 0x4d) -DEFREG(DCDCMODE, 0x80) -DEFREG(ADCEN1, 0x82) -DEFREG(ADCEN2, 0x83) -DEFREG(ADCCTL, 0x84) -DEFREG(ADCRANGE, 0x85) -DEFREG(TIMERCTL, 0x8a) -DEFREG(VBUSSRP, 0x8b) -DEFREG(OTPOWEROFF, 0x8f) -DEFREG(GPIO0FUNC, 0x90) -DEFREG(GPIO0LDO, 0x91) -DEFREG(GPIO1FUNC, 0x92) -DEFREG(GPIO2FUNC, 0x93) -DEFREG(GPIOLEVEL1, 0x94) -DEFREG(GPIO3GPIO4FUNC, 0x95) -DEFREG(GPIOLEVEL2, 0x96) -DEFREG(GPIOPULL, 0x97) -DEFREG(PWM1X, 0x98) -DEFREG(PWM1Y1, 0x99) -DEFREG(PWM1Y2, 0x9a) -DEFREG(PWM2X, 0x9b) -DEFREG(PWM2Y1, 0x9c) -DEFREG(PWM2Y2, 0x9d) -DEFREG(NRSTO, 0x9e) -DEFREG(CC_CTL, 0xb8) - -DEFBIT(PWRSTS, ACIN_PRESENT, 7) -DEFBIT(PWRSTS, ACIN_VALID, 6) -DEFBIT(PWRSTS, VBUS_PRESENT, 5) -DEFBIT(PWRSTS, VBUS_VALID, 4) -DEFBIT(PWRSTS, VBUS_VHOLD, 3) -DEFBIT(PWRSTS, BATT_CURR_DIR, 2) -DEFBIT(PWRSTS, PCB_SHORTED, 1) -DEFBIT(PWRSTS, BOOT_TRIG, 0) - -DEFBIT(VBUSSTS, VALID, 2) -DEFBIT(VBUSSTS, SESS_AB_VALID, 1) -DEFBIT(VBUSSTS, SESS_END, 0) - -DEFBIT(CHGSTS, OVER_TEMP, 7) -DEFBIT(CHGSTS, CHARGING, 6) -DEFBIT(CHGSTS, BATT_PRESENT, 5) -DEFBIT(CHGSTS, BATT_ERROR, 3) -DEFBIT(CHGSTS, LOW_CHARGE, 2) - -/* NOTE: These two bits are mirrored in the upper nibble of PWRCTL2. - * Modifications through one register will immediately reflect in the - * other register. */ -DEFBIT(PWRCTL1, EXTEN_SW, 2) -DEFBIT(PWRCTL1, DCDC2_SW, 0) - -DEFBIT(PWRCTL2, EXTEN_SW, 6) -DEFBIT(PWRCTL2, DCDC2_SW, 4) -DEFBIT(PWRCTL2, LDO3_SW, 3) -DEFBIT(PWRCTL2, LDO2_SW, 2) -DEFBIT(PWRCTL2, DCDC3_SW, 1) -DEFBIT(PWRCTL2, DCDC1_SW, 0) - -DEFFLD(DCDC2VOLT, VALUE, 5, 0) - -DEFBIT(DCDC2RAMP, ENABLE, 2) -DEFBIT(DCDC2RAMP, SLOPE, 0) - -DEFFLD(DCDC1VOLT, VALUE, 6, 0) -DEFFLD(DCDC3VOLT, VALUE, 6, 0) - -DEFFLD(LDO2LDO3VOLT, LDO2_VALUE, 7, 4) -DEFFLD(LDO2LDO3VOLT, LDO3_VALUE, 3, 0) - -DEFBIT(VBUSIPSOUT, ACCESS, 7) -DEFBIT(VBUSIPSOUT, VHOLD_LIM, 6) -DEFFLD(VBUSIPSOUT, VHOLD_LEV, 5, 3) -DEFBIT(VBUSIPSOUT, VBUS_LIM, 1) -DEFBIT(VBUSIPSOUT, LIM_100mA, 0) - -DEFFLD(VOFF, VALUE, 3, 0) - -DEFBIT(PWROFF, SHUTDOWN, 7) -DEFBIT(PWROFF, MON_EN, 6) -DEFFLD(PWROFF, LEDFUNC, 5, 4) -DEFBIT(PWROFF, LEDCTL, 3) -DEFBIT(PWROFF, DELAY, 1, 0) - -DEFBIT(CHGCTL1, CHARGE_EN, 7) -DEFFLD(CHGCTL1, CHARGE_TGT, 6, 5) -DEFBIT(CHGCTL1, CHARGE_ENDCURR, 4) -DEFFLD(CHGCTL1, CHARGE_CURRENT, 3, 0) - -DEFFLD(CHGCTL2, PRECHARGE_OT, 7, 6) -DEFFLD(CHGCTL2, EACCESS_CURRENT, 5, 3) -DEFBIT(CHGCTL2, EACCESS_CHG_EN, 2) -DEFFLD(CHGCTL2, CONST_CURR_OT, 1, 0) - -DEFBIT(BKPCHGCTL, ENABLE, 7) -DEFFLD(BKPCHGCTL, TGT_VOLTAGE, 6, 5) -DEFFLD(BKPCHGCTL, CHARGE_CURRENT, 1, 0) - -DEFFLD(PEKPARAM, POWER_ON_TIME, 7, 6) -DEFFLD(PEKPARAM, LONG_TIME, 5, 4) -DEFBIT(PEKPARAM, POWEROFF_EN, 3) -DEFBIT(PEKPARAM, PWROK_DELAY, 2) -DEFFLD(PEKPARAM, POWEROFF_TIME, 1, 0) - -DEFFLD(DCDCFREQ, VALUE, 3, 0) -DEFFLD(VLTFCHG, VALUE, 7, 0) -DEFFLD(VHTFCHG, VALUE, 7, 0) -DEFFLD(APSLOW1, VALUE, 7, 0) -DEFFLD(APSLOW2, VALUE, 7, 0) -DEFFLD(VLTFDCHG, VALUE, 7, 0) -DEFFLD(VHTFDCHG, VALUE, 7, 0) - -DEFBIT(IRQEN1, ACIN_OVER_VOLTAGE, 7) -DEFBIT(IRQEN1, ACIN_INSERT, 6) -DEFBIT(IRQEN1, ACIN_REMOVE, 5) -DEFBIT(IRQEN1, VBUS_OVER_VOLTAGE, 4) -DEFBIT(IRQEN1, VBUS_INSERT, 3) -DEFBIT(IRQEN1, VBUS_REMOVE, 2) -DEFBIT(IRQEN1, VBUS_BELOW_VHOLD, 1) -DEFBIT(IRQEN2, BATTERY_INSERT, 7) -DEFBIT(IRQEN2, BATTERY_REMOVE, 6) -DEFBIT(IRQEN2, BATTERY_ERROR, 5) -DEFBIT(IRQEN2, BATTERY_ERROR_CLR, 4) -DEFBIT(IRQEN2, CHARGING_STARTED, 3) -DEFBIT(IRQEN2, CHARGING_COMPLETE, 2) -DEFBIT(IRQEN2, BATTERY_OVER_TEMP, 1) -DEFBIT(IRQEN2, BATTERY_UNDER_TEMP, 0) -DEFBIT(IRQEN3, INTERNAL_OVER_TEMP, 7) -DEFBIT(IRQEN3, LOW_CHARGE_CURRENT, 6) -DEFBIT(IRQEN3, DCDC1_UNDER_VOLT, 5) -DEFBIT(IRQEN3, DCDC2_UNDER_VOLT, 4) -DEFBIT(IRQEN3, DCDC3_UNDER_VOLT, 3) -DEFBIT(IRQEN3, SHORT_PRESS, 1) -DEFBIT(IRQEN3, LONG_PRESS, 0) -DEFBIT(IRQEN4, POWER_ON_N_OE, 7) -DEFBIT(IRQEN4, POWER_OFF_N_OE, 6) -DEFBIT(IRQEN4, VBUS_VALID, 5) -DEFBIT(IRQEN4, VBUS_INVALID, 4) -DEFBIT(IRQEN4, VBUS_SESS_AB, 3) -DEFBIT(IRQEN4, VBUS_SESS_END, 2) -DEFBIT(IRQEN4, APS_UNDER_VOLT, 0) - -DEFBIT(IRQSTS1, ACIN_OVER_VOLTAGE, 7) -DEFBIT(IRQSTS1, ACIN_INSERT, 6) -DEFBIT(IRQSTS1, ACIN_REMOVE, 5) -DEFBIT(IRQSTS1, VBUS_OVER_VOLTAGE, 4) -DEFBIT(IRQSTS1, VBUS_INSERT, 3) -DEFBIT(IRQSTS1, VBUS_REMOVE, 2) -DEFBIT(IRQSTS1, VBUS_BELOW_VHOLD, 1) -DEFBIT(IRQSTS2, BATTERY_INSERT, 7) -DEFBIT(IRQSTS2, BATTERY_REMOVE, 6) -DEFBIT(IRQSTS2, BATTERY_ERROR, 5) -DEFBIT(IRQSTS2, BATTERY_ERROR_CLR, 4) -DEFBIT(IRQSTS2, CHARGING_STARTED, 3) -DEFBIT(IRQSTS2, CHARGING_STOPPED, 2) -DEFBIT(IRQSTS2, BATTERY_OVER_TEMP, 1) -DEFBIT(IRQSTS2, BATTERY_UNDER_TEMP, 0) -DEFBIT(IRQSTS3, INTERNAL_OVER_TEMP, 7) -DEFBIT(IRQSTS3, LOW_CHARGE_CURRENT, 6) -DEFBIT(IRQSTS3, DCDC1_UNDER_VOLT, 5) -DEFBIT(IRQSTS3, DCDC2_UNDER_VOLT, 4) -DEFBIT(IRQSTS3, DCDC3_UNDER_VOLT, 3) -DEFBIT(IRQSTS3, SHORT_PRESS, 1) -DEFBIT(IRQSTS3, LONG_PRESS, 0) -DEFBIT(IRQSTS4, POWER_ON_N_OE, 7) -DEFBIT(IRQSTS4, POWER_OFF_N_OE, 6) -DEFBIT(IRQSTS4, VBUS_VALID, 5) -DEFBIT(IRQSTS4, VBUS_INVALID, 4) -DEFBIT(IRQSTS4, VBUS_SESS_AB, 3) -DEFBIT(IRQSTS4, VBUS_SESS_END, 2) -DEFBIT(IRQSTS4, APS_UNDER_VOLT, 0) - -/* NOTE: IRQEN5 and IRQSTS5 are only listed on the Chinese datasheet. */ -DEFBIT(IRQEN5, TIME_OUT, 7) -DEFBIT(IRQEN5, GPIO2_CHANGE, 2) -DEFBIT(IRQEN5, GPIO1_CHANGE, 1) -DEFBIT(IRQEN5, GPIO0_CHANGE, 0) - -DEFBIT(IRQSTS5, TIME_OUT, 7) -DEFBIT(IRQSTS5, GPIO2_CHANGE, 2) -DEFBIT(IRQSTS5, GPIO1_CHANGE, 1) -DEFBIT(IRQSTS5, GPIO0_CHANGE, 0) - -DEFFLD(DCDCMODE, VALUE, 3, 1) - -DEFBIT(ADCEN1, BATTERY_VOLTAGE, 7) -DEFBIT(ADCEN1, BATTERY_CURRENT, 6) -DEFBIT(ADCEN1, ACIN_VOLTAGE, 5) -DEFBIT(ADCEN1, ACIN_CURRENT, 4) -DEFBIT(ADCEN1, VBUS_VOLTAGE, 3) -DEFBIT(ADCEN1, VBUS_CURRENT, 2) -DEFBIT(ADCEN1, APS_VOLTAGE, 1) -DEFBIT(ADCEN1, TS_PIN, 0) - -DEFBIT(ADCEN2, INTERNAL_TEMP, 7) -DEFBIT(ADCEN2, GPIO0, 3) -DEFBIT(ADCEN2, GPIO1, 2) -DEFBIT(ADCEN2, GPIO2, 1) -DEFBIT(ADCEN2, GPIO3, 0) - -DEFFLD(ADCCTL, SAMPLE_RATE, 7, 6) -DEFFLD(ADCCTL, TS_OUT_CURR, 5, 4) -DEFBIT(ADCCTL, TS_FUNCTION, 2) -DEFFLD(ADCCTL, TS_OUT_MODE, 1, 0) - -DEFBIT(ADCRANGE, GPIO3HIGH, 3) -DEFBIT(ADCRANGE, GPIO2HIGH, 2) -DEFBIT(ADCRANGE, GPIO1HIGH, 1) -DEFBIT(ADCRANGE, GPIO0HIGH, 0) - -DEFBIT(TIMERCTL, TIMEOUT, 7) -DEFFLD(TIMERCTL, DURATION, 6, 0) - -DEFFLD(VBUSSRP, VBUSVALID_VOLTAGE, 5, 4) -DEFBIT(VBUSSRP, VBUSVALID_MONITOR, 3) -DEFBIT(VBUSSRP, VBUS_SESS_MONITOR, 2) -DEFBIT(VBUSSRP, VBUS_DCHG_RESISTOR, 1) -DEFBIT(VBUSSRP, VBUS_CHG_RESISTOR, 0) - -DEFBIT(OTPOWEROFF, ENABLE, 2) - -DEFFLD(GPIO0FUNC, VALUE, 2, 0) -DEFFLD(GPIO0LDO, VALUE, 7, 4) -DEFFLD(GPIO1FUNC, VALUE, 2, 0) -DEFFLD(GPIO2FUNC, VALUE, 2, 0) - -DEFBIT(GPIOLEVEL1, IN2, 6) -DEFBIT(GPIOLEVEL1, IN1, 5) -DEFBIT(GPIOLEVEL1, IN0, 4) -DEFBIT(GPIOLEVEL1, OUT2, 2) -DEFBIT(GPIOLEVEL1, OUT1, 1) -DEFBIT(GPIOLEVEL1, OUT0, 0) - -DEFFLD(GPIO3GPIO4FUNC, FUNC3, 3, 2) -DEFFLD(GPIO3GPIO4FUNC, FUNC4, 1, 0) - -DEFBIT(GPIOLEVEL2, IN4, 5) -DEFBIT(GPIOLEVEL2, IN3, 4) -DEFBIT(GPIOLEVEL2, OUT4, 1) -DEFBIT(GPIOLEVEL2, OUT3, 0) - -DEFBIT(GPIOPULL, PULL2, 2) -DEFBIT(GPIOPULL, PULL1, 1) -DEFBIT(GPIOPULL, PULL0, 0) - -DEFBIT(NRSTO, FUNC, 7) -DEFBIT(NRSTO, GPIO_DIR, 6) -DEFBIT(NRSTO, GPIO_OUT, 5) -DEFBIT(NRSTO, GPIO_IN, 4) - -DEFBIT(CC_CTL, OPEN, 7) -DEFBIT(CC_CTL, PAUSE, 6) -DEFBIT(CC_CTL, CLEAR, 5) - -#undef DEFBIT -#undef DEFFLD -#undef DEFREG diff --git a/firmware/export/axp192.h b/firmware/export/axp192.h deleted file mode 100644 index 6ed278d086..0000000000 --- a/firmware/export/axp192.h +++ /dev/null @@ -1,131 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2021 Aidan MacDonald - * - * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ****************************************************************************/ - -#ifndef __AXP192_H__ -#define __AXP192_H__ - -#include -#include - -enum { -#define DEFREG(regname, addr) AXP_REG_##regname = addr, -#include "axp192-defs.h" -}; - -enum { -#define DEFFLD(regname, fldname, msb, lsb, ...) \ - BM_AXP_##regname##_##fldname = ((1 << ((msb) - (lsb) + 1)) - 1) << lsb, \ - BP_AXP_##regname##_##fldname = lsb, -#include "axp192-defs.h" -}; - -enum { - AXP_SUPPLY_EXTEN, - AXP_SUPPLY_DCDC1, - AXP_SUPPLY_DCDC2, - AXP_SUPPLY_DCDC3, - AXP_SUPPLY_LDO2, - AXP_SUPPLY_LDO3, - AXP_SUPPLY_LDOIO0, - AXP_NUM_SUPPLIES, -}; - -enum { - AXP_ADC_ACIN_VOLTAGE, - AXP_ADC_ACIN_CURRENT, - AXP_ADC_VBUS_VOLTAGE, - AXP_ADC_VBUS_CURRENT, - AXP_ADC_INTERNAL_TEMP, - AXP_ADC_TS_INPUT, - AXP_ADC_GPIO0, - AXP_ADC_GPIO1, - AXP_ADC_GPIO2, - AXP_ADC_GPIO3, - AXP_ADC_BATTERY_VOLTAGE, - AXP_ADC_CHARGE_CURRENT, - AXP_ADC_DISCHARGE_CURRENT, - AXP_ADC_APS_VOLTAGE, - AXP_NUM_ADCS, -}; - -enum { - AXP_GPIO_OPEN_DRAIN_OUTPUT = 0x0, - AXP_GPIO_INPUT = 0x1, - AXP_GPIO_SPECIAL = 0x2, - AXP_GPIO_ADC_IN = 0x4, - AXP_GPIO_LOW_OUTPUT = 0x5, - AXP_GPIO_FLOATING = 0x7, -}; - -enum { - /* Limit USB current consumption to 100 mA. */ - AXP_VBUS_LIMIT_100mA = (1 << BP_AXP_VBUSIPSOUT_VHOLD_LIM) | - (1 << BP_AXP_VBUSIPSOUT_VBUS_LIM) | - (1 << BP_AXP_VBUSIPSOUT_LIM_100mA), - - /* Limit USB current consumption to 500 mA. */ - AXP_VBUS_LIMIT_500mA = (1 << BP_AXP_VBUSIPSOUT_VHOLD_LIM) | - (1 << BP_AXP_VBUSIPSOUT_VBUS_LIM) | - (0 << BP_AXP_VBUSIPSOUT_LIM_100mA), - - /* No upper bound on USB current, but the current will still - * be reduced to maintain the bus voltage above V_hold. */ - AXP_VBUS_UNLIMITED = (1 << BP_AXP_VBUSIPSOUT_VHOLD_LIM) | - (0 << BP_AXP_VBUSIPSOUT_VBUS_LIM) | - (0 << BP_AXP_VBUSIPSOUT_LIM_100mA), - - /* Unlimited USB current consumption. Voltage is allowed to drop - * below V_hold, which may interfere with normal USB operation. - * This mode is really only useful with AC charging adapters. */ - AXP_VBUS_FULLY_UNLIMITED = (0 << BP_AXP_VBUSIPSOUT_VHOLD_LIM) | - (0 << BP_AXP_VBUSIPSOUT_VBUS_LIM) | - (0 << BP_AXP_VBUSIPSOUT_LIM_100mA), -}; - -extern int axp_read(uint8_t reg); -extern int axp_write(uint8_t reg, uint8_t value); -extern int axp_modify(uint8_t reg, uint8_t clr, uint8_t set); - -extern void axp_enable_supply(int supply, bool enable); -extern void axp_set_enabled_supplies(unsigned int supply_mask); -extern void axp_set_supply_voltage(int supply, int output_mV); - -extern void axp_enable_adc(int adc, bool enable); -extern void axp_set_enabled_adcs(unsigned int adc_mask); -extern int axp_read_adc_raw(int adc); -extern int axp_conv_adc(int adc, int value); -extern int axp_read_adc(int adc); - -extern void axp_set_gpio_function(int gpio, int function); -extern void axp_set_gpio_pulldown(int gpio, bool enable); -extern int axp_get_gpio(int gpio); -extern void axp_set_gpio(int gpio, bool enable); - -extern void axp_set_charge_current(int current_mA); -extern int axp_get_charge_current(void); -extern void axp_set_vbus_limit(int vbus_limit); -extern void axp_set_vhold_level(int vhold_mV); -extern bool axp_is_charging(void); -extern unsigned int axp_power_input_status(void); - -extern void axp_power_off(void); - -#endif /* __AXP192_H__ */ diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/button-erosqnative.c b/firmware/target/mips/ingenic_x1000/erosqnative/button-erosqnative.c index 64041795a3..1583db175a 100644 --- a/firmware/target/mips/ingenic_x1000/erosqnative/button-erosqnative.c +++ b/firmware/target/mips/ingenic_x1000/erosqnative/button-erosqnative.c @@ -24,7 +24,7 @@ #include "backlight.h" #include "powermgmt.h" #include "panic.h" -#include "axp192.h" +#include "axp-pmu.h" #include "gpio-x1000.h" #include "irq-x1000.h" #include "i2c-x1000.h" @@ -89,7 +89,7 @@ static int hp_detect_tmo_cb(struct timeout* tmo) static void hp_detect_init(void) { static struct timeout tmo; - static const uint8_t gpio_reg = AXP_REG_GPIOLEVEL1; + static const uint8_t gpio_reg = AXP192_REG_GPIOSTATE1; static i2c_descriptor desc = { .slave_addr = AXP_PMU_ADDR, .bus_cond = I2C_START | I2C_STOP, @@ -105,11 +105,11 @@ static void hp_detect_init(void) /* Headphone and LO detects are wired to AXP192 GPIOs 0 and 1, * set them to inputs. */ - axp_set_gpio_function(0, AXP_GPIO_INPUT); /* HP detect */ - axp_set_gpio_function(1, AXP_GPIO_INPUT); /* LO detect */ + i2c_reg_write1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP192_REG_GPIO0FUNCTION, 0x01); /* HP detect */ + i2c_reg_write1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP192_REG_GPIO1FUNCTION, 0x01); /* LO detect */ /* Get an initial reading before startup */ - int r = axp_read(gpio_reg); + int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, gpio_reg); if(r >= 0) { hp_detect_reg = r; diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c b/firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c index 9cf64cee01..a1a4d2c2b2 100644 --- a/firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c +++ b/firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c @@ -28,7 +28,7 @@ #ifdef HAVE_USB_CHARGING_ENABLE # include "usb_core.h" #endif -#include "axp192.h" +#include "axp-pmu.h" #include "i2c-x1000.h" const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] = @@ -56,35 +56,27 @@ const unsigned short percent_to_volt_charge[11] = void power_init(void) { - /* Configure I2C bus */ - i2c_x1000_set_freq(AXP_PMU_BUS, I2C_FREQ_400K); + /* Initialize driver */ + i2c_x1000_set_freq(2, I2C_FREQ_400K); + axp_init(); - /* FIXME: Copy paste from M3K. Probably not necessary */ - axp_modify(AXP_REG_DCDCMODE, 0, 0xc0); + /* Set lowest sample rate */ + axp_adc_set_rate(AXP_ADC_RATE_25HZ); - /* Power on required supplies - * TODO: This should be checked, though likely all but EXTEN are needed */ - axp_set_enabled_supplies( - (1 << AXP_SUPPLY_EXTEN) | - (1 << AXP_SUPPLY_DCDC1) | - (1 << AXP_SUPPLY_DCDC2) | - (1 << AXP_SUPPLY_DCDC3) | - (1 << AXP_SUPPLY_LDO2) | - (1 << AXP_SUPPLY_LDO3)); + /* Ensure battery voltage ADC is enabled */ + int bits = axp_adc_get_enabled(); + bits |= (1 << ADC_BATTERY_VOLTAGE); + axp_adc_set_enabled(bits); - /* Enable required ADCs */ - axp_set_enabled_adcs( - (1 << AXP_ADC_BATTERY_VOLTAGE) | - (1 << AXP_ADC_CHARGE_CURRENT) | - (1 << AXP_ADC_DISCHARGE_CURRENT) | - (1 << AXP_ADC_VBUS_VOLTAGE) | - (1 << AXP_ADC_VBUS_CURRENT) | - (1 << AXP_ADC_INTERNAL_TEMP) | - (1 << AXP_ADC_APS_VOLTAGE)); + /* Turn on all power outputs */ + i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR, + AXP_REG_PWROUTPUTCTRL2, 0, 0x5f, NULL); + i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR, + AXP_REG_DCDCWORKINGMODE, 0, 0xc0, NULL); - /* Configure USB charging */ - axp_set_vhold_level(4400); - usb_charging_maxcurrent_change(100); + /* Set the default charging current. This is the same as the + * OF's setting, although it's not strictly within the USB spec. */ + axp_set_charge_current(780); /* Delay to give power outputs time to stabilize. * With the power thread delay, this can apparently go as low as 50, @@ -96,22 +88,7 @@ void power_init(void) #ifdef HAVE_USB_CHARGING_ENABLE void usb_charging_maxcurrent_change(int maxcurrent) { - int vbus_limit; - int charge_current; - - /* Note that the charge current setting is a maximum: it will be - * reduced dynamically by the AXP192 so the combined load is less - * than the set VBUS current limit. */ - if(maxcurrent <= 100) { - vbus_limit = AXP_VBUS_LIMIT_500mA; - charge_current = 100; - } else { - vbus_limit = AXP_VBUS_LIMIT_500mA; - charge_current = 550; - } - - axp_set_vbus_limit(vbus_limit); - axp_set_charge_current(charge_current); + axp_set_charge_current(maxcurrent); } #endif @@ -127,25 +104,20 @@ void power_off(void) bool charging_state(void) { - return axp_is_charging(); -} - -unsigned int power_input_status(void) -{ - return axp_power_input_status(); + return axp_battery_status() == AXP_BATT_CHARGING; } int _battery_voltage(void) { - return axp_read_adc(AXP_ADC_BATTERY_VOLTAGE); + return axp_adc_read(ADC_BATTERY_VOLTAGE); } #if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE int _battery_current(void) { if(charging_state()) - return axp_read_adc(AXP_ADC_CHARGE_CURRENT); + return axp_adc_read(ADC_CHARGE_CURRENT); else - return axp_read_adc(AXP_ADC_DISCHARGE_CURRENT); + return axp_adc_read(ADC_DISCHARGE_CURRENT); } #endif diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c index 0ef7bd2f64..24daf2ef69 100644 --- a/firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c @@ -24,7 +24,7 @@ #include "backlight.h" #include "powermgmt.h" #include "panic.h" -#include "axp192.h" +#include "axp-pmu.h" #include "ft6x06.h" #include "gpio-x1000.h" #include "irq-x1000.h" @@ -393,7 +393,7 @@ static int hp_detect_tmo_cb(struct timeout* tmo) static void hp_detect_init(void) { static struct timeout tmo; - static const uint8_t gpio_reg = AXP_REG_GPIOLEVEL1; + static const uint8_t gpio_reg = AXP192_REG_GPIOSTATE1; static i2c_descriptor desc = { .slave_addr = AXP_PMU_ADDR, .bus_cond = I2C_START | I2C_STOP, @@ -408,10 +408,10 @@ static void hp_detect_init(void) }; /* Headphone detect is wired to AXP192 GPIO: set it to input state */ - axp_set_gpio_function(2, AXP_GPIO_INPUT); + i2c_reg_write1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP192_REG_GPIO2FUNCTION, 0x01); /* Get an initial reading before startup */ - int r = axp_read(gpio_reg); + int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, gpio_reg); if(r >= 0) hp_detect_reg = r; diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c index b20bbd9e8c..2d28ad0975 100644 --- a/firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c @@ -26,7 +26,7 @@ #ifdef HAVE_USB_CHARGING_ENABLE # include "usb_core.h" #endif -#include "axp192.h" +#include "axp-pmu.h" #include "i2c-x1000.h" const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] = @@ -54,33 +54,27 @@ const unsigned short percent_to_volt_charge[11] = void power_init(void) { - /* Configure I2C bus */ - i2c_x1000_set_freq(AXP_PMU_BUS, I2C_FREQ_400K); + /* Initialize driver */ + i2c_x1000_set_freq(2, I2C_FREQ_400K); + axp_init(); - /* Set DCDC1 and DCDC2 to fixed PWM mode to match OF settings. */ - axp_modify(AXP_REG_DCDCMODE, 0, 0x0c); + /* Set lowest sample rate */ + axp_adc_set_rate(AXP_ADC_RATE_25HZ); - /* Power on required supplies */ - axp_set_enabled_supplies( - (1 << AXP_SUPPLY_DCDC1) | /* not sure (3.3 V) */ - (1 << AXP_SUPPLY_DCDC2) | /* not sure (1.4 V) */ - (1 << AXP_SUPPLY_DCDC3) | /* for CPU (1.8 V) */ - (1 << AXP_SUPPLY_LDO2) | /* LCD controller (3.3 V) */ - (1 << AXP_SUPPLY_LDO3)); /* SD bus (3.3 V) */ + /* Ensure battery voltage ADC is enabled */ + int bits = axp_adc_get_enabled(); + bits |= (1 << ADC_BATTERY_VOLTAGE); + axp_adc_set_enabled(bits); - /* Enable required ADCs */ - axp_set_enabled_adcs( - (1 << AXP_ADC_BATTERY_VOLTAGE) | - (1 << AXP_ADC_CHARGE_CURRENT) | - (1 << AXP_ADC_DISCHARGE_CURRENT) | - (1 << AXP_ADC_VBUS_VOLTAGE) | - (1 << AXP_ADC_VBUS_CURRENT) | - (1 << AXP_ADC_INTERNAL_TEMP) | - (1 << AXP_ADC_APS_VOLTAGE)); + /* Turn on all power outputs */ + i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR, + AXP_REG_PWROUTPUTCTRL2, 0, 0x5f, NULL); + i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR, + AXP_REG_DCDCWORKINGMODE, 0, 0xc0, NULL); - /* Configure USB charging */ - axp_set_vhold_level(4400); - usb_charging_maxcurrent_change(100); + /* Set the default charging current. This is the same as the + * OF's setting, although it's not strictly within the USB spec. */ + axp_set_charge_current(780); /* Short delay to give power outputs time to stabilize */ mdelay(200); @@ -89,22 +83,7 @@ void power_init(void) #ifdef HAVE_USB_CHARGING_ENABLE void usb_charging_maxcurrent_change(int maxcurrent) { - int vbus_limit; - int charge_current; - - /* Note that the charge current setting is a maximum: it will be - * reduced dynamically by the AXP192 so the combined load is less - * than the set VBUS current limit. */ - if(maxcurrent <= 100) { - vbus_limit = AXP_VBUS_LIMIT_500mA; - charge_current = 100; - } else { - vbus_limit = AXP_VBUS_LIMIT_500mA; - charge_current = 550; - } - - axp_set_vbus_limit(vbus_limit); - axp_set_charge_current(charge_current); + axp_set_charge_current(maxcurrent); } #endif @@ -120,25 +99,20 @@ void power_off(void) bool charging_state(void) { - return axp_is_charging(); -} - -unsigned int power_input_status(void) -{ - return axp_power_input_status(); + return axp_battery_status() == AXP_BATT_CHARGING; } int _battery_voltage(void) { - return axp_read_adc(AXP_ADC_BATTERY_VOLTAGE); + return axp_adc_read(ADC_BATTERY_VOLTAGE); } #if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE int _battery_current(void) { if(charging_state()) - return axp_read_adc(AXP_ADC_CHARGE_CURRENT); + return axp_adc_read(ADC_CHARGE_CURRENT); else - return axp_read_adc(AXP_ADC_DISCHARGE_CURRENT); + return axp_adc_read(ADC_DISCHARGE_CURRENT); } #endif diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c b/firmware/target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c index 1976dde793..13b0cdd078 100644 --- a/firmware/target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c +++ b/firmware/target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c @@ -23,7 +23,7 @@ #include "button.h" #include "touchscreen.h" #include "ft6x06.h" -#include "axp192.h" +#include "axp-pmu.h" #include "kernel.h" #include "backlight.h" #include "powermgmt.h" @@ -57,7 +57,7 @@ static void hp_detect_init(void) { /* TODO: replace this copy paste cruft with an API in axp-pmu */ static struct timeout tmo; - static const uint8_t gpio_reg = AXP_REG_GPIOLEVEL1; + static const uint8_t gpio_reg = AXP192_REG_GPIOSTATE1; static i2c_descriptor desc = { .slave_addr = AXP_PMU_ADDR, .bus_cond = I2C_START | I2C_STOP, @@ -72,10 +72,10 @@ static void hp_detect_init(void) }; /* Headphone detect is wired to AXP192 GPIO: set it to input state */ - axp_set_gpio_function(1, AXP_GPIO_INPUT); + i2c_reg_write1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP192_REG_GPIO1FUNCTION, 0x01); /* Get an initial reading before startup */ - int r = axp_read(gpio_reg); + int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, gpio_reg); if(r >= 0) hp_detect_reg = r; diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c b/firmware/target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c index 86ee84c37a..75f8031dd9 100644 --- a/firmware/target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c +++ b/firmware/target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c @@ -22,7 +22,7 @@ #include "power.h" #include "adc.h" #include "system.h" -#include "axp192.h" +#include "axp-pmu.h" #ifdef HAVE_CW2015 # include "cw2015.h" #endif @@ -73,34 +73,24 @@ const unsigned short percent_to_volt_charge[11] = void power_init(void) { i2c_x1000_set_freq(AXP_PMU_BUS, I2C_FREQ_400K); + axp_init(); #ifdef HAVE_CW2015 cw2015_init(); #endif - /* Set DCDC2 to 1.2 V to match OF settings. */ - axp_set_supply_voltage(AXP_SUPPLY_DCDC2, 1200); + /* Change supply voltage from the default of 1250 mV to 1200 mV, + * this matches the original firmware's settings. Didn't observe + * any obviously bad behavior at 1250 mV, but better to be safe. */ + axp_supply_set_voltage(AXP_SUPPLY_DCDC2, 1200); - /* Power on required supplies */ - axp_set_enabled_supplies( - (1 << AXP_SUPPLY_DCDC1) | /* SD bus (3.3 V) */ - (1 << AXP_SUPPLY_DCDC2) | /* LCD (1.2 V) */ - (1 << AXP_SUPPLY_DCDC3) | /* CPU (1.8 V) */ - (1 << AXP_SUPPLY_LDO2) | /* Touchscreen (3.3 V) */ - (1 << AXP_SUPPLY_LDO3)); /* USB analog (2.5 V) */ - - /* Enable required ADCs */ - axp_set_enabled_adcs( - (1 << AXP_ADC_BATTERY_VOLTAGE) | - (1 << AXP_ADC_CHARGE_CURRENT) | - (1 << AXP_ADC_DISCHARGE_CURRENT) | - (1 << AXP_ADC_VBUS_VOLTAGE) | - (1 << AXP_ADC_VBUS_CURRENT) | - (1 << AXP_ADC_INTERNAL_TEMP) | - (1 << AXP_ADC_APS_VOLTAGE)); - - /* Configure USB charging */ - axp_set_vhold_level(4400); - usb_charging_maxcurrent_change(100); + /* For now, just turn everything on... definitely the touchscreen + * is powered by one of the outputs */ + i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR, + AXP_REG_PWROUTPUTCTRL1, 0, 0x05, NULL); + i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR, + AXP_REG_PWROUTPUTCTRL2, 0, 0x0f, NULL); + i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR, + AXP_REG_DCDCWORKINGMODE, 0, 0xc0, NULL); /* Delay to give power output time to stabilize */ mdelay(20); @@ -109,22 +99,7 @@ void power_init(void) #ifdef HAVE_USB_CHARGING_ENABLE void usb_charging_maxcurrent_change(int maxcurrent) { - int vbus_limit; - int charge_current; - - /* Note that the charge current setting is a maximum: it will be - * reduced dynamically by the AXP192 so the combined load is less - * than the set VBUS current limit. */ - if(maxcurrent <= 100) { - vbus_limit = AXP_VBUS_LIMIT_500mA; - charge_current = 100; - } else { - vbus_limit = AXP_VBUS_LIMIT_500mA; - charge_current = 550; - } - - axp_set_vbus_limit(vbus_limit); - axp_set_charge_current(charge_current); + axp_set_charge_current(maxcurrent); } #endif @@ -136,28 +111,23 @@ void power_off(void) bool charging_state(void) { - return axp_is_charging(); -} - -unsigned int power_input_status(void) -{ - return axp_power_input_status(); + return axp_battery_status() == AXP_BATT_CHARGING; } int _battery_voltage(void) { /* CW2015 can also read battery voltage, but the AXP consistently * reads ~20-30 mV higher so I suspect it's the "real" voltage. */ - return axp_read_adc(AXP_ADC_BATTERY_VOLTAGE); + return axp_adc_read(ADC_BATTERY_VOLTAGE); } #if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE int _battery_current(void) { if(charging_state()) - return axp_read_adc(AXP_ADC_CHARGE_CURRENT); + return axp_adc_read(ADC_CHARGE_CURRENT); else - return axp_read_adc(AXP_ADC_DISCHARGE_CURRENT); + return axp_adc_read(ADC_DISCHARGE_CURRENT); } #endif