Created
June 23, 2019 19:13
-
-
Save johalun/ffc271a07a0cf50d0bc816138c4eec81 to your computer and use it in GitHub Desktop.
iichid suspend resume
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
commit c86f6c434c203ab8325cd56fff2bc27f2ae0b142 | |
Author: Johannes Lundberg <[email protected]> | |
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