diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
index 10a54dd..cacd1f2 100644
--- a/drivers/input/serio/Kconfig
+++ b/drivers/input/serio/Kconfig
@@ -130,3 +130,13 @@ config SERIO_PCIPS2
 
 	  To compile this driver as a module, choose M here: the
 	  module will be called pcips2.
+
+config SERIO_MACEPS2
+	tristate "SGI O2 MACE PS/2 controller"
+	depends on SGI_IP32 && SERIO
+	help
+	  Say Y here if you have SGI O2 workstation and want to use its
+	  PS/2 ports.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called maceps2.
diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile
index 2eb86eb..a47dec2 100644
--- a/drivers/input/serio/Makefile
+++ b/drivers/input/serio/Makefile
@@ -16,3 +16,4 @@ obj-$(CONFIG_SERIO_Q40KBD)	+= q40kbd.o
 obj-$(CONFIG_SERIO_98KBD)	+= 98kbd-io.o
 obj-$(CONFIG_SERIO_GSCPS2)	+= gscps2.o
 obj-$(CONFIG_SERIO_PCIPS2)	+= pcips2.o
+obj-$(CONFIG_SERIO_MACEPS2)	+= maceps2.o
diff --git a/drivers/input/serio/i8042-io.h b/drivers/input/serio/i8042-io.h
index 6b434f8..9b36485 100644
--- a/drivers/input/serio/i8042-io.h
+++ b/drivers/input/serio/i8042-io.h
@@ -69,7 +69,7 @@ static inline int i8042_platform_init(void)
  * On ix86 platforms touching the i8042 data register region can do really
  * bad things. Because of this the region is always reserved on ix86 boxes.
  */
-#if !defined(__i386__) && !defined(__sh__) && !defined(__alpha__) && !defined(__x86_64__)
+#if !defined(__i386__) && !defined(__sh__) && !defined(__alpha__) && !defined(__x86_64__) && !defined(__mips__)
 	if (!request_region(I8042_DATA_REG, 16, "i8042"))
 		return -1;
 #endif
diff --git a/drivers/input/serio/i8042-ip22io.h b/drivers/input/serio/i8042-ip22io.h
new file mode 100644
index 0000000..863b9c9
--- /dev/null
+++ b/drivers/input/serio/i8042-ip22io.h
@@ -0,0 +1,76 @@
+#ifndef _I8042_IP22_H
+#define _I8042_IP22_H
+
+#include <asm/sgi/ioc.h>
+#include <asm/sgi/ip22.h>
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * Names.
+ */
+
+#define I8042_KBD_PHYS_DESC "hpc3ps2/serio0"
+#define I8042_AUX_PHYS_DESC "hpc3ps2/serio1"
+#define I8042_MUX_PHYS_DESC "hpc3ps2/serio%d"
+
+/*
+ * IRQs.
+ */
+
+#define I8042_KBD_IRQ SGI_KEYBD_IRQ
+#define I8042_AUX_IRQ SGI_KEYBD_IRQ
+
+/*
+ * Register numbers.
+ */
+
+#define I8042_COMMAND_REG	((unsigned long)&sgioc->kbdmouse.command)
+#define I8042_STATUS_REG	((unsigned long)&sgioc->kbdmouse.command)
+#define I8042_DATA_REG		((unsigned long)&sgioc->kbdmouse.data)
+
+static inline int i8042_read_data(void)
+{
+	return sgioc->kbdmouse.data;
+}
+
+static inline int i8042_read_status(void)
+{
+	return sgioc->kbdmouse.command;
+}
+
+static inline void i8042_write_data(int val)
+{
+	sgioc->kbdmouse.data = val;
+}
+
+static inline void i8042_write_command(int val)
+{
+	sgioc->kbdmouse.command = val;
+}
+
+static inline int i8042_platform_init(void)
+{
+#if 0
+	/* XXX sgi_kh is a virtual address */
+	if (!request_mem_region(sgi_kh, sizeof(struct hpc_keyb), "i8042"))
+		return 1;
+#endif
+
+	i8042_reset = 1;
+
+	return 0;
+}
+
+static inline void i8042_platform_exit(void)
+{
+#if 0
+	release_mem_region(JAZZ_KEYBOARD_ADDRESS, sizeof(struct hpc_keyb));
+#endif
+}
+
+#endif /* _I8042_IP22_H */
diff --git a/drivers/input/serio/i8042-jazzio.h b/drivers/input/serio/i8042-jazzio.h
new file mode 100644
index 0000000..5c20ab1
--- /dev/null
+++ b/drivers/input/serio/i8042-jazzio.h
@@ -0,0 +1,69 @@
+#ifndef _I8042_JAZZ_H
+#define _I8042_JAZZ_H
+
+#include <asm/jazz.h>
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * Names.
+ */
+
+#define I8042_KBD_PHYS_DESC "R4030/serio0"
+#define I8042_AUX_PHYS_DESC "R4030/serio1"
+#define I8042_MUX_PHYS_DESC "R4030/serio%d"
+
+/*
+ * IRQs.
+ */
+
+#define I8042_KBD_IRQ JAZZ_KEYBOARD_IRQ
+#define I8042_AUX_IRQ JAZZ_MOUSE_IRQ
+
+#define I8042_COMMAND_REG	((unsigned long)&jazz_kh->command)
+#define I8042_STATUS_REG	((unsigned long)&jazz_kh->command)
+#define I8042_DATA_REG		((unsigned long)&jazz_kh->data)
+
+static inline int i8042_read_data(void)
+{
+	return jazz_kh->data;
+}
+
+static inline int i8042_read_status(void)
+{
+	return jazz_kh->command;
+}
+
+static inline void i8042_write_data(int val)
+{
+	jazz_kh->data = val;
+}
+
+static inline void i8042_write_command(int val)
+{
+	jazz_kh->command = val;
+}
+
+static inline int i8042_platform_init(void)
+{
+#if 0
+	/* XXX JAZZ_KEYBOARD_ADDRESS is a virtual address */
+	if (!request_mem_region(JAZZ_KEYBOARD_ADDRESS, 2, "i8042"))
+		return 1;
+#endif
+
+	return 0;
+}
+
+static inline void i8042_platform_exit(void)
+{
+#if 0
+	release_mem_region(JAZZ_KEYBOARD_ADDRESS, 2);
+#endif
+}
+
+#endif /* _I8042_JAZZ_H */
diff --git a/drivers/input/serio/i8042.h b/drivers/input/serio/i8042.h
index 3d59eb2..f0f6374 100644
--- a/drivers/input/serio/i8042.h
+++ b/drivers/input/serio/i8042.h
@@ -1,6 +1,8 @@
 #ifndef _I8042_H
 #define _I8042_H
 
