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 <jmcne...@invisible.ca>
@@ -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);
 		}
 	}
 

Reply via email to