summaryrefslogtreecommitdiffstats
path: root/drivers/input/keyboard/hilkbd.c
diff options
context:
space:
mode:
authorMiguel Ojeda <ojeda@kernel.org>2026-08-16 15:32:33 +0200
committerMiguel Ojeda <ojeda@kernel.org>2026-08-23 21:27:17 +0200
commit5febf432df1cfa5b25d99b54c32103fafbdd0eb9 (patch)
tree93a1ed1d337bc53adf1e572c2586d347703b286e /drivers/input/keyboard/hilkbd.c
downloadlinux-stable-5febf432df1cfa5b25d99b54c32103fafbdd0eb9.tar.gz
linux-stable-5febf432df1cfa5b25d99b54c32103fafbdd0eb9.zip
kbuild: rust: keep Rust objects out of Clang LTO with inline helpersgrafted
Under `CONFIG_LTO_CLANG` + `CONFIG_RUST_INLINE_HELPERS`, one may hit `objtool` errors such as: vmlinux.o: error: objtool: _R..._3Gsp4boot+0xd6a: can't find jump dest instruction at .text._R..._3Gsp4boot+0x1dfd The reason is that in such builds, the Clang invocation that compiles the combined Rust plus helpers bitcode emits LLVM bitcode (again) -- the final code generation happens in the linker's LTO step, which the `-mllvm` trap options passed to Clang do not reach. This, in turn, means that unreachable traps are missing, and the impossible paths do not merely fallthrough to the next symbol, but past the end of their own section, since LTO builds place each function in its own section. Thus filter `CC_FLAGS_LTO` out of the Clang invocation, so that it always emits machine code directly, with the traps in place. Assisted-by: LLM Cc: Gary Guo <gary@garyguo.net> Cc: Boqun Feng <boqun@kernel.org> Cc: Alice Ryhl <aliceryhl@google.com> Cc: Matthew Maurer <mmaurer@google.com> Cc: Josh Poimboeuf <jpoimboe@kernel.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: stable@vger.kernel.org Fixes: 3a2486cc1da5 ("kbuild: rust: provide an option to inline C helpers into Rust") Acked-by: Gary Guo <gary@garyguo.net> Link: https://patch.msgid.link/20260816133233.197500-2-ojeda@kernel.org Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
Diffstat (limited to 'drivers/input/keyboard/hilkbd.c')
-rw-r--r--drivers/input/keyboard/hilkbd.c390
1 files changed, 390 insertions, 0 deletions
diff --git a/drivers/input/keyboard/hilkbd.c b/drivers/input/keyboard/hilkbd.c
new file mode 100644
index 000000000..c8d8d0ea3
--- /dev/null
+++ b/drivers/input/keyboard/hilkbd.c
@@ -0,0 +1,390 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * linux/drivers/hil/hilkbd.c
+ *
+ * Copyright (C) 1998 Philip Blundell <philb@gnu.org>
+ * Copyright (C) 1999 Matthew Wilcox <willy@infradead.org>
+ * Copyright (C) 1999-2007 Helge Deller <deller@gmx.de>
+ *
+ * Very basic HP Human Interface Loop (HIL) driver.
+ * This driver handles the keyboard on HP300 (m68k) and on some
+ * HP700 (parisc) series machines.
+ */
+
+#include <linux/pci_ids.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/hil.h>
+#include <linux/io.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <asm/irq.h>
+#ifdef CONFIG_HP300
+#include <asm/hwtest.h>
+#endif
+
+
+MODULE_AUTHOR("Philip Blundell, Matthew Wilcox, Helge Deller");
+MODULE_DESCRIPTION("HIL keyboard driver (basic functionality)");
+MODULE_LICENSE("GPL v2");
+
+
+#if defined(CONFIG_PARISC)
+
+ #include <asm/io.h>
+ #include <asm/hardware.h>
+ #include <asm/parisc-device.h>
+ static unsigned long hil_base; /* HPA for the HIL device */
+ static unsigned int hil_irq;
+ #define HILBASE hil_base /* HPPA (parisc) port address */
+ #define HIL_DATA 0x800
+ #define HIL_CMD 0x801
+ #define HIL_IRQ hil_irq
+ #define hil_readb(p) gsc_readb(p)
+ #define hil_writeb(v,p) gsc_writeb((v),(p))
+
+#elif defined(CONFIG_HP300)
+
+ #define HILBASE 0xf0428000UL /* HP300 (m68k) port address */
+ #define HIL_DATA 0x1
+ #define HIL_CMD 0x3
+ #define HIL_IRQ 2
+ #define hil_readb(p) readb((const volatile void __iomem *)(p))
+ #define hil_writeb(v, p) writeb((v), (volatile void __iomem *)(p))
+
+#else
+#error "HIL is not supported on this platform"
+#endif
+
+
+
+/* HIL helper functions */
+
+#define hil_busy() (hil_readb(HILBASE + HIL_CMD) & HIL_BUSY)
+#define hil_data_available() (hil_readb(HILBASE + HIL_CMD) & HIL_DATA_RDY)
+#define hil_status() (hil_readb(HILBASE + HIL_CMD))
+#define hil_command(x) do { hil_writeb((x), HILBASE + HIL_CMD); } while (0)
+#define hil_read_data() (hil_readb(HILBASE + HIL_DATA))
+#define hil_write_data(x) do { hil_writeb((x), HILBASE + HIL_DATA); } while (0)
+
+/* HIL constants */
+
+#define HIL_BUSY 0x02
+#define HIL_DATA_RDY 0x01
+
+#define HIL_SETARD 0xA0 /* set auto-repeat delay */
+#define HIL_SETARR 0xA2 /* set auto-repeat rate */
+#define HIL_SETTONE 0xA3 /* set tone generator */
+#define HIL_CNMT 0xB2 /* clear nmi */
+#define HIL_INTON 0x5C /* Turn on interrupts. */
+#define HIL_INTOFF 0x5D /* Turn off interrupts. */
+
+#define HIL_READKBDSADR 0xF9
+#define HIL_WRITEKBDSADR 0xE9
+
+static unsigned int hphilkeyb_keycode[HIL_KEYCODES_SET1_TBLSIZE] __read_mostly =
+ { HIL_KEYCODES_SET1 };
+
+/* HIL structure */
+static struct {
+ struct input_dev *dev;
+
+ unsigned int curdev;
+
+ unsigned char s;
+ unsigned char c;
+ int valid;
+
+ unsigned char data[16];
+ unsigned int ptr;
+ spinlock_t lock;
+
+ void *dev_id; /* native bus device */
+} hil_dev;
+
+
+static void poll_finished(void)
+{
+ int down;
+ int key;
+ unsigned char scode;
+
+ switch (hil_dev.data[0]) {
+ case 0x40:
+ down = (hil_dev.data[1] & 1) == 0;
+ scode = hil_dev.data[1] >> 1;
+ key = hphilkeyb_keycode[scode];
+ input_report_key(hil_dev.dev, key, down);
+ break;
+ }
+ hil_dev.curdev = 0;
+}
+
+
+static inline void handle_status(unsigned char s, unsigned char c)
+{
+ if (c & 0x8) {
+ /* End of block */
+ if (c & 0x10)
+ poll_finished();
+ } else {
+ if (c & 0x10) {
+ if (hil_dev.curdev)
+ poll_finished(); /* just in case */
+ hil_dev.curdev = c & 7;
+ hil_dev.ptr = 0;
+ }
+ }
+}
+
+
+static inline void handle_data(unsigned char s, unsigned char c)
+{
+ if (hil_dev.curdev) {
+ hil_dev.data[hil_dev.ptr++] = c;
+ hil_dev.ptr &= 15;
+ }
+}
+
+
+/* handle HIL interrupts */
+static irqreturn_t hil_interrupt(int irq, void *handle)
+{
+ unsigned char s, c;
+
+ s = hil_status();
+ c = hil_read_data();
+
+ switch (s >> 4) {
+ case 0x5:
+ handle_status(s, c);
+ break;
+ case 0x6:
+ handle_data(s, c);
+ break;
+ case 0x4:
+ hil_dev.s = s;
+ hil_dev.c = c;
+ mb();
+ hil_dev.valid = 1;
+ break;
+ }
+ return IRQ_HANDLED;
+}
+
+
+/* send a command to the HIL */
+static void hil_do(unsigned char cmd, unsigned char *data, unsigned int len)
+{
+ guard(spinlock_irqsave)(&hil_dev.lock);
+
+ while (hil_busy())
+ /* wait */;
+ hil_command(cmd);
+ while (len--) {
+ while (hil_busy())
+ /* wait */;
+ hil_write_data(*(data++));
+ }
+}
+
+
+/* initialize HIL */
+static int hil_keyb_init(void)
+{
+ unsigned char c;
+ unsigned int i, kbid;
+ wait_queue_head_t hil_wait;
+ int err;
+
+ if (hil_dev.dev)
+ return -ENODEV; /* already initialized */
+
+ init_waitqueue_head(&hil_wait);
+ spin_lock_init(&hil_dev.lock);
+
+ hil_dev.dev = input_allocate_device();
+ if (!hil_dev.dev)
+ return -ENOMEM;
+
+ err = request_irq(HIL_IRQ, hil_interrupt, 0, "hil", hil_dev.dev_id);
+ if (err) {
+ printk(KERN_ERR "HIL: Can't get IRQ\n");
+ goto err1;
+ }
+
+ /* Turn on interrupts */
+ hil_do(HIL_INTON, NULL, 0);
+
+ /* Look for keyboards */
+ hil_dev.valid = 0; /* clear any pending data */
+ hil_do(HIL_READKBDSADR, NULL, 0);
+
+ wait_event_interruptible_timeout(hil_wait, hil_dev.valid, 3 * HZ);
+ if (!hil_dev.valid)
+ printk(KERN_WARNING "HIL: timed out, assuming no keyboard present\n");
+
+ c = hil_dev.c;
+ hil_dev.valid = 0;
+ if (c == 0) {
+ kbid = -1;
+ printk(KERN_WARNING "HIL: no keyboard present\n");
+ } else {
+ kbid = ffz(~c);
+ printk(KERN_INFO "HIL: keyboard found at id %d\n", kbid);
+ }
+
+ /* set it to raw mode */
+ c = 0;
+ hil_do(HIL_WRITEKBDSADR, &c, 1);
+
+ for (i = 0; i < HIL_KEYCODES_SET1_TBLSIZE; i++)
+ if (hphilkeyb_keycode[i] != KEY_RESERVED)
+ __set_bit(hphilkeyb_keycode[i], hil_dev.dev->keybit);
+
+ hil_dev.dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+ hil_dev.dev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) |
+ BIT_MASK(LED_SCROLLL);
+ hil_dev.dev->keycodemax = HIL_KEYCODES_SET1_TBLSIZE;
+ hil_dev.dev->keycodesize= sizeof(hphilkeyb_keycode[0]);
+ hil_dev.dev->keycode = hphilkeyb_keycode;
+ hil_dev.dev->name = "HIL keyboard";
+ hil_dev.dev->phys = "hpkbd/input0";
+
+ hil_dev.dev->id.bustype = BUS_HIL;
+ hil_dev.dev->id.vendor = PCI_VENDOR_ID_HP;
+ hil_dev.dev->id.product = 0x0001;
+ hil_dev.dev->id.version = 0x0010;
+
+ err = input_register_device(hil_dev.dev);
+ if (err) {
+ printk(KERN_ERR "HIL: Can't register device\n");
+ goto err2;
+ }
+
+ printk(KERN_INFO "input: %s, ID %d at 0x%08lx (irq %d) found and attached\n",
+ hil_dev.dev->name, kbid, HILBASE, HIL_IRQ);
+
+ return 0;
+
+err2:
+ hil_do(HIL_INTOFF, NULL, 0);
+ free_irq(HIL_IRQ, hil_dev.dev_id);
+err1:
+ input_free_device(hil_dev.dev);
+ hil_dev.dev = NULL;
+ return err;
+}
+
+static void hil_keyb_exit(void)
+{
+ if (HIL_IRQ)
+ free_irq(HIL_IRQ, hil_dev.dev_id);
+
+ /* Turn off interrupts */
+ hil_do(HIL_INTOFF, NULL, 0);
+
+ input_unregister_device(hil_dev.dev);
+ hil_dev.dev = NULL;
+}
+
+#if defined(CONFIG_PARISC)
+static int __init hil_probe_chip(struct parisc_device *dev)
+{
+ /* Only allow one HIL keyboard */
+ if (hil_dev.dev)
+ return -ENODEV;
+
+ if (!dev->irq) {
+ printk(KERN_WARNING "HIL: IRQ not found for HIL bus at 0x%p\n",
+ (void *)dev->hpa.start);
+ return -ENODEV;
+ }
+
+ hil_base = dev->hpa.start;
+ hil_irq = dev->irq;
+ hil_dev.dev_id = dev;
+
+ printk(KERN_INFO "Found HIL bus at 0x%08lx, IRQ %d\n", hil_base, hil_irq);
+
+ return hil_keyb_init();
+}
+
+static void __exit hil_remove_chip(struct parisc_device *dev)
+{
+ hil_keyb_exit();
+}
+
+static const struct parisc_device_id hil_tbl[] __initconst = {
+ { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00073 },
+ { 0, }
+};
+
+#if 0
+/* Disabled to avoid conflicts with the HP SDC HIL drivers */
+MODULE_DEVICE_TABLE(parisc, hil_tbl);
+#endif
+
+static struct parisc_driver hil_driver __refdata = {
+ .name = "hil",
+ .id_table = hil_tbl,
+ .probe = hil_probe_chip,
+ .remove = __exit_p(hil_remove_chip),
+};
+
+static int __init hil_init(void)
+{
+ return register_parisc_driver(&hil_driver);
+}
+
+static void __exit hil_exit(void)
+{
+ unregister_parisc_driver(&hil_driver);
+}
+
+#else /* !CONFIG_PARISC */
+
+static int __init hil_init(void)
+{
+ int error;
+
+ /* Only allow one HIL keyboard */
+ if (hil_dev.dev)
+ return -EBUSY;
+
+ if (!MACH_IS_HP300)
+ return -ENODEV;
+
+ if (!hwreg_present((void *)(HILBASE + HIL_DATA))) {
+ printk(KERN_ERR "HIL: hardware register was not found\n");
+ return -ENODEV;
+ }
+
+ if (!request_region(HILBASE + HIL_DATA, 2, "hil")) {
+ printk(KERN_ERR "HIL: IOPORT region already used\n");
+ return -EIO;
+ }
+
+ error = hil_keyb_init();
+ if (error) {
+ release_region(HILBASE + HIL_DATA, 2);
+ return error;
+ }
+
+ return 0;
+}
+
+static void __exit hil_exit(void)
+{
+ hil_keyb_exit();
+ release_region(HILBASE + HIL_DATA, 2);
+}
+
+#endif /* CONFIG_PARISC */
+
+module_init(hil_init);
+module_exit(hil_exit);