From 0187fca64024c872c8c7708583d5f1ced5224544 Mon Sep 17 00:00:00 2001 From: Aidan MacDonald Date: Thu, 27 May 2021 00:23:17 +0100 Subject: axp173 driver: rename to "axp-pmu" + other changes The old name was a bit misleading. AXP173 is sort of the lowest common denominator of a series of related chips. The M3K uses an AXP192 which has a few extra features vs. the AXP173. New voltage regulator stuff was added for the sake of the Shanling Q1 native port (that player also uses an AXP192). Change-Id: Id0c162c23094bb03d13fae2d6c332e3047968d6e --- firmware/SOURCES | 4 +- firmware/drivers/axp-pmu.c | 670 +++++++++++++++++++++ firmware/drivers/axp173.c | 476 --------------- firmware/export/axp-pmu.h | 151 +++++ firmware/export/axp173.h | 118 ---- firmware/export/config/fiiom3k.h | 2 +- firmware/target/mips/ingenic_x1000/debug-x1000.c | 8 +- .../mips/ingenic_x1000/fiiom3k/button-fiiom3k.c | 10 +- .../target/mips/ingenic_x1000/fiiom3k/i2c-target.h | 4 +- .../mips/ingenic_x1000/fiiom3k/power-fiiom3k.c | 34 +- 10 files changed, 853 insertions(+), 624 deletions(-) create mode 100644 firmware/drivers/axp-pmu.c delete mode 100644 firmware/drivers/axp173.c create mode 100644 firmware/export/axp-pmu.h delete mode 100644 firmware/export/axp173.h diff --git a/firmware/SOURCES b/firmware/SOURCES index 052847a6a6..0d93439ff8 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -1929,8 +1929,8 @@ drivers/touchpad.c drivers/i2c-async.c #endif -#ifdef HAVE_AXP173 -drivers/axp173.c +#ifdef HAVE_AXP_PMU +drivers/axp-pmu.c #endif /* firmware/kernel section */ 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/axp173.c b/firmware/drivers/axp173.c deleted file mode 100644 index 0c9b9c81a4..0000000000 --- a/firmware/drivers/axp173.c +++ /dev/null @@ -1,476 +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 "axp173.h" -#include "power.h" -#include "i2c-async.h" - -/* Headers for the debug menu */ -#ifndef BOOTLOADER -# include "action.h" -# include "list.h" -# include -#endif - -static const struct axp173_adc_info { - uint8_t reg; - uint8_t en_reg; - uint8_t en_bit; -} axp173_adc_info[NUM_ADC_CHANNELS] = { - {0x56, AXP173_REG_ADCENABLE1, 5}, /* ACIN_VOLTAGE */ - {0x58, AXP173_REG_ADCENABLE1, 4}, /* ACIN_CURRENT */ - {0x5a, AXP173_REG_ADCENABLE1, 3}, /* VBUS_VOLTAGE */ - {0x5c, AXP173_REG_ADCENABLE1, 2}, /* VBUS_CURRENT */ - {0x5e, AXP173_REG_ADCENABLE2, 7}, /* INTERNAL_TEMP */ - {0x62, AXP173_REG_ADCENABLE1, 1}, /* TS_INPUT */ - {0x78, AXP173_REG_ADCENABLE1, 7}, /* BATTERY_VOLTAGE */ - {0x7a, AXP173_REG_ADCENABLE1, 6}, /* CHARGE_CURRENT */ - {0x7c, AXP173_REG_ADCENABLE1, 6}, /* DISCHARGE_CURRENT */ - {0x7e, AXP173_REG_ADCENABLE1, 1}, /* APS_VOLTAGE */ - {0x70, 0xff, 0}, /* BATTERY_POWER */ -}; - -static struct axp173 { - int adc_enable; - int chargecurrent_setting; -} axp173; - -static void axp173_init_enabled_adcs(void) -{ - axp173.adc_enable = 0; - - /* Read enabled ADCs from the hardware */ - uint8_t regs[2]; - int rc = i2c_reg_read(AXP173_BUS, AXP173_ADDR, - AXP173_REG_ADCENABLE1, 2, ®s[0]); - if(rc != I2C_STATUS_OK) - return; - - /* Parse registers to set ADC enable bits */ - const struct axp173_adc_info* info = axp173_adc_info; - for(int i = 0; i < NUM_ADC_CHANNELS; ++i) { - if(info[i].en_reg == 0xff) - continue; - - if(regs[info[i].en_reg - AXP173_REG_ADCENABLE1] & info[i].en_bit) - axp173.adc_enable |= 1 << i; - } - - /* Handle battery power ADC */ - if((axp173.adc_enable & (1 << ADC_BATTERY_VOLTAGE)) && - (axp173.adc_enable & (1 << ADC_DISCHARGE_CURRENT))) { - axp173.adc_enable |= (1 << ADC_BATTERY_POWER); - } -} - -void axp173_init(void) -{ - axp173_init_enabled_adcs(); - - /* We need discharge current ADC to reliably poll for a full battery */ - int bits = axp173.adc_enable; - bits |= (1 << ADC_DISCHARGE_CURRENT); - axp173_adc_set_enabled(bits); - - /* Read the maximum charging current */ - int value = i2c_reg_read1(AXP173_BUS, AXP173_ADDR, AXP173_REG_CHARGECONTROL1); - axp173.chargecurrent_setting = (value < 0) ? -1 : value; -} - -/* TODO: this can STILL indicate some false positives! */ -int axp173_battery_status(void) -{ - int r = i2c_reg_read1(AXP173_BUS, AXP173_ADDR, AXP173_REG_POWERSTATUS); - if(r >= 0) { - /* Charging bit indicates we're currently charging */ - if((r & 0x04) != 0) - return AXP173_BATT_CHARGING; - - /* Not plugged in means we're discharging */ - if((r & 0xf0) == 0) - return AXP173_BATT_DISCHARGING; - } else { - /* Report discharging if we can't find out power status */ - return AXP173_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 = axp173_adc_read_raw(ADC_DISCHARGE_CURRENT); - if(d == 0) - return AXP173_BATT_FULL; - - return AXP173_BATT_DISCHARGING; -} - -int axp173_input_status(void) -{ -#ifdef HAVE_BATTERY_SWITCH - int input_status = 0; -#else - int input_status = AXP173_INPUT_BATTERY; -#endif - - int r = i2c_reg_read1(AXP173_BUS, AXP173_ADDR, AXP173_REG_POWERSTATUS); - if(r < 0) - return input_status; - - /* Check for AC input */ - if(r & 0x80) - input_status |= AXP173_INPUT_AC; - - /* Only report USB if ACIN and VBUS are not shorted */ - if((r & 0x20) != 0 && (r & 0x02) == 0) - input_status |= AXP173_INPUT_USB; - -#ifdef HAVE_BATTERY_SWITCH - /* Check for battery presence if target defines it as removable */ - r = i2c_reg_read1(AXP173_BUS, AXP173_ADDR, AXP173_REG_CHARGESTATUS); - if(r >= 0 && (r & 0x20) != 0) - input_status |= AXP173_INPUT_BATTERY; -#endif - - return input_status; -} - -int axp173_adc_read(int adc) -{ - int value = axp173_adc_read_raw(adc); - if(value == INT_MIN) - return INT_MIN; - - return axp173_adc_conv_raw(adc, value); -} - -int axp173_adc_read_raw(int adc) -{ - /* Don't give a reading if the ADC is not enabled */ - if((axp173.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 = axp173_adc_info[adc].reg; - int rc = i2c_reg_read(AXP173_BUS, AXP173_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 axp173_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 axp173_adc_get_enabled(void) -{ - return axp173.adc_enable; -} - -void axp173_adc_set_enabled(int adc_bits) -{ - /* Ignore no-op */ - if(adc_bits == axp173.adc_enable) - return; - - /* Compute the new register values */ - const struct axp173_adc_info* info = axp173_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(AXP173_BUS, AXP173_ADDR, AXP173_REG_ADCENABLE1, 2, ®s[0]); - axp173.adc_enable = adc_bits; -} - -int axp173_adc_get_rate(void) -{ - int r = i2c_reg_read1(AXP173_BUS, AXP173_ADDR, AXP173_REG_ADCSAMPLERATE); - if(r < 0) - return AXP173_ADC_RATE_100HZ; /* an arbitrary value */ - - return (r >> 6) & 3; -} - -void axp173_adc_set_rate(int rate) -{ - i2c_reg_modify1(AXP173_BUS, AXP173_ADDR, AXP173_REG_ADCSAMPLERATE, - 0xc0, (rate & 3) << 6, NULL); -} - -static uint32_t axp173_cc_parse(const uint8_t* buf) -{ - return ((uint32_t)buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; -} - -void axp173_cc_read(uint32_t* charge, uint32_t* discharge) -{ - uint8_t buf[8]; - int rc = i2c_reg_read(AXP173_BUS, AXP173_ADDR, - AXP173_REG_COULOMBCOUNTERBASE, 8, &buf[0]); - if(rc != I2C_STATUS_OK) { - if(charge) - *charge = 0; - if(discharge) - *discharge = 0; - return; - } - - if(charge) - *charge = axp173_cc_parse(&buf[0]); - if(discharge) - *discharge = axp173_cc_parse(&buf[4]); -} - -void axp173_cc_clear(void) -{ - i2c_reg_setbit1(AXP173_BUS, AXP173_ADDR, - AXP173_REG_COULOMBCOUNTERCTRL, 5, 1, NULL); -} - -void axp173_cc_enable(bool en) -{ - i2c_reg_setbit1(AXP173_BUS, AXP173_ADDR, - AXP173_REG_COULOMBCOUNTERCTRL, 7, en ? 1 : 0, NULL); -} - -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 axp173_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 == axp173.chargecurrent_setting) - return; - - /* Update register */ - i2c_reg_modify1(AXP173_BUS, AXP173_ADDR, - AXP173_REG_CHARGECONTROL1, 0x0f, value, NULL); - axp173.chargecurrent_setting = value; -} - -int axp173_get_charge_current(void) -{ - if(axp173.chargecurrent_setting < 0) - return chargecurrent_tbl[0]; - else - return chargecurrent_tbl[axp173.chargecurrent_setting]; -} - -#ifndef BOOTLOADER -#define AXP173_DEBUG_BATTERY_STATUS 0 -#define AXP173_DEBUG_INPUT_STATUS 1 -#define AXP173_DEBUG_CHARGE_CURRENT 2 -#define AXP173_DEBUG_ADC_RATE 3 -#define AXP173_DEBUG_FIRST_ADC 4 -#define AXP173_DEBUG_ENTRIES (AXP173_DEBUG_FIRST_ADC + NUM_ADC_CHANNELS) - -static int axp173_debug_menu_cb(int action, struct gui_synclist* lists) -{ - (void)lists; - - if(action == ACTION_NONE) - action = ACTION_REDRAW; - - return action; -} - -static const char* axp173_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", - }; - - int adc = item - AXP173_DEBUG_FIRST_ADC; - if(item >= AXP173_DEBUG_FIRST_ADC && adc < NUM_ADC_CHANNELS) { - int raw_value = axp173_adc_read_raw(adc); - if(raw_value == INT_MIN) { - snprintf(buf, buflen, "%s: [Disabled]", adc_names[adc]); - return buf; - } - - int value = axp173_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; - } - - switch(item) { - case AXP173_DEBUG_BATTERY_STATUS: { - switch(axp173_battery_status()) { - case AXP173_BATT_FULL: - return "Battery: Full"; - case AXP173_BATT_CHARGING: - return "Battery: Charging"; - case AXP173_BATT_DISCHARGING: - return "Battery: Discharging"; - default: - return "Battery: Unknown"; - } - } break; - - case AXP173_DEBUG_INPUT_STATUS: { - int s = axp173_input_status(); - const char* ac = (s & AXP173_INPUT_AC) ? " AC" : ""; - const char* usb = (s & AXP173_INPUT_USB) ? " USB" : ""; - const char* batt = (s & AXP173_INPUT_BATTERY) ? " Battery" : ""; - snprintf(buf, buflen, "Inputs:%s%s%s", ac, usb, batt); - return buf; - } break; - - case AXP173_DEBUG_CHARGE_CURRENT: { - int current = axp173_get_charge_current(); - snprintf(buf, buflen, "Max charge current: %d mA", current); - return buf; - } break; - - case AXP173_DEBUG_ADC_RATE: { - int rate = 25 << axp173_adc_get_rate(); - snprintf(buf, buflen, "ADC sample rate: %d Hz", rate); - return buf; - } break; - - default: - return "---"; - } -} - -bool axp173_debug_menu(void) -{ - struct simplelist_info info; - simplelist_info_init(&info, "AXP173 debug", AXP173_DEBUG_ENTRIES, NULL); - info.action_callback = axp173_debug_menu_cb; - info.get_name = axp173_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 = axp173_input_status(); - - if(input_status & AXP173_INPUT_AC) - state |= POWER_INPUT_MAIN_CHARGER; - - if(input_status & AXP173_INPUT_USB) - state |= POWER_INPUT_USB_CHARGER; - -#ifdef HAVE_BATTERY_SWITCH - if(input_status & AXP173_INPUT_BATTERY) - state |= POWER_INPUT_BATTERY; -#endif - - return state; -} 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/axp173.h b/firmware/export/axp173.h deleted file mode 100644 index 34f0c2ec19..0000000000 --- a/firmware/export/axp173.h +++ /dev/null @@ -1,118 +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 __AXP173_H__ -#define __AXP173_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 AXP173_ADC_RATE_25HZ 0 -#define AXP173_ADC_RATE_50HZ 1 -#define AXP173_ADC_RATE_100HZ 2 -#define AXP173_ADC_RATE_200HZ 3 - -/* Return values of axp173_battery_status() */ -#define AXP173_BATT_DISCHARGING 0 -#define AXP173_BATT_CHARGING 1 -#define AXP173_BATT_FULL 2 - -/* Bits returned by axp173_input_status() */ -#define AXP173_INPUT_AC (1 << 0) -#define AXP173_INPUT_USB (1 << 1) -#define AXP173_INPUT_BATTERY (1 << 2) -#define AXP173_INPUT_EXTERNAL (AXP173_INPUT_AC|AXP173_INPUT_USB) - -/* Registers -- common to AXP173 and AXP192 (incomplete listing) */ -#define AXP173_REG_POWERSTATUS 0x00 -#define AXP173_REG_CHARGESTATUS 0x01 -#define AXP173_REG_PWROUTPUTCTRL 0x12 -#define AXP173_REG_SHUTDOWNLEDCTRL 0x32 -#define AXP173_REG_CHARGECONTROL1 0x33 -#define AXP173_REG_DCDCWORKINGMODE 0x80 -#define AXP173_REG_ADCENABLE1 0x82 -#define AXP173_REG_ADCENABLE2 0x83 -#define AXP173_REG_ADCSAMPLERATE 0x84 -#define AXP173_REG_COULOMBCOUNTERBASE 0xb0 -#define AXP173_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 axp173_init(void); - -/* Basic battery and power supply status */ -extern int axp173_battery_status(void); -extern int axp173_input_status(void); - -/* ADC access -- ADCs which are not enabled will return INT_MIN if read. - * The output of axp173_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 axp173_adc_conv_raw() for raw value precision/scale. - */ -extern int axp173_adc_read(int adc); -extern int axp173_adc_read_raw(int adc); -extern int axp173_adc_conv_raw(int adc, int value); -extern int axp173_adc_get_enabled(void); -extern void axp173_adc_set_enabled(int adc_bits); -extern int axp173_adc_get_rate(void); -extern void axp173_adc_set_rate(int rate); - -/* - axp173_cc_read() reads the coulomb counters - * - axp173_cc_clear() resets both counters to zero - * - axp173_cc_enable() will stop/start the counters running - */ -extern void axp173_cc_read(uint32_t* charge, uint32_t* discharge); -extern void axp173_cc_clear(void); -extern void axp173_cc_enable(bool en); - -/* Set/get maximum charging current in milliamps */ -extern void axp173_set_charge_current(int maxcurrent); -extern int axp173_get_charge_current(void); - -/* Debug menu */ -extern bool axp173_debug_menu(void); - -#endif /* __AXP173_H__ */ diff --git a/firmware/export/config/fiiom3k.h b/firmware/export/config/fiiom3k.h index 2c212b031d..a28efd43a5 100644 --- a/firmware/export/config/fiiom3k.h +++ b/firmware/export/config/fiiom3k.h @@ -90,7 +90,7 @@ #define HAVE_SW_POWEROFF #ifndef SIMULATOR -#define HAVE_AXP173 +#define HAVE_AXP_PMU 192 #define HAVE_POWEROFF_WHILE_CHARGING #endif diff --git a/firmware/target/mips/ingenic_x1000/debug-x1000.c b/firmware/target/mips/ingenic_x1000/debug-x1000.c index 74bbcd77a6..fe469b1a72 100644 --- a/firmware/target/mips/ingenic_x1000/debug-x1000.c +++ b/firmware/target/mips/ingenic_x1000/debug-x1000.c @@ -148,7 +148,9 @@ static bool dbg_cpuidle(void) #ifdef FIIO_M3K extern bool dbg_fiiom3k_touchpad(void); -extern bool axp173_debug_menu(void); +#endif +#ifdef HAVE_AXP_PMU +extern bool axp_debug_menu(void); #endif /* Menu definition */ @@ -164,7 +166,9 @@ static const struct { {"Audio", &dbg_audio}, #ifdef FIIO_M3K {"Touchpad", &dbg_fiiom3k_touchpad}, - {"Power stats", &axp173_debug_menu}, +#endif +#ifdef HAVE_AXP_PMU + {"Power stats", &axp_debug_menu}, #endif }; diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c index 85ccc5cf65..efc652c84f 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 "axp173.h" +#include "axp-pmu.h" #include "gpio-x1000.h" #include "i2c-x1000.h" #include @@ -418,7 +418,7 @@ static uint8_t hp_detect_reg = 0x00; static int hp_detect_tmo_cb(struct timeout* tmo) { i2c_descriptor* d = (i2c_descriptor*)tmo->data; - i2c_async_queue(AXP173_BUS, TIMEOUT_NOBLOCK, I2C_Q_ADD, 0, d); + i2c_async_queue(AXP_PMU_BUS, TIMEOUT_NOBLOCK, I2C_Q_ADD, 0, d); return HPD_POLL_TIME; } @@ -427,7 +427,7 @@ static void hp_detect_init(void) static struct timeout tmo; static const uint8_t gpio_reg = AXP192_REG_GPIOSTATE1; static i2c_descriptor desc = { - .slave_addr = AXP173_ADDR, + .slave_addr = AXP_PMU_ADDR, .bus_cond = I2C_START | I2C_STOP, .tran_mode = I2C_READ, .buffer[0] = (void*)&gpio_reg, @@ -440,10 +440,10 @@ static void hp_detect_init(void) }; /* Headphone detect is wired to AXP192 GPIO: set it to input state */ - i2c_reg_write1(AXP173_BUS, AXP173_ADDR, AXP192_REG_GPIO2FUNCTION, 0x01); + i2c_reg_write1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP192_REG_GPIO2FUNCTION, 0x01); /* Get an initial reading before startup */ - int r = i2c_reg_read1(AXP173_BUS, AXP173_ADDR, 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/i2c-target.h b/firmware/target/mips/ingenic_x1000/fiiom3k/i2c-target.h index a389d2af42..1e8ebfbb15 100644 --- a/firmware/target/mips/ingenic_x1000/fiiom3k/i2c-target.h +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/i2c-target.h @@ -31,7 +31,7 @@ #define FT6x06_BUS 1 #define FT6x06_ADDR 0x38 -#define AXP173_BUS 2 -#define AXP173_ADDR 0x34 +#define AXP_PMU_BUS 2 +#define AXP_PMU_ADDR 0x34 #endif /* __I2C_TARGET_H__ */ diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c index c346d16890..a7f6165980 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 "axp173.h" +#include "axp-pmu.h" #include "i2c-x1000.h" #include "gpio-x1000.h" @@ -53,32 +53,32 @@ const unsigned short percent_to_volt_charge[11] = 3485, 3780, 3836, 3857, 3890, 3930, 3986, 4062, 4158, 4185, 4196 }; -#define AXP173_IRQ_PORT GPIO_B -#define AXP173_IRQ_PIN (1 << 10) +#define AXP_IRQ_PORT GPIO_B +#define AXP_IRQ_PIN (1 << 10) void power_init(void) { /* Initialize driver */ i2c_x1000_set_freq(2, I2C_FREQ_400K); - axp173_init(); + axp_init(); /* Set lowest sample rate */ - axp173_adc_set_rate(AXP173_ADC_RATE_25HZ); + axp_adc_set_rate(AXP_ADC_RATE_25HZ); /* Ensure battery voltage ADC is enabled */ - int bits = axp173_adc_get_enabled(); + int bits = axp_adc_get_enabled(); bits |= (1 << ADC_BATTERY_VOLTAGE); - axp173_adc_set_enabled(bits); + axp_adc_set_enabled(bits); /* Turn on all power outputs */ - i2c_reg_modify1(AXP173_BUS, AXP173_ADDR, - AXP173_REG_PWROUTPUTCTRL, 0, 0x5f, NULL); - i2c_reg_modify1(AXP173_BUS, AXP173_ADDR, - AXP173_REG_DCDCWORKINGMODE, 0, 0xc0, NULL); + 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. */ - axp173_set_charge_current(780); + axp_set_charge_current(780); /* Short delay to give power outputs time to stabilize */ mdelay(5); @@ -87,7 +87,7 @@ void power_init(void) #ifdef HAVE_USB_CHARGING_ENABLE void usb_charging_maxcurrent_change(int maxcurrent) { - axp173_set_charge_current(maxcurrent); + axp_set_charge_current(maxcurrent); } #endif @@ -97,18 +97,16 @@ void adc_init(void) void power_off(void) { - /* Set the shutdown bit */ - i2c_reg_setbit1(AXP173_BUS, AXP173_ADDR, - AXP173_REG_SHUTDOWNLEDCTRL, 7, 1, NULL); + axp_power_off(); while(1); } bool charging_state(void) { - return axp173_battery_status() == AXP173_BATT_CHARGING; + return axp_battery_status() == AXP_BATT_CHARGING; } int _battery_voltage(void) { - return axp173_adc_read(ADC_BATTERY_VOLTAGE); + return axp_adc_read(ADC_BATTERY_VOLTAGE); } -- cgit v1.2.3