1
0
Fork 0
forked from len0rd/rockbox
foxbox/firmware/drivers/axp192.c
Aidan MacDonald 2d89143962 AXP PMU rewrite (again)
I noticed a few mistakes in the old driver code and it was in
need of an overhaul anyway... I decided to scale things back,
simplify the code and remove most of the debug menus, netting
a nice code size savings.

One new feature is an advanced debug menu which is accessible
by recompiling the code with AXP_EXTRA_DEBUG. It adds quite a
bit of code size and isn't useful other than for development
so it must be manually enabled by editing the source.

Change-Id: I30e17c1194c14823decd726a574ed14451d4cb2d
2021-12-28 15:20:27 +00:00

810 lines
22 KiB
C

/***************************************************************************
* __________ __ ___.
* 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