Skip to content

Instantly share code, notes, and snippets.

@johalun
Created June 23, 2019 19:13
Show Gist options
  • Save johalun/ffc271a07a0cf50d0bc816138c4eec81 to your computer and use it in GitHub Desktop.
Save johalun/ffc271a07a0cf50d0bc816138c4eec81 to your computer and use it in GitHub Desktop.
iichid suspend resume
commit c86f6c434c203ab8325cd56fff2bc27f2ae0b142
Author: Johannes Lundberg <johalun0@gmail.com>
Date: Wed May 22 14:54:23 2019
Implement suspend resume for ig4 and i2c devices
diff --git a/sys/dev/ichiic/ig4_acpi.c b/sys/dev/ichiic/ig4_acpi.c
index f0b431fd1007..c3d8be6e0149 100644
--- a/sys/dev/ichiic/ig4_acpi.c
+++ b/sys/dev/ichiic/ig4_acpi.c
@@ -153,6 +153,8 @@ static device_method_t ig4iic_acpi_methods[] = {
DEVMETHOD(device_probe, ig4iic_acpi_probe),
DEVMETHOD(device_attach, ig4iic_acpi_attach),
DEVMETHOD(device_detach, ig4iic_acpi_detach),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
/* iicbus interface */
DEVMETHOD(iicbus_transfer, ig4iic_transfer),
diff --git a/sys/dev/ichiic/ig4_iic.c b/sys/dev/ichiic/ig4_iic.c
index 11beb743ed4b..8dab36550f09 100644
--- a/sys/dev/ichiic/ig4_iic.c
+++ b/sys/dev/ichiic/ig4_iic.c
@@ -655,6 +655,97 @@ ig4iic_attach(ig4iic_softc_t *sc)
return (error);
}
+int
+ig4iic_resume(ig4iic_softc_t *sc)
+{
+ int error = 0;
+ uint32_t v;
+
+ v = reg_read(sc, IG4_REG_DEVIDLE_CTRL);
+ if (sc->version == IG4_SKYLAKE && (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);
+
+ reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_ASSERT_SKL);
+ reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_DEASSERT_SKL);
+ DELAY(1000);
+ }
+
+ if (sc->version == IG4_ATOM)
+ v = reg_read(sc, IG4_REG_COMP_TYPE);
+
+ if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) {
+ v = reg_read(sc, IG4_REG_COMP_PARAM1);
+ v = reg_read(sc, IG4_REG_GENERAL);
+ /*
+ * The content of IG4_REG_GENERAL is different for each
+ * controller version.
+ */
+ if (sc->version == IG4_HASWELL &&
+ (v & IG4_GENERAL_SWMODE) == 0) {
+ v |= IG4_GENERAL_SWMODE;
+ reg_write(sc, IG4_REG_GENERAL, v);
+ v = reg_read(sc, IG4_REG_GENERAL);
+ }
+ }
+
+ if (sc->version == IG4_HASWELL) {
+ v = reg_read(sc, IG4_REG_SW_LTR_VALUE);
+ v = reg_read(sc, IG4_REG_AUTO_LTR_VALUE);
+ } else if (sc->version == IG4_SKYLAKE) {
+ v = reg_read(sc, IG4_REG_ACTIVE_LTR_VALUE);
+ v = reg_read(sc, IG4_REG_IDLE_LTR_VALUE);
+ }
+
+ if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) {
+ v = reg_read(sc, IG4_REG_COMP_VER);
+ if (v < IG4_COMP_MIN_VER) {
+ error = ENXIO;
+ goto done;
+ }
+ }
+ v = reg_read(sc, IG4_REG_SS_SCL_HCNT);
+ v = reg_read(sc, IG4_REG_SS_SCL_LCNT);
+ v = reg_read(sc, IG4_REG_FS_SCL_HCNT);
+ v = reg_read(sc, IG4_REG_FS_SCL_LCNT);
+ v = reg_read(sc, IG4_REG_SDA_HOLD);
+
+ v = reg_read(sc, IG4_REG_SS_SCL_HCNT);
+ reg_write(sc, IG4_REG_FS_SCL_HCNT, v);
+ v = reg_read(sc, IG4_REG_SS_SCL_LCNT);
+ reg_write(sc, IG4_REG_FS_SCL_LCNT, v);
+
+ /*
+ * Program based on a 25000 Hz clock. This is a bit of a
+ * hack (obviously). The defaults are 400 and 470 for standard
+ * and 60 and 130 for fast. The defaults for standard fail
+ * utterly (presumably cause an abort) because the clock time
+ * is ~18.8ms by default. This brings it down to ~4ms (for now).
+ */
+ reg_write(sc, IG4_REG_SS_SCL_HCNT, 100);
+ reg_write(sc, IG4_REG_SS_SCL_LCNT, 125);
+ reg_write(sc, IG4_REG_FS_SCL_HCNT, 100);
+ reg_write(sc, IG4_REG_FS_SCL_LCNT, 125);
+
+ /*
+ * Use a threshold of 1 so we get interrupted on each character,
+ * allowing us to use mtx_sleep() in our poll code. Not perfect
+ * but this is better than using DELAY() for receiving data.
+ *
+ * See ig4_var.h for details on interrupt handler synchronization.
+ */
+ reg_write(sc, IG4_REG_RX_TL, 1);
+
+ reg_write(sc, IG4_REG_CTL,
+ IG4_CTL_MASTER |
+ IG4_CTL_SLAVE_DISABLE |
+ IG4_CTL_RESTARTEN |
+ IG4_CTL_SPEED_STD);
+
+done:
+ return (error);
+}
+
void
ig4iic_start(void *xdev)
{
diff --git a/sys/dev/ichiic/ig4_pci.c b/sys/dev/ichiic/ig4_pci.c
index 8d7275ae57e6..d1267478b653 100644
--- a/sys/dev/ichiic/ig4_pci.c
+++ b/sys/dev/ichiic/ig4_pci.c
@@ -206,11 +206,32 @@ ig4iic_pci_detach(device_t dev)
return (0);
}
+static int
+ig4iic_pci_suspend(device_t dev)
+{
+ device_printf(dev, "%s\n", __func__);
+
+ /* ig4iic_softc_t *sc = device_get_softc(dev); */
+ return (bus_generic_suspend(dev));
+}
+
+static int
+ig4iic_pci_resume(device_t dev)
+{
+ device_printf(dev, "%s\n", __func__);
+
+ ig4iic_softc_t *sc = device_get_softc(dev);
+ ig4iic_resume(sc);
+ return (bus_generic_resume(dev));
+}
+
static device_method_t ig4iic_pci_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, ig4iic_pci_probe),
DEVMETHOD(device_attach, ig4iic_pci_attach),
DEVMETHOD(device_detach, ig4iic_pci_detach),
+ DEVMETHOD(device_suspend, ig4iic_pci_suspend),
+ DEVMETHOD(device_resume, ig4iic_pci_resume),
DEVMETHOD(iicbus_transfer, ig4iic_transfer),
DEVMETHOD(iicbus_reset, ig4iic_reset),
diff --git a/sys/dev/ichiic/ig4_var.h b/sys/dev/ichiic/ig4_var.h
index 3250df5f7496..c80eb680de72 100644
--- a/sys/dev/ichiic/ig4_var.h
+++ b/sys/dev/ichiic/ig4_var.h
@@ -104,6 +104,7 @@ typedef struct ig4iic_softc ig4iic_softc_t;
/* Attach/Detach called from ig4iic_pci_*() */
int ig4iic_attach(ig4iic_softc_t *sc);
int ig4iic_detach(ig4iic_softc_t *sc);
+int ig4iic_resume(ig4iic_softc_t *sc);
/* iicbus methods */
extern iicbus_transfer_t ig4iic_transfer;
diff --git a/sys/dev/iicbus/iicbus.c b/sys/dev/iicbus/iicbus.c
index de30c0168901..3fbe47eff8af 100644
--- a/sys/dev/iicbus/iicbus.c
+++ b/sys/dev/iicbus/iicbus.c
@@ -142,6 +142,51 @@ iicbus_detach(device_t dev)
return (0);
}
+static int
+iicbus_suspend(device_t dev)
+{
+ device_printf(dev, "%s\n", __func__);
+
+ device_t child, *devlist;
+ int error, i, numdevs;
+
+ if ((error = device_get_children(dev, &devlist, &numdevs)) != 0)
+ return (error);
+
+ device_printf(dev, "%s: Have %d children\n", __func__, numdevs);
+
+ for (i = 0; i < numdevs; i++) {
+ child = devlist[i];
+ device_printf(dev, "%s: Suspend child %d\n", __func__, i);
+ bus_generic_suspend_child(dev, child);
+ }
+ free(devlist, M_TEMP);
+ return (0);
+}
+
+static int
+iicbus_resume(device_t dev)
+{
+ device_printf(dev, "%s\n", __func__);
+
+ device_t child, *devlist;
+ int error, i, numdevs;
+
+ if ((error = device_get_children(dev, &devlist, &numdevs)) != 0)
+ return (error);
+
+ device_printf(dev, "%s: Have %d children\n", __func__, numdevs);
+
+ for (i = 0; i < numdevs; i++) {
+ child = devlist[i];
+ device_printf(dev, "%s: Resume child %d\n", __func__, i);
+ bus_generic_resume_child(dev, child);
+ }
+ free(devlist, M_TEMP);
+
+ return (0);
+}
+
static int
iicbus_print_child(device_t dev, device_t child)
{
@@ -336,6 +381,8 @@ static device_method_t iicbus_methods[] = {
DEVMETHOD(device_probe, iicbus_probe),
DEVMETHOD(device_attach, iicbus_attach),
DEVMETHOD(device_detach, iicbus_detach),
+ DEVMETHOD(device_suspend, iicbus_suspend),
+ DEVMETHOD(device_resume, iicbus_resume),
/* bus interface */
DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
diff --git a/sys/dev/iicbus/input/acpi_iichid.c b/sys/dev/iicbus/input/acpi_iichid.c
index bd779b14a2b8..4370dcedc8a6 100644
--- a/sys/dev/iicbus/input/acpi_iichid.c
+++ b/sys/dev/iicbus/input/acpi_iichid.c
@@ -19,6 +19,8 @@
static device_probe_t acpi_iichid_probe;
static device_attach_t acpi_iichid_attach;
static device_detach_t acpi_iichid_detach;
+static device_suspend_t acpi_iichid_suspend;
+static device_resume_t acpi_iichid_resume;
static devclass_t acpi_iichid_devclass;
@@ -27,6 +29,8 @@ static device_method_t acpi_iichid_methods[] = {
DEVMETHOD(device_probe, acpi_iichid_probe),
DEVMETHOD(device_attach, acpi_iichid_attach),
DEVMETHOD(device_detach, acpi_iichid_detach),
+ DEVMETHOD(device_suspend, acpi_iichid_suspend),
+ DEVMETHOD(device_resume, acpi_iichid_resume),
DEVMETHOD_END
};
@@ -515,6 +519,64 @@ acpi_iichid_detach(device_t dev)
return (0);
}
+static int
+acpi_iichid_suspend(device_t dev)
+{
+ struct acpi_iichid_softc *sc;
+ sc = device_get_softc(dev);
+
+ mtx_lock(&sc->lock);
+
+ if (sc->sampling_rate >= 0) {
+ device_printf(dev, "%s: Disable periodic callout\n", __func__);
+ acpi_iichid_teardown_callout(dev, sc);
+ } else {
+ if (sc->irq_res) {
+ device_printf(dev, "%s: Disable interrupt\n", __func__);
+ bus_suspend_intr(dev, sc->irq_res);
+ }
+ }
+
+ if (sc->taskqueue) {
+ taskqueue_block(sc->taskqueue);
+ taskqueue_drain(sc->taskqueue, &sc->event_task);
+ }
+
+ mtx_unlock(&sc->lock);
+
+ return (0);
+}
+
+static int
+acpi_iichid_resume(device_t dev)
+{
+ struct acpi_iichid_softc *sc;
+ sc = device_get_softc(dev);
+
+ mtx_lock(&sc->lock);
+
+ if (sc->sampling_rate >= 0) {
+ device_printf(dev, "%s: Enable periodic callout\n", __func__);
+ acpi_iichid_setup_callout(sc->dev, sc);
+ acpi_iichid_reset_callout(sc->dev, sc);
+ } else {
+ if (sc->irq_res) {
+ device_printf(dev, "%s: Enable interrupt\n", __func__);
+ bus_resume_intr(dev, sc->irq_res);
+ }
+ }
+
+ if (sc->sampling_rate > 0 )
+ acpi_iichid_reset_callout(sc->dev, sc);
+
+ if (sc->taskqueue)
+ taskqueue_unblock(sc->taskqueue);
+
+ mtx_unlock(&sc->lock);
+
+ return (0);
+}
+
DRIVER_MODULE(acpi_iichid, acpi, acpi_iichid_driver, acpi_iichid_devclass, NULL, 0);
MODULE_DEPEND(acpi_iichid, acpi, 1, 1, 1);
MODULE_DEPEND(acpi_iichid, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
diff --git a/sys/dev/iicbus/input/iichid.c b/sys/dev/iicbus/input/iichid.c
index fe0e3fd96f4f..332fd596c2bd 100644
--- a/sys/dev/iicbus/input/iichid.c
+++ b/sys/dev/iicbus/input/iichid.c
@@ -44,6 +44,8 @@
static device_probe_t iichid_probe;
static device_attach_t iichid_attach;
static device_detach_t iichid_detach;
+static device_suspend_t iichid_suspend;
+static device_resume_t iichid_resume;
static devclass_t iichid_devclass;
@@ -51,7 +53,8 @@ static device_method_t iichid_methods[] = {
DEVMETHOD(device_probe, iichid_probe),
DEVMETHOD(device_attach, iichid_attach),
DEVMETHOD(device_detach, iichid_detach),
-
+ DEVMETHOD(device_suspend, iichid_suspend),
+ DEVMETHOD(device_resume, iichid_resume),
DEVMETHOD_END
};
@@ -90,6 +93,34 @@ static const struct evdev_methods iichid_evdev_methods = {
};
#endif
+static int
+iichid_power_on(device_t dev)
+{
+ device_printf(dev, "%s\n", __func__);
+
+ struct iichid_softc *sc;
+
+ sc = device_get_softc(dev);
+ mtx_lock(&sc->lock);
+
+ uint16_t addr = iicbus_get_addr(dev);
+ uint8_t *reg = (uint8_t *)&sc->desc.wCommandRegister;
+ uint8_t cmd[] = { reg[0] , reg[1], 0, 0x08 };
+ int cmdlen = 4;
+ struct iic_msg msgs[] = {
+ { addr << 1, IIC_M_WR, cmdlen, cmd },
+ };
+ mtx_unlock(&sc->lock);
+
+ int ret = (iicbus_transfer(dev, msgs, nitems(msgs)));
+ device_printf(dev, "%s: 1st i2c cmd returned %d\n", __func__, ret);
+ pause("i2c_hid", hz);
+ ret = (iicbus_transfer(dev, msgs, nitems(msgs)));
+ device_printf(dev, "%s: 2nd i2c cmd returned %d\n", __func__, ret);
+
+ return (0);
+}
+
static int
iichid_fetch_buffer(device_t dev, uint8_t* cmd, int cmdlen, uint8_t *buf, int buflen)
{
@@ -911,6 +942,24 @@ iichid_detach(device_t dev)
return (0);
}
+static int
+iichid_suspend(device_t dev)
+{
+ device_printf(dev, "%s\n", __func__);
+
+ return (0);
+}
+
+static int
+iichid_resume(device_t dev)
+{
+ device_printf(dev, "%s\n", __func__);
+
+ iichid_power_on(dev);
+
+ return (0);
+}
+
DRIVER_MODULE(iichid, iicbus, iichid_driver, iichid_devclass, NULL, 0);
MODULE_DEPEND(iichid, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
MODULE_VERSION(iichid, 1);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment