iichid(4): Perform bus_teardown_intr/bus_setup_intr to disable interrupts
during suspend/resume cycle. Previously used bus_generic_suspend_intr and bus_generic_resume_intr may cause interrupt storm because of missed interrupt acknowledges caused by blocking of intr handler. Reported by: J.R. Oldroyd <jr_AT_opal_DOT_com> MFC after: 1 week
This commit is contained in:
parent
c5cbef2f85
commit
82626fef62
@ -177,6 +177,7 @@ struct iichid_softc {
|
|||||||
struct task event_task;
|
struct task event_task;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
struct task suspend_task;
|
||||||
bool open; /* iicbus lock */
|
bool open; /* iicbus lock */
|
||||||
bool suspend; /* iicbus lock */
|
bool suspend; /* iicbus lock */
|
||||||
bool power_on; /* iicbus lock */
|
bool power_on; /* iicbus lock */
|
||||||
@ -188,6 +189,8 @@ static device_detach_t iichid_detach;
|
|||||||
static device_resume_t iichid_resume;
|
static device_resume_t iichid_resume;
|
||||||
static device_suspend_t iichid_suspend;
|
static device_suspend_t iichid_suspend;
|
||||||
|
|
||||||
|
static void iichid_suspend_task(void *, int);
|
||||||
|
|
||||||
#ifdef IICHID_SAMPLING
|
#ifdef IICHID_SAMPLING
|
||||||
static int iichid_setup_callout(struct iichid_softc *);
|
static int iichid_setup_callout(struct iichid_softc *);
|
||||||
static int iichid_reset_callout(struct iichid_softc *);
|
static int iichid_reset_callout(struct iichid_softc *);
|
||||||
@ -1075,6 +1078,7 @@ iichid_attach(device_t dev)
|
|||||||
|
|
||||||
sc->power_on = true;
|
sc->power_on = true;
|
||||||
|
|
||||||
|
TASK_INIT(&sc->suspend_task, 0, iichid_suspend_task, sc);
|
||||||
#ifdef IICHID_SAMPLING
|
#ifdef IICHID_SAMPLING
|
||||||
TASK_INIT(&sc->event_task, 0, iichid_event_task, sc);
|
TASK_INIT(&sc->event_task, 0, iichid_event_task, sc);
|
||||||
/* taskqueue_create can't fail with M_WAITOK mflag passed. */
|
/* taskqueue_create can't fail with M_WAITOK mflag passed. */
|
||||||
@ -1146,7 +1150,6 @@ iichid_attach(device_t dev)
|
|||||||
device_printf(dev, "failed to attach child: error %d\n", error);
|
device_printf(dev, "failed to attach child: error %d\n", error);
|
||||||
iichid_detach(dev);
|
iichid_detach(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
(void)iichid_set_power(sc, I2C_HID_POWER_OFF);
|
(void)iichid_set_power(sc, I2C_HID_POWER_OFF);
|
||||||
sc->power_on = false;
|
sc->power_on = false;
|
||||||
@ -1175,6 +1178,14 @@ iichid_detach(device_t dev)
|
|||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
iichid_suspend_task(void *context, int pending)
|
||||||
|
{
|
||||||
|
struct iichid_softc *sc = context;
|
||||||
|
|
||||||
|
iichid_teardown_interrupt(sc);
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
iichid_suspend(device_t dev)
|
iichid_suspend(device_t dev)
|
||||||
{
|
{
|
||||||
@ -1183,12 +1194,6 @@ iichid_suspend(device_t dev)
|
|||||||
|
|
||||||
sc = device_get_softc(dev);
|
sc = device_get_softc(dev);
|
||||||
(void)bus_generic_suspend(dev);
|
(void)bus_generic_suspend(dev);
|
||||||
#ifdef IICHID_SAMPLING
|
|
||||||
if (sc->sampling_rate_slow < 0)
|
|
||||||
#endif
|
|
||||||
(void)bus_generic_suspend_intr(device_get_parent(dev), dev,
|
|
||||||
sc->irq_res);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 8.2 - The HOST is going into a deep power optimized state and wishes
|
* 8.2 - The HOST is going into a deep power optimized state and wishes
|
||||||
* to put all the devices into a low power state also. The HOST
|
* to put all the devices into a low power state also. The HOST
|
||||||
@ -1202,6 +1207,24 @@ iichid_suspend(device_t dev)
|
|||||||
else
|
else
|
||||||
DPRINTF(sc, "Successfully set power_state\n");
|
DPRINTF(sc, "Successfully set power_state\n");
|
||||||
|
|
||||||
|
#ifdef IICHID_SAMPLING
|
||||||
|
if (sc->sampling_rate_slow < 0)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* bus_teardown_intr can not be executed right here as it wants
|
||||||
|
* to run on certain CPU to interacts with LAPIC while suspend
|
||||||
|
* thread is bound to CPU0. So run it from taskqueue context.
|
||||||
|
*/
|
||||||
|
#ifdef IICHID_SAMPLING
|
||||||
|
#define suspend_thread sc->taskqueue
|
||||||
|
#else
|
||||||
|
#define suspend_thread taskqueue_thread
|
||||||
|
#endif
|
||||||
|
taskqueue_enqueue(suspend_thread, &sc->suspend_task);
|
||||||
|
taskqueue_drain(suspend_thread, &sc->suspend_task);
|
||||||
|
}
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1212,6 +1235,11 @@ iichid_resume(device_t dev)
|
|||||||
int error;
|
int error;
|
||||||
|
|
||||||
sc = device_get_softc(dev);
|
sc = device_get_softc(dev);
|
||||||
|
#ifdef IICHID_SAMPLING
|
||||||
|
if (sc->sampling_rate_slow < 0)
|
||||||
|
#endif
|
||||||
|
iichid_setup_interrupt(sc);
|
||||||
|
|
||||||
DPRINTF(sc, "Resume called, setting device to power_state 0\n");
|
DPRINTF(sc, "Resume called, setting device to power_state 0\n");
|
||||||
error = iichid_set_power_state(sc, IICHID_PS_NULL, IICHID_PS_ON);
|
error = iichid_set_power_state(sc, IICHID_PS_NULL, IICHID_PS_ON);
|
||||||
if (error != 0)
|
if (error != 0)
|
||||||
@ -1219,11 +1247,6 @@ iichid_resume(device_t dev)
|
|||||||
else
|
else
|
||||||
DPRINTF(sc, "Successfully set power_state\n");
|
DPRINTF(sc, "Successfully set power_state\n");
|
||||||
(void)bus_generic_resume(dev);
|
(void)bus_generic_resume(dev);
|
||||||
#ifdef IICHID_SAMPLING
|
|
||||||
if (sc->sampling_rate_slow < 0)
|
|
||||||
#endif
|
|
||||||
(void)bus_generic_resume_intr(device_get_parent(dev), dev,
|
|
||||||
sc->irq_res);
|
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user