Identity
| Part | InvenSense MPU-6500 (3-axis accel + 3-axis gyro + temperature) |
| Role | Orientation / motion sensing |
| Bus / address | i2c4 addr 0x68; WHO_AM_I (reg 0x75) = 0x70 |
| GPIO / IRQ | INT on GPIO1_C6, active-low (DTS: interrupts = <RK_PC6 IRQ_TYPE_LEVEL_LOW>) — wired |
| Datasheet | InvenSense MPU-6500 product specification |
| Pine64 wiki | PinePhone Pro — Sensors |
| Schematic | sheet 11 (sensor cluster on i2c4) |
Status — ● working
The driver attaches, reports WHO_AM_I = 0x70, and exposes raw accel,
gyro, and temperature sysctls under dev.mpu6500.0.*. On the bench a
flat phone reads a Z axis around -15700, plausible for a unit at 1 g
with the device’s sign convention. The data-ready IRQ is wired
(INT_ENABLE.DATA_RDY_EN, ack via INT_STATUS); sysctl reads serve
from a softc cache that a taskqueue_thread worker refreshes on each
edge. Live on 2026-05-03 the chip reports irq_count=7516089 after
~18 minutes of uptime — the IRQ path is genuinely firing, not
falling back to polled reads.
What remains for userspace integration
The driver is fully working at the kernel level; everything below is how a desktop / phone shell consumes it:
- No evdev
EV_ABSinterface yet. Sway/Hyprland accept rotation events from a touch / orientation device, but our driver only exposes sysctls. The shipped userland workaround isoverlay/usr/local/bin/phone-orientation(4 Hz sysctl poller →swaymsg output * transform <N>/hyprctl keyword monitor … transform <N>, with 500 ms debounce). It works; an in-kernelEV_ABSemit would be cleaner. - No calibration. Zero-rate offsets and sensitivity scale are not measured per device; orientation is robust enough without them because the script only cares about the dominant gravity axis, but motion-aware features (step counting, free-fall detection) would need it.
- No power management. Chip stays in the same mode regardless of whether anyone reads the sysctl. Linux’s IIO driver puts the part in cycle / low-power mode when no consumer is active; we don’t.
Driver
- Our tree:
src/sys/dev/iicbus/mpu6500.c(~320 lines) — minimaliicbusdriver, polled sysctl interface. - Linux mainline:
drivers/iio/imu/inv_mpu6050/— full IIO driver with FIFO, DMP firmware loading, and trigger plumbing.
The MPU-6500 shares its register map and WHO_AM_I byte (0x70) with
several siblings (the 0x68 MPU-6050 and the 0x71 MPU-9250). Our
probe accepts 0x70 only. The Linux driver runs the part in
interrupt-driven FIFO mode and exposes IIO buffers; we read the data
registers synchronously on every sysctl access. That’s enough to
prove the chip is alive but nowhere near efficient enough for a real
orientation pipeline.
Open work
Wire INT on— done; piggy-backs the sameGPIO1_C6as a real interruptrk_i2cbus_*method passthrough fix that unblocked Goodix (commit2972362).- Calibration: zero-rate offsets, scale, and a way to persist the result.
Orientation policy: emit evdev— first cut landed as a userland sh poller:EV_ABSevents (or expose via a small daemon) so Sway/Hyprland can rotate the panel.overlay/usr/local/bin/phone-orientationreadsdev.mpu6500.0.accel_*at 4 Hz, debounces ~500 ms, and callsswaymsg output * transform <N>(orhyprctl keyword monitor DSI-1, …, transform, <N>). Wired as commented-outexec_always/exec-oncein the Sway and Hyprland configs; opt in after the bench predicate below. evdevEV_ABSwould still be the right long-term answer.- Power management: park the chip in cycle / low-power mode when no consumer is reading.
Parity verification
mise run debug:sensors:phone captures this receipt together with the
STK3311, magnetometer, TSADC, and eFuse sysctls.
- Pre-fix (polling, 2026-04-29): every sysctl read issued a synchronous I2C transfer. There was no idle CPU cost when nothing read the sysctls, but a tight
while sysctl dev.mpu6500.0.accel_z_raw; do :; doneloop pegged the i2c4 controller and the userland loop alike.top -SH | grep mpushowed nothing because the work happened in the reader thread. - Post-fix (IRQ-driven, 2026-04-30):
sysctl dev.mpu6500.0.irq_activeis1after attach.sysctl dev.mpu6500.0.irq_countincrements roughly every 10 ms when stationary (sample rate ~100 Hz default withCONFIG = 0x03DLPF).sysctl dev.mpu6500.0.accel_z_rawreturns the cached value with no I2C traffic per read.- Idle CPU usage by
mpu6500work intaskqueue_threadis near zero between samples.
- Falsifier — IRQ never fires:
irq_countstays at 0. Eitherbus_alloc_resource_any(SYS_RES_IRQ, …)returned NULL (check dmesg forno IRQ resource), or the chip’sINT_ENABLEis wrong, or the GPIO PIC didn’t accept the FDT mapping. Cross-checksysctl dev.mpu6500.0.irq_activeand dmesg forIRQ-driven sample updates. - Falsifier — IRQ storm:
irq_countincrements much faster than 100 Hz. MeansINT_STATUSisn’t actually being read (driver bug) orINT_PIN_CFGpolarity is wrong and the line is held permanently asserted. The current driver inverts polarity to active-low (INT_PIN_CFG = 0x80) to matchIRQ_TYPE_LEVEL_LOWin the DTS. - Orientation script bench predicate: with
phone-orientationrunning under the user’s Sway / Hyprland session, rotate the phone 90° clockwise and hold. Within ~1 s,swaymsg -t get_outputs | jq '.[].transform'reports the new transform (90/180/270/0); on Hyprland,hyprctl monitors -j | jq '.[].transform'does the same. The--dry-runmode logs the sametransition: portrait -> landscape (ax=… ay=… az=…)line to stderr without driving the compositor — useful for tuning thresholds. - Falsifier — rotation doesn’t trigger:
phone-orientation --dry-runto see what the classifier is doing. Either the poller isn’t running (pgrep -f phone-orientation), the sysctl values aren’t moving (kernel-side IRQ never fired — see the irq_count predicate above), or the 8000 LSB threshold is too tight for that orientation; lower it and re-test. If the script logs transitions but the compositor doesn’t rotate, the script is missingSWAYSOCK/HYPRLAND_INSTANCE_SIGNATURE— launch it from the compositor’s autostart, not a bare SSH shell.
Related
- Where we are — current sensor status.
- Hardware reference — chip manifest.