The fast polling of ihidev may cause a problem during suspend/resume
because dwiic may be in an unknown state, so add a DVACT_QUIESCE
handler to properly shut it down.
Even if polling is requested, register the interrupt handler too.
If it ever fires, stop polling and just use the interrupt. This is
what led me to discover that ihidev's interrupt starts firing after
a suspend/resume cycle (but I still have no idea why it doesn't work
before that).
Index: dev/acpi/dwiic_acpi.c
===================================================================
RCS file: /cvs/src/sys/dev/acpi/dwiic_acpi.c,v
retrieving revision 1.9
diff -u -p -u -p -r1.9 dwiic_acpi.c
--- dev/acpi/dwiic_acpi.c 16 Jul 2019 19:12:32 -0000 1.9
+++ dev/acpi/dwiic_acpi.c 19 Jul 2019 19:11:21 -0000
@@ -457,8 +457,9 @@ dwiic_acpi_found_ihidev(struct dwiic_sof
aml_freevalue(&res);
- if (!sc->sc_poll_ihidev &&
- !(crs.irq_int == 0 && crs.gpio_int_node == NULL))
+ if (sc->sc_poll_ihidev)
+ ia.ia_poll = 1;
+ if (!(crs.irq_int == 0 && crs.gpio_int_node == NULL))
ia.ia_intr = &crs;
if (config_found(sc->sc_iic, &ia, dwiic_i2c_print)) {
Index: dev/i2c/i2cvar.h
===================================================================
RCS file: /cvs/src/sys/dev/i2c/i2cvar.h,v
retrieving revision 1.16
diff -u -p -u -p -r1.16 i2cvar.h
--- dev/i2c/i2cvar.h 23 Apr 2016 09:40:28 -0000 1.16
+++ dev/i2c/i2cvar.h 19 Jul 2019 19:11:21 -0000
@@ -113,6 +113,7 @@ struct i2c_attach_args {
char *ia_name; /* chip name */
void *ia_cookie; /* pass extra info from bus to dev */
void *ia_intr; /* interrupt info */
+ int ia_poll; /* to force polling */
};
/*
Index: dev/i2c/ihidev.c
===================================================================
RCS file: /cvs/src/sys/dev/i2c/ihidev.c,v
retrieving revision 1.19
diff -u -p -u -p -r1.19 ihidev.c
--- dev/i2c/ihidev.c 8 Apr 2019 17:50:45 -0000 1.19
+++ dev/i2c/ihidev.c 19 Jul 2019 19:11:21 -0000
@@ -63,6 +63,7 @@ static int I2C_HID_POWER_OFF = 0x1;
int ihidev_match(struct device *, void *, void *);
void ihidev_attach(struct device *, struct device *, void *);
int ihidev_detach(struct device *, int);
+int ihidev_activate(struct device *, int);
int ihidev_hid_command(struct ihidev_softc *, int, void *);
int ihidev_intr(void *);
@@ -80,7 +81,7 @@ struct cfattach ihidev_ca = {
ihidev_match,
ihidev_attach,
ihidev_detach,
- NULL
+ ihidev_activate,
};
struct cfdriver ihidev_cd = {
@@ -128,7 +129,7 @@ ihidev_attach(struct device *parent, str
printf(", can't establish interrupt");
}
- if (sc->sc_ih == NULL) {
+ if (ia->ia_poll) {
printf(" (polling)");
sc->sc_poll = 1;
sc->sc_fastpoll = 1;
@@ -227,6 +228,39 @@ ihidev_detach(struct device *self, int f
return (0);
}
+int
+ihidev_activate(struct device *self, int act)
+{
+ struct ihidev_softc *sc = (struct ihidev_softc *)self;
+
+ DPRINTF(("%s(%d)\n", __func__, act));
+
+ switch (act) {
+ case DVACT_QUIESCE:
+ sc->sc_dying = 1;
+ if (sc->sc_poll && timeout_initialized(&sc->sc_timer)) {
+ DPRINTF(("%s: canceling polling\n",
+ sc->sc_dev.dv_xname));
+ timeout_del_barrier(&sc->sc_timer);
+ }
+ if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER,
+ &I2C_HID_POWER_OFF))
+ printf("%s: failed to power down\n",
+ sc->sc_dev.dv_xname);
+ break;
+ case DVACT_WAKEUP:
+ ihidev_reset(sc);
+ sc->sc_dying = 0;
+ if (sc->sc_poll && timeout_initialized(&sc->sc_timer))
+ timeout_add(&sc->sc_timer, 2000);
+ break;
+ }
+
+ config_activate_children(self, act);
+
+ return 0;
+}
+
void
ihidev_sleep(struct ihidev_softc *sc, int ms)
{
@@ -580,6 +614,16 @@ ihidev_hid_desc_parse(struct ihidev_soft
return (0);
}
+void
+ihidev_poll(void *arg)
+{
+ struct ihidev_softc *sc = arg;
+
+ sc->sc_frompoll = 1;
+ ihidev_intr(sc);
+ sc->sc_frompoll = 0;
+}
+
int
ihidev_intr(void *arg)
{
@@ -589,6 +633,16 @@ ihidev_intr(void *arg)
u_char *p;
u_int rep = 0;
+ if (sc->sc_dying)
+ return 1;
+
+ if (sc->sc_poll && !sc->sc_frompoll) {
+ printf("%s: received interrupt while polling, disabling "
+ "polling\n", sc->sc_dev.dv_xname);
+ sc->sc_poll = 0;
+ timeout_del_barrier(&sc->sc_timer);
+ }
+
/*
* XXX: force I2C_F_POLL for now to avoid dwiic interrupting
* while we are interrupting
@@ -660,7 +714,7 @@ ihidev_intr(void *arg)
scd->sc_intr(scd, p, psize);
- if (sc->sc_poll && fast != sc->sc_fastpoll) {
+ if (sc->sc_poll && (fast != sc->sc_fastpoll)) {
DPRINTF(("%s: %s->%s polling\n", sc->sc_dev.dv_xname,
sc->sc_fastpoll ? "fast" : "slow",
fast ? "fast" : "slow"));
@@ -668,7 +722,8 @@ ihidev_intr(void *arg)
}
more_polling:
- if (sc->sc_poll && sc->sc_refcnt && !timeout_pending(&sc->sc_timer))
+ if (sc->sc_poll && sc->sc_refcnt && !sc->sc_dying &&
+ !timeout_pending(&sc->sc_timer))
timeout_add_msec(&sc->sc_timer,
sc->sc_fastpoll ? FAST_POLL_MS : SLOW_POLL_MS);
@@ -740,7 +795,7 @@ ihidev_open(struct ihidev *scd)
if (sc->sc_poll) {
if (!timeout_initialized(&sc->sc_timer))
- timeout_set(&sc->sc_timer, (void *)ihidev_intr, sc);
+ timeout_set(&sc->sc_timer, (void *)ihidev_poll, sc);
if (!timeout_pending(&sc->sc_timer))
timeout_add(&sc->sc_timer, FAST_POLL_MS);
}
@@ -766,7 +821,7 @@ ihidev_close(struct ihidev *scd)
/* no sub-devices open, conserve power */
- if (sc->sc_poll)
+ if (sc->sc_poll && timeout_pending(&sc->sc_timer))
timeout_del(&sc->sc_timer);
if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, &I2C_HID_POWER_OFF))
Index: dev/i2c/ihidev.h
===================================================================
RCS file: /cvs/src/sys/dev/i2c/ihidev.h,v
retrieving revision 1.6
diff -u -p -u -p -r1.6 ihidev.h
--- dev/i2c/ihidev.h 25 Aug 2018 18:32:05 -0000 1.6
+++ dev/i2c/ihidev.h 19 Jul 2019 19:11:21 -0000
@@ -89,8 +89,10 @@ struct ihidev_softc {
int sc_refcnt;
int sc_poll;
+ int sc_frompoll;
int sc_fastpoll;
struct timeout sc_timer;
+ int sc_dying;
};
struct ihidev {