Revert "AXP PMU rewrite (again)"
This caused LCD problems on the ErosQ, where the screen would go white until being put through a sleep/wake cycle. The exact reason for this isn't obvious, but the problem didn't exist prior to the AXP driver rewrite. The two dependent changes,42999913ba
- x1000: Increase USB current limit to 500 mA at all times90dd2f84a9
- x1000: Correctly limit USB charging current ended up bringing the USB charging situation back to where it was prior to the rewrite, so the cleanest option is to revert the whole lot. This reverts commit42999913ba
. This reverts commit90dd2f84a9
. This reverts commit2d89143962
. Change-Id: I1cff2bfdd1b189df14bcf8cce42db725caa470d7
This commit is contained in:
parent
8f063d49c2
commit
eaee5e7339
12 changed files with 901 additions and 1413 deletions
|
@ -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
|
||||
|
|
670
firmware/drivers/axp-pmu.c
Normal file
670
firmware/drivers/axp-pmu.c
Normal file
|
@ -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, ®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;
|
||||
}
|
|
@ -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
|
151
firmware/export/axp-pmu.h
Normal file
151
firmware/export/axp-pmu.h
Normal file
|
@ -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__ */
|
|
@ -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
|
|
@ -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__ */
|
|
@ -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;
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
#ifdef HAVE_USB_CHARGING_ENABLE
|
||||
# include "usb_core.h"
|
||||
#endif
|
||||
#include "axp192.h"
|
||||
#include "axp-pmu.h"
|
||||
#include "i2c-x1000.h"
|
||||
|
||||
const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] =
|
||||
|
@ -56,35 +56,27 @@ const unsigned short percent_to_volt_charge[11] =
|
|||
|
||||
void power_init(void)
|
||||
{
|
||||
/* Configure I2C bus */
|
||||
i2c_x1000_set_freq(AXP_PMU_BUS, I2C_FREQ_400K);
|
||||
/* Initialize driver */
|
||||
i2c_x1000_set_freq(2, I2C_FREQ_400K);
|
||||
axp_init();
|
||||
|
||||
/* FIXME: Copy paste from M3K. Probably not necessary */
|
||||
axp_modify(AXP_REG_DCDCMODE, 0, 0xc0);
|
||||
/* Set lowest sample rate */
|
||||
axp_adc_set_rate(AXP_ADC_RATE_25HZ);
|
||||
|
||||
/* Power on required supplies
|
||||
* TODO: This should be checked, though likely all but EXTEN are needed */
|
||||
axp_set_enabled_supplies(
|
||||
(1 << AXP_SUPPLY_EXTEN) |
|
||||
(1 << AXP_SUPPLY_DCDC1) |
|
||||
(1 << AXP_SUPPLY_DCDC2) |
|
||||
(1 << AXP_SUPPLY_DCDC3) |
|
||||
(1 << AXP_SUPPLY_LDO2) |
|
||||
(1 << AXP_SUPPLY_LDO3));
|
||||
/* Ensure battery voltage ADC is enabled */
|
||||
int bits = axp_adc_get_enabled();
|
||||
bits |= (1 << ADC_BATTERY_VOLTAGE);
|
||||
axp_adc_set_enabled(bits);
|
||||
|
||||
/* Enable required ADCs */
|
||||
axp_set_enabled_adcs(
|
||||
(1 << AXP_ADC_BATTERY_VOLTAGE) |
|
||||
(1 << AXP_ADC_CHARGE_CURRENT) |
|
||||
(1 << AXP_ADC_DISCHARGE_CURRENT) |
|
||||
(1 << AXP_ADC_VBUS_VOLTAGE) |
|
||||
(1 << AXP_ADC_VBUS_CURRENT) |
|
||||
(1 << AXP_ADC_INTERNAL_TEMP) |
|
||||
(1 << AXP_ADC_APS_VOLTAGE));
|
||||
/* Turn on all power outputs */
|
||||
i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
|
||||
AXP_REG_PWROUTPUTCTRL2, 0, 0x5f, NULL);
|
||||
i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
|
||||
AXP_REG_DCDCWORKINGMODE, 0, 0xc0, NULL);
|
||||
|
||||
/* Configure USB charging */
|
||||
axp_set_vhold_level(4400);
|
||||
usb_charging_maxcurrent_change(100);
|
||||
/* Set the default charging current. This is the same as the
|
||||
* OF's setting, although it's not strictly within the USB spec. */
|
||||
axp_set_charge_current(780);
|
||||
|
||||
/* Delay to give power outputs time to stabilize.
|
||||
* With the power thread delay, this can apparently go as low as 50,
|
||||
|
@ -96,22 +88,7 @@ void power_init(void)
|
|||
#ifdef HAVE_USB_CHARGING_ENABLE
|
||||
void usb_charging_maxcurrent_change(int maxcurrent)
|
||||
{
|
||||
int vbus_limit;
|
||||
int charge_current;
|
||||
|
||||
/* Note that the charge current setting is a maximum: it will be
|
||||
* reduced dynamically by the AXP192 so the combined load is less
|
||||
* than the set VBUS current limit. */
|
||||
if(maxcurrent <= 100) {
|
||||
vbus_limit = AXP_VBUS_LIMIT_500mA;
|
||||
charge_current = 100;
|
||||
} else {
|
||||
vbus_limit = AXP_VBUS_LIMIT_500mA;
|
||||
charge_current = 550;
|
||||
}
|
||||
|
||||
axp_set_vbus_limit(vbus_limit);
|
||||
axp_set_charge_current(charge_current);
|
||||
axp_set_charge_current(maxcurrent);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -127,25 +104,20 @@ void power_off(void)
|
|||
|
||||
bool charging_state(void)
|
||||
{
|
||||
return axp_is_charging();
|
||||
}
|
||||
|
||||
unsigned int power_input_status(void)
|
||||
{
|
||||
return axp_power_input_status();
|
||||
return axp_battery_status() == AXP_BATT_CHARGING;
|
||||
}
|
||||
|
||||
int _battery_voltage(void)
|
||||
{
|
||||
return axp_read_adc(AXP_ADC_BATTERY_VOLTAGE);
|
||||
return axp_adc_read(ADC_BATTERY_VOLTAGE);
|
||||
}
|
||||
|
||||
#if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE
|
||||
int _battery_current(void)
|
||||
{
|
||||
if(charging_state())
|
||||
return axp_read_adc(AXP_ADC_CHARGE_CURRENT);
|
||||
return axp_adc_read(ADC_CHARGE_CURRENT);
|
||||
else
|
||||
return axp_read_adc(AXP_ADC_DISCHARGE_CURRENT);
|
||||
return axp_adc_read(ADC_DISCHARGE_CURRENT);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
#ifdef HAVE_USB_CHARGING_ENABLE
|
||||
# include "usb_core.h"
|
||||
#endif
|
||||
#include "axp192.h"
|
||||
#include "axp-pmu.h"
|
||||
#include "i2c-x1000.h"
|
||||
|
||||
const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] =
|
||||
|
@ -54,33 +54,27 @@ const unsigned short percent_to_volt_charge[11] =
|
|||
|
||||
void power_init(void)
|
||||
{
|
||||
/* Configure I2C bus */
|
||||
i2c_x1000_set_freq(AXP_PMU_BUS, I2C_FREQ_400K);
|
||||
/* Initialize driver */
|
||||
i2c_x1000_set_freq(2, I2C_FREQ_400K);
|
||||
axp_init();
|
||||
|
||||
/* Set DCDC1 and DCDC2 to fixed PWM mode to match OF settings. */
|
||||
axp_modify(AXP_REG_DCDCMODE, 0, 0x0c);
|
||||
/* Set lowest sample rate */
|
||||
axp_adc_set_rate(AXP_ADC_RATE_25HZ);
|
||||
|
||||
/* Power on required supplies */
|
||||
axp_set_enabled_supplies(
|
||||
(1 << AXP_SUPPLY_DCDC1) | /* not sure (3.3 V) */
|
||||
(1 << AXP_SUPPLY_DCDC2) | /* not sure (1.4 V) */
|
||||
(1 << AXP_SUPPLY_DCDC3) | /* for CPU (1.8 V) */
|
||||
(1 << AXP_SUPPLY_LDO2) | /* LCD controller (3.3 V) */
|
||||
(1 << AXP_SUPPLY_LDO3)); /* SD bus (3.3 V) */
|
||||
/* Ensure battery voltage ADC is enabled */
|
||||
int bits = axp_adc_get_enabled();
|
||||
bits |= (1 << ADC_BATTERY_VOLTAGE);
|
||||
axp_adc_set_enabled(bits);
|
||||
|
||||
/* Enable required ADCs */
|
||||
axp_set_enabled_adcs(
|
||||
(1 << AXP_ADC_BATTERY_VOLTAGE) |
|
||||
(1 << AXP_ADC_CHARGE_CURRENT) |
|
||||
(1 << AXP_ADC_DISCHARGE_CURRENT) |
|
||||
(1 << AXP_ADC_VBUS_VOLTAGE) |
|
||||
(1 << AXP_ADC_VBUS_CURRENT) |
|
||||
(1 << AXP_ADC_INTERNAL_TEMP) |
|
||||
(1 << AXP_ADC_APS_VOLTAGE));
|
||||
/* Turn on all power outputs */
|
||||
i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
|
||||
AXP_REG_PWROUTPUTCTRL2, 0, 0x5f, NULL);
|
||||
i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
|
||||
AXP_REG_DCDCWORKINGMODE, 0, 0xc0, NULL);
|
||||
|
||||
/* Configure USB charging */
|
||||
axp_set_vhold_level(4400);
|
||||
usb_charging_maxcurrent_change(100);
|
||||
/* Set the default charging current. This is the same as the
|
||||
* OF's setting, although it's not strictly within the USB spec. */
|
||||
axp_set_charge_current(780);
|
||||
|
||||
/* Short delay to give power outputs time to stabilize */
|
||||
mdelay(200);
|
||||
|
@ -89,22 +83,7 @@ void power_init(void)
|
|||
#ifdef HAVE_USB_CHARGING_ENABLE
|
||||
void usb_charging_maxcurrent_change(int maxcurrent)
|
||||
{
|
||||
int vbus_limit;
|
||||
int charge_current;
|
||||
|
||||
/* Note that the charge current setting is a maximum: it will be
|
||||
* reduced dynamically by the AXP192 so the combined load is less
|
||||
* than the set VBUS current limit. */
|
||||
if(maxcurrent <= 100) {
|
||||
vbus_limit = AXP_VBUS_LIMIT_500mA;
|
||||
charge_current = 100;
|
||||
} else {
|
||||
vbus_limit = AXP_VBUS_LIMIT_500mA;
|
||||
charge_current = 550;
|
||||
}
|
||||
|
||||
axp_set_vbus_limit(vbus_limit);
|
||||
axp_set_charge_current(charge_current);
|
||||
axp_set_charge_current(maxcurrent);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -120,25 +99,20 @@ void power_off(void)
|
|||
|
||||
bool charging_state(void)
|
||||
{
|
||||
return axp_is_charging();
|
||||
}
|
||||
|
||||
unsigned int power_input_status(void)
|
||||
{
|
||||
return axp_power_input_status();
|
||||
return axp_battery_status() == AXP_BATT_CHARGING;
|
||||
}
|
||||
|
||||
int _battery_voltage(void)
|
||||
{
|
||||
return axp_read_adc(AXP_ADC_BATTERY_VOLTAGE);
|
||||
return axp_adc_read(ADC_BATTERY_VOLTAGE);
|
||||
}
|
||||
|
||||
#if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE
|
||||
int _battery_current(void)
|
||||
{
|
||||
if(charging_state())
|
||||
return axp_read_adc(AXP_ADC_CHARGE_CURRENT);
|
||||
return axp_adc_read(ADC_CHARGE_CURRENT);
|
||||
else
|
||||
return axp_read_adc(AXP_ADC_DISCHARGE_CURRENT);
|
||||
return axp_adc_read(ADC_DISCHARGE_CURRENT);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include "power.h"
|
||||
#include "adc.h"
|
||||
#include "system.h"
|
||||
#include "axp192.h"
|
||||
#include "axp-pmu.h"
|
||||
#ifdef HAVE_CW2015
|
||||
# include "cw2015.h"
|
||||
#endif
|
||||
|
@ -73,34 +73,24 @@ const unsigned short percent_to_volt_charge[11] =
|
|||
void power_init(void)
|
||||
{
|
||||
i2c_x1000_set_freq(AXP_PMU_BUS, I2C_FREQ_400K);
|
||||
axp_init();
|
||||
#ifdef HAVE_CW2015
|
||||
cw2015_init();
|
||||
#endif
|
||||
|
||||
/* Set DCDC2 to 1.2 V to match OF settings. */
|
||||
axp_set_supply_voltage(AXP_SUPPLY_DCDC2, 1200);
|
||||
/* Change supply voltage from the default of 1250 mV to 1200 mV,
|
||||
* this matches the original firmware's settings. Didn't observe
|
||||
* any obviously bad behavior at 1250 mV, but better to be safe. */
|
||||
axp_supply_set_voltage(AXP_SUPPLY_DCDC2, 1200);
|
||||
|
||||
/* Power on required supplies */
|
||||
axp_set_enabled_supplies(
|
||||
(1 << AXP_SUPPLY_DCDC1) | /* SD bus (3.3 V) */
|
||||
(1 << AXP_SUPPLY_DCDC2) | /* LCD (1.2 V) */
|
||||
(1 << AXP_SUPPLY_DCDC3) | /* CPU (1.8 V) */
|
||||
(1 << AXP_SUPPLY_LDO2) | /* Touchscreen (3.3 V) */
|
||||
(1 << AXP_SUPPLY_LDO3)); /* USB analog (2.5 V) */
|
||||
|
||||
/* Enable required ADCs */
|
||||
axp_set_enabled_adcs(
|
||||
(1 << AXP_ADC_BATTERY_VOLTAGE) |
|
||||
(1 << AXP_ADC_CHARGE_CURRENT) |
|
||||
(1 << AXP_ADC_DISCHARGE_CURRENT) |
|
||||
(1 << AXP_ADC_VBUS_VOLTAGE) |
|
||||
(1 << AXP_ADC_VBUS_CURRENT) |
|
||||
(1 << AXP_ADC_INTERNAL_TEMP) |
|
||||
(1 << AXP_ADC_APS_VOLTAGE));
|
||||
|
||||
/* Configure USB charging */
|
||||
axp_set_vhold_level(4400);
|
||||
usb_charging_maxcurrent_change(100);
|
||||
/* For now, just turn everything on... definitely the touchscreen
|
||||
* is powered by one of the outputs */
|
||||
i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
|
||||
AXP_REG_PWROUTPUTCTRL1, 0, 0x05, NULL);
|
||||
i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
|
||||
AXP_REG_PWROUTPUTCTRL2, 0, 0x0f, NULL);
|
||||
i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
|
||||
AXP_REG_DCDCWORKINGMODE, 0, 0xc0, NULL);
|
||||
|
||||
/* Delay to give power output time to stabilize */
|
||||
mdelay(20);
|
||||
|
@ -109,22 +99,7 @@ void power_init(void)
|
|||
#ifdef HAVE_USB_CHARGING_ENABLE
|
||||
void usb_charging_maxcurrent_change(int maxcurrent)
|
||||
{
|
||||
int vbus_limit;
|
||||
int charge_current;
|
||||
|
||||
/* Note that the charge current setting is a maximum: it will be
|
||||
* reduced dynamically by the AXP192 so the combined load is less
|
||||
* than the set VBUS current limit. */
|
||||
if(maxcurrent <= 100) {
|
||||
vbus_limit = AXP_VBUS_LIMIT_500mA;
|
||||
charge_current = 100;
|
||||
} else {
|
||||
vbus_limit = AXP_VBUS_LIMIT_500mA;
|
||||
charge_current = 550;
|
||||
}
|
||||
|
||||
axp_set_vbus_limit(vbus_limit);
|
||||
axp_set_charge_current(charge_current);
|
||||
axp_set_charge_current(maxcurrent);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -136,28 +111,23 @@ void power_off(void)
|
|||
|
||||
bool charging_state(void)
|
||||
{
|
||||
return axp_is_charging();
|
||||
}
|
||||
|
||||
unsigned int power_input_status(void)
|
||||
{
|
||||
return axp_power_input_status();
|
||||
return axp_battery_status() == AXP_BATT_CHARGING;
|
||||
}
|
||||
|
||||
int _battery_voltage(void)
|
||||
{
|
||||
/* CW2015 can also read battery voltage, but the AXP consistently
|
||||
* reads ~20-30 mV higher so I suspect it's the "real" voltage. */
|
||||
return axp_read_adc(AXP_ADC_BATTERY_VOLTAGE);
|
||||
return axp_adc_read(ADC_BATTERY_VOLTAGE);
|
||||
}
|
||||
|
||||
#if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE
|
||||
int _battery_current(void)
|
||||
{
|
||||
if(charging_state())
|
||||
return axp_read_adc(AXP_ADC_CHARGE_CURRENT);
|
||||
return axp_adc_read(ADC_CHARGE_CURRENT);
|
||||
else
|
||||
return axp_read_adc(AXP_ADC_DISCHARGE_CURRENT);
|
||||
return axp_adc_read(ADC_DISCHARGE_CURRENT);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
Loading…
Reference in a new issue