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