summaryrefslogtreecommitdiffstats
path: root/drivers/input/serio/maceps2.c
diff options
context:
space:
mode:
authorSteven Rostedt <rostedt@goodmis.org>2026-09-02 09:55:01 -0400
committerSteven Rostedt <rostedt@goodmis.org>2026-09-02 11:08:21 -0400
commit4617721c502b2ddaa4e324e86da4997edf738fa5 (patch)
tree350aa83b47da0b574a814795392b20b0bed83cbc /drivers/input/serio/maceps2.c
downloadlinux-stable-4617721c502b2ddaa4e324e86da4997edf738fa5.tar.gz
linux-stable-4617721c502b2ddaa4e324e86da4997edf738fa5.zip
ftrace: Synchronize the initialization of ftrace_opsgrafted
There's some internal state that ftrace_ops needs to have set, but since it can be declared outside of the ftrace.c code, it calls ftrace_ops_init() on the ops in every global function. The issue is that if two tasks call it on the same ops at the same time it is possible to have the initialization of one corrupt the initialization of the other call. Create a ops_mutex to use to synchronize every initialization of the ftrace_ops. The mutex is taken within checking the ftrace_ops flag that states it was initializied but the flag is checked again after the mutex has been taken. Checking first outside the mutex allows it to shortcut having to take the mutex. But then the check needs to be done again after the mute is taken in case of races. Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260902095501.6b59af20@gandalf.local.home Fixes: f04f24fb7e48d ("ftrace, kprobes: Fix a deadlock on ftrace_regex_lock") Reported-by: sashiko-bot@kernel.org Close: https://lore.kernel.org/all/20260829025528.49A831F000E9@smtp.kernel.org/ Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'drivers/input/serio/maceps2.c')
-rw-r--r--drivers/input/serio/maceps2.c204
1 files changed, 204 insertions, 0 deletions
diff --git a/drivers/input/serio/maceps2.c b/drivers/input/serio/maceps2.c
new file mode 100644
index 000000000..fb41e7f5a
--- /dev/null
+++ b/drivers/input/serio/maceps2.c
@@ -0,0 +1,204 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * SGI O2 MACE PS2 controller driver for linux
+ *
+ * Copyright (C) 2002 Vivien Chappelier
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/err.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/ip32/mace.h>
+#include <asm/ip32/ip32_ints.h>
+
+MODULE_AUTHOR("Vivien Chappelier <vivien.chappelier@linux-mips.org");
+MODULE_DESCRIPTION("SGI O2 MACE PS2 controller driver");
+MODULE_LICENSE("GPL");
+
+#define MACE_PS2_TIMEOUT 10000 /* in 50us unit */
+
+#define PS2_STATUS_CLOCK_SIGNAL BIT(0) /* external clock signal */
+#define PS2_STATUS_CLOCK_INHIBIT BIT(1) /* clken output signal */
+#define PS2_STATUS_TX_INPROGRESS BIT(2) /* transmission in progress */
+#define PS2_STATUS_TX_EMPTY BIT(3) /* empty transmit buffer */
+#define PS2_STATUS_RX_FULL BIT(4) /* full receive buffer */
+#define PS2_STATUS_RX_INPROGRESS BIT(5) /* reception in progress */
+#define PS2_STATUS_ERROR_PARITY BIT(6) /* parity error */
+#define PS2_STATUS_ERROR_FRAMING BIT(7) /* framing error */
+
+#define PS2_CONTROL_TX_CLOCK_DISABLE BIT(0) /* inhibit clock signal after TX */
+#define PS2_CONTROL_TX_ENABLE BIT(1) /* transmit enable */
+#define PS2_CONTROL_TX_INT_ENABLE BIT(2) /* enable transmit interrupt */
+#define PS2_CONTROL_RX_INT_ENABLE BIT(3) /* enable receive interrupt */
+#define PS2_CONTROL_RX_CLOCK_ENABLE BIT(4) /* pause reception if set to 0 */
+#define PS2_CONTROL_RESET BIT(5) /* reset */
+
+struct maceps2_data {
+ struct mace_ps2port *port;
+ int irq;
+};
+
+static struct maceps2_data port_data[2];
+static struct serio *maceps2_port[2];
+static struct platform_device *maceps2_device;
+
+static int maceps2_write(struct serio *dev, unsigned char val)
+{
+ struct mace_ps2port *port = ((struct maceps2_data *)dev->port_data)->port;
+ unsigned int timeout = MACE_PS2_TIMEOUT;
+
+ do {
+ if (port->status & PS2_STATUS_TX_EMPTY) {
+ port->tx = val;
+ return 0;
+ }
+ udelay(50);
+ } while (timeout--);
+
+ return -1;
+}
+
+static irqreturn_t maceps2_interrupt(int irq, void *dev_id)
+{
+ struct serio *dev = dev_id;
+ struct mace_ps2port *port = ((struct maceps2_data *)dev->port_data)->port;
+ unsigned long byte;
+
+ if (port->status & PS2_STATUS_RX_FULL) {
+ byte = port->rx;
+ serio_interrupt(dev, byte & 0xff, 0);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int maceps2_open(struct serio *dev)
+{
+ struct maceps2_data *data = (struct maceps2_data *)dev->port_data;
+
+ if (request_irq(data->irq, maceps2_interrupt, 0, "PS2 port", dev)) {
+ printk(KERN_ERR "Could not allocate PS/2 IRQ\n");
+ return -EBUSY;
+ }
+
+ /* Reset port */
+ data->port->control = PS2_CONTROL_TX_CLOCK_DISABLE | PS2_CONTROL_RESET;
+ udelay(100);
+
+ /* Enable interrupts */
+ data->port->control = PS2_CONTROL_RX_CLOCK_ENABLE |
+ PS2_CONTROL_TX_ENABLE |
+ PS2_CONTROL_RX_INT_ENABLE;
+
+ return 0;
+}
+
+static void maceps2_close(struct serio *dev)
+{
+ struct maceps2_data *data = (struct maceps2_data *)dev->port_data;
+
+ data->port->control = PS2_CONTROL_TX_CLOCK_DISABLE | PS2_CONTROL_RESET;
+ udelay(100);
+ free_irq(data->irq, dev);
+}
+
+
+static struct serio *maceps2_allocate_port(int idx)
+{
+ struct serio *serio;
+
+ serio = kzalloc_obj(*serio);
+ if (serio) {
+ serio->id.type = SERIO_8042;
+ serio->write = maceps2_write;
+ serio->open = maceps2_open;
+ serio->close = maceps2_close;
+ snprintf(serio->name, sizeof(serio->name), "MACE PS/2 port%d", idx);
+ snprintf(serio->phys, sizeof(serio->phys), "mace/serio%d", idx);
+ serio->port_data = &port_data[idx];
+ serio->dev.parent = &maceps2_device->dev;
+ }
+
+ return serio;
+}
+
+static int maceps2_probe(struct platform_device *dev)
+{
+ maceps2_port[0] = maceps2_allocate_port(0);
+ maceps2_port[1] = maceps2_allocate_port(1);
+ if (!maceps2_port[0] || !maceps2_port[1]) {
+ kfree(maceps2_port[0]);
+ kfree(maceps2_port[1]);
+ return -ENOMEM;
+ }
+
+ serio_register_port(maceps2_port[0]);
+ serio_register_port(maceps2_port[1]);
+
+ return 0;
+}
+
+static void maceps2_remove(struct platform_device *dev)
+{
+ serio_unregister_port(maceps2_port[0]);
+ serio_unregister_port(maceps2_port[1]);
+}
+
+static struct platform_driver maceps2_driver = {
+ .driver = {
+ .name = "maceps2",
+ },
+ .probe = maceps2_probe,
+ .remove = maceps2_remove,
+};
+
+static int __init maceps2_init(void)
+{
+ int error;
+
+ error = platform_driver_register(&maceps2_driver);
+ if (error)
+ return error;
+
+ maceps2_device = platform_device_alloc("maceps2", -1);
+ if (!maceps2_device) {
+ error = -ENOMEM;
+ goto err_unregister_driver;
+ }
+
+ port_data[0].port = &mace->perif.ps2.keyb;
+ port_data[0].irq = MACEISA_KEYB_IRQ;
+ port_data[1].port = &mace->perif.ps2.mouse;
+ port_data[1].irq = MACEISA_MOUSE_IRQ;
+
+ error = platform_device_add(maceps2_device);
+ if (error)
+ goto err_free_device;
+
+ return 0;
+
+ err_free_device:
+ platform_device_put(maceps2_device);
+ err_unregister_driver:
+ platform_driver_unregister(&maceps2_driver);
+ return error;
+}
+
+static void __exit maceps2_exit(void)
+{
+ platform_device_unregister(maceps2_device);
+ platform_driver_unregister(&maceps2_driver);
+}
+
+module_init(maceps2_init);
+module_exit(maceps2_exit);