Skip to content

Commit

Permalink
iichid(4): Improve idle sampling hysteresis
Browse files Browse the repository at this point in the history
In sampling mode some devices return same data indefinitely even if
there is nothing to report.  Previous idle hysteresis implementation
activated only when device returned no data, so some devices ended up
polled at fast rate all the time.  This new implementation compares
each new report with the previous, and, if they are identical, after
reaching threshold also drop sampling rate to slow.

On my Dell XPS 13 9310 with iichid(4) touchscreen and touchpad this
reduces idle power consumption by ~0.5W by reducing number of context
switches in the driver from ~4000 to ~700 per second when not touched.

MFC after:	1 month
  • Loading branch information
amotin authored and bsdjhb committed Mar 12, 2024
2 parents d1209b5 + 8c86b98 commit 7d5d855
Showing 1 changed file with 29 additions and 10 deletions.
39 changes: 29 additions & 10 deletions sys/dev/iicbus/iichid.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ enum {
*/
#define IICHID_SAMPLING_RATE_FAST 60
#define IICHID_SAMPLING_RATE_SLOW 10
#define IICHID_SAMPLING_HYSTERESIS 1
#define IICHID_SAMPLING_HYSTERESIS 12 /* ~ 2x fast / slow */

/* 5.1.1 - HID Descriptor Format */
struct i2c_hid_desc {
Expand Down Expand Up @@ -177,9 +177,12 @@ struct iichid_softc {
int sampling_rate_fast;
int sampling_hysteresis;
int missing_samples; /* iicbus lock */
struct timeout_task periodic_task; /* iicbus lock */
int dup_samples; /* iicbus lock */
iichid_size_t dup_size; /* iicbus lock */
bool callout_setup; /* iicbus lock */
uint8_t *dup_buf;
struct taskqueue *taskqueue;
struct timeout_task periodic_task; /* iicbus lock */
struct task event_task;
#endif

Expand Down Expand Up @@ -523,7 +526,7 @@ iichid_event_task(void *context, int pending)
device_t parent;
iichid_size_t actual;
bool bus_requested;
int error;
int error, rate;

sc = context;
parent = device_get_parent(sc->dev);
Expand All @@ -541,18 +544,30 @@ iichid_event_task(void *context, int pending)
if (actual > 0) {
sc->intr_handler(sc->intr_ctx, sc->intr_buf, actual);
sc->missing_samples = 0;
} else
++sc->missing_samples;
if (sc->dup_size != actual ||
memcmp(sc->dup_buf, sc->intr_buf, actual) != 0) {
sc->dup_size = actual;
memcpy(sc->dup_buf, sc->intr_buf, actual);
sc->dup_samples = 0;
} else
++sc->dup_samples;
} else {
if (++sc->missing_samples == 1)
sc->intr_handler(sc->intr_ctx, sc->intr_buf, 0);
sc->dup_samples = 0;
}
} else
DPRINTF(sc, "read error occurred: %d\n", error);

rearm:
if (sc->callout_setup && sc->sampling_rate_slow > 0) {
if (sc->missing_samples == sc->sampling_hysteresis)
sc->intr_handler(sc->intr_ctx, sc->intr_buf, 0);
taskqueue_enqueue_timeout(sc->taskqueue, &sc->periodic_task,
hz / MAX(sc->missing_samples >= sc->sampling_hysteresis ?
sc->sampling_rate_slow : sc->sampling_rate_fast, 1));
if (sc->missing_samples >= sc->sampling_hysteresis ||
sc->dup_samples >= sc->sampling_hysteresis)
rate = sc->sampling_rate_slow;
else
rate = sc->sampling_rate_fast;
taskqueue_enqueue_timeout_sbt(sc->taskqueue, &sc->periodic_task,
SBT_1S / MAX(rate, 1), 0, C_PREL(1));
}
out:
if (bus_requested)
Expand Down Expand Up @@ -725,6 +740,8 @@ iichid_reset_callout(struct iichid_softc *sc)

/* Start with slow sampling. */
sc->missing_samples = sc->sampling_hysteresis;
sc->dup_samples = 0;
sc->dup_size = 0;
taskqueue_enqueue(sc->taskqueue, &sc->event_task);

return (0);
Expand Down Expand Up @@ -812,6 +829,7 @@ iichid_intr_setup(device_t dev, device_t child __unused, hid_intr_t intr,
sc->intr_buf = malloc(rdesc->rdsize, M_DEVBUF, M_WAITOK | M_ZERO);
sc->intr_bufsize = rdesc->rdsize;
#ifdef IICHID_SAMPLING
sc->dup_buf = malloc(rdesc->rdsize, M_DEVBUF, M_WAITOK | M_ZERO);
taskqueue_start_threads(&sc->taskqueue, 1, PI_TTY,
"%s taskq", device_get_nameunit(sc->dev));
#endif
Expand All @@ -825,6 +843,7 @@ iichid_intr_unsetup(device_t dev, device_t child __unused)
sc = device_get_softc(dev);
#ifdef IICHID_SAMPLING
taskqueue_drain_all(sc->taskqueue);
free(sc->dup_buf, M_DEVBUF);
#endif
free(sc->intr_buf, M_DEVBUF);
}
Expand Down

0 comments on commit 7d5d855

Please sign in to comment.