From 2d891439623bb76d38b98202ca5f3eea3c01c5f0 Mon Sep 17 00:00:00 2001 From: Aidan MacDonald Date: Sun, 5 Dec 2021 14:30:03 +0000 Subject: [PATCH] AXP PMU rewrite (again) I noticed a few mistakes in the old driver code and it was in need of an overhaul anyway... I decided to scale things back, simplify the code and remove most of the debug menus, netting a nice code size savings. One new feature is an advanced debug menu which is accessible by recompiling the code with AXP_EXTRA_DEBUG. It adds quite a bit of code size and isn't useful other than for development so it must be manually enabled by editing the source. Change-Id: I30e17c1194c14823decd726a574ed14451d4cb2d --- firmware/SOURCES | 4 +- firmware/drivers/axp-pmu.c | 670 --------------- firmware/drivers/axp192.c | 810 ++++++++++++++++++ firmware/export/axp-pmu.h | 151 ---- firmware/export/axp192-defs.h | 308 +++++++ firmware/export/axp192.h | 131 +++ .../erosqnative/button-erosqnative.c | 10 +- .../erosqnative/power-erosqnative.c | 54 +- .../ingenic_x1000/fiiom3k/button-fiiom3k.c | 8 +- .../ingenic_x1000/fiiom3k/power-fiiom3k.c | 53 +- .../shanlingq1/button-shanlingq1.c | 8 +- .../shanlingq1/power-shanlingq1.c | 47 +- 12 files changed, 1354 insertions(+), 900 deletions(-) delete mode 100644 firmware/drivers/axp-pmu.c create mode 100644 firmware/drivers/axp192.c delete mode 100644 firmware/export/axp-pmu.h create mode 100644 firmware/export/axp192-defs.h create mode 100644 firmware/export/axp192.h diff --git a/firmware/SOURCES b/firmware/SOURCES index 87db67d8fd..8868e4f21f 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -1947,8 +1947,8 @@ drivers/touchpad.c #ifdef HAVE_I2C_ASYNC drivers/i2c-async.c #endif -#ifdef HAVE_AXP_PMU -drivers/axp-pmu.c +#if defined(HAVE_AXP_PMU) && HAVE_AXP_PMU == 192 +drivers/axp192.c #endif #ifdef HAVE_FT6x06 drivers/ft6x06.c diff --git a/firmware/drivers/axp-pmu.c b/firmware/drivers/axp-pmu.c deleted file mode 100644 index fd1126dbbf..0000000000 --- a/firmware/drivers/axp-pmu.c +++ /dev/null @@ -1,670 +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 "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 new file mode 100644 index 0000000000..3c61d8c533 --- /dev/null +++ b/firmware/drivers/axp192.c @@ -0,0 +1,810 @@ +/*************************************************************************** + * __________ __ ___. + * 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 deleted file mode 100644 index 457f746e8c..0000000000 --- a/firmware/export/axp-pmu.h +++ /dev/null @@ -1,151 +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 __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 new file mode 100644 index 0000000000..13b465351b --- /dev/null +++ b/firmware/export/axp192-defs.h @@ -0,0 +1,308 @@ +/* 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 new file mode 100644 index 0000000000..6ed278d086 --- /dev/null +++ b/firmware/export/axp192.h @@ -0,0 +1,131 @@ +/*************************************************************************** + * __________ __ ___. + * 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 1583db175a..64041795a3 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 "axp-pmu.h" +#include "axp192.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 = AXP192_REG_GPIOSTATE1; + static const uint8_t gpio_reg = AXP_REG_GPIOLEVEL1; 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. */ - 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 */ + axp_set_gpio_function(0, AXP_GPIO_INPUT); /* HP detect */ + axp_set_gpio_function(1, AXP_GPIO_INPUT); /* LO detect */ /* Get an initial reading before startup */ - int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, gpio_reg); + int r = axp_read(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 a1a4d2c2b2..17cdb4d645 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 "axp-pmu.h" +#include "axp192.h" #include "i2c-x1000.h" const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] = @@ -56,27 +56,30 @@ const unsigned short percent_to_volt_charge[11] = void power_init(void) { - /* Initialize driver */ - i2c_x1000_set_freq(2, I2C_FREQ_400K); - axp_init(); + /* Configure I2C bus */ + i2c_x1000_set_freq(AXP_PMU_BUS, I2C_FREQ_400K); - /* Set lowest sample rate */ - axp_adc_set_rate(AXP_ADC_RATE_25HZ); + /* FIXME: Copy paste from M3K. Probably not necessary */ + axp_modify(AXP_REG_DCDCMODE, 0, 0xc0); - /* Ensure battery voltage ADC is enabled */ - int bits = axp_adc_get_enabled(); - bits |= (1 << ADC_BATTERY_VOLTAGE); - axp_adc_set_enabled(bits); + /* Power on required supplies */ + 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)); - /* 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); - - /* 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); + /* 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)); /* Delay to give power outputs time to stabilize. * With the power thread delay, this can apparently go as low as 50, @@ -104,20 +107,25 @@ void power_off(void) bool charging_state(void) { - return axp_battery_status() == AXP_BATT_CHARGING; + return axp_is_charging(); +} + +unsigned int power_input_status(void) +{ + return axp_power_input_status(); } int _battery_voltage(void) { - return axp_adc_read(ADC_BATTERY_VOLTAGE); + return axp_read_adc(AXP_ADC_BATTERY_VOLTAGE); } #if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE int _battery_current(void) { if(charging_state()) - return axp_adc_read(ADC_CHARGE_CURRENT); + return axp_read_adc(AXP_ADC_CHARGE_CURRENT); else - return axp_adc_read(ADC_DISCHARGE_CURRENT); + return axp_read_adc(AXP_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 24daf2ef69..0ef7bd2f64 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 "axp-pmu.h" +#include "axp192.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 = AXP192_REG_GPIOSTATE1; + static const uint8_t gpio_reg = AXP_REG_GPIOLEVEL1; 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 */ - i2c_reg_write1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP192_REG_GPIO2FUNCTION, 0x01); + axp_set_gpio_function(2, AXP_GPIO_INPUT); /* Get an initial reading before startup */ - int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, gpio_reg); + int r = axp_read(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 2d28ad0975..53451ffa6c 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 "axp-pmu.h" +#include "axp192.h" #include "i2c-x1000.h" const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] = @@ -54,27 +54,29 @@ const unsigned short percent_to_volt_charge[11] = void power_init(void) { - /* Initialize driver */ - i2c_x1000_set_freq(2, I2C_FREQ_400K); - axp_init(); + /* Configure I2C bus */ + i2c_x1000_set_freq(AXP_PMU_BUS, I2C_FREQ_400K); - /* Set lowest sample rate */ - axp_adc_set_rate(AXP_ADC_RATE_25HZ); + /* Set DCDC1 and DCDC2 to fixed PWM mode to match OF settings. */ + axp_modify(AXP_REG_DCDCMODE, 0, 0x0c); - /* Ensure battery voltage ADC is enabled */ - int bits = axp_adc_get_enabled(); - bits |= (1 << ADC_BATTERY_VOLTAGE); - axp_adc_set_enabled(bits); + /* 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) */ - /* 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); - - /* 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); + /* 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)); /* Short delay to give power outputs time to stabilize */ mdelay(200); @@ -99,20 +101,25 @@ void power_off(void) bool charging_state(void) { - return axp_battery_status() == AXP_BATT_CHARGING; + return axp_is_charging(); +} + +unsigned int power_input_status(void) +{ + return axp_power_input_status(); } int _battery_voltage(void) { - return axp_adc_read(ADC_BATTERY_VOLTAGE); + return axp_read_adc(AXP_ADC_BATTERY_VOLTAGE); } #if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE int _battery_current(void) { if(charging_state()) - return axp_adc_read(ADC_CHARGE_CURRENT); + return axp_read_adc(AXP_ADC_CHARGE_CURRENT); else - return axp_adc_read(ADC_DISCHARGE_CURRENT); + return axp_read_adc(AXP_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 13b0cdd078..1976dde793 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 "axp-pmu.h" +#include "axp192.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 = AXP192_REG_GPIOSTATE1; + static const uint8_t gpio_reg = AXP_REG_GPIOLEVEL1; 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 */ - i2c_reg_write1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP192_REG_GPIO1FUNCTION, 0x01); + axp_set_gpio_function(1, AXP_GPIO_INPUT); /* Get an initial reading before startup */ - int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, gpio_reg); + int r = axp_read(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 75f8031dd9..65d1bc163f 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 "axp-pmu.h" +#include "axp192.h" #ifdef HAVE_CW2015 # include "cw2015.h" #endif @@ -73,24 +73,30 @@ 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 - /* 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); + /* Set DCDC2 to 1.2 V to match OF settings. */ + axp_set_supply_voltage(AXP_SUPPLY_DCDC2, 1200); - /* 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); + /* 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)); /* not sure (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)); /* Delay to give power output time to stabilize */ mdelay(20); @@ -111,23 +117,28 @@ void power_off(void) bool charging_state(void) { - return axp_battery_status() == AXP_BATT_CHARGING; + return axp_is_charging(); +} + +unsigned int power_input_status(void) +{ + return axp_power_input_status(); } 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_adc_read(ADC_BATTERY_VOLTAGE); + return axp_read_adc(AXP_ADC_BATTERY_VOLTAGE); } #if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE int _battery_current(void) { if(charging_state()) - return axp_adc_read(ADC_CHARGE_CURRENT); + return axp_read_adc(AXP_ADC_CHARGE_CURRENT); else - return axp_adc_read(ADC_DISCHARGE_CURRENT); + return axp_read_adc(AXP_ADC_DISCHARGE_CURRENT); } #endif