Add sensors support for AXP803/AXP813. Sensor values such as

battery charging, charge state, voltage, charging current, discharging current,
battery capacity etc. can be obtained via sysctl.

Reviewed by:	manu
Differential Revision:	https://reviews.freebsd.org/D19145
This commit is contained in:
Ganbold Tsagaankhuu 2019-02-11 14:31:19 +00:00
parent 3af08701cd
commit 66bddb4c70

View File

@ -194,6 +194,11 @@ MALLOC_DEFINE(M_AXP8XX_REG, "AXP8xx regulator", "AXP8xx power regulator");
#define AXP_BAT_CAP_WARN_LV1 0xf0 /* Bits 4, 5, 6, 7 */
#define AXP_BAT_CAP_WARN_LV2 0xf /* Bits 0, 1, 2, 3 */
/* Sensor conversion macros */
#define AXP_SENSOR_BAT_H(hi) ((hi) << 4)
#define AXP_SENSOR_BAT_L(lo) ((lo) & 0xf)
#define AXP_SENSOR_COULOMB(hi, lo) (((hi & ~(1 << 7)) << 8) | (lo))
static const struct {
const char *name;
uint8_t ctrl_reg;
@ -538,6 +543,123 @@ static struct axp8xx_regdef axp8xx_common_regdefs[] = {
},
};
enum axp8xx_sensor {
AXP_SENSOR_ACIN_PRESENT,
AXP_SENSOR_VBUS_PRESENT,
AXP_SENSOR_BATT_PRESENT,
AXP_SENSOR_BATT_CHARGING,
AXP_SENSOR_BATT_CHARGE_STATE,
AXP_SENSOR_BATT_VOLTAGE,
AXP_SENSOR_BATT_CHARGE_CURRENT,
AXP_SENSOR_BATT_DISCHARGE_CURRENT,
AXP_SENSOR_BATT_CAPACITY_PERCENT,
AXP_SENSOR_BATT_MAXIMUM_CAPACITY,
AXP_SENSOR_BATT_CURRENT_CAPACITY,
};
enum battery_capacity_state {
BATT_CAPACITY_NORMAL = 1, /* normal cap in battery */
BATT_CAPACITY_WARNING, /* warning cap in battery */
BATT_CAPACITY_CRITICAL, /* critical cap in battery */
BATT_CAPACITY_HIGH, /* high cap in battery */
BATT_CAPACITY_MAX, /* maximum cap in battery */
BATT_CAPACITY_LOW /* low cap in battery */
};
struct axp8xx_sensors {
int id;
const char *name;
const char *desc;
const char *format;
};
static const struct axp8xx_sensors axp8xx_common_sensors[] = {
{
.id = AXP_SENSOR_ACIN_PRESENT,
.name = "acin",
.format = "I",
.desc = "ACIN Present",
},
{
.id = AXP_SENSOR_VBUS_PRESENT,
.name = "vbus",
.format = "I",
.desc = "VBUS Present",
},
{
.id = AXP_SENSOR_BATT_PRESENT,
.name = "bat",
.format = "I",
.desc = "Battery Present",
},
{
.id = AXP_SENSOR_BATT_CHARGING,
.name = "batcharging",
.format = "I",
.desc = "Battery Charging",
},
{
.id = AXP_SENSOR_BATT_CHARGE_STATE,
.name = "batchargestate",
.format = "I",
.desc = "Battery Charge State",
},
{
.id = AXP_SENSOR_BATT_VOLTAGE,
.name = "batvolt",
.format = "I",
.desc = "Battery Voltage",
},
{
.id = AXP_SENSOR_BATT_CHARGE_CURRENT,
.name = "batchargecurrent",
.format = "I",
.desc = "Battery Charging Current",
},
{
.id = AXP_SENSOR_BATT_DISCHARGE_CURRENT,
.name = "batdischargecurrent",
.format = "I",
.desc = "Battery Discharging Current",
},
{
.id = AXP_SENSOR_BATT_CAPACITY_PERCENT,
.name = "batcapacitypercent",
.format = "I",
.desc = "Battery Capacity Percentage",
},
{
.id = AXP_SENSOR_BATT_MAXIMUM_CAPACITY,
.name = "batmaxcapacity",
.format = "I",
.desc = "Battery Maximum Capacity",
},
{
.id = AXP_SENSOR_BATT_CURRENT_CAPACITY,
.name = "batcurrentcapacity",
.format = "I",
.desc = "Battery Current Capacity",
},
};
struct axp8xx_config {
const char *name;
int batsense_step; /* uV */
int charge_step; /* uA */
int discharge_step; /* uA */
int maxcap_step; /* uAh */
int coulomb_step; /* uAh */
};
static struct axp8xx_config axp803_config = {
.name = "AXP803",
.batsense_step = 1100,
.charge_step = 1000,
.discharge_step = 1000,
.maxcap_step = 1456,
.coulomb_step = 1456,
};
struct axp8xx_softc;
struct axp8xx_reg_sc {
@ -558,9 +680,20 @@ struct axp8xx_softc {
int type;
/* Configs */
const struct axp8xx_config *config;
/* Sensors */
const struct axp8xx_sensors *sensors;
int nsensors;
/* Regulators */
struct axp8xx_reg_sc **regs;
int nregs;
/* Warning, shutdown thresholds */
int warn_thres;
int shut_thres;
};
#define AXP_LOCK(sc) mtx_lock(&(sc)->mtx)
@ -756,6 +889,110 @@ axp8xx_shutdown(void *devp, int howto)
axp8xx_write(dev, AXP_POWERBAT, AXP_POWERBAT_SHUTDOWN);
}
static int
axp8xx_sysctl(SYSCTL_HANDLER_ARGS)
{
struct axp8xx_softc *sc;
device_t dev = arg1;
enum axp8xx_sensor sensor = arg2;
const struct axp8xx_config *c;
uint8_t data;
int val, i, found, batt_val;
uint8_t lo, hi;
sc = device_get_softc(dev);
c = sc->config;
for (found = 0, i = 0; i < sc->nsensors; i++) {
if (sc->sensors[i].id == sensor) {
found = 1;
break;
}
}
if (found == 0)
return (ENOENT);
switch (sensor) {
case AXP_SENSOR_ACIN_PRESENT:
if (axp8xx_read(dev, AXP_POWERSRC, &data, 1) == 0)
val = !!(data & AXP_POWERSRC_ACIN);
break;
case AXP_SENSOR_VBUS_PRESENT:
if (axp8xx_read(dev, AXP_POWERSRC, &data, 1) == 0)
val = !!(data & AXP_POWERSRC_VBUS);
break;
case AXP_SENSOR_BATT_PRESENT:
if (axp8xx_read(dev, AXP_POWERMODE, &data, 1) == 0) {
if (data & AXP_POWERMODE_BAT_VALID)
val = !!(data & AXP_POWERMODE_BAT_PRESENT);
}
break;
case AXP_SENSOR_BATT_CHARGING:
if (axp8xx_read(dev, AXP_POWERMODE, &data, 1) == 0)
val = !!(data & AXP_POWERMODE_BAT_CHARGING);
break;
case AXP_SENSOR_BATT_CHARGE_STATE:
if (axp8xx_read(dev, AXP_BAT_CAP, &data, 1) == 0 &&
(data & AXP_BAT_CAP_VALID) != 0) {
batt_val = (data & AXP_BAT_CAP_PERCENT);
if (batt_val <= sc->shut_thres)
val = BATT_CAPACITY_CRITICAL;
else if (batt_val <= sc->warn_thres)
val = BATT_CAPACITY_WARNING;
else
val = BATT_CAPACITY_NORMAL;
}
break;
case AXP_SENSOR_BATT_CAPACITY_PERCENT:
if (axp8xx_read(dev, AXP_BAT_CAP, &data, 1) == 0 &&
(data & AXP_BAT_CAP_VALID) != 0)
val = (data & AXP_BAT_CAP_PERCENT);
break;
case AXP_SENSOR_BATT_VOLTAGE:
if (axp8xx_read(dev, AXP_BATSENSE_HI, &hi, 1) == 0 &&
axp8xx_read(dev, AXP_BATSENSE_LO, &lo, 1) == 0) {
val = (AXP_SENSOR_BAT_H(hi) | AXP_SENSOR_BAT_L(lo));
val *= c->batsense_step;
}
break;
case AXP_SENSOR_BATT_CHARGE_CURRENT:
if (axp8xx_read(dev, AXP_POWERSRC, &data, 1) == 0 &&
(data & AXP_POWERSRC_CHARING) != 0 &&
axp8xx_read(dev, AXP_BATCHG_HI, &hi, 1) == 0 &&
axp8xx_read(dev, AXP_BATCHG_LO, &lo, 1) == 0) {
val = (AXP_SENSOR_BAT_H(hi) | AXP_SENSOR_BAT_L(lo));
val *= c->charge_step;
}
break;
case AXP_SENSOR_BATT_DISCHARGE_CURRENT:
if (axp8xx_read(dev, AXP_POWERSRC, &data, 1) == 0 &&
(data & AXP_POWERSRC_CHARING) == 0 &&
axp8xx_read(dev, AXP_BATDISCHG_HI, &hi, 1) == 0 &&
axp8xx_read(dev, AXP_BATDISCHG_LO, &lo, 1) == 0) {
val = (AXP_SENSOR_BAT_H(hi) | AXP_SENSOR_BAT_L(lo));
val *= c->discharge_step;
}
break;
case AXP_SENSOR_BATT_MAXIMUM_CAPACITY:
if (axp8xx_read(dev, AXP_BAT_MAX_CAP_HI, &hi, 1) == 0 &&
axp8xx_read(dev, AXP_BAT_MAX_CAP_LO, &lo, 1) == 0) {
val = AXP_SENSOR_COULOMB(hi, lo);
val *= c->maxcap_step;
}
break;
case AXP_SENSOR_BATT_CURRENT_CAPACITY:
if (axp8xx_read(dev, AXP_BAT_COULOMB_HI, &hi, 1) == 0 &&
axp8xx_read(dev, AXP_BAT_COULOMB_LO, &lo, 1) == 0) {
val = AXP_SENSOR_COULOMB(hi, lo);
val *= c->coulomb_step;
}
break;
}
return sysctl_handle_opaque(oidp, &val, sizeof(val), req);
}
static void
axp8xx_intr(void *arg)
{
@ -1157,7 +1394,7 @@ axp8xx_attach(device_t dev)
{
struct axp8xx_softc *sc;
struct axp8xx_reg_sc *reg;
uint8_t chip_id;
uint8_t chip_id, val;
phandle_t rnode, child;
int error, i;
@ -1187,6 +1424,10 @@ axp8xx_attach(device_t dev)
sc->nregs += nitems(axp813_regdefs);
break;
}
sc->config = &axp803_config;
sc->sensors = axp8xx_common_sensors;
sc->nsensors = nitems(axp8xx_common_sensors);
sc->regs = malloc(sizeof(struct axp8xx_reg_sc *) * sc->nregs,
M_AXP8XX_REG, M_WAITOK | M_ZERO);
@ -1231,6 +1472,31 @@ axp8xx_attach(device_t dev)
}
}
/* Add sensors */
for (i = 0; i < sc->nsensors; i++) {
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
OID_AUTO, sc->sensors[i].name,
CTLTYPE_INT | CTLFLAG_RD,
dev, sc->sensors[i].id, axp8xx_sysctl,
sc->sensors[i].format,
sc->sensors[i].desc);
}
/* Get thresholds */
if (axp8xx_read(dev, AXP_BAT_CAP_WARN, &val, 1) == 0) {
sc->warn_thres = (val & AXP_BAT_CAP_WARN_LV1) >> 4;
sc->shut_thres = (val & AXP_BAT_CAP_WARN_LV2);
if (bootverbose) {
device_printf(dev,
"Raw reg val: 0x%02x\n", val);
device_printf(dev,
"Warning threshold: 0x%02x\n", sc->warn_thres);
device_printf(dev,
"Shutdown threshold: 0x%02x\n", sc->shut_thres);
}
}
/* Enable interrupts */
axp8xx_write(dev, AXP_IRQEN1,
AXP_IRQEN1_VBUS_LO |