Module Name: src
Committed By: thorpej
Date: Sun Feb 16 20:32:29 UTC 2020
Modified Files:
src/sys/dev/i2c: axppmic.c
Log Message:
Don't access the i2c bus in interrupt context. Instead, mask the
interrupt and process it on a work queue.
To generate a diff of this commit:
cvs rdiff -u -r1.28 -r1.29 src/sys/dev/i2c/axppmic.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/dev/i2c/axppmic.c
diff -u src/sys/dev/i2c/axppmic.c:1.28 src/sys/dev/i2c/axppmic.c:1.29
--- src/sys/dev/i2c/axppmic.c:1.28 Mon Dec 23 14:34:23 2019
+++ src/sys/dev/i2c/axppmic.c Sun Feb 16 20:32:29 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: axppmic.c,v 1.28 2019/12/23 14:34:23 thorpej Exp $ */
+/* $NetBSD: axppmic.c,v 1.29 2020/02/16 20:32:29 thorpej Exp $ */
/*-
* Copyright (c) 2014-2018 Jared McNeill <[email protected]>
@@ -27,7 +27,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: axppmic.c,v 1.28 2019/12/23 14:34:23 thorpej Exp $");
+__KERNEL_RCSID(0, "$NetBSD: axppmic.c,v 1.29 2020/02/16 20:32:29 thorpej Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -36,6 +36,7 @@ __KERNEL_RCSID(0, "$NetBSD: axppmic.c,v
#include <sys/conf.h>
#include <sys/bus.h>
#include <sys/kmem.h>
+#include <sys/workqueue.h>
#include <dev/i2c/i2cvar.h>
@@ -366,6 +367,13 @@ struct axppmic_softc {
i2c_addr_t sc_addr;
int sc_phandle;
+ void *sc_ih;
+ struct workqueue *sc_wq;
+
+ kmutex_t sc_intr_lock;
+ struct work sc_work;
+ bool sc_work_scheduled;
+
const struct axppmic_config *sc_conf;
struct sysmon_pswitch sc_smpsw;
@@ -481,7 +489,6 @@ axppmic_write(i2c_tag_t tag, i2c_addr_t
static int
axppmic_set_voltage(i2c_tag_t tag, i2c_addr_t addr, const struct axppmic_ctrl *c, u_int min, u_int max)
{
- const int flags = 0;
u_int vol, reg_val;
int nstep, error;
uint8_t val;
@@ -512,13 +519,13 @@ axppmic_set_voltage(i2c_tag_t tag, i2c_a
if (vol > max)
return EINVAL;
- iic_acquire_bus(tag, flags);
- if ((error = axppmic_read(tag, addr, c->c_voltage_reg, &val, flags)) == 0) {
+ iic_acquire_bus(tag, 0);
+ if ((error = axppmic_read(tag, addr, c->c_voltage_reg, &val, 0)) == 0) {
val &= ~c->c_voltage_mask;
val |= __SHIFTIN(reg_val, c->c_voltage_mask);
- error = axppmic_write(tag, addr, c->c_voltage_reg, val, flags);
+ error = axppmic_write(tag, addr, c->c_voltage_reg, val, 0);
}
- iic_release_bus(tag, flags);
+ iic_release_bus(tag, 0);
return error;
}
@@ -526,16 +533,15 @@ axppmic_set_voltage(i2c_tag_t tag, i2c_a
static int
axppmic_get_voltage(i2c_tag_t tag, i2c_addr_t addr, const struct axppmic_ctrl *c, u_int *pvol)
{
- const int flags = 0;
int reg_val, error;
uint8_t val;
if (!c->c_voltage_mask)
return EINVAL;
- iic_acquire_bus(tag, flags);
- error = axppmic_read(tag, addr, c->c_voltage_reg, &val, flags);
- iic_release_bus(tag, flags);
+ iic_acquire_bus(tag, 0);
+ error = axppmic_read(tag, addr, c->c_voltage_reg, &val, 0);
+ iic_release_bus(tag, 0);
if (error)
return error;
@@ -561,11 +567,11 @@ axppmic_power_poweroff(device_t dev)
delay(1000000);
- error = iic_acquire_bus(sc->sc_i2c, I2C_F_POLL);
+ error = iic_acquire_bus(sc->sc_i2c, 0);
if (error == 0) {
error = axppmic_write(sc->sc_i2c, sc->sc_addr,
- AXP_POWER_DISABLE_REG, AXP_POWER_DISABLE_CTRL, I2C_F_POLL);
- iic_release_bus(sc->sc_i2c, I2C_F_POLL);
+ AXP_POWER_DISABLE_REG, AXP_POWER_DISABLE_CTRL, 0);
+ iic_release_bus(sc->sc_i2c, 0);
}
if (error) {
device_printf(dev, "WARNING: unable to power off, error %d\n",
@@ -590,7 +596,6 @@ axppmic_sensor_update(struct sysmon_envs
{
struct axppmic_softc *sc = sme->sme_cookie;
const struct axppmic_config *c = sc->sc_conf;
- const int flags = I2C_F_POLL;
uint8_t val, lo, hi;
e->state = ENVSYS_SINVALID;
@@ -601,19 +606,19 @@ axppmic_sensor_update(struct sysmon_envs
switch (e->private) {
case AXP_SENSOR_ACIN_PRESENT:
- if (axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_POWER_SOURCE_REG, &val, flags) == 0) {
+ if (axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_POWER_SOURCE_REG, &val, 0) == 0) {
e->state = ENVSYS_SVALID;
e->value_cur = !!(val & AXP_POWER_SOURCE_ACIN_PRESENT);
}
break;
case AXP_SENSOR_VBUS_PRESENT:
- if (axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_POWER_SOURCE_REG, &val, flags) == 0) {
+ if (axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_POWER_SOURCE_REG, &val, 0) == 0) {
e->state = ENVSYS_SVALID;
e->value_cur = !!(val & AXP_POWER_SOURCE_VBUS_PRESENT);
}
break;
case AXP_SENSOR_BATT_PRESENT:
- if (axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_POWER_MODE_REG, &val, flags) == 0) {
+ if (axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_POWER_MODE_REG, &val, 0) == 0) {
if (val & AXP_POWER_MODE_BATT_VALID) {
e->state = ENVSYS_SVALID;
e->value_cur = !!(val & AXP_POWER_MODE_BATT_PRESENT);
@@ -621,14 +626,14 @@ axppmic_sensor_update(struct sysmon_envs
}
break;
case AXP_SENSOR_BATT_CHARGING:
- if (axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_POWER_MODE_REG, &val, flags) == 0) {
+ if (axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_POWER_MODE_REG, &val, 0) == 0) {
e->state = ENVSYS_SVALID;
e->value_cur = !!(val & AXP_POWER_MODE_BATT_CHARGING);
}
break;
case AXP_SENSOR_BATT_CHARGE_STATE:
if (battery_present &&
- axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_BATT_CAP_REG, &val, flags) == 0 &&
+ axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_BATT_CAP_REG, &val, 0) == 0 &&
(val & AXP_BATT_CAP_VALID) != 0) {
const u_int batt_val = __SHIFTOUT(val, AXP_BATT_CAP_PERCENT);
if (batt_val <= sc->sc_shut_thres) {
@@ -645,7 +650,7 @@ axppmic_sensor_update(struct sysmon_envs
break;
case AXP_SENSOR_BATT_CAPACITY_PERCENT:
if (battery_present &&
- axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_BATT_CAP_REG, &val, flags) == 0 &&
+ axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_BATT_CAP_REG, &val, 0) == 0 &&
(val & AXP_BATT_CAP_VALID) != 0) {
e->state = ENVSYS_SVALID;
e->value_cur = __SHIFTOUT(val, AXP_BATT_CAP_PERCENT);
@@ -653,44 +658,44 @@ axppmic_sensor_update(struct sysmon_envs
break;
case AXP_SENSOR_BATT_VOLTAGE:
if (battery_present &&
- axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_BATSENSE_HI_REG, &hi, flags) == 0 &&
- axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_BATSENSE_LO_REG, &lo, flags) == 0) {
+ axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_BATSENSE_HI_REG, &hi, 0) == 0 &&
+ axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_BATSENSE_LO_REG, &lo, 0) == 0) {
e->state = ENVSYS_SVALID;
e->value_cur = AXP_ADC_RAW(hi, lo) * c->batsense_step;
}
break;
case AXP_SENSOR_BATT_CHARGE_CURRENT:
if (battery_present &&
- axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_POWER_SOURCE_REG, &val, flags) == 0 &&
+ axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_POWER_SOURCE_REG, &val, 0) == 0 &&
(val & AXP_POWER_SOURCE_CHARGE_DIRECTION) != 0 &&
- axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_BATTCHG_HI_REG, &hi, flags) == 0 &&
- axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_BATTCHG_LO_REG, &lo, flags) == 0) {
+ axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_BATTCHG_HI_REG, &hi, 0) == 0 &&
+ axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_BATTCHG_LO_REG, &lo, 0) == 0) {
e->state = ENVSYS_SVALID;
e->value_cur = AXP_ADC_RAW(hi, lo) * c->charge_step;
}
break;
case AXP_SENSOR_BATT_DISCHARGE_CURRENT:
if (battery_present &&
- axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_POWER_SOURCE_REG, &val, flags) == 0 &&
+ axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_POWER_SOURCE_REG, &val, 0) == 0 &&
(val & AXP_POWER_SOURCE_CHARGE_DIRECTION) == 0 &&
- axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_BATTDISCHG_HI_REG, &hi, flags) == 0 &&
- axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_BATTDISCHG_LO_REG, &lo, flags) == 0) {
+ axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_BATTDISCHG_HI_REG, &hi, 0) == 0 &&
+ axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_BATTDISCHG_LO_REG, &lo, 0) == 0) {
e->state = ENVSYS_SVALID;
e->value_cur = AXP_ADC_RAW(hi, lo) * c->discharge_step;
}
break;
case AXP_SENSOR_BATT_MAXIMUM_CAPACITY:
if (battery_present &&
- axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_BATT_MAX_CAP_HI_REG, &hi, flags) == 0 &&
- axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_BATT_MAX_CAP_LO_REG, &lo, flags) == 0) {
+ axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_BATT_MAX_CAP_HI_REG, &hi, 0) == 0 &&
+ axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_BATT_MAX_CAP_LO_REG, &lo, 0) == 0) {
e->state = (hi & AXP_BATT_MAX_CAP_VALID) ? ENVSYS_SVALID : ENVSYS_SINVALID;
e->value_cur = AXP_COULOMB_RAW(hi, lo) * c->maxcap_step;
}
break;
case AXP_SENSOR_BATT_CURRENT_CAPACITY:
if (battery_present &&
- axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_BATT_COULOMB_HI_REG, &hi, flags) == 0 &&
- axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_BATT_COULOMB_LO_REG, &lo, flags) == 0) {
+ axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_BATT_COULOMB_HI_REG, &hi, 0) == 0 &&
+ axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_BATT_COULOMB_LO_REG, &lo, 0) == 0) {
e->state = (hi & AXP_BATT_COULOMB_VALID) ? ENVSYS_SVALID : ENVSYS_SINVALID;
e->value_cur = AXP_COULOMB_RAW(hi, lo) * c->coulomb_step;
}
@@ -702,7 +707,6 @@ static void
axppmic_sensor_refresh(struct sysmon_envsys *sme, envsys_data_t *e)
{
struct axppmic_softc *sc = sme->sme_cookie;
- const int flags = I2C_F_POLL;
switch (e->private) {
case AXP_SENSOR_BATT_CAPACITY_PERCENT:
@@ -710,16 +714,16 @@ axppmic_sensor_refresh(struct sysmon_env
case AXP_SENSOR_BATT_CHARGE_CURRENT:
case AXP_SENSOR_BATT_DISCHARGE_CURRENT:
/* Always update battery capacity and ADCs */
- iic_acquire_bus(sc->sc_i2c, flags);
+ iic_acquire_bus(sc->sc_i2c, 0);
axppmic_sensor_update(sme, e);
- iic_release_bus(sc->sc_i2c, flags);
+ iic_release_bus(sc->sc_i2c, 0);
break;
default:
/* Refresh if the sensor is not in valid state */
if (e->state != ENVSYS_SVALID) {
- iic_acquire_bus(sc->sc_i2c, flags);
+ iic_acquire_bus(sc->sc_i2c, 0);
axppmic_sensor_update(sme, e);
- iic_release_bus(sc->sc_i2c, flags);
+ iic_release_bus(sc->sc_i2c, 0);
}
break;
}
@@ -728,15 +732,42 @@ axppmic_sensor_refresh(struct sysmon_env
static int
axppmic_intr(void *priv)
{
- struct axppmic_softc *sc = priv;
- const struct axppmic_config *c = sc->sc_conf;
- const int flags = I2C_F_POLL;
+ struct axppmic_softc * const sc = priv;
+
+ mutex_enter(&sc->sc_intr_lock);
+
+ fdtbus_intr_mask(sc->sc_phandle, sc->sc_ih);
+
+ /* Interrupt is always masked when work is scheduled! */
+ KASSERT(!sc->sc_work_scheduled);
+ sc->sc_work_scheduled = true;
+ workqueue_enqueue(sc->sc_wq, &sc->sc_work, NULL);
+
+ mutex_exit(&sc->sc_intr_lock);
+
+ return 1;
+}
+
+static void
+axppmic_work(struct work *work, void *arg)
+{
+ struct axppmic_softc * const sc =
+ container_of(work, struct axppmic_softc, sc_work);
+ const struct axppmic_config * const c = sc->sc_conf;
+ const int flags = 0;
uint8_t stat;
u_int n;
+ KASSERT(sc->sc_work_scheduled);
+
iic_acquire_bus(sc->sc_i2c, flags);
for (n = 1; n <= c->irq_regs; n++) {
if (axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_IRQ_STATUS_REG(n), &stat, flags) == 0) {
+ if (stat != 0) {
+ axppmic_write(sc->sc_i2c, sc->sc_addr,
+ AXP_IRQ_STATUS_REG(n), stat, flags);
+ }
+
if (n == c->poklirq.reg && (stat & c->poklirq.mask) != 0)
sysmon_task_queue_sched(0, axppmic_task_shut, sc);
if (n == c->acinirq.reg && (stat & c->acinirq.mask) != 0)
@@ -749,15 +780,14 @@ axppmic_intr(void *priv)
axppmic_sensor_update(sc->sc_sme, &sc->sc_sensor[AXP_SENSOR_BATT_CHARGING]);
if (n == c->chargestirq.reg && (stat & c->chargestirq.mask) != 0)
axppmic_sensor_update(sc->sc_sme, &sc->sc_sensor[AXP_SENSOR_BATT_CHARGE_STATE]);
-
- if (stat != 0)
- axppmic_write(sc->sc_i2c, sc->sc_addr,
- AXP_IRQ_STATUS_REG(n), stat, flags);
}
}
iic_release_bus(sc->sc_i2c, flags);
- return 1;
+ mutex_enter(&sc->sc_intr_lock);
+ sc->sc_work_scheduled = false;
+ fdtbus_intr_unmask(sc->sc_phandle, sc->sc_ih);
+ mutex_exit(&sc->sc_intr_lock);
}
static void
@@ -788,7 +818,7 @@ axppmic_attach_battery(struct axppmic_so
uint8_t val;
iic_acquire_bus(sc->sc_i2c, 0);
- if (axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_BATT_CAP_WARN_REG, &val, I2C_F_POLL) == 0) {
+ if (axppmic_read(sc->sc_i2c, sc->sc_addr, AXP_BATT_CAP_WARN_REG, &val, 0) == 0) {
sc->sc_warn_thres = __SHIFTOUT(val, AXP_BATT_CAP_WARN_LV1) + 5;
sc->sc_shut_thres = __SHIFTOUT(val, AXP_BATT_CAP_WARN_LV2);
}
@@ -917,7 +947,6 @@ axppmic_attach(device_t parent, device_t
int phandle, child, i;
uint8_t irq_mask, val;
int error;
- void *ih;
(void) iic_compatible_match(ia, compat_data, &dce);
KASSERT(dce != NULL);
@@ -955,30 +984,57 @@ axppmic_attach(device_t parent, device_t
sc->sc_smpsw.smpsw_type = PSWITCH_TYPE_POWER;
sysmon_pswitch_register(&sc->sc_smpsw);
+ mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_VM);
+
if (c->irq_regs > 0) {
- iic_acquire_bus(sc->sc_i2c, 0);
- for (i = 1; i <= c->irq_regs; i++) {
- irq_mask = 0;
- if (i == c->poklirq.reg)
- irq_mask |= c->poklirq.mask;
- if (i == c->acinirq.reg)
- irq_mask |= c->acinirq.mask;
- if (i == c->vbusirq.reg)
- irq_mask |= c->vbusirq.mask;
- if (i == c->battirq.reg)
- irq_mask |= c->battirq.mask;
- if (i == c->chargeirq.reg)
- irq_mask |= c->chargeirq.mask;
- if (i == c->chargestirq.reg)
- irq_mask |= c->chargestirq.mask;
- axppmic_write(sc->sc_i2c, sc->sc_addr, AXP_IRQ_ENABLE_REG(i), irq_mask, 0);
+ char intrstr[128];
+
+ if (!fdtbus_intr_str(sc->sc_phandle, 0,
+ intrstr, sizeof(intrstr))) {
+ aprint_error_dev(self,
+ "WARNING: failed to decode interrupt\n");
+ }
+
+ sc->sc_ih = fdtbus_intr_establish(sc->sc_phandle, 0, IPL_VM,
+ FDT_INTR_MPSAFE,
+ axppmic_intr, sc);
+ if (sc->sc_ih == NULL) {
+ aprint_error_dev(self,
+ "WARNING: couldn't establish interrupt handler\n");
}
- iic_release_bus(sc->sc_i2c, 0);
- ih = fdtbus_intr_establish(sc->sc_phandle, 0, IPL_VM, FDT_INTR_MPSAFE,
- axppmic_intr, sc);
- if (ih == NULL) {
- aprint_error_dev(self, "WARNING: couldn't establish interrupt handler\n");
+ error = workqueue_create(&sc->sc_wq, device_xname(self),
+ axppmic_work, NULL,
+ PRI_SOFTSERIAL, IPL_VM,
+ WQ_MPSAFE);
+ if (error) {
+ sc->sc_wq = NULL;
+ aprint_error_dev(self,
+ "WARNING: couldn't create work queue: error %d\n",
+ error);
+ }
+
+ if (sc->sc_ih != NULL && sc->sc_wq != NULL) {
+ iic_acquire_bus(sc->sc_i2c, 0);
+ for (i = 1; i <= c->irq_regs; i++) {
+ irq_mask = 0;
+ if (i == c->poklirq.reg)
+ irq_mask |= c->poklirq.mask;
+ if (i == c->acinirq.reg)
+ irq_mask |= c->acinirq.mask;
+ if (i == c->vbusirq.reg)
+ irq_mask |= c->vbusirq.mask;
+ if (i == c->battirq.reg)
+ irq_mask |= c->battirq.mask;
+ if (i == c->chargeirq.reg)
+ irq_mask |= c->chargeirq.mask;
+ if (i == c->chargestirq.reg)
+ irq_mask |= c->chargestirq.mask;
+ axppmic_write(sc->sc_i2c, sc->sc_addr,
+ AXP_IRQ_ENABLE_REG(i),
+ irq_mask, 0);
+ }
+ iic_release_bus(sc->sc_i2c, 0);
}
}