aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Motin <mav@FreeBSD.org>2023-12-23 03:50:52 +0000
committerAlexander Motin <mav@FreeBSD.org>2023-12-23 03:50:52 +0000
commit8c86b981240324c1daaa387d4d3f8e3e53db3d2e (patch)
treea38cf96bc51b939c860e9427772ea0479a8dd2ee
parenteebb9c2caea1584773ae4cec311cee1eea5b1655 (diff)
downloadsrc-8c86b981240324c1daaa387d4d3f8e3e53db3d2e.tar.gz
src-8c86b981240324c1daaa387d4d3f8e3e53db3d2e.zip
iichid(4): Improve idle sampling hysteresis
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
-rw-r--r--sys/dev/iicbus/iichid.c39
1 files changed, 29 insertions, 10 deletions
diff --git a/sys/dev/iicbus/iichid.c b/sys/dev/iicbus/iichid.c
index 7ab5b61d9be8..1035088c11b5 100644
--- a/sys/dev/iicbus/iichid.c
+++ b/sys/dev/iicbus/iichid.c
@@ -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 {
@@ -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
@@ -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);
@@ -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)
@@ -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);
@@ -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
@@ -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);
}