git: a4ed783cee2a - main - ig4: unconditionally un-idle the controller core on resume
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sun, 21 Jun 2026 05:29:51 UTC
The branch main has been updated by dteske:
URL: https://cgit.FreeBSD.org/src/commit/?id=a4ed783cee2a10629db7a6c254712f96bd820288
commit a4ed783cee2a10629db7a6c254712f96bd820288
Author: Devin Teske <dteske@FreeBSD.org>
AuthorDate: 2026-06-19 17:24:58 +0000
Commit: Devin Teske <dteske@FreeBSD.org>
CommitDate: 2026-06-21 05:28:47 +0000
ig4: unconditionally un-idle the controller core on resume
On controllers with the LPSS "additional registers" (Skylake and later,
IG4_HAS_ADDREGS), ig4iic_suspend() places the controller in the device
idle state (IG4_DEVICE_IDLE) and asserts core reset. While idle the
DesignWare core is power-gated: its register bank reads back as zero and
writes are dropped until the core is taken out of the idle state again.
ig4iic_set_config(), called from ig4iic_resume(), only performs that
un-idle handshake when it observes IG4_RESTORE_REQUIRED set in
DEVIDLE_CTRL. Some platforms (e.g. Intel Alder Lake-P) do not raise
that status across suspend-to-idle (S0ix). The core is then left gated:
set_config()'s register writes have no effect, it nevertheless returns
success, and every subsequent transfer fails with IIC_ETIMEOUT, leaving
child I2C-HID devices (touchpad, touchscreen) dead after resume.
Give ig4iic_set_config() a force_restore argument and pass it from
ig4iic_resume() so the un-idle handshake runs unconditionally for
IG4_HAS_ADDREGS controllers, regardless of the RESTORE_REQUIRED status.
This keeps the handshake and its reset/restore sequence in one place
instead of duplicating it in the resume path.
The DesignWare core's register bank stays inaccessible (reads back as
zero) until it leaves reset, which happens when ig4iic_set_config()
deasserts the core reset. Rather than waiting a fixed, guessed
interval, poll the fixed component-type signature (IG4_REG_COMP_TYPE ==
IG4_COMP_TYPE) immediately after that deassert and continue as soon as
the core is ready. On Alder Lake-P the core is ready with no measurable
delay; the bounded poll keeps this correct on slower parts, and warns if
the core never leaves reset.
While integrating these I2C-HID touch devices, also raise hid(4)'s
MAXLOCCNT from 2048 to 4096: the report-descriptor parser truncates any
variable main item whose usage count exceeds MAXLOCCNT, silently
dropping the trailing report fields. Some contemporary touch devices
declare variable items with more than 2048 entries, so part of their
input reports was being discarded.
Reviewed by: adrian
MFC after: 2 weeks
Differential Revision: https://reviews.freebsd.org/D57673
---
sys/dev/hid/hid.c | 2 +-
sys/dev/ichiic/ig4_iic.c | 52 ++++++++++++++++++++++++++++++++++++++++++------
2 files changed, 47 insertions(+), 7 deletions(-)
diff --git a/sys/dev/hid/hid.c b/sys/dev/hid/hid.c
index 453c37d806fc..c07343fc057f 100644
--- a/sys/dev/hid/hid.c
+++ b/sys/dev/hid/hid.c
@@ -66,7 +66,7 @@ hid_test_quirk_t *hid_test_quirk_p = &hid_test_quirk_w;
#define MAXUSAGE 64
#define MAXPUSH 4
#define MAXID 16
-#define MAXLOCCNT 2048
+#define MAXLOCCNT 4096
struct hid_pos_data {
uint32_t rid;
diff --git a/sys/dev/ichiic/ig4_iic.c b/sys/dev/ichiic/ig4_iic.c
index cd88b28a2d52..161a940c5e7a 100644
--- a/sys/dev/ichiic/ig4_iic.c
+++ b/sys/dev/ichiic/ig4_iic.c
@@ -134,7 +134,7 @@ static const struct ig4_hw ig4iic_hw[] = {
},
};
-static int ig4iic_set_config(ig4iic_softc_t *sc, bool reset);
+static int ig4iic_set_config(ig4iic_softc_t *sc, bool reset, bool force_restore);
static driver_filter_t ig4iic_intr;
static void ig4iic_dump(ig4iic_softc_t *sc);
@@ -661,7 +661,7 @@ ig4iic_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
ig4iic_xfer_abort(sc) != 0) {
device_printf(sc->dev, "Failed to abort "
"transfer. Do the controller reset.\n");
- ig4iic_set_config(sc, true);
+ ig4iic_set_config(sc, true, false);
} else {
while (reg_read(sc, IG4_REG_I2C_STA) &
IG4_STATUS_RX_NOTEMPTY)
@@ -910,12 +910,23 @@ ig4iic_get_config(ig4iic_softc_t *sc)
}
static int
-ig4iic_set_config(ig4iic_softc_t *sc, bool reset)
+ig4iic_set_config(ig4iic_softc_t *sc, bool reset, bool force_restore)
{
uint32_t v;
+ int i;
+ /*
+ * Take the LPSS DesignWare core out of the "device idle" state. While
+ * idle the core is power-gated: its register bank reads back as zero and
+ * writes are dropped. Normally we perform this un-idle handshake only
+ * when IG4_RESTORE_REQUIRED is observed in DEVIDLE_CTRL, but some
+ * platforms (e.g. Intel Alder Lake-P) do not raise that status across
+ * suspend-to-idle (S0ix). Callers that know the core was idled (the
+ * resume path) pass force_restore to perform the handshake regardless.
+ */
v = reg_read(sc, IG4_REG_DEVIDLE_CTRL);
- if (IG4_HAS_ADDREGS(sc->version) && (v & IG4_RESTORE_REQUIRED)) {
+ if (IG4_HAS_ADDREGS(sc->version) &&
+ (force_restore || (v & IG4_RESTORE_REQUIRED))) {
reg_write(sc, IG4_REG_DEVIDLE_CTRL, IG4_DEVICE_IDLE | IG4_RESTORE_REQUIRED);
reg_write(sc, IG4_REG_DEVIDLE_CTRL, 0);
pause("i2crst", 1);
@@ -928,6 +939,23 @@ ig4iic_set_config(ig4iic_softc_t *sc, bool reset)
} else if (IG4_HAS_ADDREGS(sc->version) && reset) {
reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_ASSERT_SKL);
reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_DEASSERT_SKL);
+ /*
+ * The DesignWare core's register bank is inaccessible (reads
+ * back as zero) until it leaves reset. Poll the fixed
+ * component-type signature so we proceed exactly when the core
+ * is ready, rather than waiting a fixed, guessed interval.
+ */
+ for (i = 0; i < 100; i++) {
+ if (reg_read(sc, IG4_REG_COMP_TYPE) == IG4_COMP_TYPE)
+ break;
+ DELAY(10);
+ }
+ if (i == 100) {
+ device_printf(sc->dev, "WARNING: I2C controller core "
+ "did not leave reset (COMP_TYPE 0x%08x); device "
+ "may be unusable\n",
+ reg_read(sc, IG4_REG_COMP_TYPE));
+ }
}
if (sc->version == IG4_ATOM)
@@ -1037,7 +1065,7 @@ ig4iic_attach(ig4iic_softc_t *sc)
ig4iic_get_config(sc);
- error = ig4iic_set_config(sc, IG4_HAS_ADDREGS(sc->version));
+ error = ig4iic_set_config(sc, IG4_HAS_ADDREGS(sc->version), false);
if (error)
goto done;
ig4iic_get_fifo(sc);
@@ -1130,7 +1158,19 @@ int ig4iic_resume(ig4iic_softc_t *sc)
int error;
sx_xlock(&sc->call_lock);
- if (ig4iic_set_config(sc, IG4_HAS_ADDREGS(sc->version)))
+
+ /*
+ * ig4iic_suspend() leaves the controller power-gated in the LPSS
+ * "device idle" state. Force the un-idle/restore handshake in
+ * ig4iic_set_config() so the DesignWare core is reliably restored
+ * before reconfiguration: some platforms (e.g. Intel Alder Lake-P) do
+ * not raise IG4_RESTORE_REQUIRED across suspend-to-idle (S0ix), and
+ * without the forced handshake the core stays gated, set_config()'s
+ * writes are dropped, and every subsequent transfer fails with
+ * IIC_ETIMEOUT, leaving child I2C-HID devices (touchpad, touchscreen)
+ * dead after resume.
+ */
+ if (ig4iic_set_config(sc, IG4_HAS_ADDREGS(sc->version), true))
device_printf(sc->dev, "controller error during resume\n");
sx_xunlock(&sc->call_lock);