+#include <linux/config.h>
+
 /*
  *  Copyright (c) 1999-2002 Vojtech Pavlik
  *
@@ -13,7 +15,11 @@
  * Arch-dependent inline functions and defines.
  */
 
-#if defined(CONFIG_PPC)
+#if defined(CONFIG_MIPS_JAZZ)
+#include "i8042-jazzio.h"
+#elif defined(CONFIG_SGI_IP22)
+#include "i8042-ip22io.h"
+#elif defined(CONFIG_PPC)
 #include "i8042-ppcio.h"
 #elif defined(CONFIG_SPARC32) || defined(CONFIG_SPARC64)
 #include "i8042-sparcio.h"
diff --git a/drivers/input/serio/maceps2.c b/drivers/input/serio/maceps2.c
new file mode 100644
index 0000000..c7db1de
--- /dev/null
+++ b/drivers/input/serio/maceps2.c
@@ -0,0 +1,160 @@
+/*
+ * SGI O2 MACE PS2 controller driver for linux
+ *
+ * Copyright (C) 2002 Vivien Chappelier
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation
+ */
+#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/device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.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 int maceps2_write(struct serio *dev, unsigned char val)
+{
+	struct mace_ps2port *port = ((struct maceps2_data *)dev->driver)->port;
+	unsigned int timeout = MACE_PS2_TIMEOUT;
+
+	do {
+		if (mace_read(port->status) & PS2_STATUS_TX_EMPTY) {
+			mace_write(val, port->tx);
+			return 0;
+		}
+		udelay(50);
+	} while (timeout--);
+
+	return -1;
+}
+
+static irqreturn_t maceps2_interrupt(int irq, void *dev_id,
+				     struct pt_regs *regs)
+{
+	struct serio *dev = dev_id;
+	struct mace_ps2port *port = ((struct maceps2_data *)dev->driver)->port;
+	unsigned int byte;
+
+	if (mace_read(port->status) & PS2_STATUS_RX_FULL) {
+		byte = mace_read(port->rx);
+		serio_interrupt(dev, byte & 0xff, 0, regs);
+        }
+
+	return IRQ_HANDLED;
+}
+
+static int maceps2_open(struct serio *dev)
+{
+	struct maceps2_data *data = (struct maceps2_data *)dev->driver;
+
+	if (request_irq(data->irq, maceps2_interrupt, 0, "PS/2 port", dev)) {
+		printk(KERN_ERR "Could not allocate PS/2 IRQ\n");
+		return -EBUSY;
+	}
+
+	/* Reset port */
+	mace_write(PS2_CONTROL_TX_CLOCK_DISABLE | PS2_CONTROL_RESET,
+		   data->port->control);
+	udelay(100);
+
+        /* Enable interrupts */
+	mace_write(PS2_CONTROL_RX_CLOCK_ENABLE | PS2_CONTROL_TX_ENABLE |
+		   PS2_CONTROL_RX_INT_ENABLE, data->port->control);
+
+	return 0;
+}
+
+static void maceps2_close(struct serio *dev)
+{
+	struct maceps2_data *data = (struct maceps2_data *)dev->driver;
+
+	mace_write(PS2_CONTROL_TX_CLOCK_DISABLE | PS2_CONTROL_RESET,
+		   data->port->control);
+	udelay(100);
+	free_irq(data->irq, dev);
+}
+
+static struct maceps2_data port0_data, port1_data;
+
+static struct serio maceps2_port0 =
+{
+	.type	= SERIO_8042,
+	.open	= maceps2_open,
+	.close	= maceps2_close,
+	.write	= maceps2_write,
+	.name	= "MACE PS/2 port0",
+	.phys	= "mace/serio0",
+	.driver = &port0_data,
+};
+
+static struct serio maceps2_port1 =
+{
+	.type	= SERIO_8042,
+	.open	= maceps2_open,
+	.close	= maceps2_close,
+	.write	= maceps2_write,
+	.name	= "MACE PS/2 port1",
+	.phys	= "mace/serio1",
+	.driver = &port1_data,
+};
+
+static int __init maceps2_init(void)
+{
+	port0_data.port = &mace->perif.ps2.keyb;
+	port0_data.irq  = MACEISA_KEYB_IRQ;
+	port1_data.port = &mace->perif.ps2.mouse;
+	port1_data.irq  = MACEISA_MOUSE_IRQ;
+	serio_register_port(&maceps2_port0);
+	serio_register_port(&maceps2_port1);
+
+	return 0;
+}
+
+static void __exit maceps2_exit(void)
+{
+	serio_unregister_port(&maceps2_port0);
+	serio_unregister_port(&maceps2_port1);
+}
+
+module_init(maceps2_init);
+module_exit(maceps2_exit);
