// SPDX-License-Identifier: GPL-2.0-or-later // // GMMOS's I2C keyboards: kernel/acpi/i2c_hid.c with everything but reading a // keyboard's input reports taken out. // // No ACPI -- the firmware's AML cannot run on a dead kernel, so the devices // are the ones the live driver found at boot (i2c_hid_keyboards). No power // commands, no reset, no report descriptor: the device is already on and was // already described. No interrupt: the pin is read when the device has one, // otherwise the input register is read every few milliseconds regardless. // And the DesignWare transfer is read-only, since an input report is a plain // read with no register write before it. #include "core/types.h" #include "core/defs.h" #include "dev/hid.h" #include "gmmos/gmmos.h" #define IC_TAR 0x04 #define IC_DATA_CMD 0x10 #define IC_RAW_INTR_STAT 0x34 #define IC_CLR_INTR 0x40 #define IC_CLR_TX_ABRT 0x54 #define IC_CLR_STOP_DET 0x60 #define IC_ENABLE 0x6C #define IC_TXFLR 0x74 #define IC_RXFLR 0x78 #define IC_ENABLE_STATUS 0x9C #define CMD_READ (1 << 8) #define CMD_STOP (1 << 9) #define INTR_TX_ABRT (1 << 6) #define INTR_STOP_DET (1 << 9) #define AMD_PIN_STS (1u << 16) #define NKBD 4 static struct gmmos_i2c_kbd kb[NKBD]; static int nkb, turn; static uchar buf[4096]; static struct gmmos_hidkeys keys; static uint32 rd(struct gmmos_i2c_kbd *d, int reg) { return d->regs[reg / 4]; } static void wr(struct gmmos_i2c_kbd *d, int reg, uint32 v) { d->regs[reg / 4] = v; } static int enable(struct gmmos_i2c_kbd *d, int on) { for (int i = 0; i < 1000; i++) { wr(d, IC_ENABLE, on); if ((rd(d, IC_ENABLE_STATUS) & 1) == (uint32)on) return 0; microdelay(25); } return -1; } // n bytes from the device. Disabling the controller first also abandons // whatever transfer the live driver had going when the kernel died. static int read_bytes(struct gmmos_i2c_kbd *d, uchar *r, int n) { int sent = 0, got = 0, spin; if (enable(d, 0) < 0) return -1; wr(d, IC_TAR, d->addr); rd(d, IC_CLR_INTR); if (enable(d, 1) < 0) return -1; for (spin = 0; got < n; spin++) { if (rd(d, IC_RAW_INTR_STAT) & INTR_TX_ABRT) goto abort; while (got < n && rd(d, IC_RXFLR) > 0) r[got++] = rd(d, IC_DATA_CMD); while (sent < n && (int)rd(d, IC_TXFLR) < d->txdepth && sent - got < d->rxdepth) { wr(d, IC_DATA_CMD, CMD_READ | (sent == n - 1 ? CMD_STOP : 0)); sent++; } if (spin > 20000) goto abort; microdelay(10); } for (spin = 0; !(rd(d, IC_RAW_INTR_STAT) & INTR_STOP_DET); spin++) { if (spin > 2000) goto abort; microdelay(10); } rd(d, IC_CLR_STOP_DET); enable(d, 0); return 0; abort: rd(d, IC_CLR_TX_ABRT); rd(d, IC_CLR_INTR); enable(d, 0); return -1; } static int probe(void) { nkb = i2c_hid_keyboards(kb, NKBD); for (int i = 0; i < nkb; i++) if (kb[i].maxin > sizeof(buf)) kb[i].maxin = sizeof(buf); return nkb > 0; } static int poll(void) { int k = gmmos_hid_next(&keys); if (k) return k; // GMMOS polls every millisecond; a blind read is a whole report's worth of // bus time, so a device with no pin is read every fourth time. for (int i = 0; i < nkb; i++) { struct gmmos_i2c_kbd *d = &kb[i]; if (d->pin) { int high = (*d->pin & AMD_PIN_STS) != 0; if (high == d->active_low) continue; } else if (turn++ % 4) { continue; } if (read_bytes(d, buf, d->maxin) < 0) continue; int len = buf[0] | (buf[1] << 8); uchar boot[8]; if (len > 2 && len <= (int)d->maxin && hid_boot_report(d->hid, buf + 2, len - 2, boot)) gmmos_hid_report(&keys, boot); } return gmmos_hid_next(&keys); } const struct gmmos_kbd gmmos_i2c = { .name = "I2C", .probe = probe, .poll = poll, };