[ig4] Add suspend/resume support

That is done with re-execution of controller initialization procedure
from resume handler.

PR:		238037
This commit is contained in:
Vladimir Kondratyev 2019-11-03 21:00:55 +00:00
parent 83a66b9bda
commit db7caa2ea7
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=354308
5 changed files with 108 additions and 17 deletions

View File

@ -145,11 +145,29 @@ ig4iic_acpi_detach(device_t dev)
return (0);
}
static int
ig4iic_acpi_suspend(device_t dev)
{
ig4iic_softc_t *sc = device_get_softc(dev);
return (ig4iic_suspend(sc));
}
static int
ig4iic_acpi_resume(device_t dev)
{
ig4iic_softc_t *sc = device_get_softc(dev);
return (ig4iic_resume(sc));
}
static device_method_t ig4iic_acpi_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, ig4iic_acpi_probe),
DEVMETHOD(device_attach, ig4iic_acpi_attach),
DEVMETHOD(device_detach, ig4iic_acpi_detach),
DEVMETHOD(device_suspend, ig4iic_acpi_suspend),
DEVMETHOD(device_resume, ig4iic_acpi_resume),
/* iicbus interface */
DEVMETHOD(iicbus_transfer, ig4iic_transfer),

View File

@ -799,20 +799,11 @@ ig4iic_get_config(ig4iic_softc_t *sc)
}
}
/*
* Called from ig4iic_pci_attach/detach()
*/
int
ig4iic_attach(ig4iic_softc_t *sc)
static int
ig4iic_set_config(ig4iic_softc_t *sc)
{
int error;
uint32_t v;
mtx_init(&sc->io_lock, "IG4 I/O lock", NULL, MTX_DEF);
sx_init(&sc->call_lock, "IG4 call lock");
ig4iic_get_config(sc);
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);
@ -851,16 +842,13 @@ ig4iic_attach(ig4iic_softc_t *sc)
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;
}
if (v < IG4_COMP_MIN_VER)
return(ENXIO);
}
if (set_controller(sc, 0)) {
device_printf(sc->dev, "controller error during attach-1\n");
error = ENXIO;
goto done;
return (ENXIO);
}
reg_read(sc, IG4_REG_CLR_INTR);
@ -890,6 +878,26 @@ ig4iic_attach(ig4iic_softc_t *sc)
IG4_CTL_RESTARTEN |
(sc->cfg.bus_speed & IG4_CTL_SPEED_MASK));
return (0);
}
/*
* Called from ig4iic_pci_attach/detach()
*/
int
ig4iic_attach(ig4iic_softc_t *sc)
{
int error;
mtx_init(&sc->io_lock, "IG4 I/O lock", NULL, MTX_DEF);
sx_init(&sc->call_lock, "IG4 call lock");
ig4iic_get_config(sc);
error = ig4iic_set_config(sc);
if (error)
goto done;
sc->iicbus = device_add_child(sc->dev, "iicbus", -1);
if (sc->iicbus == NULL) {
device_printf(sc->dev, "iicbus driver not found\n");
@ -967,6 +975,49 @@ ig4iic_detach(ig4iic_softc_t *sc)
return (0);
}
int
ig4iic_suspend(ig4iic_softc_t *sc)
{
int error;
/* suspend all children */
error = bus_generic_suspend(sc->dev);
sx_xlock(&sc->call_lock);
set_controller(sc, 0);
if (sc->version == IG4_SKYLAKE) {
/*
* Place the device in the idle state, just to be safe
*/
reg_write(sc, IG4_REG_DEVIDLE_CTRL, IG4_DEVICE_IDLE);
/*
* Controller can become dysfunctional if I2C lines are pulled
* down when suspend procedure turns off power to I2C device.
* Place device in the reset state to avoid this.
*/
reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_ASSERT_SKL);
}
sx_xunlock(&sc->call_lock);
return (error);
}
int ig4iic_resume(ig4iic_softc_t *sc)
{
int error;
sx_xlock(&sc->call_lock);
if (ig4iic_set_config(sc))
device_printf(sc->dev, "controller error during resume\n");
/* Force setting of the target address on the next transfer */
sc->slave_valid = 0;
sx_xunlock(&sc->call_lock);
error = bus_generic_resume(sc->dev);
return (error);
}
/*
* Interrupt Operation, see ig4_var.h for locking semantics.
*/

View File

@ -206,11 +206,29 @@ ig4iic_pci_detach(device_t dev)
return (0);
}
static int
ig4iic_pci_suspend(device_t dev)
{
ig4iic_softc_t *sc = device_get_softc(dev);
return (ig4iic_suspend(sc));
}
static int
ig4iic_pci_resume(device_t dev)
{
ig4iic_softc_t *sc = device_get_softc(dev);
return (ig4iic_resume(sc));
}
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),

View File

@ -114,6 +114,8 @@ extern devclass_t ig4iic_devclass;
/* Attach/Detach called from ig4iic_pci_*() */
int ig4iic_attach(ig4iic_softc_t *sc);
int ig4iic_detach(ig4iic_softc_t *sc);
int ig4iic_suspend(ig4iic_softc_t *sc);
int ig4iic_resume(ig4iic_softc_t *sc);
/* iicbus methods */
extern iicbus_transfer_t ig4iic_transfer;

View File

@ -330,6 +330,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, bus_generic_suspend),
DEVMETHOD(device_resume, bus_generic_resume),
/* bus interface */
DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),