Merge rsync://rsync.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6

Conflicts:

	drivers/usb/input/hid.h
diff --git a/Documentation/DocBook/kernel-api.tmpl b/Documentation/DocBook/kernel-api.tmpl
index ca09491..3fa0c4b 100644
--- a/Documentation/DocBook/kernel-api.tmpl
+++ b/Documentation/DocBook/kernel-api.tmpl
@@ -559,4 +559,12 @@
 -->
      </sect1>
   </chapter>
+
+  <chapter id="input_subsystem">
+     <title>Input Subsystem</title>
+!Iinclude/linux/input.h
+!Edrivers/input/input.c
+!Edrivers/input/ff-core.c
+!Edrivers/input/ff-memless.c
+  </chapter>
 </book>
diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c
index 20b6c8b..7a6c1c0 100644
--- a/drivers/char/keyboard.c
+++ b/drivers/char/keyboard.c
@@ -710,7 +710,7 @@
 
 static void k_cur(struct vc_data *vc, unsigned char value, char up_flag)
 {
-	static const char *cur_chars = "BDCA";
+	static const char cur_chars[] = "BDCA";
 
 	if (up_flag)
 		return;
diff --git a/drivers/input/ff-core.c b/drivers/input/ff-core.c
index 35656ca..783b341 100644
--- a/drivers/input/ff-core.c
+++ b/drivers/input/ff-core.c
@@ -203,7 +203,7 @@
 }
 
 /**
- * input_ff_erase - erase an effect from device
+ * input_ff_erase - erase a force-feedback effect from device
  * @dev: input device to erase effect from
  * @effect_id: id of the ffect to be erased
  * @file: purported owner of the request
@@ -347,7 +347,7 @@
 
 /**
  * input_ff_free() - frees force feedback portion of input device
- * @dev: input device supporintg force feedback
+ * @dev: input device supporting force feedback
  *
  * This function is only needed in error path as input core will
  * automatically free force feedback structures when device is
diff --git a/drivers/input/ff-memless.c b/drivers/input/ff-memless.c
index cd8b729..eba18b6 100644
--- a/drivers/input/ff-memless.c
+++ b/drivers/input/ff-memless.c
@@ -460,7 +460,7 @@
 }
 
 /**
- * input_ff_create_memless() - create memoryless FF device
+ * input_ff_create_memless() - create memoryless force-feedback device
  * @dev: input device supporting force-feedback
  * @data: driver-specific data to be passed into @play_effect
  * @play_effect: driver-specific method for playing FF effect
diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c
index 79dfb4b..a00fe47 100644
--- a/drivers/input/gameport/gameport.c
+++ b/drivers/input/gameport/gameport.c
@@ -731,12 +731,6 @@
 	return 0;
 }
 
-static struct bus_type gameport_bus = {
-	.name	= "gameport",
-	.probe	= gameport_driver_probe,
-	.remove	= gameport_driver_remove,
-};
-
 static void gameport_add_driver(struct gameport_driver *drv)
 {
 	int error;
@@ -782,6 +776,15 @@
 	return !gameport_drv->ignore;
 }
 
+static struct bus_type gameport_bus = {
+	.name		= "gameport",
+	.dev_attrs	= gameport_device_attrs,
+	.drv_attrs	= gameport_driver_attrs,
+	.match		= gameport_bus_match,
+	.probe		= gameport_driver_probe,
+	.remove		= gameport_driver_remove,
+};
+
 static void gameport_set_drv(struct gameport *gameport, struct gameport_driver *drv)
 {
 	mutex_lock(&gameport->drv_mutex);
@@ -791,7 +794,6 @@
 
 int gameport_open(struct gameport *gameport, struct gameport_driver *drv, int mode)
 {
-
 	if (gameport->open) {
 		if (gameport->open(gameport, mode)) {
 			return -1;
@@ -819,9 +821,6 @@
 {
 	int error;
 
-	gameport_bus.dev_attrs = gameport_device_attrs;
-	gameport_bus.drv_attrs = gameport_driver_attrs;
-	gameport_bus.match = gameport_bus_match;
 	error = bus_register(&gameport_bus);
 	if (error) {
 		printk(KERN_ERR "gameport: failed to register gameport bus, error: %d\n", error);
diff --git a/drivers/input/gameport/lightning.c b/drivers/input/gameport/lightning.c
index d65d810..6b4d456 100644
--- a/drivers/input/gameport/lightning.c
+++ b/drivers/input/gameport/lightning.c
@@ -309,7 +309,7 @@
 	int i, cards = 0;
 
 	if (!request_region(L4_PORT, 1, "lightning"))
-		return -1;
+		return -EBUSY;
 
 	for (i = 0; i < 2; i++)
 		if (l4_add_card(i) == 0)
@@ -319,7 +319,7 @@
 
 	if (!cards) {
 		release_region(L4_PORT, 1);
-		return -1;
+		return -ENODEV;
 	}
 
 	return 0;
diff --git a/drivers/input/input.c b/drivers/input/input.c
index 1c8c8a5..7cf2b4f 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -37,7 +37,7 @@
 
 /**
  * input_event() - report new input event
- * @handle: device that generated the event
+ * @dev: device that generated the event
  * @type: type of the event
  * @code: event code
  * @value: value of the event
@@ -900,6 +900,15 @@
 };
 EXPORT_SYMBOL_GPL(input_class);
 
+/**
+ * input_allocate_device - allocate memory for new input device
+ *
+ * Returns prepared struct input_dev or NULL.
+ *
+ * NOTE: Use input_free_device() to free devices that have not been
+ * registered; input_unregister_device() should be used for already
+ * registered devices.
+ */
 struct input_dev *input_allocate_device(void)
 {
 	struct input_dev *dev;
@@ -919,6 +928,20 @@
 }
 EXPORT_SYMBOL(input_allocate_device);
 
+/**
+ * input_free_device - free memory occupied by input_dev structure
+ * @dev: input device to free
+ *
+ * This function should only be used if input_register_device()
+ * was not called yet or if it failed. Once device was registered
+ * use input_unregister_device() and memory will be freed once last
+ * refrence to the device is dropped.
+ *
+ * Device should be allocated by input_allocate_device().
+ *
+ * NOTE: If there are references to the input device then memory
+ * will not be freed until last reference is dropped.
+ */
 void input_free_device(struct input_dev *dev)
 {
 	if (dev) {
diff --git a/drivers/input/joystick/adi.c b/drivers/input/joystick/adi.c
index 704bf70..6279ced 100644
--- a/drivers/input/joystick/adi.c
+++ b/drivers/input/joystick/adi.c
@@ -521,11 +521,19 @@
 	for (i = 0; i < 2; i++)
 		if (port->adi[i].length > 0) {
 			adi_init_center(port->adi + i);
-			input_register_device(port->adi[i].dev);
+			err = input_register_device(port->adi[i].dev);
+			if (err)
+				goto fail3;
 		}
 
 	return 0;
 
+ fail3: while (--i >= 0) {
+		if (port->adi[i].length > 0) {
+			input_unregister_device(port->adi[i].dev);
+			port->adi[i].dev = NULL;
+		}
+	}
  fail2:	for (i = 0; i < 2; i++)
 		if (port->adi[i].dev)
 			input_free_device(port->adi[i].dev);
diff --git a/drivers/input/joystick/amijoy.c b/drivers/input/joystick/amijoy.c
index 650acf3..e608691 100644
--- a/drivers/input/joystick/amijoy.c
+++ b/drivers/input/joystick/amijoy.c
@@ -147,7 +147,11 @@
 			amijoy_dev[i]->absmax[ABS_X + j] = 1;
 		}
 
-		input_register_device(amijoy_dev[i]);
+		err = input_register_device(amijoy_dev[i]);
+		if (err) {
+			input_free_device(amijoy_dev[i]);
+			goto fail;
+		}
 	}
 	return 0;
 
diff --git a/drivers/input/joystick/analog.c b/drivers/input/joystick/analog.c
index e9a02db..7ef6845 100644
--- a/drivers/input/joystick/analog.c
+++ b/drivers/input/joystick/analog.c
@@ -434,6 +434,7 @@
 {
 	struct input_dev *input_dev;
 	int i, j, t, v, w, x, y, z;
+	int error;
 
 	analog_name(analog);
 	snprintf(analog->phys, sizeof(analog->phys),
@@ -505,7 +506,11 @@
 
 	analog_decode(analog, port->axes, port->initial, port->buttons);
 
-	input_register_device(analog->dev);
+	error = input_register_device(analog->dev);
+	if (error) {
+		input_free_device(analog->dev);
+		return error;
+	}
 
 	return 0;
 }
@@ -668,7 +673,8 @@
 	return 0;
 
  fail3: while (--i >= 0)
-		input_unregister_device(port->analog[i].dev);
+		if (port->analog[i].mask)
+			input_unregister_device(port->analog[i].dev);
  fail2:	gameport_close(gameport);
  fail1:	gameport_set_drvdata(gameport, NULL);
 	kfree(port);
diff --git a/drivers/input/joystick/cobra.c b/drivers/input/joystick/cobra.c
index d5e42eb..034ec39 100644
--- a/drivers/input/joystick/cobra.c
+++ b/drivers/input/joystick/cobra.c
@@ -223,12 +223,15 @@
 		for (j = 0; cobra_btn[j]; j++)
 			set_bit(cobra_btn[j], input_dev->keybit);
 
-		input_register_device(cobra->dev[i]);
+		err = input_register_device(cobra->dev[i]);
+		if (err)
+			goto fail4;
 	}
 
 	return 0;
 
- fail3:	for (i = 0; i < 2; i++)
+ fail4:	input_free_device(cobra->dev[i]);
+ fail3:	while (--i >= 0)
 		if (cobra->dev[i])
 			input_unregister_device(cobra->dev[i]);
  fail2:	gameport_close(gameport);
diff --git a/drivers/input/joystick/gf2k.c b/drivers/input/joystick/gf2k.c
index e4a699f..bacbab5 100644
--- a/drivers/input/joystick/gf2k.c
+++ b/drivers/input/joystick/gf2k.c
@@ -341,7 +341,9 @@
 		input_dev->absflat[gf2k_abs[i]] = (i < 2) ? 24 : 0;
 	}
 
-	input_register_device(gf2k->dev);
+	err = input_register_device(gf2k->dev);
+	if (err)
+		goto fail2;
 
 	return 0;
 
diff --git a/drivers/input/joystick/grip_mp.c b/drivers/input/joystick/grip_mp.c
index 62438944..8120a9c 100644
--- a/drivers/input/joystick/grip_mp.c
+++ b/drivers/input/joystick/grip_mp.c
@@ -423,7 +423,10 @@
 
 		if (!port->registered) {
 			dbg("New Grip pad in multiport slot %d.\n", slot);
-			register_slot(slot, grip);
+			if (register_slot(slot, grip)) {
+				port->mode = GRIP_MODE_RESET;
+				port->dirty = 0;
+			}
 		}
 		return flags;
 	}
@@ -585,6 +588,7 @@
 	struct grip_port *port = grip->port[slot];
 	struct input_dev *input_dev;
 	int j, t;
+	int err;
 
 	port->dev = input_dev = input_allocate_device();
 	if (!input_dev)
@@ -610,7 +614,12 @@
 		if (t > 0)
 			set_bit(t, input_dev->keybit);
 
-	input_register_device(port->dev);
+	err = input_register_device(port->dev);
+	if (err) {
+		input_free_device(port->dev);
+		return err;
+	}
+
 	port->registered = 1;
 
 	if (port->dirty)	            /* report initial state, if any */
diff --git a/drivers/input/joystick/guillemot.c b/drivers/input/joystick/guillemot.c
index 840ed9b..dbc5d92 100644
--- a/drivers/input/joystick/guillemot.c
+++ b/drivers/input/joystick/guillemot.c
@@ -250,7 +250,9 @@
 	for (i = 0; (t = guillemot->type->btn[i]) >= 0; i++)
 		set_bit(t, input_dev->keybit);
 
-	input_register_device(guillemot->dev);
+	err = input_register_device(guillemot->dev);
+	if (err)
+		goto fail2;
 
 	return 0;
 
diff --git a/drivers/input/joystick/iforce/iforce-main.c b/drivers/input/joystick/iforce/iforce-main.c
index 24c684b..3393a37 100644
--- a/drivers/input/joystick/iforce/iforce-main.c
+++ b/drivers/input/joystick/iforce/iforce-main.c
@@ -325,8 +325,8 @@
 
 	if (i == 20) { /* 5 seconds */
 		printk(KERN_ERR "iforce-main.c: Timeout waiting for response from device.\n");
-		input_free_device(input_dev);
-		return -ENODEV;
+		error = -ENODEV;
+		goto fail;
 	}
 
 /*
@@ -439,10 +439,8 @@
 			set_bit(iforce->type->ff[i], input_dev->ffbit);
 
 		error = input_ff_create(input_dev, ff_effects);
-		if (error) {
-			input_free_device(input_dev);
-			return error;
-		}
+		if (error)
+			goto fail;
 
 		ff = input_dev->ff;
 		ff->upload = iforce_upload_effect;
@@ -455,22 +453,35 @@
  * Register input device.
  */
 
-	input_register_device(iforce->dev);
+	error = input_register_device(iforce->dev);
+	if (error)
+		goto fail;
 
 	printk(KERN_DEBUG "iforce->dev->open = %p\n", iforce->dev->open);
 
 	return 0;
+
+ fail:	input_free_device(input_dev);
+	return error;
 }
 
 static int __init iforce_init(void)
 {
+	int err = 0;
+
 #ifdef CONFIG_JOYSTICK_IFORCE_USB
-	usb_register(&iforce_usb_driver);
+	err = usb_register(&iforce_usb_driver);
+	if (err)
+		return err;
 #endif
 #ifdef CONFIG_JOYSTICK_IFORCE_232
-	serio_register_driver(&iforce_serio_drv);
+	err = serio_register_driver(&iforce_serio_drv);
+#ifdef CONFIG_JOYSTICK_IFORCE_USB
+	if (err)
+		usb_deregister(&iforce_usb_driver);
 #endif
-	return 0;
+#endif
+	return err;
 }
 
 static void __exit iforce_exit(void)
diff --git a/drivers/input/joystick/iforce/iforce-serio.c b/drivers/input/joystick/iforce/iforce-serio.c
index ca08f45..ec4be53 100644
--- a/drivers/input/joystick/iforce/iforce-serio.c
+++ b/drivers/input/joystick/iforce/iforce-serio.c
@@ -141,21 +141,19 @@
 	serio_set_drvdata(serio, iforce);
 
 	err = serio_open(serio, drv);
-	if (err) {
-		serio_set_drvdata(serio, NULL);
-		kfree(iforce);
-		return err;
-	}
+	if (err)
+		goto fail1;
 
 	err = iforce_init_device(iforce);
-	if (err) {
-		serio_close(serio);
-		serio_set_drvdata(serio, NULL);
-		kfree(iforce);
-		return -ENODEV;
-	}
+	if (err)
+		goto fail2;
 
 	return 0;
+
+ fail2:	serio_close(serio);
+ fail1:	serio_set_drvdata(serio, NULL);
+	kfree(iforce);
+	return err;
 }
 
 static void iforce_serio_disconnect(struct serio *serio)
diff --git a/drivers/input/joystick/interact.c b/drivers/input/joystick/interact.c
index bbfeb9c..fec8b3d 100644
--- a/drivers/input/joystick/interact.c
+++ b/drivers/input/joystick/interact.c
@@ -283,7 +283,9 @@
 	for (i = 0; (t = interact_type[interact->type].btn[i]) >= 0; i++)
 		set_bit(t, input_dev->keybit);
 
-	input_register_device(interact->dev);
+	err = input_register_device(interact->dev);
+	if (err)
+		goto fail2;
 
 	return 0;
 
diff --git a/drivers/input/joystick/magellan.c b/drivers/input/joystick/magellan.c
index e3d1944..4112789 100644
--- a/drivers/input/joystick/magellan.c
+++ b/drivers/input/joystick/magellan.c
@@ -157,7 +157,7 @@
 	magellan = kzalloc(sizeof(struct magellan), GFP_KERNEL);
 	input_dev = input_allocate_device();
 	if (!magellan || !input_dev)
-		goto fail;
+		goto fail1;
 
 	magellan->dev = input_dev;
 	snprintf(magellan->phys, sizeof(magellan->phys), "%s/input0", serio->phys);
@@ -183,13 +183,17 @@
 
 	err = serio_open(serio, drv);
 	if (err)
-		goto fail;
+		goto fail2;
 
-	input_register_device(magellan->dev);
+	err = input_register_device(magellan->dev);
+	if (err)
+		goto fail3;
+
 	return 0;
 
- fail:	serio_set_drvdata(serio, NULL);
-	input_free_device(input_dev);
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
 	kfree(magellan);
 	return err;
 }
@@ -227,8 +231,7 @@
 
 static int __init magellan_init(void)
 {
-	serio_register_driver(&magellan_drv);
-	return 0;
+	return serio_register_driver(&magellan_drv);
 }
 
 static void __exit magellan_exit(void)
diff --git a/drivers/input/joystick/spaceball.c b/drivers/input/joystick/spaceball.c
index 2a9808c..08bf113 100644
--- a/drivers/input/joystick/spaceball.c
+++ b/drivers/input/joystick/spaceball.c
@@ -215,7 +215,7 @@
 	spaceball = kmalloc(sizeof(struct spaceball), GFP_KERNEL);
 	input_dev = input_allocate_device();
 	if (!spaceball || !input_dev)
-		goto fail;
+		goto fail1;
 
 	spaceball->dev = input_dev;
 	snprintf(spaceball->phys, sizeof(spaceball->phys), "%s/input0", serio->phys);
@@ -252,13 +252,17 @@
 
 	err = serio_open(serio, drv);
 	if (err)
-		goto fail;
+		goto fail2;
 
-	input_register_device(spaceball->dev);
+	err = input_register_device(spaceball->dev);
+	if (err)
+		goto fail3;
+
 	return 0;
 
- fail:	serio_set_drvdata(serio, NULL);
-	input_free_device(input_dev);
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
 	kfree(spaceball);
 	return err;
 }
@@ -296,8 +300,7 @@
 
 static int __init spaceball_init(void)
 {
-	serio_register_driver(&spaceball_drv);
-	return 0;
+	return serio_register_driver(&spaceball_drv);
 }
 
 static void __exit spaceball_exit(void)
diff --git a/drivers/input/joystick/spaceorb.c b/drivers/input/joystick/spaceorb.c
index c4db024..c9c7921 100644
--- a/drivers/input/joystick/spaceorb.c
+++ b/drivers/input/joystick/spaceorb.c
@@ -172,7 +172,7 @@
 	spaceorb = kzalloc(sizeof(struct spaceorb), GFP_KERNEL);
 	input_dev = input_allocate_device();
 	if (!spaceorb || !input_dev)
-		goto fail;
+		goto fail1;
 
 	spaceorb->dev = input_dev;
 	snprintf(spaceorb->phys, sizeof(spaceorb->phys), "%s/input0", serio->phys);
@@ -198,13 +198,17 @@
 
 	err = serio_open(serio, drv);
 	if (err)
-		goto fail;
+		goto fail2;
 
-	input_register_device(spaceorb->dev);
+	err = input_register_device(spaceorb->dev);
+	if (err)
+		goto fail3;
+
 	return 0;
 
- fail:	serio_set_drvdata(serio, NULL);
-	input_free_device(input_dev);
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
 	kfree(spaceorb);
 	return err;
 }
@@ -242,8 +246,7 @@
 
 static int __init spaceorb_init(void)
 {
-	serio_register_driver(&spaceorb_drv);
-	return 0;
+	return serio_register_driver(&spaceorb_drv);
 }
 
 static void __exit spaceorb_exit(void)
diff --git a/drivers/input/joystick/stinger.c b/drivers/input/joystick/stinger.c
index 1ffb032..ecb0916 100644
--- a/drivers/input/joystick/stinger.c
+++ b/drivers/input/joystick/stinger.c
@@ -143,7 +143,7 @@
 	stinger = kmalloc(sizeof(struct stinger), GFP_KERNEL);
 	input_dev = input_allocate_device();
 	if (!stinger || !input_dev)
-		goto fail;
+		goto fail1;
 
 	stinger->dev = input_dev;
 	snprintf(stinger->phys, sizeof(stinger->phys), "%s/serio0", serio->phys);
@@ -168,13 +168,17 @@
 
 	err = serio_open(serio, drv);
 	if (err)
-		goto fail;
+		goto fail2;
 
-	input_register_device(stinger->dev);
+	err = input_register_device(stinger->dev);
+	if (err)
+		goto fail3;
+
 	return 0;
 
- fail:	serio_set_drvdata(serio, NULL);
-	input_free_device(input_dev);
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
 	kfree(stinger);
 	return err;
 }
@@ -212,8 +216,7 @@
 
 static int __init stinger_init(void)
 {
-	serio_register_driver(&stinger_drv);
-	return 0;
+	return serio_register_driver(&stinger_drv);
 }
 
 static void __exit stinger_exit(void)
diff --git a/drivers/input/joystick/twidjoy.c b/drivers/input/joystick/twidjoy.c
index 49085df..9cf17d6 100644
--- a/drivers/input/joystick/twidjoy.c
+++ b/drivers/input/joystick/twidjoy.c
@@ -194,7 +194,7 @@
 	twidjoy = kzalloc(sizeof(struct twidjoy), GFP_KERNEL);
 	input_dev = input_allocate_device();
 	if (!twidjoy || !input_dev)
-		goto fail;
+		goto fail1;
 
 	twidjoy->dev = input_dev;
 	snprintf(twidjoy->phys, sizeof(twidjoy->phys), "%s/input0", serio->phys);
@@ -221,13 +221,17 @@
 
 	err = serio_open(serio, drv);
 	if (err)
-		goto fail;
+		goto fail2;
 
-	input_register_device(twidjoy->dev);
+	err = input_register_device(twidjoy->dev);
+	if (err)
+		goto fail3;
+
 	return 0;
 
- fail:	serio_set_drvdata(serio, NULL);
-	input_free_device(input_dev);
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
 	kfree(twidjoy);
 	return err;
 }
@@ -265,8 +269,7 @@
 
 static int __init twidjoy_init(void)
 {
-	serio_register_driver(&twidjoy_drv);
-	return 0;
+	return serio_register_driver(&twidjoy_drv);
 }
 
 static void __exit twidjoy_exit(void)
diff --git a/drivers/input/joystick/warrior.c b/drivers/input/joystick/warrior.c
index 35edea1..29d339a 100644
--- a/drivers/input/joystick/warrior.c
+++ b/drivers/input/joystick/warrior.c
@@ -149,7 +149,7 @@
 	warrior = kzalloc(sizeof(struct warrior), GFP_KERNEL);
 	input_dev = input_allocate_device();
 	if (!warrior || !input_dev)
-		goto fail;
+		goto fail1;
 
 	warrior->dev = input_dev;
 	snprintf(warrior->phys, sizeof(warrior->phys), "%s/input0", serio->phys);
@@ -176,13 +176,17 @@
 
 	err = serio_open(serio, drv);
 	if (err)
-		goto fail;
+		goto fail2;
 
-	input_register_device(warrior->dev);
+	err = input_register_device(warrior->dev);
+	if (err)
+		goto fail3;
+
 	return 0;
 
- fail:	serio_set_drvdata(serio, NULL);
-	input_free_device(input_dev);
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
 	kfree(warrior);
 	return err;
 }
@@ -220,8 +224,7 @@
 
 static int __init warrior_init(void)
 {
-	serio_register_driver(&warrior_drv);
-	return 0;
+	return serio_register_driver(&warrior_drv);
 }
 
 static void __exit warrior_exit(void)
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 81a333f..049f2f5 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -203,4 +203,15 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called omap-keypad.
 
+config KEYBOARD_AAED2000
+	tristate "AAED-2000 keyboard"
+	depends on MACH_AAED2000
+	default y
+	help
+	  Say Y here to enable the keyboard on the Agilent AAED-2000
+	  development board.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called aaed2000_kbd.
+
 endif
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 4c79e7b..5687979 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -17,4 +17,5 @@
 obj-$(CONFIG_KEYBOARD_HIL)		+= hil_kbd.o
 obj-$(CONFIG_KEYBOARD_HIL_OLD)		+= hilkbd.o
 obj-$(CONFIG_KEYBOARD_OMAP)             += omap-keypad.o
+obj-$(CONFIG_KEYBOARD_AAED2000)         += aaed2000_kbd.o
 
diff --git a/drivers/input/keyboard/aaed2000_kbd.c b/drivers/input/keyboard/aaed2000_kbd.c
new file mode 100644
index 0000000..65fcb6a
--- /dev/null
+++ b/drivers/input/keyboard/aaed2000_kbd.c
@@ -0,0 +1,203 @@
+/*
+ *  Keyboard driver for the AAED-2000 dev board
+ *
+ *  Copyright (c) 2006 Nicolas Bellido Y Ortega
+ *
+ *  Based on corgikbd.c
+ *
+ *  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/delay.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#include <asm/arch/hardware.h>
+#include <asm/arch/aaed2000.h>
+
+#define KB_ROWS			12
+#define KB_COLS			8
+#define KB_ROWMASK(r)		(1 << (r))
+#define SCANCODE(r,c)		(((c) * KB_ROWS) + (r))
+#define NR_SCANCODES		(KB_COLS * KB_ROWS)
+
+#define SCAN_INTERVAL		(50) /* ms */
+#define KB_ACTIVATE_DELAY	(20) /* us */
+
+static unsigned char aaedkbd_keycode[NR_SCANCODES] = {
+	KEY_9, KEY_0, KEY_MINUS, KEY_EQUAL, KEY_BACKSPACE, 0, KEY_SPACE, KEY_KP6, 0, KEY_KPDOT, 0, 0,
+	KEY_K, KEY_M, KEY_O, KEY_DOT, KEY_SLASH, 0, KEY_F, 0, 0, 0, KEY_LEFTSHIFT, 0,
+	KEY_I, KEY_P, KEY_LEFTBRACE, KEY_RIGHTBRACE, KEY_BACKSLASH, 0, 0, 0, 0, 0, KEY_RIGHTSHIFT, 0,
+	KEY_8, KEY_L, KEY_SEMICOLON, KEY_APOSTROPHE, KEY_ENTER, 0, 0, 0, 0, 0, 0, 0,
+	KEY_J, KEY_H, KEY_B, KEY_KP8, KEY_KP4, 0, KEY_C, KEY_D, KEY_S, KEY_A, 0, KEY_CAPSLOCK,
+	KEY_Y, KEY_U, KEY_N, KEY_T, 0, 0, KEY_R, KEY_E, KEY_W, KEY_Q, 0, KEY_TAB,
+	KEY_7, KEY_6, KEY_G, 0, KEY_5, 0, KEY_4, KEY_3, KEY_2, KEY_1, 0, KEY_GRAVE,
+	0, 0, KEY_COMMA, 0, KEY_KP2, 0, KEY_V, KEY_LEFTALT, KEY_X, KEY_Z, 0, KEY_LEFTCTRL
+};
+
+struct aaedkbd {
+	unsigned char keycode[ARRAY_SIZE(aaedkbd_keycode)];
+	struct input_dev *input;
+	struct work_struct workq;
+	int kbdscan_state[KB_COLS];
+	int kbdscan_count[KB_COLS];
+};
+
+#define KBDSCAN_STABLE_COUNT 2
+
+static void aaedkbd_report_col(struct aaedkbd *aaedkbd,
+				unsigned int col, unsigned int rowd)
+{
+	unsigned int scancode, pressed;
+	unsigned int row;
+
+	for (row = 0; row < KB_ROWS; row++) {
+		scancode = SCANCODE(row, col);
+		pressed = rowd & KB_ROWMASK(row);
+
+		input_report_key(aaedkbd->input, aaedkbd->keycode[scancode], pressed);
+	}
+}
+
+/* Scan the hardware keyboard and push any changes up through the input layer */
+static void aaedkbd_work(void *data)
+{
+	struct aaedkbd *aaedkbd = data;
+	unsigned int col, rowd;
+
+	col = 0;
+	do {
+		AAEC_GPIO_KSCAN = col + 8;
+		udelay(KB_ACTIVATE_DELAY);
+		rowd = AAED_EXT_GPIO & AAED_EGPIO_KBD_SCAN;
+
+		if (rowd != aaedkbd->kbdscan_state[col]) {
+			aaedkbd->kbdscan_count[col] = 0;
+			aaedkbd->kbdscan_state[col] = rowd;
+		} else if (++aaedkbd->kbdscan_count[col] >= KBDSCAN_STABLE_COUNT) {
+			aaedkbd_report_col(aaedkbd, col, rowd);
+			col++;
+		}
+	} while (col < KB_COLS);
+
+	AAEC_GPIO_KSCAN = 0x07;
+	input_sync(aaedkbd->input);
+
+	schedule_delayed_work(&aaedkbd->workq, msecs_to_jiffies(SCAN_INTERVAL));
+}
+
+static int aaedkbd_open(struct input_dev *indev)
+{
+	struct aaedkbd *aaedkbd = indev->private;
+
+	schedule_delayed_work(&aaedkbd->workq, msecs_to_jiffies(SCAN_INTERVAL));
+
+	return 0;
+}
+
+static void aaedkbd_close(struct input_dev *indev)
+{
+	struct aaedkbd *aaedkbd = indev->private;
+
+	cancel_delayed_work(&aaedkbd->workq);
+	flush_scheduled_work();
+}
+
+static int __devinit aaedkbd_probe(struct platform_device *pdev)
+{
+	struct aaedkbd *aaedkbd;
+	struct input_dev *input_dev;
+	int i;
+	int error;
+
+	aaedkbd = kzalloc(sizeof(struct aaedkbd), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!aaedkbd || !input_dev) {
+		error = -ENOMEM;
+		goto fail;
+	}
+
+	platform_set_drvdata(pdev, aaedkbd);
+
+	aaedkbd->input = input_dev;
+
+	/* Init keyboard rescan workqueue */
+	INIT_WORK(&aaedkbd->workq, aaedkbd_work, aaedkbd);
+
+	memcpy(aaedkbd->keycode, aaedkbd_keycode, sizeof(aaedkbd->keycode));
+
+	input_dev->name = "AAED-2000 Keyboard";
+	input_dev->phys = "aaedkbd/input0";
+	input_dev->id.bustype = BUS_HOST;
+	input_dev->id.vendor = 0x0001;
+	input_dev->id.product = 0x0001;
+	input_dev->id.version = 0x0100;
+	input_dev->cdev.dev = &pdev->dev;
+	input_dev->private = aaedkbd;
+
+	input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+	input_dev->keycode = aaedkbd->keycode;
+	input_dev->keycodesize = sizeof(unsigned char);
+	input_dev->keycodemax = ARRAY_SIZE(aaedkbd_keycode);
+
+	for (i = 0; i < ARRAY_SIZE(aaedkbd_keycode); i++)
+		set_bit(aaedkbd->keycode[i], input_dev->keybit);
+	clear_bit(0, input_dev->keybit);
+
+	input_dev->open = aaedkbd_open;
+	input_dev->close = aaedkbd_close;
+
+	error = input_register_device(aaedkbd->input);
+	if (error)
+		goto fail;
+
+	return 0;
+
+ fail:	kfree(aaedkbd);
+	input_free_device(input_dev);
+	return error;
+}
+
+static int __devexit aaedkbd_remove(struct platform_device *pdev)
+{
+	struct aaedkbd *aaedkbd = platform_get_drvdata(pdev);
+
+	input_unregister_device(aaedkbd->input);
+	kfree(aaedkbd);
+
+	return 0;
+}
+
+static struct platform_driver aaedkbd_driver = {
+	.probe		= aaedkbd_probe,
+	.remove		= __devexit_p(aaedkbd_remove),
+	.driver		= {
+		.name	= "aaed2000-keyboard",
+	},
+};
+
+static int __init aaedkbd_init(void)
+{
+	return platform_driver_register(&aaedkbd_driver);
+}
+
+static void __exit aaedkbd_exit(void)
+{
+	platform_driver_unregister(&aaedkbd_driver);
+}
+
+module_init(aaedkbd_init);
+module_exit(aaedkbd_exit);
+
+MODULE_AUTHOR("Nicolas Bellido Y Ortega");
+MODULE_DESCRIPTION("AAED-2000 Keyboard Driver");
+MODULE_LICENSE("GPLv2");
diff --git a/drivers/input/keyboard/amikbd.c b/drivers/input/keyboard/amikbd.c
index 8abdbd0..16583d7 100644
--- a/drivers/input/keyboard/amikbd.c
+++ b/drivers/input/keyboard/amikbd.c
@@ -190,7 +190,7 @@
 	int i, j;
 
 	if (!AMIGAHW_PRESENT(AMI_KEYBOARD))
-		return -EIO;
+		return -ENODEV;
 
 	if (!request_mem_region(CIAA_PHYSADDR-1+0xb00, 0x100, "amikeyb"))
 		return -EBUSY;
@@ -198,8 +198,8 @@
 	amikbd_dev = input_allocate_device();
 	if (!amikbd_dev) {
 		printk(KERN_ERR "amikbd: not enough memory for input device\n");
-		release_mem_region(CIAA_PHYSADDR - 1 + 0xb00, 0x100);
-		return -ENOMEM;
+		err = -ENOMEM;
+		goto fail1;
 	}
 
 	amikbd_dev->name = "Amiga Keyboard";
@@ -231,10 +231,22 @@
 		memcpy(key_maps[i], temp_map, sizeof(temp_map));
 	}
 	ciaa.cra &= ~0x41;	 /* serial data in, turn off TA */
-	request_irq(IRQ_AMIGA_CIAA_SP, amikbd_interrupt, 0, "amikbd", amikbd_interrupt);
+	if (request_irq(IRQ_AMIGA_CIAA_SP, amikbd_interrupt, 0, "amikbd",
+			amikbd_interrupt)) {
+		err = -EBUSY;
+		goto fail2;
+	}
 
-	input_register_device(amikbd_dev);
+	err = input_register_device(amikbd_dev);
+	if (err)
+		goto fail3;
+
 	return 0;
+
+ fail3:	free_irq(IRQ_AMIGA_CIAA_SP, amikbd_interrupt);
+ fail2:	input_free_device(amikbd_dev);
+ fail1:	release_mem_region(CIAA_PHYSADDR - 1 + 0xb00, 0x100);
+	return err;
 }
 
 static void __exit amikbd_exit(void)
diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c
index 8451b29..c621a91 100644
--- a/drivers/input/keyboard/atkbd.c
+++ b/drivers/input/keyboard/atkbd.c
@@ -939,7 +939,7 @@
 	atkbd = kzalloc(sizeof(struct atkbd), GFP_KERNEL);
 	dev = input_allocate_device();
 	if (!atkbd || !dev)
-		goto fail;
+		goto fail1;
 
 	atkbd->dev = dev;
 	ps2_init(&atkbd->ps2dev, serio);
@@ -967,14 +967,13 @@
 
 	err = serio_open(serio, drv);
 	if (err)
-		goto fail;
+		goto fail2;
 
 	if (atkbd->write) {
 
 		if (atkbd_probe(atkbd)) {
-			serio_close(serio);
 			err = -ENODEV;
-			goto fail;
+			goto fail3;
 		}
 
 		atkbd->set = atkbd_select_set(atkbd, atkbd_set, atkbd_extra);
@@ -988,16 +987,22 @@
 	atkbd_set_keycode_table(atkbd);
 	atkbd_set_device_attrs(atkbd);
 
-	sysfs_create_group(&serio->dev.kobj, &atkbd_attribute_group);
+	err = sysfs_create_group(&serio->dev.kobj, &atkbd_attribute_group);
+	if (err)
+		goto fail3;
 
 	atkbd_enable(atkbd);
 
-	input_register_device(atkbd->dev);
+	err = input_register_device(atkbd->dev);
+	if (err)
+		goto fail4;
 
 	return 0;
 
- fail:	serio_set_drvdata(serio, NULL);
-	input_free_device(dev);
+ fail4: sysfs_remove_group(&serio->dev.kobj, &atkbd_attribute_group);
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(dev);
 	kfree(atkbd);
 	return err;
 }
@@ -1133,9 +1138,11 @@
 
 static ssize_t atkbd_set_extra(struct atkbd *atkbd, const char *buf, size_t count)
 {
-	struct input_dev *new_dev;
+	struct input_dev *old_dev, *new_dev;
 	unsigned long value;
 	char *rest;
+	int err;
+	unsigned char old_extra, old_set;
 
 	if (!atkbd->write)
 		return -EIO;
@@ -1147,17 +1154,36 @@
 	if (atkbd->extra != value) {
 		/*
 		 * Since device's properties will change we need to
-		 * unregister old device. But allocate new one first
-		 * to make sure we have it.
+		 * unregister old device. But allocate and register
+		 * new one first to make sure we have it.
 		 */
-		if (!(new_dev = input_allocate_device()))
+		old_dev = atkbd->dev;
+		old_extra = atkbd->extra;
+		old_set = atkbd->set;
+
+		new_dev = input_allocate_device();
+		if (!new_dev)
 			return -ENOMEM;
-		input_unregister_device(atkbd->dev);
+
 		atkbd->dev = new_dev;
 		atkbd->set = atkbd_select_set(atkbd, atkbd->set, value);
 		atkbd_activate(atkbd);
+		atkbd_set_keycode_table(atkbd);
 		atkbd_set_device_attrs(atkbd);
-		input_register_device(atkbd->dev);
+
+		err = input_register_device(atkbd->dev);
+		if (err) {
+			input_free_device(new_dev);
+
+			atkbd->dev = old_dev;
+			atkbd->set = atkbd_select_set(atkbd, old_set, old_extra);
+			atkbd_set_keycode_table(atkbd);
+			atkbd_set_device_attrs(atkbd);
+
+			return err;
+		}
+		input_unregister_device(old_dev);
+
 	}
 	return count;
 }
@@ -1169,23 +1195,41 @@
 
 static ssize_t atkbd_set_scroll(struct atkbd *atkbd, const char *buf, size_t count)
 {
-	struct input_dev *new_dev;
+	struct input_dev *old_dev, *new_dev;
 	unsigned long value;
 	char *rest;
+	int err;
+	unsigned char old_scroll;
 
 	value = simple_strtoul(buf, &rest, 10);
 	if (*rest || value > 1)
 		return -EINVAL;
 
 	if (atkbd->scroll != value) {
-		if (!(new_dev = input_allocate_device()))
+		old_dev = atkbd->dev;
+		old_scroll = atkbd->scroll;
+
+		new_dev = input_allocate_device();
+		if (!new_dev)
 			return -ENOMEM;
-		input_unregister_device(atkbd->dev);
+
 		atkbd->dev = new_dev;
 		atkbd->scroll = value;
 		atkbd_set_keycode_table(atkbd);
 		atkbd_set_device_attrs(atkbd);
-		input_register_device(atkbd->dev);
+
+		err = input_register_device(atkbd->dev);
+		if (err) {
+			input_free_device(new_dev);
+
+			atkbd->scroll = old_scroll;
+			atkbd->dev = old_dev;
+			atkbd_set_keycode_table(atkbd);
+			atkbd_set_device_attrs(atkbd);
+
+			return err;
+		}
+		input_unregister_device(old_dev);
 	}
 	return count;
 }
@@ -1197,9 +1241,11 @@
 
 static ssize_t atkbd_set_set(struct atkbd *atkbd, const char *buf, size_t count)
 {
-	struct input_dev *new_dev;
+	struct input_dev *old_dev, *new_dev;
 	unsigned long value;
 	char *rest;
+	int err;
+	unsigned char old_set, old_extra;
 
 	if (!atkbd->write)
 		return -EIO;
@@ -1209,15 +1255,32 @@
 		return -EINVAL;
 
 	if (atkbd->set != value) {
-		if (!(new_dev = input_allocate_device()))
+		old_dev = atkbd->dev;
+		old_extra = atkbd->extra;
+		old_set = atkbd->set;
+
+		new_dev = input_allocate_device();
+		if (!new_dev)
 			return -ENOMEM;
-		input_unregister_device(atkbd->dev);
+
 		atkbd->dev = new_dev;
 		atkbd->set = atkbd_select_set(atkbd, value, atkbd->extra);
 		atkbd_activate(atkbd);
 		atkbd_set_keycode_table(atkbd);
 		atkbd_set_device_attrs(atkbd);
-		input_register_device(atkbd->dev);
+
+		err = input_register_device(atkbd->dev);
+		if (err) {
+			input_free_device(new_dev);
+
+			atkbd->dev = old_dev;
+			atkbd->set = atkbd_select_set(atkbd, old_set, old_extra);
+			atkbd_set_keycode_table(atkbd);
+			atkbd_set_device_attrs(atkbd);
+
+			return err;
+		}
+		input_unregister_device(old_dev);
 	}
 	return count;
 }
@@ -1229,9 +1292,11 @@
 
 static ssize_t atkbd_set_softrepeat(struct atkbd *atkbd, const char *buf, size_t count)
 {
-	struct input_dev *new_dev;
+	struct input_dev *old_dev, *new_dev;
 	unsigned long value;
 	char *rest;
+	int err;
+	unsigned char old_softrepeat, old_softraw;
 
 	if (!atkbd->write)
 		return -EIO;
@@ -1241,15 +1306,32 @@
 		return -EINVAL;
 
 	if (atkbd->softrepeat != value) {
-		if (!(new_dev = input_allocate_device()))
+		old_dev = atkbd->dev;
+		old_softrepeat = atkbd->softrepeat;
+		old_softraw = atkbd->softraw;
+
+		new_dev = input_allocate_device();
+		if (!new_dev)
 			return -ENOMEM;
-		input_unregister_device(atkbd->dev);
+
 		atkbd->dev = new_dev;
 		atkbd->softrepeat = value;
 		if (atkbd->softrepeat)
 			atkbd->softraw = 1;
 		atkbd_set_device_attrs(atkbd);
-		input_register_device(atkbd->dev);
+
+		err = input_register_device(atkbd->dev);
+		if (err) {
+			input_free_device(new_dev);
+
+			atkbd->dev = old_dev;
+			atkbd->softrepeat = old_softrepeat;
+			atkbd->softraw = old_softraw;
+			atkbd_set_device_attrs(atkbd);
+
+			return err;
+		}
+		input_unregister_device(old_dev);
 	}
 	return count;
 }
@@ -1262,22 +1344,39 @@
 
 static ssize_t atkbd_set_softraw(struct atkbd *atkbd, const char *buf, size_t count)
 {
-	struct input_dev *new_dev;
+	struct input_dev *old_dev, *new_dev;
 	unsigned long value;
 	char *rest;
+	int err;
+	unsigned char old_softraw;
 
 	value = simple_strtoul(buf, &rest, 10);
 	if (*rest || value > 1)
 		return -EINVAL;
 
 	if (atkbd->softraw != value) {
-		if (!(new_dev = input_allocate_device()))
+		old_dev = atkbd->dev;
+		old_softraw = atkbd->softraw;
+
+		new_dev = input_allocate_device();
+		if (!new_dev)
 			return -ENOMEM;
-		input_unregister_device(atkbd->dev);
+
 		atkbd->dev = new_dev;
 		atkbd->softraw = value;
 		atkbd_set_device_attrs(atkbd);
-		input_register_device(atkbd->dev);
+
+		err = input_register_device(atkbd->dev);
+		if (err) {
+			input_free_device(new_dev);
+
+			atkbd->dev = old_dev;
+			atkbd->softraw = old_softraw;
+			atkbd_set_device_attrs(atkbd);
+
+			return err;
+		}
+		input_unregister_device(old_dev);
 	}
 	return count;
 }
@@ -1290,8 +1389,7 @@
 
 static int __init atkbd_init(void)
 {
-	serio_register_driver(&atkbd_drv);
-	return 0;
+	return serio_register_driver(&atkbd_drv);
 }
 
 static void __exit atkbd_exit(void)
diff --git a/drivers/input/keyboard/corgikbd.c b/drivers/input/keyboard/corgikbd.c
index befdd60..1016c94 100644
--- a/drivers/input/keyboard/corgikbd.c
+++ b/drivers/input/keyboard/corgikbd.c
@@ -291,15 +291,12 @@
 {
 	struct corgikbd *corgikbd;
 	struct input_dev *input_dev;
-	int i;
+	int i, err = -ENOMEM;
 
 	corgikbd = kzalloc(sizeof(struct corgikbd), GFP_KERNEL);
 	input_dev = input_allocate_device();
-	if (!corgikbd || !input_dev) {
-		kfree(corgikbd);
-		input_free_device(input_dev);
-		return -ENOMEM;
-	}
+	if (!corgikbd || !input_dev)
+		goto fail;
 
 	platform_set_drvdata(pdev, corgikbd);
 
@@ -341,7 +338,9 @@
 	set_bit(SW_TABLET_MODE, input_dev->swbit);
 	set_bit(SW_HEADPHONE_INSERT, input_dev->swbit);
 
-	input_register_device(corgikbd->input);
+	err = input_register_device(corgikbd->input);
+	if (err)
+		goto fail;
 
 	mod_timer(&corgikbd->htimer, jiffies + msecs_to_jiffies(HINGE_SCAN_INTERVAL));
 
@@ -362,6 +361,10 @@
 	pxa_gpio_mode(CORGI_GPIO_AK_INT | GPIO_IN);
 
 	return 0;
+
+ fail:	input_free_device(input_dev);
+	kfree(corgikbd);
+	return err;
 }
 
 static int corgikbd_remove(struct platform_device *pdev)
diff --git a/drivers/input/keyboard/hil_kbd.c b/drivers/input/keyboard/hil_kbd.c
index e774dd3..7cc9728 100644
--- a/drivers/input/keyboard/hil_kbd.c
+++ b/drivers/input/keyboard/hil_kbd.c
@@ -381,8 +381,7 @@
 
 static int __init hil_kbd_init(void)
 {
-	serio_register_driver(&hil_kbd_serio_drv);
-        return 0;
+	return serio_register_driver(&hil_kbd_serio_drv);
 }
                 
 static void __exit hil_kbd_exit(void)
diff --git a/drivers/input/keyboard/lkkbd.c b/drivers/input/keyboard/lkkbd.c
index b7f049b..3d4d0a0 100644
--- a/drivers/input/keyboard/lkkbd.c
+++ b/drivers/input/keyboard/lkkbd.c
@@ -646,7 +646,7 @@
 	input_dev = input_allocate_device ();
 	if (!lk || !input_dev) {
 		err = -ENOMEM;
-		goto fail;
+		goto fail1;
 	}
 
 	lk->serio = serio;
@@ -691,15 +691,19 @@
 
 	err = serio_open (serio, drv);
 	if (err)
-		goto fail;
+		goto fail2;
 
-	input_register_device (lk->dev);
+	err = input_register_device (lk->dev);
+	if (err)
+		goto fail3;
+
 	lk->serio->write (lk->serio, LK_CMD_POWERCYCLE_RESET);
 
 	return 0;
 
- fail:	serio_set_drvdata (serio, NULL);
-	input_free_device (input_dev);
+ fail3:	serio_close (serio);
+ fail2:	serio_set_drvdata (serio, NULL);
+ fail1:	input_free_device (input_dev);
 	kfree (lk);
 	return err;
 }
@@ -749,8 +753,7 @@
 static int __init
 lkkbd_init (void)
 {
-	serio_register_driver(&lkkbd_drv);
-	return 0;
+	return serio_register_driver(&lkkbd_drv);
 }
 
 static void __exit
diff --git a/drivers/input/keyboard/locomokbd.c b/drivers/input/keyboard/locomokbd.c
index 5788dbc..2ade518 100644
--- a/drivers/input/keyboard/locomokbd.c
+++ b/drivers/input/keyboard/locomokbd.c
@@ -193,22 +193,22 @@
 {
 	struct locomokbd *locomokbd;
 	struct input_dev *input_dev;
-	int i, ret;
+	int i, err;
 
 	locomokbd = kzalloc(sizeof(struct locomokbd), GFP_KERNEL);
 	input_dev = input_allocate_device();
 	if (!locomokbd || !input_dev) {
-		ret = -ENOMEM;
-		goto free;
+		err = -ENOMEM;
+		goto err_free_mem;
 	}
 
 	/* try and claim memory region */
 	if (!request_mem_region((unsigned long) dev->mapbase,
 				dev->length,
 				LOCOMO_DRIVER_NAME(dev))) {
-		ret = -EBUSY;
+		err = -EBUSY;
 		printk(KERN_ERR "locomokbd: Can't acquire access to io memory for keyboard\n");
-		goto free;
+		goto err_free_mem;
 	}
 
 	locomokbd->ldev = dev;
@@ -244,24 +244,28 @@
 	clear_bit(0, input_dev->keybit);
 
 	/* attempt to get the interrupt */
-	ret = request_irq(dev->irq[0], locomokbd_interrupt, 0, "locomokbd", locomokbd);
-	if (ret) {
+	err = request_irq(dev->irq[0], locomokbd_interrupt, 0, "locomokbd", locomokbd);
+	if (err) {
 		printk(KERN_ERR "locomokbd: Can't get irq for keyboard\n");
-		goto out;
+		goto err_release_region;
 	}
 
-	input_register_device(locomokbd->input);
+	err = input_register_device(locomokbd->input);
+	if (err)
+		goto err_free_irq;
 
 	return 0;
 
-out:
+ err_free_irq:
+	free_irq(dev->irq[0], locomokbd);
+ err_release_region:
 	release_mem_region((unsigned long) dev->mapbase, dev->length);
 	locomo_set_drvdata(dev, NULL);
-free:
+ err_free_mem:
 	input_free_device(input_dev);
 	kfree(locomokbd);
 
-	return ret;
+	return err;
 }
 
 static int locomokbd_remove(struct locomo_dev *dev)
diff --git a/drivers/input/keyboard/maple_keyb.c b/drivers/input/keyboard/maple_keyb.c
index cc6aaf9..99836b3 100644
--- a/drivers/input/keyboard/maple_keyb.c
+++ b/drivers/input/keyboard/maple_keyb.c
@@ -94,13 +94,13 @@
 	struct input_dev *input_dev;
 	unsigned long data = be32_to_cpu(dev->devinfo.function_data[0]);
 	int i;
+	int err;
 
 	dev->private_data = kbd = kzalloc(sizeof(struct dc_kbd), GFP_KERNEL);
 	input_dev = input_allocate_device();
 	if (!kbd || !input_dev) {
-		kfree(kbd);
-		input_free_device(input_dev);
-		return -ENOMEM;
+		err = -ENOMEM;
+		goto fail;
 	}
 
 	kbd->dev = input_dev;
@@ -113,10 +113,16 @@
 		set_bit(dc_kbd_keycode[i], input_dev->keybit);
 	clear_bit(0, input_dev->keybit);
 
-	input_register_device(kbd->dev);
+	err = input_register_device(kbd->dev);
+	if (err)
+		goto fail;
 
 	maple_getcond_callback(dev, dc_kbd_callback, 1, MAPLE_FUNC_KEYBOARD);
 	return 0;
+
+ fail:	input_free_device(input_dev);
+	kfree(kbd);
+	return err;
 }
 
 
diff --git a/drivers/input/keyboard/newtonkbd.c b/drivers/input/keyboard/newtonkbd.c
index 9282e4e..aa29b50 100644
--- a/drivers/input/keyboard/newtonkbd.c
+++ b/drivers/input/keyboard/newtonkbd.c
@@ -91,7 +91,7 @@
 	nkbd = kzalloc(sizeof(struct nkbd), GFP_KERNEL);
 	input_dev = input_allocate_device();
 	if (!nkbd || !input_dev)
-		goto fail;
+		goto fail1;
 
 	nkbd->serio = serio;
 	nkbd->dev = input_dev;
@@ -119,13 +119,17 @@
 
 	err = serio_open(serio, drv);
 	if (err)
-		goto fail;
+		goto fail2;
 
-	input_register_device(nkbd->dev);
+	err = input_register_device(nkbd->dev);
+	if (err)
+		goto fail3;
+
 	return 0;
 
- fail:	serio_set_drvdata(serio, NULL);
-	input_free_device(input_dev);
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
 	kfree(nkbd);
 	return err;
 }
@@ -165,8 +169,7 @@
 
 static int __init nkbd_init(void)
 {
-	serio_register_driver(&nkbd_drv);
-	return 0;
+	return serio_register_driver(&nkbd_drv);
 }
 
 static void __exit nkbd_exit(void)
diff --git a/drivers/input/keyboard/spitzkbd.c b/drivers/input/keyboard/spitzkbd.c
index 28b2748..8a2166c 100644
--- a/drivers/input/keyboard/spitzkbd.c
+++ b/drivers/input/keyboard/spitzkbd.c
@@ -346,17 +346,12 @@
 {
 	struct spitzkbd *spitzkbd;
 	struct input_dev *input_dev;
-	int i;
+	int i, err = -ENOMEM;
 
 	spitzkbd = kzalloc(sizeof(struct spitzkbd), GFP_KERNEL);
-	if (!spitzkbd)
-		return -ENOMEM;
-
 	input_dev = input_allocate_device();
-	if (!input_dev) {
-		kfree(spitzkbd);
-		return -ENOMEM;
-	}
+	if (!spitzkbd || !input_dev)
+		goto fail;
 
 	platform_set_drvdata(dev, spitzkbd);
 	strcpy(spitzkbd->phys, "spitzkbd/input0");
@@ -400,7 +395,9 @@
 	set_bit(SW_TABLET_MODE, input_dev->swbit);
 	set_bit(SW_HEADPHONE_INSERT, input_dev->swbit);
 
-	input_register_device(input_dev);
+	err = input_register_device(input_dev);
+	if (err)
+		goto fail;
 
 	mod_timer(&spitzkbd->htimer, jiffies + msecs_to_jiffies(HINGE_SCAN_INTERVAL));
 
@@ -434,13 +431,15 @@
 	request_irq(SPITZ_IRQ_GPIO_SWB, spitzkbd_hinge_isr,
 		    IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
 		    "Spitzkbd SWB", spitzkbd);
- 	request_irq(SPITZ_IRQ_GPIO_AK_INT, spitzkbd_hinge_isr,
+	request_irq(SPITZ_IRQ_GPIO_AK_INT, spitzkbd_hinge_isr,
 		    IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
 		    "Spitzkbd HP", spitzkbd);
 
-	printk(KERN_INFO "input: Spitz Keyboard Registered\n");
-
 	return 0;
+
+ fail:	input_free_device(input_dev);
+	kfree(spitzkbd);
+	return err;
 }
 
 static int spitzkbd_remove(struct platform_device *dev)
@@ -474,6 +473,7 @@
 	.resume		= spitzkbd_resume,
 	.driver		= {
 		.name	= "spitz-keyboard",
+		.owner	= THIS_MODULE,
 	},
 };
 
diff --git a/drivers/input/keyboard/stowaway.c b/drivers/input/keyboard/stowaway.c
index e60937d..f7b5c5b 100644
--- a/drivers/input/keyboard/stowaway.c
+++ b/drivers/input/keyboard/stowaway.c
@@ -173,8 +173,7 @@
 
 static int __init skbd_init(void)
 {
-	serio_register_driver(&skbd_drv);
-	return 0;
+	return serio_register_driver(&skbd_drv);
 }
 
 static void __exit skbd_exit(void)
diff --git a/drivers/input/keyboard/sunkbd.c b/drivers/input/keyboard/sunkbd.c
index 6cd887c..3826db9 100644
--- a/drivers/input/keyboard/sunkbd.c
+++ b/drivers/input/keyboard/sunkbd.c
@@ -243,7 +243,7 @@
 	sunkbd = kzalloc(sizeof(struct sunkbd), GFP_KERNEL);
 	input_dev = input_allocate_device();
 	if (!sunkbd || !input_dev)
-		goto fail;
+		goto fail1;
 
 	sunkbd->serio = serio;
 	sunkbd->dev = input_dev;
@@ -255,11 +255,11 @@
 
 	err = serio_open(serio, drv);
 	if (err)
-		goto fail;
+		goto fail2;
 
 	if (sunkbd_initialize(sunkbd) < 0) {
-		serio_close(serio);
-		goto fail;
+		err = -ENODEV;
+		goto fail3;
 	}
 
 	snprintf(sunkbd->name, sizeof(sunkbd->name), "Sun Type %d keyboard", sunkbd->type);
@@ -287,11 +287,17 @@
 	clear_bit(0, input_dev->keybit);
 
 	sunkbd_enable(sunkbd, 1);
-	input_register_device(sunkbd->dev);
+
+	err = input_register_device(sunkbd->dev);
+	if (err)
+		goto fail4;
+
 	return 0;
 
- fail:	serio_set_drvdata(serio, NULL);
-	input_free_device(input_dev);
+ fail4:	sunkbd_enable(sunkbd, 0);
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
 	kfree(sunkbd);
 	return err;
 }
@@ -346,8 +352,7 @@
 
 static int __init sunkbd_init(void)
 {
-	serio_register_driver(&sunkbd_drv);
-	return 0;
+	return serio_register_driver(&sunkbd_drv);
 }
 
 static void __exit sunkbd_exit(void)
diff --git a/drivers/input/keyboard/xtkbd.c b/drivers/input/keyboard/xtkbd.c
index 8c11dc9..a820934 100644
--- a/drivers/input/keyboard/xtkbd.c
+++ b/drivers/input/keyboard/xtkbd.c
@@ -95,7 +95,7 @@
 	xtkbd = kmalloc(sizeof(struct xtkbd), GFP_KERNEL);
 	input_dev = input_allocate_device();
 	if (!xtkbd || !input_dev)
-		goto fail;
+		goto fail1;
 
 	xtkbd->serio = serio;
 	xtkbd->dev = input_dev;
@@ -124,13 +124,17 @@
 
 	err = serio_open(serio, drv);
 	if (err)
-		goto fail;
+		goto fail2;
 
-	input_register_device(xtkbd->dev);
+	err = input_register_device(xtkbd->dev);
+	if (err)
+		goto fail3;
+
 	return 0;
 
- fail:	serio_set_drvdata(serio, NULL);
-	input_free_device(input_dev);
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
 	kfree(xtkbd);
 	return err;
 }
@@ -170,8 +174,7 @@
 
 static int __init xtkbd_init(void)
 {
-	serio_register_driver(&xtkbd_drv);
-	return 0;
+	return serio_register_driver(&xtkbd_drv);
 }
 
 static void __exit xtkbd_exit(void)
diff --git a/drivers/input/mouse/amimouse.c b/drivers/input/mouse/amimouse.c
index 599a7b2..239a0e1 100644
--- a/drivers/input/mouse/amimouse.c
+++ b/drivers/input/mouse/amimouse.c
@@ -95,10 +95,13 @@
 
 static int __init amimouse_init(void)
 {
+	int err;
+
 	if (!MACH_IS_AMIGA || !AMIGAHW_PRESENT(AMI_MOUSE))
 		return -ENODEV;
 
-	if (!(amimouse_dev = input_allocate_device()))
+	amimouse_dev = input_allocate_device();
+	if (!amimouse_dev)
 		return -ENOMEM;
 
 	amimouse_dev->name = "Amiga mouse";
@@ -114,7 +117,11 @@
 	amimouse_dev->open = amimouse_open;
 	amimouse_dev->close = amimouse_close;
 
-	input_register_device(amimouse_dev);
+	err = input_register_device(amimouse_dev);
+	if (err) {
+		input_free_device(amimouse_dev);
+		return err;
+	}
 
 	return 0;
 }
diff --git a/drivers/input/mouse/hil_ptr.c b/drivers/input/mouse/hil_ptr.c
index 4f2b503..bfb174f 100644
--- a/drivers/input/mouse/hil_ptr.c
+++ b/drivers/input/mouse/hil_ptr.c
@@ -417,8 +417,7 @@
 
 static int __init hil_ptr_init(void)
 {
-	serio_register_driver(&hil_ptr_serio_driver);
-        return 0;
+	return serio_register_driver(&hil_ptr_serio_driver);
 }
                 
 static void __exit hil_ptr_exit(void)
diff --git a/drivers/input/mouse/inport.c b/drivers/input/mouse/inport.c
index e1252fa..13dd967 100644
--- a/drivers/input/mouse/inport.c
+++ b/drivers/input/mouse/inport.c
@@ -135,6 +135,7 @@
 static int __init inport_init(void)
 {
 	unsigned char a, b, c;
+	int err;
 
 	if (!request_region(INPORT_BASE, INPORT_EXTENT, "inport")) {
 		printk(KERN_ERR "inport.c: Can't allocate ports at %#x\n", INPORT_BASE);
@@ -145,15 +146,16 @@
 	b = inb(INPORT_SIGNATURE_PORT);
 	c = inb(INPORT_SIGNATURE_PORT);
 	if (a == b || a != c) {
-		release_region(INPORT_BASE, INPORT_EXTENT);
 		printk(KERN_ERR "inport.c: Didn't find InPort mouse at %#x\n", INPORT_BASE);
-		return -ENODEV;
+		err = -ENODEV;
+		goto err_release_region;
 	}
 
-	if (!(inport_dev = input_allocate_device())) {
+	inport_dev = input_allocate_device();
+	if (!inport_dev) {
 		printk(KERN_ERR "inport.c: Not enough memory for input device\n");
-		release_region(INPORT_BASE, INPORT_EXTENT);
-		return -ENOMEM;
+		err = -ENOMEM;
+		goto err_release_region;
 	}
 
 	inport_dev->name = INPORT_NAME;
@@ -174,9 +176,18 @@
 	outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
 	outb(INPORT_MODE_BASE, INPORT_DATA_PORT);
 
-	input_register_device(inport_dev);
+	err = input_register_device(inport_dev);
+	if (err)
+		goto err_free_dev;
 
 	return 0;
+
+ err_free_dev:
+	input_free_device(inport_dev);
+ err_release_region:
+	release_region(INPORT_BASE, INPORT_EXTENT);
+
+	return err;
 }
 
 static void __exit inport_exit(void)
diff --git a/drivers/input/mouse/lifebook.c b/drivers/input/mouse/lifebook.c
index c57e885..29542f0 100644
--- a/drivers/input/mouse/lifebook.c
+++ b/drivers/input/mouse/lifebook.c
@@ -21,47 +21,51 @@
 #include "lifebook.h"
 
 static struct dmi_system_id lifebook_dmi_table[] = {
-       {
-               .ident = "LifeBook B",
-               .matches = {
-                       DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B Series"),
-               },
-       },
-       {
-               .ident = "Lifebook B",
-               .matches = {
-                       DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK B Series"),
-               },
-       },
-       {
-               .ident = "Lifebook B213x/B2150",
-               .matches = {
-                       DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B2131/B2133/B2150"),
-               },
-       },
-       {
-               .ident = "Zephyr",
-               .matches = {
-                       DMI_MATCH(DMI_PRODUCT_NAME, "ZEPHYR"),
-               },
-       },
-       {
-               .ident = "CF-18",
-               .matches = {
-                       DMI_MATCH(DMI_PRODUCT_NAME, "CF-18"),
-               },
-       },
-       {
-               .ident = "Lifebook B142",
-               .matches = {
-                       DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B142"),
-               },
-
-       },
-       { }
+	{
+		.ident = "FLORA-ie 55mi",
+		.matches = {
+			DMI_MATCH(DMI_PRODUCT_NAME, "FLORA-ie 55mi"),
+		},
+	},
+	{
+		.ident = "LifeBook B",
+		.matches = {
+			DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B Series"),
+		},
+	},
+	{
+		.ident = "Lifebook B",
+		.matches = {
+			DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK B Series"),
+		},
+	},
+	{
+		.ident = "Lifebook B213x/B2150",
+		.matches = {
+			DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B2131/B2133/B2150"),
+		},
+	},
+	{
+		.ident = "Zephyr",
+		.matches = {
+			DMI_MATCH(DMI_PRODUCT_NAME, "ZEPHYR"),
+		},
+	},
+	{
+		.ident = "CF-18",
+		.matches = {
+			DMI_MATCH(DMI_PRODUCT_NAME, "CF-18"),
+		},
+	},
+	{
+		.ident = "Lifebook B142",
+		.matches = {
+			DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B142"),
+		},
+	},
+	{ }
 };
 
-
 static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse)
 {
 	unsigned char *packet = psmouse->packet;
diff --git a/drivers/input/mouse/logibm.c b/drivers/input/mouse/logibm.c
index 8e9c2f3..db20599 100644
--- a/drivers/input/mouse/logibm.c
+++ b/drivers/input/mouse/logibm.c
@@ -124,6 +124,8 @@
 
 static int __init logibm_init(void)
 {
+	int err;
+
 	if (!request_region(LOGIBM_BASE, LOGIBM_EXTENT, "logibm")) {
 		printk(KERN_ERR "logibm.c: Can't allocate ports at %#x\n", LOGIBM_BASE);
 		return -EBUSY;
@@ -134,18 +136,19 @@
 	udelay(100);
 
 	if (inb(LOGIBM_SIGNATURE_PORT) != LOGIBM_SIGNATURE_BYTE) {
-		release_region(LOGIBM_BASE, LOGIBM_EXTENT);
 		printk(KERN_ERR "logibm.c: Didn't find Logitech busmouse at %#x\n", LOGIBM_BASE);
-		return -ENODEV;
+		err = -ENODEV;
+		goto err_release_region;
 	}
 
 	outb(LOGIBM_DEFAULT_MODE, LOGIBM_CONFIG_PORT);
 	outb(LOGIBM_DISABLE_IRQ, LOGIBM_CONTROL_PORT);
 
-	if (!(logibm_dev = input_allocate_device())) {
+	logibm_dev = input_allocate_device();
+	if (!logibm_dev) {
 		printk(KERN_ERR "logibm.c: Not enough memory for input device\n");
-		release_region(LOGIBM_BASE, LOGIBM_EXTENT);
-		return -ENOMEM;
+		err = -ENOMEM;
+		goto err_release_region;
 	}
 
 	logibm_dev->name = "Logitech bus mouse";
@@ -162,9 +165,18 @@
 	logibm_dev->open  = logibm_open;
 	logibm_dev->close = logibm_close;
 
-	input_register_device(logibm_dev);
+	err = input_register_device(logibm_dev);
+	if (err)
+		goto err_free_dev;
 
 	return 0;
+
+ err_free_dev:
+	input_free_device(logibm_dev);
+ err_release_region:
+	release_region(LOGIBM_BASE, LOGIBM_EXTENT);
+
+	return err;
 }
 
 static void __exit logibm_exit(void)
diff --git a/drivers/input/mouse/logips2pp.c b/drivers/input/mouse/logips2pp.c
index 8a4f862..d3ddea2 100644
--- a/drivers/input/mouse/logips2pp.c
+++ b/drivers/input/mouse/logips2pp.c
@@ -328,6 +328,7 @@
 	unsigned char model, buttons;
 	const struct ps2pp_info *model_info;
 	int use_ps2pp = 0;
+	int error;
 
 	param[0] = 0;
 	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
@@ -393,8 +394,14 @@
 				psmouse->set_resolution = ps2pp_set_resolution;
 				psmouse->disconnect = ps2pp_disconnect;
 
-				device_create_file(&psmouse->ps2dev.serio->dev,
-						   &psmouse_attr_smartscroll.dattr);
+				error = device_create_file(&psmouse->ps2dev.serio->dev,
+							   &psmouse_attr_smartscroll.dattr);
+				if (error) {
+					printk(KERN_ERR
+						"logips2pp.c: failed to create smartscroll "
+						"sysfs attribute, error: %d\n", error);
+					return -1;
+				}
 			}
 		}
 
diff --git a/drivers/input/mouse/pc110pad.c b/drivers/input/mouse/pc110pad.c
index 8c075aa..f155c1f 100644
--- a/drivers/input/mouse/pc110pad.c
+++ b/drivers/input/mouse/pc110pad.c
@@ -108,6 +108,7 @@
 static int __init pc110pad_init(void)
 {
 	struct pci_dev *dev;
+	int err;
 
 	dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
 	if (dev) {
@@ -124,16 +125,16 @@
 	outb(PC110PAD_OFF, pc110pad_io + 2);
 
 	if (request_irq(pc110pad_irq, pc110pad_interrupt, 0, "pc110pad", NULL)) {
-		release_region(pc110pad_io, 4);
 		printk(KERN_ERR "pc110pad: Unable to get irq %d.\n", pc110pad_irq);
-		return -EBUSY;
+		err = -EBUSY;
+		goto err_release_region;
 	}
 
-	if (!(pc110pad_dev = input_allocate_device())) {
-		free_irq(pc110pad_irq, NULL);
-		release_region(pc110pad_io, 4);
+	pc110pad_dev = input_allocate_device();
+	if (!pc110pad_dev) {
 		printk(KERN_ERR "pc110pad: Not enough memory.\n");
-		return -ENOMEM;
+		err = -ENOMEM;
+		goto err_free_irq;
 	}
 
 	pc110pad_dev->name = "IBM PC110 TouchPad";
@@ -153,9 +154,20 @@
 	pc110pad_dev->open = pc110pad_open;
 	pc110pad_dev->close = pc110pad_close;
 
-	input_register_device(pc110pad_dev);
+	err = input_register_device(pc110pad_dev);
+	if (err)
+		goto err_free_dev;
 
 	return 0;
+
+ err_free_dev:
+	input_free_device(pc110pad_dev);
+ err_free_irq:
+	free_irq(pc110pad_irq, NULL);
+ err_release_region:
+	release_region(pc110pad_io, 4);
+
+	return err;
 }
 
 static void __exit pc110pad_exit(void)
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index 52bb222..a0e4a03 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -1103,7 +1103,7 @@
 {
 	struct psmouse *psmouse, *parent = NULL;
 	struct input_dev *input_dev;
-	int retval = -ENOMEM;
+	int retval = 0, error = -ENOMEM;
 
 	mutex_lock(&psmouse_mutex);
 
@@ -1119,7 +1119,7 @@
 	psmouse = kzalloc(sizeof(struct psmouse), GFP_KERNEL);
 	input_dev = input_allocate_device();
 	if (!psmouse || !input_dev)
-		goto out;
+		goto err_free;
 
 	ps2_init(&psmouse->ps2dev, serio);
 	INIT_WORK(&psmouse->resync_work, psmouse_resync);
@@ -1130,14 +1130,13 @@
 
 	serio_set_drvdata(serio, psmouse);
 
-	retval = serio_open(serio, drv);
-	if (retval)
-		goto out;
+	error = serio_open(serio, drv);
+	if (error)
+		goto err_clear_drvdata;
 
 	if (psmouse_probe(psmouse) < 0) {
-		serio_close(serio);
-		retval = -ENODEV;
-		goto out;
+		error = -ENODEV;
+		goto err_close_serio;
 	}
 
 	psmouse->rate = psmouse_rate;
@@ -1151,30 +1150,44 @@
 	psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
 	psmouse_initialize(psmouse);
 
-	input_register_device(psmouse->dev);
+	error = input_register_device(psmouse->dev);
+	if (error)
+		goto err_protocol_disconnect;
 
 	if (parent && parent->pt_activate)
 		parent->pt_activate(parent);
 
-	sysfs_create_group(&serio->dev.kobj, &psmouse_attribute_group);
+	error = sysfs_create_group(&serio->dev.kobj, &psmouse_attribute_group);
+	if (error)
+		goto err_pt_deactivate;
 
 	psmouse_activate(psmouse);
 
-	retval = 0;
-
-out:
-	if (retval) {
-		serio_set_drvdata(serio, NULL);
-		input_free_device(input_dev);
-		kfree(psmouse);
-	}
-
+ out:
 	/* If this is a pass-through port the parent needs to be re-activated */
 	if (parent)
 		psmouse_activate(parent);
 
 	mutex_unlock(&psmouse_mutex);
 	return retval;
+
+ err_pt_deactivate:
+	if (parent && parent->pt_deactivate)
+		parent->pt_deactivate(parent);
+ err_protocol_disconnect:
+	if (psmouse->disconnect)
+		psmouse->disconnect(psmouse);
+	psmouse_set_state(psmouse, PSMOUSE_IGNORE);
+ err_close_serio:
+	serio_close(serio);
+ err_clear_drvdata:
+	serio_set_drvdata(serio, NULL);
+ err_free:
+	input_free_device(input_dev);
+	kfree(psmouse);
+
+	retval = error;
+	goto out;
 }
 
 
@@ -1337,14 +1350,14 @@
 
 static ssize_t psmouse_show_int_attr(struct psmouse *psmouse, void *offset, char *buf)
 {
-	unsigned long *field = (unsigned long *)((char *)psmouse + (size_t)offset);
+	unsigned int *field = (unsigned int *)((char *)psmouse + (size_t)offset);
 
-	return sprintf(buf, "%lu\n", *field);
+	return sprintf(buf, "%u\n", *field);
 }
 
 static ssize_t psmouse_set_int_attr(struct psmouse *psmouse, void *offset, const char *buf, size_t count)
 {
-	unsigned long *field = (unsigned long *)((char *)psmouse + (size_t)offset);
+	unsigned int *field = (unsigned int *)((char *)psmouse + (size_t)offset);
 	unsigned long value;
 	char *rest;
 
@@ -1352,6 +1365,9 @@
 	if (*rest)
 		return -EINVAL;
 
+	if ((unsigned int)value != value)
+		return -EINVAL;
+
 	*field = value;
 
 	return count;
@@ -1366,17 +1382,20 @@
 {
 	struct serio *serio = psmouse->ps2dev.serio;
 	struct psmouse *parent = NULL;
-	struct input_dev *new_dev;
-	const struct psmouse_protocol *proto;
+	struct input_dev *old_dev, *new_dev;
+	const struct psmouse_protocol *proto, *old_proto;
+	int error;
 	int retry = 0;
 
-	if (!(proto = psmouse_protocol_by_name(buf, count)))
+	proto = psmouse_protocol_by_name(buf, count);
+	if (!proto)
 		return -EINVAL;
 
 	if (psmouse->type == proto->type)
 		return count;
 
-	if (!(new_dev = input_allocate_device()))
+	new_dev = input_allocate_device();
+	if (!new_dev)
 		return -ENOMEM;
 
 	while (serio->child) {
@@ -1409,11 +1428,13 @@
 			parent->pt_deactivate(parent);
 	}
 
+	old_dev = psmouse->dev;
+	old_proto = psmouse_protocol_by_type(psmouse->type);
+
 	if (psmouse->disconnect)
 		psmouse->disconnect(psmouse);
 
 	psmouse_set_state(psmouse, PSMOUSE_IGNORE);
-	input_unregister_device(psmouse->dev);
 
 	psmouse->dev = new_dev;
 	psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
@@ -1427,7 +1448,23 @@
 	psmouse_initialize(psmouse);
 	psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
 
-	input_register_device(psmouse->dev);
+	error = input_register_device(psmouse->dev);
+	if (error) {
+		if (psmouse->disconnect)
+			psmouse->disconnect(psmouse);
+
+		psmouse_set_state(psmouse, PSMOUSE_IGNORE);
+		input_free_device(new_dev);
+		psmouse->dev = old_dev;
+		psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
+		psmouse_switch_protocol(psmouse, old_proto);
+		psmouse_initialize(psmouse);
+		psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+
+		return error;
+	}
+
+	input_unregister_device(old_dev);
 
 	if (parent && parent->pt_activate)
 		parent->pt_activate(parent);
@@ -1488,15 +1525,19 @@
 
 static int __init psmouse_init(void)
 {
+	int err;
+
 	kpsmoused_wq = create_singlethread_workqueue("kpsmoused");
 	if (!kpsmoused_wq) {
 		printk(KERN_ERR "psmouse: failed to create kpsmoused workqueue\n");
 		return -ENOMEM;
 	}
 
-	serio_register_driver(&psmouse_drv);
+	err = serio_register_driver(&psmouse_drv);
+	if (err)
+		destroy_workqueue(kpsmoused_wq);
 
-	return 0;
+	return err;
 }
 
 static void __exit psmouse_exit(void)
diff --git a/drivers/input/mouse/rpcmouse.c b/drivers/input/mouse/rpcmouse.c
index ea04685..fbdcfd8 100644
--- a/drivers/input/mouse/rpcmouse.c
+++ b/drivers/input/mouse/rpcmouse.c
@@ -66,7 +66,10 @@
 
 static int __init rpcmouse_init(void)
 {
-	if (!(rpcmouse_dev = input_allocate_device()))
+	int err;
+
+	rpcmouse_dev = input_allocate_device();
+	if (!rpcmouse_dev)
 		return -ENOMEM;
 
 	rpcmouse_dev->name = "Acorn RiscPC Mouse";
@@ -85,13 +88,22 @@
 
 	if (request_irq(IRQ_VSYNCPULSE, rpcmouse_irq, IRQF_SHARED, "rpcmouse", rpcmouse_dev)) {
 		printk(KERN_ERR "rpcmouse: unable to allocate VSYNC interrupt\n");
-		input_free_device(rpcmouse_dev);
-		return -EBUSY;
+		err = -EBUSY;
+		goto err_free_dev;
 	}
 
-	input_register_device(rpcmouse_dev);
+	err = input_register_device(rpcmouse_dev);
+	if (err)
+		goto err_free_irq;
 
 	return 0;
+
+ err_free_irq:
+	free_irq(IRQ_VSYNCPULSE, rpcmouse_dev);
+ err_free_dev:
+	input_free_device(rpcmouse_dev);
+
+	return err;
 }
 
 static void __exit rpcmouse_exit(void)
diff --git a/drivers/input/mouse/sermouse.c b/drivers/input/mouse/sermouse.c
index 2a272c5..a85d747 100644
--- a/drivers/input/mouse/sermouse.c
+++ b/drivers/input/mouse/sermouse.c
@@ -246,7 +246,7 @@
 	sermouse = kzalloc(sizeof(struct sermouse), GFP_KERNEL);
 	input_dev = input_allocate_device();
 	if (!sermouse || !input_dev)
-		goto fail;
+		goto fail1;
 
 	sermouse->dev = input_dev;
 	snprintf(sermouse->phys, sizeof(sermouse->phys), "%s/input0", serio->phys);
@@ -275,14 +275,17 @@
 
 	err = serio_open(serio, drv);
 	if (err)
-		goto fail;
+		goto fail2;
 
-	input_register_device(sermouse->dev);
+	err = input_register_device(sermouse->dev);
+	if (err)
+		goto fail3;
 
 	return 0;
 
- fail:	serio_set_drvdata(serio, NULL);
-	input_free_device(input_dev);
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
 	kfree(sermouse);
 	return err;
 }
@@ -348,8 +351,7 @@
 
 static int __init sermouse_init(void)
 {
-	serio_register_driver(&sermouse_drv);
-	return 0;
+	return serio_register_driver(&sermouse_drv);
 }
 
 static void __exit sermouse_exit(void)
diff --git a/drivers/input/mouse/trackpoint.c b/drivers/input/mouse/trackpoint.c
index ae5871a..9ab5b5e 100644
--- a/drivers/input/mouse/trackpoint.c
+++ b/drivers/input/mouse/trackpoint.c
@@ -293,6 +293,7 @@
 	struct ps2dev *ps2dev = &psmouse->ps2dev;
 	unsigned char firmware_id;
 	unsigned char button_info;
+	int error;
 
 	if (trackpoint_start_protocol(psmouse, &firmware_id))
 		return -1;
@@ -305,7 +306,7 @@
 		button_info = 0;
 	}
 
-	psmouse->private = priv = kcalloc(1, sizeof(struct trackpoint_data), GFP_KERNEL);
+	psmouse->private = priv = kzalloc(sizeof(struct trackpoint_data), GFP_KERNEL);
 	if (!priv)
 		return -1;
 
@@ -318,7 +319,14 @@
 	trackpoint_defaults(priv);
 	trackpoint_sync(psmouse);
 
-	sysfs_create_group(&ps2dev->serio->dev.kobj, &trackpoint_attr_group);
+	error = sysfs_create_group(&ps2dev->serio->dev.kobj, &trackpoint_attr_group);
+	if (error) {
+		printk(KERN_ERR
+			"trackpoint.c: failed to create sysfs attributes, error: %d\n",
+			error);
+		kfree(priv);
+		return -1;
+	}
 
 	printk(KERN_INFO "IBM TrackPoint firmware: 0x%02x, buttons: %d/%d\n",
 		firmware_id, (button_info & 0xf0) >> 4, button_info & 0x0f);
diff --git a/drivers/input/mouse/vsxxxaa.c b/drivers/input/mouse/vsxxxaa.c
index ffdb50e..c3d64fc 100644
--- a/drivers/input/mouse/vsxxxaa.c
+++ b/drivers/input/mouse/vsxxxaa.c
@@ -497,7 +497,7 @@
 	mouse = kzalloc (sizeof (struct vsxxxaa), GFP_KERNEL);
 	input_dev = input_allocate_device ();
 	if (!mouse || !input_dev)
-		goto fail;
+		goto fail1;
 
 	mouse->dev = input_dev;
 	mouse->serio = serio;
@@ -527,7 +527,7 @@
 
 	err = serio_open (serio, drv);
 	if (err)
-		goto fail;
+		goto fail2;
 
 	/*
 	 * Request selftest. Standard packet format and differential
@@ -535,12 +535,15 @@
 	 */
 	serio->write (serio, 'T'); /* Test */
 
-	input_register_device (input_dev);
+	err = input_register_device (input_dev);
+	if (err)
+		goto fail3;
 
 	return 0;
 
- fail:	serio_set_drvdata (serio, NULL);
-	input_free_device (input_dev);
+ fail3:	serio_close (serio);
+ fail2:	serio_set_drvdata (serio, NULL);
+ fail1:	input_free_device (input_dev);
 	kfree (mouse);
 	return err;
 }
@@ -571,8 +574,7 @@
 static int __init
 vsxxxaa_init (void)
 {
-	serio_register_driver(&vsxxxaa_drv);
-	return 0;
+	return serio_register_driver(&vsxxxaa_drv);
 }
 
 static void __exit
diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c
index a22a74a..664bcc8 100644
--- a/drivers/input/mousedev.c
+++ b/drivers/input/mousedev.c
@@ -196,12 +196,12 @@
 	switch (code) {
 		case BTN_TOUCH:
 		case BTN_0:
-		case BTN_FORWARD:
 		case BTN_LEFT:		index = 0; break;
 		case BTN_STYLUS:
 		case BTN_1:
 		case BTN_RIGHT:		index = 1; break;
 		case BTN_2:
+		case BTN_FORWARD:
 		case BTN_STYLUS2:
 		case BTN_MIDDLE:	index = 2; break;
 		case BTN_3:
diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
index 7e3141f..debe944 100644
--- a/drivers/input/serio/i8042.c
+++ b/drivers/input/serio/i8042.c
@@ -255,25 +255,10 @@
 static int i8042_aux_write(struct serio *serio, unsigned char c)
 {
 	struct i8042_port *port = serio->port_data;
-	int retval;
 
-/*
- * Send the byte out.
- */
-
-	if (port->mux == -1)
-		retval = i8042_command(&c, I8042_CMD_AUX_SEND);
-	else
-		retval = i8042_command(&c, I8042_CMD_MUX_SEND + port->mux);
-
-/*
- * Make sure the interrupt happens and the character is received even
- * in the case the IRQ isn't wired, so that we can receive further
- * characters later.
- */
-
-	i8042_interrupt(0, NULL);
-	return retval;
+	return i8042_command(&c, port->mux == -1 ?
+					I8042_CMD_AUX_SEND :
+					I8042_CMD_MUX_SEND + port->mux);
 }
 
 /*
@@ -337,23 +322,27 @@
 		dfl = 0;
 		if (str & I8042_STR_MUXERR) {
 			dbg("MUX error, status is %02x, data is %02x", str, data);
-			switch (data) {
-				default:
 /*
  * When MUXERR condition is signalled the data register can only contain
  * 0xfd, 0xfe or 0xff if implementation follows the spec. Unfortunately
- * it is not always the case. Some KBC just get confused which port the
- * data came from and signal error leaving the data intact. They _do not_
- * revert to legacy mode (actually I've never seen KBC reverting to legacy
- * mode yet, when we see one we'll add proper handling).
- * Anyway, we will assume that the data came from the same serio last byte
+ * it is not always the case. Some KBCs also report 0xfc when there is
+ * nothing connected to the port while others sometimes get confused which
+ * port the data came from and signal error leaving the data intact. They
+ * _do not_ revert to legacy mode (actually I've never seen KBC reverting
+ * to legacy mode yet, when we see one we'll add proper handling).
+ * Anyway, we process 0xfc, 0xfd, 0xfe and 0xff as timeouts, and for the
+ * rest assume that the data came from the same serio last byte
  * was transmitted (if transmission happened not too long ago).
  */
+
+			switch (data) {
+				default:
 					if (time_before(jiffies, last_transmit + HZ/10)) {
 						str = last_str;
 						break;
 					}
 					/* fall through - report timeout */
+				case 0xfc:
 				case 0xfd:
 				case 0xfe: dfl = SERIO_TIMEOUT; data = 0xfe; break;
 				case 0xff: dfl = SERIO_PARITY;  data = 0xfe; break;
diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c
index 5f1d403..f0ce822 100644
--- a/drivers/input/serio/serio.c
+++ b/drivers/input/serio/serio.c
@@ -45,8 +45,7 @@
 EXPORT_SYMBOL(__serio_register_port);
 EXPORT_SYMBOL(serio_unregister_port);
 EXPORT_SYMBOL(serio_unregister_child_port);
-EXPORT_SYMBOL(__serio_unregister_port_delayed);
-EXPORT_SYMBOL(__serio_register_driver);
+EXPORT_SYMBOL(serio_register_driver);
 EXPORT_SYMBOL(serio_unregister_driver);
 EXPORT_SYMBOL(serio_open);
 EXPORT_SYMBOL(serio_close);
@@ -63,11 +62,10 @@
 
 static struct bus_type serio_bus;
 
-static void serio_add_driver(struct serio_driver *drv);
 static void serio_add_port(struct serio *serio);
-static void serio_destroy_port(struct serio *serio);
 static void serio_reconnect_port(struct serio *serio);
 static void serio_disconnect_port(struct serio *serio);
+static void serio_attach_driver(struct serio_driver *drv);
 
 static int serio_connect_driver(struct serio *serio, struct serio_driver *drv)
 {
@@ -171,11 +169,10 @@
  */
 
 enum serio_event_type {
-	SERIO_RESCAN,
-	SERIO_RECONNECT,
+	SERIO_RESCAN_PORT,
+	SERIO_RECONNECT_PORT,
 	SERIO_REGISTER_PORT,
-	SERIO_UNREGISTER_PORT,
-	SERIO_REGISTER_DRIVER,
+	SERIO_ATTACH_DRIVER,
 };
 
 struct serio_event {
@@ -190,11 +187,12 @@
 static DECLARE_WAIT_QUEUE_HEAD(serio_wait);
 static struct task_struct *serio_task;
 
-static void serio_queue_event(void *object, struct module *owner,
-			      enum serio_event_type event_type)
+static int serio_queue_event(void *object, struct module *owner,
+			     enum serio_event_type event_type)
 {
 	unsigned long flags;
 	struct serio_event *event;
+	int retval = 0;
 
 	spin_lock_irqsave(&serio_event_lock, flags);
 
@@ -213,24 +211,34 @@
 		}
 	}
 
-	if ((event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC))) {
-		if (!try_module_get(owner)) {
-			printk(KERN_WARNING "serio: Can't get module reference, dropping event %d\n", event_type);
-			kfree(event);
-			goto out;
-		}
-
-		event->type = event_type;
-		event->object = object;
-		event->owner = owner;
-
-		list_add_tail(&event->node, &serio_event_list);
-		wake_up(&serio_wait);
-	} else {
-		printk(KERN_ERR "serio: Not enough memory to queue event %d\n", event_type);
+	event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC);
+	if (!event) {
+		printk(KERN_ERR
+			"serio: Not enough memory to queue event %d\n",
+			event_type);
+		retval = -ENOMEM;
+		goto out;
 	}
+
+	if (!try_module_get(owner)) {
+		printk(KERN_WARNING
+			"serio: Can't get module reference, dropping event %d\n",
+			event_type);
+		kfree(event);
+		retval = -EINVAL;
+		goto out;
+	}
+
+	event->type = event_type;
+	event->object = object;
+	event->owner = owner;
+
+	list_add_tail(&event->node, &serio_event_list);
+	wake_up(&serio_wait);
+
 out:
 	spin_unlock_irqrestore(&serio_event_lock, flags);
+	return retval;
 }
 
 static void serio_free_event(struct serio_event *event)
@@ -308,22 +316,17 @@
 				serio_add_port(event->object);
 				break;
 
-			case SERIO_UNREGISTER_PORT:
-				serio_disconnect_port(event->object);
-				serio_destroy_port(event->object);
-				break;
-
-			case SERIO_RECONNECT:
+			case SERIO_RECONNECT_PORT:
 				serio_reconnect_port(event->object);
 				break;
 
-			case SERIO_RESCAN:
+			case SERIO_RESCAN_PORT:
 				serio_disconnect_port(event->object);
 				serio_find_driver(event->object);
 				break;
 
-			case SERIO_REGISTER_DRIVER:
-				serio_add_driver(event->object);
+			case SERIO_ATTACH_DRIVER:
+				serio_attach_driver(event->object);
 				break;
 
 			default:
@@ -675,12 +678,12 @@
 
 void serio_rescan(struct serio *serio)
 {
-	serio_queue_event(serio, NULL, SERIO_RESCAN);
+	serio_queue_event(serio, NULL, SERIO_RESCAN_PORT);
 }
 
 void serio_reconnect(struct serio *serio)
 {
-	serio_queue_event(serio, NULL, SERIO_RECONNECT);
+	serio_queue_event(serio, NULL, SERIO_RECONNECT_PORT);
 }
 
 /*
@@ -717,16 +720,6 @@
 	mutex_unlock(&serio_mutex);
 }
 
-/*
- * Submits register request to kseriod for subsequent execution.
- * Can be used when it is not obvious whether the serio_mutex is
- * taken or not and when delayed execution is feasible.
- */
-void __serio_unregister_port_delayed(struct serio *serio, struct module *owner)
-{
-	serio_queue_event(serio, owner, SERIO_UNREGISTER_PORT);
-}
-
 
 /*
  * Serio driver operations
@@ -785,28 +778,52 @@
 	return 0;
 }
 
-static struct bus_type serio_bus = {
-	.name =	"serio",
-	.probe = serio_driver_probe,
-	.remove = serio_driver_remove,
-};
-
-static void serio_add_driver(struct serio_driver *drv)
+static void serio_attach_driver(struct serio_driver *drv)
 {
 	int error;
 
-	error = driver_register(&drv->driver);
+	error = driver_attach(&drv->driver);
 	if (error)
-		printk(KERN_ERR
-			"serio: driver_register() failed for %s, error: %d\n",
+		printk(KERN_WARNING
+			"serio: driver_attach() failed for %s with error %d\n",
 			drv->driver.name, error);
 }
 
-void __serio_register_driver(struct serio_driver *drv, struct module *owner)
+int serio_register_driver(struct serio_driver *drv)
 {
+	int manual_bind = drv->manual_bind;
+	int error;
+
 	drv->driver.bus = &serio_bus;
 
-	serio_queue_event(drv, owner, SERIO_REGISTER_DRIVER);
+	/*
+	 * Temporarily disable automatic binding because probing
+	 * takes long time and we are better off doing it in kseriod
+	 */
+	drv->manual_bind = 1;
+
+	error = driver_register(&drv->driver);
+	if (error) {
+		printk(KERN_ERR
+			"serio: driver_register() failed for %s, error: %d\n",
+			drv->driver.name, error);
+		return error;
+	}
+
+	/*
+	 * Restore original bind mode and let kseriod bind the
+	 * driver to free ports
+	 */
+	if (!manual_bind) {
+		drv->manual_bind = 0;
+		error = serio_queue_event(drv, NULL, SERIO_ATTACH_DRIVER);
+		if (error) {
+			driver_unregister(&drv->driver);
+			return error;
+		}
+	}
+
+	return 0;
 }
 
 void serio_unregister_driver(struct serio_driver *drv)
@@ -947,15 +964,21 @@
 	return ret;
 }
 
+static struct bus_type serio_bus = {
+	.name		= "serio",
+	.dev_attrs	= serio_device_attrs,
+	.drv_attrs	= serio_driver_attrs,
+	.match		= serio_bus_match,
+	.uevent		= serio_uevent,
+	.probe		= serio_driver_probe,
+	.remove		= serio_driver_remove,
+	.resume		= serio_resume,
+};
+
 static int __init serio_init(void)
 {
 	int error;
 
-	serio_bus.dev_attrs = serio_device_attrs;
-	serio_bus.drv_attrs = serio_driver_attrs;
-	serio_bus.match = serio_bus_match;
-	serio_bus.uevent = serio_uevent;
-	serio_bus.resume = serio_resume;
 	error = bus_register(&serio_bus);
 	if (error) {
 		printk(KERN_ERR "serio: failed to register serio bus, error: %d\n", error);
diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c
index 7c8d039..088ebc3 100644
--- a/drivers/input/serio/serio_raw.c
+++ b/drivers/input/serio/serio_raw.c
@@ -389,8 +389,7 @@
 
 static int __init serio_raw_init(void)
 {
-	serio_register_driver(&serio_raw_drv);
-	return 0;
+	return serio_register_driver(&serio_raw_drv);
 }
 
 static void __exit serio_raw_exit(void)
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 9418bbe..29ca0ab 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -144,4 +144,19 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called touchwin.
 
+config TOUCHSCREEN_UCB1400
+	tristate "Philips UCB1400 touchscreen"
+	select SND_AC97_BUS
+	help
+	  This enables support for the Philips UCB1400 touchscreen interface.
+	  The UCB1400 is an AC97 audio codec.  The touchscreen interface
+	  will be initialized only after the ALSA subsystem has been
+	  brought up and the UCB1400 detected.  You therefore have to
+	  configure ALSA support as well (either built-in or modular,
+	  independently of whether this driver is itself built-in or
+	  modular) for this driver to work.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ucb1400_ts.
+
 endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 1abb8f1..30e6e22 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -15,3 +15,4 @@
 obj-$(CONFIG_TOUCHSCREEN_PENMOUNT)	+= penmount.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT)	+= touchright.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN)	+= touchwin.o
+obj-$(CONFIG_TOUCHSCREEN_UCB1400)	+= ucb1400_ts.o
diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c
index 0517c73..c6164b6 100644
--- a/drivers/input/touchscreen/ads7846.c
+++ b/drivers/input/touchscreen/ads7846.c
@@ -76,6 +76,7 @@
 	char			phys[32];
 
 	struct spi_device	*spi;
+	struct attribute_group	*attr_group;
 	u16			model;
 	u16			vref_delay_usecs;
 	u16			x_plate_ohms;
@@ -317,6 +318,48 @@
 
 static DEVICE_ATTR(disable, 0664, ads7846_disable_show, ads7846_disable_store);
 
+static struct attribute *ads7846_attributes[] = {
+	&dev_attr_temp0.attr,
+	&dev_attr_temp1.attr,
+	&dev_attr_vbatt.attr,
+	&dev_attr_vaux.attr,
+	&dev_attr_pen_down.attr,
+	&dev_attr_disable.attr,
+	NULL,
+};
+
+static struct attribute_group ads7846_attr_group = {
+	.attrs = ads7846_attributes,
+};
+
+/*
+ * ads7843/7845 don't have temperature sensors, and
+ * use the other sensors a bit differently too
+ */
+
+static struct attribute *ads7843_attributes[] = {
+	&dev_attr_vbatt.attr,
+	&dev_attr_vaux.attr,
+	&dev_attr_pen_down.attr,
+	&dev_attr_disable.attr,
+	NULL,
+};
+
+static struct attribute_group ads7843_attr_group = {
+	.attrs = ads7843_attributes,
+};
+
+static struct attribute *ads7845_attributes[] = {
+	&dev_attr_vaux.attr,
+	&dev_attr_pen_down.attr,
+	&dev_attr_disable.attr,
+	NULL,
+};
+
+static struct attribute_group ads7845_attr_group = {
+	.attrs = ads7845_attributes,
+};
+
 /*--------------------------------------------------------------------------*/
 
 /*
@@ -788,38 +831,30 @@
 	(void) ads7846_read12_ser(&spi->dev,
 			  READ_12BIT_SER(vaux) | ADS_PD10_ALL_ON);
 
-	/* ads7843/7845 don't have temperature sensors, and
-	 * use the other sensors a bit differently too
-	 */
-	if (ts->model == 7846) {
-		device_create_file(&spi->dev, &dev_attr_temp0);
-		device_create_file(&spi->dev, &dev_attr_temp1);
+	switch (ts->model) {
+	case 7846:
+		ts->attr_group = &ads7846_attr_group;
+		break;
+	case 7845:
+		ts->attr_group = &ads7845_attr_group;
+		break;
+	default:
+		ts->attr_group = &ads7843_attr_group;
+		break;
 	}
-	if (ts->model != 7845)
-		device_create_file(&spi->dev, &dev_attr_vbatt);
-	device_create_file(&spi->dev, &dev_attr_vaux);
-
-	device_create_file(&spi->dev, &dev_attr_pen_down);
-
-	device_create_file(&spi->dev, &dev_attr_disable);
+	err = sysfs_create_group(&spi->dev.kobj, ts->attr_group);
+	if (err)
+		goto err_free_irq;
 
 	err = input_register_device(input_dev);
 	if (err)
-		goto err_remove_attr;
+		goto err_remove_attr_group;
 
 	return 0;
 
- err_remove_attr:
-	device_remove_file(&spi->dev, &dev_attr_disable);
-	device_remove_file(&spi->dev, &dev_attr_pen_down);
-	if (ts->model == 7846) {
-		device_remove_file(&spi->dev, &dev_attr_temp1);
-		device_remove_file(&spi->dev, &dev_attr_temp0);
-	}
-	if (ts->model != 7845)
-		device_remove_file(&spi->dev, &dev_attr_vbatt);
-	device_remove_file(&spi->dev, &dev_attr_vaux);
-
+ err_remove_attr_group:
+	sysfs_remove_group(&spi->dev.kobj, ts->attr_group);
+ err_free_irq:
 	free_irq(spi->irq, ts);
  err_free_mem:
 	input_free_device(input_dev);
@@ -835,15 +870,7 @@
 
 	ads7846_suspend(spi, PMSG_SUSPEND);
 
-	device_remove_file(&spi->dev, &dev_attr_disable);
-	device_remove_file(&spi->dev, &dev_attr_pen_down);
-	if (ts->model == 7846) {
-		device_remove_file(&spi->dev, &dev_attr_temp1);
-		device_remove_file(&spi->dev, &dev_attr_temp0);
-	}
-	if (ts->model != 7845)
-		device_remove_file(&spi->dev, &dev_attr_vbatt);
-	device_remove_file(&spi->dev, &dev_attr_vaux);
+	sysfs_remove_group(&spi->dev.kobj, ts->attr_group);
 
 	free_irq(ts->spi->irq, ts);
 	/* suspend left the IRQ disabled */
diff --git a/drivers/input/touchscreen/corgi_ts.c b/drivers/input/touchscreen/corgi_ts.c
index 66121f6..e294558 100644
--- a/drivers/input/touchscreen/corgi_ts.c
+++ b/drivers/input/touchscreen/corgi_ts.c
@@ -175,17 +175,19 @@
 
 static void new_data(struct corgi_ts *corgi_ts)
 {
+	struct input_dev *dev = corgi_ts->input;
+
 	if (corgi_ts->power_mode != PWR_MODE_ACTIVE)
 		return;
 
 	if (!corgi_ts->tc.pressure && corgi_ts->pendown == 0)
 		return;
 
-	input_report_abs(corgi_ts->input, ABS_X, corgi_ts->tc.x);
-	input_report_abs(corgi_ts->input, ABS_Y, corgi_ts->tc.y);
-	input_report_abs(corgi_ts->input, ABS_PRESSURE, corgi_ts->tc.pressure);
-	input_report_key(corgi_ts->input, BTN_TOUCH, (corgi_ts->pendown != 0));
-	input_sync(corgi_ts->input);
+	input_report_abs(dev, ABS_X, corgi_ts->tc.x);
+	input_report_abs(dev, ABS_Y, corgi_ts->tc.y);
+	input_report_abs(dev, ABS_PRESSURE, corgi_ts->tc.pressure);
+	input_report_key(dev, BTN_TOUCH, corgi_ts->pendown);
+	input_sync(dev);
 }
 
 static void ts_interrupt_main(struct corgi_ts *corgi_ts, int isTimer)
@@ -219,12 +221,14 @@
 static void corgi_ts_timer(unsigned long data)
 {
 	struct corgi_ts *corgits_data = (struct corgi_ts *) data;
+
 	ts_interrupt_main(corgits_data, 1);
 }
 
 static irqreturn_t ts_interrupt(int irq, void *dev_id)
 {
 	struct corgi_ts *corgits_data = dev_id;
+
 	ts_interrupt_main(corgits_data, 0);
 	return IRQ_HANDLED;
 }
@@ -272,7 +276,7 @@
 	corgi_ts = kzalloc(sizeof(struct corgi_ts), GFP_KERNEL);
 	input_dev = input_allocate_device();
 	if (!corgi_ts || !input_dev)
-		goto fail;
+		goto fail1;
 
 	platform_set_drvdata(pdev, corgi_ts);
 
@@ -281,7 +285,7 @@
 
 	if (corgi_ts->irq_gpio < 0) {
 		err = -ENODEV;
-		goto fail;
+		goto fail1;
 	}
 
 	corgi_ts->input = input_dev;
@@ -319,10 +323,12 @@
 
 	if (request_irq(corgi_ts->irq_gpio, ts_interrupt, IRQF_DISABLED, "ts", corgi_ts)) {
 		err = -EBUSY;
-		goto fail;
+		goto fail1;
 	}
 
-	input_register_device(corgi_ts->input);
+	err = input_register_device(corgi_ts->input);
+	if (err)
+		goto fail2;
 
 	corgi_ts->power_mode = PWR_MODE_ACTIVE;
 
@@ -331,17 +337,17 @@
 
 	return 0;
 
- fail:	input_free_device(input_dev);
+ fail2:	free_irq(corgi_ts->irq_gpio, corgi_ts);
+ fail1:	input_free_device(input_dev);
 	kfree(corgi_ts);
 	return err;
-
 }
 
 static int corgits_remove(struct platform_device *pdev)
 {
 	struct corgi_ts *corgi_ts = platform_get_drvdata(pdev);
 
-	free_irq(corgi_ts->irq_gpio, NULL);
+	free_irq(corgi_ts->irq_gpio, corgi_ts);
 	del_timer_sync(&corgi_ts->timer);
 	corgi_ts->machinfo->put_hsync();
 	input_unregister_device(corgi_ts->input);
diff --git a/drivers/input/touchscreen/elo.c b/drivers/input/touchscreen/elo.c
index 913e1b7..9d61cd1 100644
--- a/drivers/input/touchscreen/elo.c
+++ b/drivers/input/touchscreen/elo.c
@@ -397,8 +397,7 @@
 
 static int __init elo_init(void)
 {
-	serio_register_driver(&elo_drv);
-	return 0;
+	return serio_register_driver(&elo_drv);
 }
 
 static void __exit elo_exit(void)
diff --git a/drivers/input/touchscreen/gunze.c b/drivers/input/touchscreen/gunze.c
index 817c219..9157eb1 100644
--- a/drivers/input/touchscreen/gunze.c
+++ b/drivers/input/touchscreen/gunze.c
@@ -123,7 +123,7 @@
 	input_dev = input_allocate_device();
 	if (!gunze || !input_dev) {
 		err = -ENOMEM;
-		goto fail;
+		goto fail1;
 	}
 
 	gunze->serio = serio;
@@ -146,13 +146,17 @@
 
 	err = serio_open(serio, drv);
 	if (err)
-		goto fail;
+		goto fail2;
 
-	input_register_device(gunze->dev);
+	err = input_register_device(gunze->dev);
+	if (err)
+		goto fail3;
+
 	return 0;
 
- fail:	serio_set_drvdata(serio, NULL);
-	input_free_device(input_dev);
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
 	kfree(gunze);
 	return err;
 }
@@ -190,8 +194,7 @@
 
 static int __init gunze_init(void)
 {
-	serio_register_driver(&gunze_drv);
-	return 0;
+	return serio_register_driver(&gunze_drv);
 }
 
 static void __exit gunze_exit(void)
diff --git a/drivers/input/touchscreen/h3600_ts_input.c b/drivers/input/touchscreen/h3600_ts_input.c
index d9e61ee..c4116d4 100644
--- a/drivers/input/touchscreen/h3600_ts_input.c
+++ b/drivers/input/touchscreen/h3600_ts_input.c
@@ -478,8 +478,7 @@
 
 static int __init h3600ts_init(void)
 {
-	serio_register_driver(&h3600ts_drv);
-	return 0;
+	return serio_register_driver(&h3600ts_drv);
 }
 
 static void __exit h3600ts_exit(void)
diff --git a/drivers/input/touchscreen/hp680_ts_input.c b/drivers/input/touchscreen/hp680_ts_input.c
index 58fca31..2490874 100644
--- a/drivers/input/touchscreen/hp680_ts_input.c
+++ b/drivers/input/touchscreen/hp680_ts_input.c
@@ -76,38 +76,47 @@
 
 static int __init hp680_ts_init(void)
 {
+	int err;
+
 	hp680_ts_dev = input_allocate_device();
 	if (!hp680_ts_dev)
 		return -ENOMEM;
 
 	hp680_ts_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
-	hp680_ts_dev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
 	hp680_ts_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
 
-	hp680_ts_dev->absmin[ABS_X] = HP680_TS_ABS_X_MIN;
-	hp680_ts_dev->absmin[ABS_Y] = HP680_TS_ABS_Y_MIN;
-	hp680_ts_dev->absmax[ABS_X] = HP680_TS_ABS_X_MAX;
-	hp680_ts_dev->absmax[ABS_Y] = HP680_TS_ABS_Y_MAX;
+	input_set_abs_params(hp680_ts_dev, ABS_X,
+		HP680_TS_ABS_X_MIN, HP680_TS_ABS_X_MAX, 0, 0);
+	input_set_abs_params(hp680_ts_dev, ABS_Y,
+		HP680_TS_ABS_Y_MIN, HP680_TS_ABS_Y_MAX, 0, 0);
 
 	hp680_ts_dev->name = "HP Jornada touchscreen";
 	hp680_ts_dev->phys = "hp680_ts/input0";
 
-	input_register_device(hp680_ts_dev);
-
 	if (request_irq(HP680_TS_IRQ, hp680_ts_interrupt,
 			IRQF_DISABLED, MODNAME, 0) < 0) {
 		printk(KERN_ERR "hp680_touchscreen.c: Can't allocate irq %d\n",
 		       HP680_TS_IRQ);
-		input_unregister_device(hp680_ts_dev);
-		return -EBUSY;
+		err = -EBUSY;
+		goto fail1;
 	}
 
+	err = input_register_device(hp680_ts_dev);
+	if (err)
+		goto fail2;
+
 	return 0;
+
+ fail2:	free_irq(HP680_TS_IRQ, NULL);
+	cancel_delayed_work(&work);
+	flush_scheduled_work();
+ fail1:	input_free_device(hp680_ts_dev);
+	return err;
 }
 
 static void __exit hp680_ts_exit(void)
 {
-	free_irq(HP680_TS_IRQ, 0);
+	free_irq(HP680_TS_IRQ, NULL);
 	cancel_delayed_work(&work);
 	flush_scheduled_work();
 	input_unregister_device(hp680_ts_dev);
diff --git a/drivers/input/touchscreen/mk712.c b/drivers/input/touchscreen/mk712.c
index 4cbcaa6..44140fe 100644
--- a/drivers/input/touchscreen/mk712.c
+++ b/drivers/input/touchscreen/mk712.c
@@ -96,15 +96,13 @@
 		goto end;
 	}
 
-	if (~status & MK712_STATUS_TOUCH)
-	{
+	if (~status & MK712_STATUS_TOUCH) {
 		debounce = 1;
 		input_report_key(mk712_dev, BTN_TOUCH, 0);
 		goto end;
 	}
 
-	if (debounce)
-	{
+	if (debounce) {
 		debounce = 0;
 		goto end;
 	}
@@ -113,8 +111,7 @@
 	input_report_abs(mk712_dev, ABS_X, last_x);
 	input_report_abs(mk712_dev, ABS_Y, last_y);
 
-end:
-
+ end:
 	last_x = inw(mk712_io + MK712_X) & 0x0fff;
 	last_y = inw(mk712_io + MK712_Y) & 0x0fff;
 	input_sync(mk712_dev);
@@ -169,13 +166,14 @@
 	    (inw(mk712_io + MK712_STATUS) & 0xf333)) {
 		printk(KERN_WARNING "mk712: device not present\n");
 		err = -ENODEV;
-		goto fail;
+		goto fail1;
 	}
 
-	if (!(mk712_dev = input_allocate_device())) {
+	mk712_dev = input_allocate_device();
+	if (!mk712_dev) {
 		printk(KERN_ERR "mk712: not enough memory\n");
 		err = -ENOMEM;
-		goto fail;
+		goto fail1;
 	}
 
 	mk712_dev->name = "ICS MicroClock MK712 TouchScreen";
@@ -196,13 +194,17 @@
 	if (request_irq(mk712_irq, mk712_interrupt, 0, "mk712", mk712_dev)) {
 		printk(KERN_WARNING "mk712: unable to get IRQ\n");
 		err = -EBUSY;
-		goto fail;
+		goto fail1;
 	}
 
-	input_register_device(mk712_dev);
+	err = input_register_device(mk712_dev);
+	if (err)
+		goto fail2;
+
 	return 0;
 
- fail:	input_free_device(mk712_dev);
+ fail2:	free_irq(mk712_irq, mk712_dev);
+ fail1:	input_free_device(mk712_dev);
 	release_region(mk712_io, 8);
 	return err;
 }
diff --git a/drivers/input/touchscreen/mtouch.c b/drivers/input/touchscreen/mtouch.c
index 3b4c616..c3c2d73 100644
--- a/drivers/input/touchscreen/mtouch.c
+++ b/drivers/input/touchscreen/mtouch.c
@@ -137,7 +137,7 @@
 	input_dev = input_allocate_device();
 	if (!mtouch || !input_dev) {
 		err = -ENOMEM;
-		goto fail;
+		goto fail1;
 	}
 
 	mtouch->serio = serio;
@@ -160,14 +160,17 @@
 
 	err = serio_open(serio, drv);
 	if (err)
-		goto fail;
+		goto fail2;
 
-	input_register_device(mtouch->dev);
+	err = input_register_device(mtouch->dev);
+	if (err)
+		goto fail3;
 
 	return 0;
 
- fail:	serio_set_drvdata(serio, NULL);
-	input_free_device(input_dev);
+ fail3:	serio_close(serio);
+ fail2:	serio_set_drvdata(serio, NULL);
+ fail1:	input_free_device(input_dev);
 	kfree(mtouch);
 	return err;
 }
@@ -205,8 +208,7 @@
 
 static int __init mtouch_init(void)
 {
-	serio_register_driver(&mtouch_drv);
-	return 0;
+	return serio_register_driver(&mtouch_drv);
 }
 
 static void __exit mtouch_exit(void)
diff --git a/drivers/input/touchscreen/penmount.c b/drivers/input/touchscreen/penmount.c
index 6c7d0c2..bd27679 100644
--- a/drivers/input/touchscreen/penmount.c
+++ b/drivers/input/touchscreen/penmount.c
@@ -171,8 +171,7 @@
 
 static int __init pm_init(void)
 {
-	serio_register_driver(&pm_drv);
-	return 0;
+	return serio_register_driver(&pm_drv);
 }
 
 static void __exit pm_exit(void)
diff --git a/drivers/input/touchscreen/touchright.c b/drivers/input/touchscreen/touchright.c
index c74f74e..35ba46c6 100644
--- a/drivers/input/touchscreen/touchright.c
+++ b/drivers/input/touchscreen/touchright.c
@@ -182,8 +182,7 @@
 
 static int __init tr_init(void)
 {
-	serio_register_driver(&tr_drv);
-	return 0;
+	return serio_register_driver(&tr_drv);
 }
 
 static void __exit tr_exit(void)
diff --git a/drivers/input/touchscreen/touchwin.c b/drivers/input/touchscreen/touchwin.c
index 9911820..4dc073d 100644
--- a/drivers/input/touchscreen/touchwin.c
+++ b/drivers/input/touchscreen/touchwin.c
@@ -189,8 +189,7 @@
 
 static int __init tw_init(void)
 {
-	serio_register_driver(&tw_drv);
-	return 0;
+	return serio_register_driver(&tw_drv);
 }
 
 static void __exit tw_exit(void)
diff --git a/drivers/input/touchscreen/ucb1400_ts.c b/drivers/input/touchscreen/ucb1400_ts.c
new file mode 100644
index 0000000..6ef0836
--- /dev/null
+++ b/drivers/input/touchscreen/ucb1400_ts.c
@@ -0,0 +1,579 @@
+/*
+ *  Philips UCB1400 touchscreen driver
+ *
+ *  Author:	Nicolas Pitre
+ *  Created:	September 25, 2006
+ *  Copyright:	MontaVista Software, Inc.
+ *
+ * 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.
+ *
+ * This code is heavily based on ucb1x00-*.c copyrighted by Russell King
+ * covering the UCB1100, UCB1200 and UCB1300..  Support for the UCB1400 has
+ * been made separate from ucb1x00-core/ucb1x00-ts on Russell's request.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/suspend.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/ac97_codec.h>
+
+
+/*
+ * Interesting UCB1400 AC-link registers
+ */
+
+#define UCB_IE_RIS		0x5e
+#define UCB_IE_FAL		0x60
+#define UCB_IE_STATUS		0x62
+#define UCB_IE_CLEAR		0x62
+#define UCB_IE_ADC		(1 << 11)
+#define UCB_IE_TSPX		(1 << 12)
+
+#define UCB_TS_CR		0x64
+#define UCB_TS_CR_TSMX_POW	(1 << 0)
+#define UCB_TS_CR_TSPX_POW	(1 << 1)
+#define UCB_TS_CR_TSMY_POW	(1 << 2)
+#define UCB_TS_CR_TSPY_POW	(1 << 3)
+#define UCB_TS_CR_TSMX_GND	(1 << 4)
+#define UCB_TS_CR_TSPX_GND	(1 << 5)
+#define UCB_TS_CR_TSMY_GND	(1 << 6)
+#define UCB_TS_CR_TSPY_GND	(1 << 7)
+#define UCB_TS_CR_MODE_INT	(0 << 8)
+#define UCB_TS_CR_MODE_PRES	(1 << 8)
+#define UCB_TS_CR_MODE_POS	(2 << 8)
+#define UCB_TS_CR_BIAS_ENA	(1 << 11)
+#define UCB_TS_CR_TSPX_LOW	(1 << 12)
+#define UCB_TS_CR_TSMX_LOW	(1 << 13)
+
+#define UCB_ADC_CR		0x66
+#define UCB_ADC_SYNC_ENA	(1 << 0)
+#define UCB_ADC_VREFBYP_CON	(1 << 1)
+#define UCB_ADC_INP_TSPX	(0 << 2)
+#define UCB_ADC_INP_TSMX	(1 << 2)
+#define UCB_ADC_INP_TSPY	(2 << 2)
+#define UCB_ADC_INP_TSMY	(3 << 2)
+#define UCB_ADC_INP_AD0		(4 << 2)
+#define UCB_ADC_INP_AD1		(5 << 2)
+#define UCB_ADC_INP_AD2		(6 << 2)
+#define UCB_ADC_INP_AD3		(7 << 2)
+#define UCB_ADC_EXT_REF		(1 << 5)
+#define UCB_ADC_START		(1 << 7)
+#define UCB_ADC_ENA		(1 << 15)
+
+#define UCB_ADC_DATA		0x68
+#define UCB_ADC_DAT_VALID	(1 << 15)
+#define UCB_ADC_DAT_VALUE(x)	((x) & 0x3ff)
+
+#define UCB_ID			0x7e
+#define UCB_ID_1400             0x4304
+
+
+struct ucb1400 {
+	ac97_t			*ac97;
+	struct input_dev	*ts_idev;
+
+	int			irq;
+
+	wait_queue_head_t	ts_wait;
+	struct task_struct	*ts_task;
+
+	unsigned int		irq_pending;	/* not bit field shared */
+	unsigned int		ts_restart:1;
+	unsigned int		adcsync:1;
+};
+
+static int adcsync;
+
+static inline u16 ucb1400_reg_read(struct ucb1400 *ucb, u16 reg)
+{
+	return ucb->ac97->bus->ops->read(ucb->ac97, reg);
+}
+
+static inline void ucb1400_reg_write(struct ucb1400 *ucb, u16 reg, u16 val)
+{
+	ucb->ac97->bus->ops->write(ucb->ac97, reg, val);
+}
+
+static inline void ucb1400_adc_enable(struct ucb1400 *ucb)
+{
+	ucb1400_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA);
+}
+
+static unsigned int ucb1400_adc_read(struct ucb1400 *ucb, u16 adc_channel)
+{
+	unsigned int val;
+
+	if (ucb->adcsync)
+		adc_channel |= UCB_ADC_SYNC_ENA;
+
+	ucb1400_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA | adc_channel);
+	ucb1400_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA | adc_channel | UCB_ADC_START);
+
+	for (;;) {
+		val = ucb1400_reg_read(ucb, UCB_ADC_DATA);
+		if (val & UCB_ADC_DAT_VALID)
+			break;
+		/* yield to other processes */
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(1);
+	}
+
+	return UCB_ADC_DAT_VALUE(val);
+}
+
+static inline void ucb1400_adc_disable(struct ucb1400 *ucb)
+{
+	ucb1400_reg_write(ucb, UCB_ADC_CR, 0);
+}
+
+/* Switch to interrupt mode. */
+static inline void ucb1400_ts_mode_int(struct ucb1400 *ucb)
+{
+	ucb1400_reg_write(ucb, UCB_TS_CR,
+			UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |
+			UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
+			UCB_TS_CR_MODE_INT);
+}
+
+/*
+ * Switch to pressure mode, and read pressure.  We don't need to wait
+ * here, since both plates are being driven.
+ */
+static inline unsigned int ucb1400_ts_read_pressure(struct ucb1400 *ucb)
+{
+	ucb1400_reg_write(ucb, UCB_TS_CR,
+			UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |
+			UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
+			UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+	return ucb1400_adc_read(ucb, UCB_ADC_INP_TSPY);
+}
+
+/*
+ * Switch to X position mode and measure Y plate.  We switch the plate
+ * configuration in pressure mode, then switch to position mode.  This
+ * gives a faster response time.  Even so, we need to wait about 55us
+ * for things to stabilise.
+ */
+static inline unsigned int ucb1400_ts_read_xpos(struct ucb1400 *ucb)
+{
+	ucb1400_reg_write(ucb, UCB_TS_CR,
+			UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
+			UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+	ucb1400_reg_write(ucb, UCB_TS_CR,
+			UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
+			UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+	ucb1400_reg_write(ucb, UCB_TS_CR,
+			UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
+			UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
+
+	udelay(55);
+
+	return ucb1400_adc_read(ucb, UCB_ADC_INP_TSPY);
+}
+
+/*
+ * Switch to Y position mode and measure X plate.  We switch the plate
+ * configuration in pressure mode, then switch to position mode.  This
+ * gives a faster response time.  Even so, we need to wait about 55us
+ * for things to stabilise.
+ */
+static inline unsigned int ucb1400_ts_read_ypos(struct ucb1400 *ucb)
+{
+	ucb1400_reg_write(ucb, UCB_TS_CR,
+			UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
+			UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+	ucb1400_reg_write(ucb, UCB_TS_CR,
+			UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
+			UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+	ucb1400_reg_write(ucb, UCB_TS_CR,
+			UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
+			UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
+
+	udelay(55);
+
+	return ucb1400_adc_read(ucb, UCB_ADC_INP_TSPX);
+}
+
+/*
+ * Switch to X plate resistance mode.  Set MX to ground, PX to
+ * supply.  Measure current.
+ */
+static inline unsigned int ucb1400_ts_read_xres(struct ucb1400 *ucb)
+{
+	ucb1400_reg_write(ucb, UCB_TS_CR,
+			UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
+			UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+	return ucb1400_adc_read(ucb, 0);
+}
+
+/*
+ * Switch to Y plate resistance mode.  Set MY to ground, PY to
+ * supply.  Measure current.
+ */
+static inline unsigned int ucb1400_ts_read_yres(struct ucb1400 *ucb)
+{
+	ucb1400_reg_write(ucb, UCB_TS_CR,
+			UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
+			UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+	return ucb1400_adc_read(ucb, 0);
+}
+
+static inline int ucb1400_ts_pen_down(struct ucb1400 *ucb)
+{
+	unsigned short val = ucb1400_reg_read(ucb, UCB_TS_CR);
+	return (val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW));
+}
+
+static inline void ucb1400_ts_irq_enable(struct ucb1400 *ucb)
+{
+	ucb1400_reg_write(ucb, UCB_IE_CLEAR, UCB_IE_TSPX);
+	ucb1400_reg_write(ucb, UCB_IE_CLEAR, 0);
+	ucb1400_reg_write(ucb, UCB_IE_FAL, UCB_IE_TSPX);
+}
+
+static inline void ucb1400_ts_irq_disable(struct ucb1400 *ucb)
+{
+	ucb1400_reg_write(ucb, UCB_IE_FAL, 0);
+}
+
+static void ucb1400_ts_evt_add(struct input_dev *idev, u16 pressure, u16 x, u16 y)
+{
+	input_report_abs(idev, ABS_X, x);
+	input_report_abs(idev, ABS_Y, y);
+	input_report_abs(idev, ABS_PRESSURE, pressure);
+	input_sync(idev);
+}
+
+static void ucb1400_ts_event_release(struct input_dev *idev)
+{
+	input_report_abs(idev, ABS_PRESSURE, 0);
+	input_sync(idev);
+}
+
+static void ucb1400_handle_pending_irq(struct ucb1400 *ucb)
+{
+	unsigned int isr;
+
+	isr = ucb1400_reg_read(ucb, UCB_IE_STATUS);
+	ucb1400_reg_write(ucb, UCB_IE_CLEAR, isr);
+	ucb1400_reg_write(ucb, UCB_IE_CLEAR, 0);
+
+	if (isr & UCB_IE_TSPX)
+		ucb1400_ts_irq_disable(ucb);
+	else
+		printk(KERN_ERR "ucb1400: unexpected IE_STATUS = %#x\n", isr);
+
+	enable_irq(ucb->irq);
+}
+
+static int ucb1400_ts_thread(void *_ucb)
+{
+	struct ucb1400 *ucb = _ucb;
+	struct task_struct *tsk = current;
+	int valid = 0;
+
+	tsk->policy = SCHED_FIFO;
+	tsk->rt_priority = 1;
+
+	while (!kthread_should_stop()) {
+		unsigned int x, y, p;
+		long timeout;
+
+		ucb->ts_restart = 0;
+
+		if (ucb->irq_pending) {
+			ucb->irq_pending = 0;
+			ucb1400_handle_pending_irq(ucb);
+		}
+
+		ucb1400_adc_enable(ucb);
+		x = ucb1400_ts_read_xpos(ucb);
+		y = ucb1400_ts_read_ypos(ucb);
+		p = ucb1400_ts_read_pressure(ucb);
+		ucb1400_adc_disable(ucb);
+
+		/* Switch back to interrupt mode. */
+		ucb1400_ts_mode_int(ucb);
+
+		msleep(10);
+
+		if (ucb1400_ts_pen_down(ucb)) {
+			ucb1400_ts_irq_enable(ucb);
+
+			/*
+			 * If we spat out a valid sample set last time,
+			 * spit out a "pen off" sample here.
+			 */
+			if (valid) {
+				ucb1400_ts_event_release(ucb->ts_idev);
+				valid = 0;
+			}
+
+			timeout = MAX_SCHEDULE_TIMEOUT;
+		} else {
+			valid = 1;
+			ucb1400_ts_evt_add(ucb->ts_idev, p, x, y);
+			timeout = msecs_to_jiffies(10);
+		}
+
+		wait_event_interruptible_timeout(ucb->ts_wait,
+			ucb->irq_pending || ucb->ts_restart || kthread_should_stop(),
+			timeout);
+		try_to_freeze();
+	}
+
+	/* Send the "pen off" if we are stopping with the pen still active */
+	if (valid)
+		ucb1400_ts_event_release(ucb->ts_idev);
+
+	ucb->ts_task = NULL;
+	return 0;
+}
+
+/*
+ * A restriction with interrupts exists when using the ucb1400, as
+ * the codec read/write routines may sleep while waiting for codec
+ * access completion and uses semaphores for access control to the
+ * AC97 bus.  A complete codec read cycle could take  anywhere from
+ * 60 to 100uSec so we *definitely* don't want to spin inside the
+ * interrupt handler waiting for codec access.  So, we handle the
+ * interrupt by scheduling a RT kernel thread to run in process
+ * context instead of interrupt context.
+ */
+static irqreturn_t ucb1400_hard_irq(int irqnr, void *devid)
+{
+	struct ucb1400 *ucb = devid;
+
+	if (irqnr == ucb->irq) {
+		disable_irq(ucb->irq);
+		ucb->irq_pending = 1;
+		wake_up(&ucb->ts_wait);
+		return IRQ_HANDLED;
+	}
+	return IRQ_NONE;
+}
+
+static int ucb1400_ts_open(struct input_dev *idev)
+{
+	struct ucb1400 *ucb = idev->private;
+	int ret = 0;
+
+	BUG_ON(ucb->ts_task);
+
+	ucb->ts_task = kthread_run(ucb1400_ts_thread, ucb, "UCB1400_ts");
+	if (IS_ERR(ucb->ts_task)) {
+		ret = PTR_ERR(ucb->ts_task);
+		ucb->ts_task = NULL;
+	}
+
+	return ret;
+}
+
+static void ucb1400_ts_close(struct input_dev *idev)
+{
+	struct ucb1400 *ucb = idev->private;
+
+	if (ucb->ts_task)
+		kthread_stop(ucb->ts_task);
+
+	ucb1400_ts_irq_disable(ucb);
+	ucb1400_reg_write(ucb, UCB_TS_CR, 0);
+}
+
+#ifdef CONFIG_PM
+static int ucb1400_ts_resume(struct device *dev)
+{
+	struct ucb1400 *ucb = dev_get_drvdata(dev);
+
+	if (ucb->ts_task) {
+		/*
+		 * Restart the TS thread to ensure the
+		 * TS interrupt mode is set up again
+		 * after sleep.
+		 */
+		ucb->ts_restart = 1;
+		wake_up(&ucb->ts_wait);
+	}
+	return 0;
+}
+#else
+#define ucb1400_ts_resume NULL
+#endif
+
+#ifndef NO_IRQ
+#define NO_IRQ	0
+#endif
+
+/*
+ * Try to probe our interrupt, rather than relying on lots of
+ * hard-coded machine dependencies.
+ */
+static int ucb1400_detect_irq(struct ucb1400 *ucb)
+{
+	unsigned long mask, timeout;
+
+	mask = probe_irq_on();
+	if (!mask) {
+		probe_irq_off(mask);
+		return -EBUSY;
+	}
+
+	/* Enable the ADC interrupt. */
+	ucb1400_reg_write(ucb, UCB_IE_RIS, UCB_IE_ADC);
+	ucb1400_reg_write(ucb, UCB_IE_FAL, UCB_IE_ADC);
+	ucb1400_reg_write(ucb, UCB_IE_CLEAR, 0xffff);
+	ucb1400_reg_write(ucb, UCB_IE_CLEAR, 0);
+
+	/* Cause an ADC interrupt. */
+	ucb1400_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA);
+	ucb1400_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA | UCB_ADC_START);
+
+	/* Wait for the conversion to complete. */
+	timeout = jiffies + HZ/2;
+	while (!(ucb1400_reg_read(ucb, UCB_ADC_DATA) & UCB_ADC_DAT_VALID)) {
+		cpu_relax();
+		if (time_after(jiffies, timeout)) {
+			printk(KERN_ERR "ucb1400: timed out in IRQ probe\n");
+			probe_irq_off(mask);
+			return -ENODEV;
+		}
+	}
+	ucb1400_reg_write(ucb, UCB_ADC_CR, 0);
+
+	/* Disable and clear interrupt. */
+	ucb1400_reg_write(ucb, UCB_IE_RIS, 0);
+	ucb1400_reg_write(ucb, UCB_IE_FAL, 0);
+	ucb1400_reg_write(ucb, UCB_IE_CLEAR, 0xffff);
+	ucb1400_reg_write(ucb, UCB_IE_CLEAR, 0);
+
+	/* Read triggered interrupt. */
+	ucb->irq = probe_irq_off(mask);
+	if (ucb->irq < 0 || ucb->irq == NO_IRQ)
+		return -ENODEV;
+
+	return 0;
+}
+
+static int ucb1400_ts_probe(struct device *dev)
+{
+	struct ucb1400 *ucb;
+	struct input_dev *idev;
+	int error, id, x_res, y_res;
+
+	ucb = kzalloc(sizeof(struct ucb1400), GFP_KERNEL);
+	idev = input_allocate_device();
+	if (!ucb || !idev) {
+		error = -ENOMEM;
+		goto err_free_devs;
+	}
+
+	ucb->ts_idev = idev;
+	ucb->adcsync = adcsync;
+	ucb->ac97 = to_ac97_t(dev);
+	init_waitqueue_head(&ucb->ts_wait);
+
+	id = ucb1400_reg_read(ucb, UCB_ID);
+	if (id != UCB_ID_1400) {
+		error = -ENODEV;
+		goto err_free_devs;
+	}
+
+	error = ucb1400_detect_irq(ucb);
+	if (error) {
+		printk(KERN_ERR "UCB1400: IRQ probe failed\n");
+		goto err_free_devs;
+	}
+
+	error = request_irq(ucb->irq, ucb1400_hard_irq, IRQF_TRIGGER_RISING,
+				"UCB1400", ucb);
+	if (error) {
+		printk(KERN_ERR "ucb1400: unable to grab irq%d: %d\n",
+				ucb->irq, error);
+		goto err_free_devs;
+	}
+	printk(KERN_DEBUG "UCB1400: found IRQ %d\n", ucb->irq);
+
+	idev->private		= ucb;
+	idev->cdev.dev		= dev;
+	idev->name		= "UCB1400 touchscreen interface";
+	idev->id.vendor		= ucb1400_reg_read(ucb, AC97_VENDOR_ID1);
+	idev->id.product	= id;
+	idev->open		= ucb1400_ts_open;
+	idev->close		= ucb1400_ts_close;
+	idev->evbit[0]		= BIT(EV_ABS);
+
+	ucb1400_adc_enable(ucb);
+	x_res = ucb1400_ts_read_xres(ucb);
+	y_res = ucb1400_ts_read_yres(ucb);
+	ucb1400_adc_disable(ucb);
+	printk(KERN_DEBUG "UCB1400: x/y = %d/%d\n", x_res, y_res);
+
+	input_set_abs_params(idev, ABS_X, 0, x_res, 0, 0);
+	input_set_abs_params(idev, ABS_Y, 0, y_res, 0, 0);
+	input_set_abs_params(idev, ABS_PRESSURE, 0, 0, 0, 0);
+
+	error = input_register_device(idev);
+	if (error)
+		goto err_free_irq;
+
+	dev_set_drvdata(dev, ucb);
+	return 0;
+
+ err_free_irq:
+	free_irq(ucb->irq, ucb);
+ err_free_devs:
+	input_free_device(idev);
+	kfree(ucb);
+	return error;
+}
+
+static int ucb1400_ts_remove(struct device *dev)
+{
+	struct ucb1400 *ucb = dev_get_drvdata(dev);
+
+	free_irq(ucb->irq, ucb);
+	input_unregister_device(ucb->ts_idev);
+	dev_set_drvdata(dev, NULL);
+	kfree(ucb);
+	return 0;
+}
+
+static struct device_driver ucb1400_ts_driver = {
+	.owner		= THIS_MODULE,
+	.bus		= &ac97_bus_type,
+	.probe		= ucb1400_ts_probe,
+	.remove		= ucb1400_ts_remove,
+	.resume		= ucb1400_ts_resume,
+};
+
+static int __init ucb1400_ts_init(void)
+{
+	return driver_register(&ucb1400_ts_driver);
+}
+
+static void __exit ucb1400_ts_exit(void)
+{
+	driver_unregister(&ucb1400_ts_driver);
+}
+
+module_param(adcsync, int, 0444);
+
+module_init(ucb1400_ts_init);
+module_exit(ucb1400_ts_exit);
+
+MODULE_DESCRIPTION("Philips UCB1400 touchscreen driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/macintosh/Kconfig b/drivers/macintosh/Kconfig
index 92ccee8..a9e747c 100644
--- a/drivers/macintosh/Kconfig
+++ b/drivers/macintosh/Kconfig
@@ -162,7 +162,6 @@
 
 config MAC_EMUMOUSEBTN
 	bool "Support for mouse button 2+3 emulation"
-	depends on INPUT_ADBHID
 	help
 	  This provides generic support for emulating the 2nd and 3rd mouse
 	  button with keypresses.  If you say Y here, the emulation is still
diff --git a/drivers/macintosh/adbhid.c b/drivers/macintosh/adbhid.c
index 5066e7a..1c7d6f2 100644
--- a/drivers/macintosh/adbhid.c
+++ b/drivers/macintosh/adbhid.c
@@ -689,7 +689,6 @@
 	if (!hid || !input_dev) {
 		err = -ENOMEM;
 		goto fail;
-
 	}
 
 	sprintf(hid->phys, "adb%d:%d.%02x/input", id, default_id, original_handler_id);
@@ -807,7 +806,9 @@
 
 	input_dev->keycode = hid->keycode;
 
-	input_register_device(input_dev);
+	err = input_register_device(input_dev);
+	if (err)
+		goto fail;
 
 	if (default_id == ADB_KEYBOARD) {
 		/* HACK WARNING!! This should go away as soon there is an utility
@@ -820,7 +821,10 @@
 	return 0;
 
  fail:	input_free_device(input_dev);
-	kfree(hid);
+	if (hid) {
+		kfree(hid->keycode);
+		kfree(hid);
+	}
 	adbhid[id] = NULL;
 	return err;
 }
diff --git a/drivers/macintosh/mac_hid.c b/drivers/macintosh/mac_hid.c
index 6b129ee..ee6b4ca 100644
--- a/drivers/macintosh/mac_hid.c
+++ b/drivers/macintosh/mac_hid.c
@@ -106,6 +106,8 @@
 
 static int emumousebtn_input_register(void)
 {
+	int ret;
+
 	emumousebtn = input_allocate_device();
 	if (!emumousebtn)
 		return -ENOMEM;
@@ -120,9 +122,11 @@
 	emumousebtn->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
 	emumousebtn->relbit[0] = BIT(REL_X) | BIT(REL_Y);
 
-	input_register_device(emumousebtn);
+	ret = input_register_device(emumousebtn);
+	if (ret)
+		input_free_device(emumousebtn);
 
-	return 0;
+	return ret;
 }
 
 int __init mac_hid_init(void)
diff --git a/drivers/usb/input/appletouch.c b/drivers/usb/input/appletouch.c
index 4c21351..c77291d 100644
--- a/drivers/usb/input/appletouch.c
+++ b/drivers/usb/input/appletouch.c
@@ -38,14 +38,29 @@
 #define APPLE_VENDOR_ID		0x05AC
 
 /* These names come from Info.plist in AppleUSBTrackpad.kext */
-#define GEYSER_ANSI_PRODUCT_ID	0x0214
-#define GEYSER_ISO_PRODUCT_ID	0x0215
-#define GEYSER_JIS_PRODUCT_ID	0x0216
+#define FOUNTAIN_ANSI_PRODUCT_ID	0x020E
+#define FOUNTAIN_ISO_PRODUCT_ID		0x020F
+
+#define FOUNTAIN_TP_ONLY_PRODUCT_ID	0x030A
+
+#define GEYSER1_TP_ONLY_PRODUCT_ID	0x030B
+
+#define GEYSER_ANSI_PRODUCT_ID		0x0214
+#define GEYSER_ISO_PRODUCT_ID		0x0215
+#define GEYSER_JIS_PRODUCT_ID		0x0216
 
 /* MacBook devices */
-#define GEYSER3_ANSI_PRODUCT_ID	0x0217
-#define GEYSER3_ISO_PRODUCT_ID	0x0218
-#define GEYSER3_JIS_PRODUCT_ID	0x0219
+#define GEYSER3_ANSI_PRODUCT_ID		0x0217
+#define GEYSER3_ISO_PRODUCT_ID		0x0218
+#define GEYSER3_JIS_PRODUCT_ID		0x0219
+
+/*
+ * Geyser IV: same as Geyser III according to Info.plist in AppleUSBTrackpad.kext
+ * -> same IOClass (AppleUSBGrIIITrackpad), same acceleration tables
+ */
+#define GEYSER4_ANSI_PRODUCT_ID	0x021A
+#define GEYSER4_ISO_PRODUCT_ID	0x021B
+#define GEYSER4_JIS_PRODUCT_ID	0x021C
 
 #define ATP_DEVICE(prod)					\
 	.match_flags = USB_DEVICE_ID_MATCH_DEVICE |		\
@@ -58,20 +73,26 @@
 
 /* table of devices that work with this driver */
 static struct usb_device_id atp_table [] = {
-	{ ATP_DEVICE(0x020E) },
-	{ ATP_DEVICE(0x020F) },
-	{ ATP_DEVICE(0x030A) },
-	{ ATP_DEVICE(0x030B) },
+	{ ATP_DEVICE(FOUNTAIN_ANSI_PRODUCT_ID) },
+	{ ATP_DEVICE(FOUNTAIN_ISO_PRODUCT_ID) },
+	{ ATP_DEVICE(FOUNTAIN_TP_ONLY_PRODUCT_ID) },
+	{ ATP_DEVICE(GEYSER1_TP_ONLY_PRODUCT_ID) },
 
 	/* PowerBooks Oct 2005 */
 	{ ATP_DEVICE(GEYSER_ANSI_PRODUCT_ID) },
 	{ ATP_DEVICE(GEYSER_ISO_PRODUCT_ID) },
 	{ ATP_DEVICE(GEYSER_JIS_PRODUCT_ID) },
 
+	/* Core Duo MacBook & MacBook Pro */
 	{ ATP_DEVICE(GEYSER3_ANSI_PRODUCT_ID) },
 	{ ATP_DEVICE(GEYSER3_ISO_PRODUCT_ID) },
 	{ ATP_DEVICE(GEYSER3_JIS_PRODUCT_ID) },
 
+	/* Core2 Duo MacBook & MacBook Pro */
+	{ ATP_DEVICE(GEYSER4_ANSI_PRODUCT_ID) },
+	{ ATP_DEVICE(GEYSER4_ISO_PRODUCT_ID) },
+	{ ATP_DEVICE(GEYSER4_JIS_PRODUCT_ID) },
+
 	/* Terminating entry */
 	{ }
 };
@@ -108,7 +129,7 @@
  */
 #define ATP_THRESHOLD	 5
 
-/* MacBook Pro (Geyser 3) initialization constants */
+/* MacBook Pro (Geyser 3 & 4) initialization constants */
 #define ATP_GEYSER3_MODE_READ_REQUEST_ID 1
 #define ATP_GEYSER3_MODE_WRITE_REQUEST_ID 9
 #define ATP_GEYSER3_MODE_REQUEST_VALUE 0x300
@@ -154,6 +175,13 @@
 MODULE_DESCRIPTION("Apple PowerBooks USB touchpad driver");
 MODULE_LICENSE("GPL");
 
+/*
+ * Make the threshold a module parameter
+ */
+static int threshold = ATP_THRESHOLD;
+module_param(threshold, int, 0644);
+MODULE_PARM_DESC(threshold, "Discards any change in data from a sensor (trackpad has hundreds of these sensors) less than this value");
+
 static int debug = 1;
 module_param(debug, int, 0644);
 MODULE_PARM_DESC(debug, "Activate debugging output");
@@ -174,7 +202,10 @@
 
 	return (productId == GEYSER3_ANSI_PRODUCT_ID) ||
 		(productId == GEYSER3_ISO_PRODUCT_ID) ||
-		(productId == GEYSER3_JIS_PRODUCT_ID);
+		(productId == GEYSER3_JIS_PRODUCT_ID) ||
+		(productId == GEYSER4_ANSI_PRODUCT_ID) ||
+		(productId == GEYSER4_ISO_PRODUCT_ID) ||
+		(productId == GEYSER4_JIS_PRODUCT_ID);
 }
 
 static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact,
@@ -183,16 +214,48 @@
 	int i;
 	/* values to calculate mean */
 	int pcum = 0, psum = 0;
+	int is_increasing = 0;
 
 	*fingers = 0;
 
 	for (i = 0; i < nb_sensors; i++) {
-		if (xy_sensors[i] < ATP_THRESHOLD)
+		if (xy_sensors[i] < threshold) {
+			if (is_increasing)
+				is_increasing = 0;
+
 			continue;
-		if ((i - 1 < 0) || (xy_sensors[i - 1] < ATP_THRESHOLD))
+		}
+
+		/*
+		 * Makes the finger detection more versatile.  For example,
+		 * two fingers with no gap will be detected.  Also, my
+		 * tests show it less likely to have intermittent loss
+		 * of multiple finger readings while moving around (scrolling).
+		 *
+		 * Changes the multiple finger detection to counting humps on
+		 * sensors (transitions from nonincreasing to increasing)
+		 * instead of counting transitions from low sensors (no
+		 * finger reading) to high sensors (finger above
+		 * sensor)
+		 *
+		 * - Jason Parekh <jasonparekh@gmail.com>
+		 */
+		if (i < 1 || (!is_increasing && xy_sensors[i - 1] < xy_sensors[i])) {
 			(*fingers)++;
-		pcum += xy_sensors[i] * i;
-		psum += xy_sensors[i];
+			is_increasing = 1;
+		} else if (i > 0 && xy_sensors[i - 1] >= xy_sensors[i]) {
+			is_increasing = 0;
+		}
+
+		/*
+		 * Subtracts threshold so a high sensor that just passes the threshold
+		 * won't skew the calculated absolute coordinate.  Fixes an issue
+		 * where slowly moving the mouse would occassionaly jump a number of
+		 * pixels (let me restate--slowly moving the mouse makes this issue
+		 * most apparent).
+		 */
+		pcum += (xy_sensors[i] - threshold) * i;
+		psum += (xy_sensors[i] - threshold);
 	}
 
 	if (psum > 0) {
diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c
index f1d0e1d..0811c39 100644
--- a/drivers/usb/input/hid-core.c
+++ b/drivers/usb/input/hid-core.c
@@ -1670,6 +1670,9 @@
 #define USB_VENDOR_ID_AIRCABLE		0x16CA
 #define USB_DEVICE_ID_AIRCABLE1		0x1502
 
+#define USB_VENDOR_ID_LOGITECH		0x046d
+#define USB_DEVICE_ID_LOGITECH_USB_RECEIVER	0xc101
+
 /*
  * Alphabetically sorted blacklist by quirk type.
  */
@@ -1841,7 +1844,9 @@
 	{ USB_VENDOR_ID_PANJIT, 0x0004, HID_QUIRK_IGNORE },
 
 	{ USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET },
-	
+
+	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_USB_RECEIVER, HID_QUIRK_BAD_RELATIVE_KEYS },
+
 	{ 0, 0 }
 };
 
diff --git a/drivers/usb/input/hid-input.c b/drivers/usb/input/hid-input.c
index 68e7ebb..3a7e5fb 100644
--- a/drivers/usb/input/hid-input.c
+++ b/drivers/usb/input/hid-input.c
@@ -581,6 +581,10 @@
 		|| ((device->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_7) && (usage->hid == 0x00090007)))
 		goto ignore;
 
+	if ((device->quirks & HID_QUIRK_BAD_RELATIVE_KEYS) &&
+	    usage->type == EV_KEY && (field->flags & HID_MAIN_ITEM_RELATIVE))
+		field->flags &= ~HID_MAIN_ITEM_RELATIVE;
+
 	set_bit(usage->type, input->evbit);
 
 	while (usage->code <= max && test_and_set_bit(usage->code, bit))
diff --git a/drivers/usb/input/hid.h b/drivers/usb/input/hid.h
index 2a9bf07..76ad68d 100644
--- a/drivers/usb/input/hid.h
+++ b/drivers/usb/input/hid.h
@@ -261,6 +261,7 @@
 #define HID_QUIRK_POWERBOOK_FN_ON		0x00002000
 #define HID_QUIRK_INVERT_HWHEEL			0x00004000
 #define HID_QUIRK_POWERBOOK_ISO_KEYBOARD	0x00008000
+#define HID_QUIRK_BAD_RELATIVE_KEYS		0x00010000
 
 /*
  * This is the global environment of the parser. This information is
diff --git a/include/linux/input.h b/include/linux/input.h
index c38507b..4e61158 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -663,7 +663,7 @@
 #define BUS_GSC			0x1A
 
 /*
- * Values describing the status of an effect
+ * Values describing the status of a force-feedback effect
  */
 #define FF_STATUS_STOPPED	0x00
 #define FF_STATUS_PLAYING	0x01
@@ -680,7 +680,7 @@
  */
 
 /**
- * struct ff_replay - defines scheduling of the effect
+ * struct ff_replay - defines scheduling of the force-feedback effect
  * @length: duration of the effect
  * @delay: delay before effect should start playing
  */
@@ -690,7 +690,7 @@
 };
 
 /**
- * struct ff_trigger - defines what triggers the effect
+ * struct ff_trigger - defines what triggers the force-feedback effect
  * @button: number of the button triggering the effect
  * @interval: controls how soon the effect can be re-triggered
  */
@@ -700,7 +700,7 @@
 };
 
 /**
- * struct ff_envelope - generic effect envelope
+ * struct ff_envelope - generic force-feedback effect envelope
  * @attack_length: duration of the attack (ms)
  * @attack_level: level at the beginning of the attack
  * @fade_length: duration of fade (ms)
@@ -719,7 +719,7 @@
 };
 
 /**
- * struct ff_constant_effect - defines parameters of a constant effect
+ * struct ff_constant_effect - defines parameters of a constant force-feedback effect
  * @level: strength of the effect; may be negative
  * @envelope: envelope data
  */
@@ -729,7 +729,7 @@
 };
 
 /**
- * struct ff_ramp_effect - defines parameters of a ramp effect
+ * struct ff_ramp_effect - defines parameters of a ramp force-feedback effect
  * @start_level: beginning strength of the effect; may be negative
  * @end_level: final strength of the effect; may be negative
  * @envelope: envelope data
@@ -741,7 +741,7 @@
 };
 
 /**
- * struct ff_condition_effect - defines a spring or friction effect
+ * struct ff_condition_effect - defines a spring or friction force-feedback effect
  * @right_saturation: maximum level when joystick moved all way to the right
  * @left_saturation: same for the left side
  * @right_coeff: controls how fast the force grows when the joystick moves
@@ -762,7 +762,7 @@
 };
 
 /**
- * struct ff_periodic_effect - defines parameters of a periodic effect
+ * struct ff_periodic_effect - defines parameters of a periodic force-feedback effect
  * @waveform: kind of the effect (wave)
  * @period: period of the wave (ms)
  * @magnitude: peak value
@@ -793,7 +793,7 @@
 };
 
 /**
- * struct ff_rumble_effect - defines parameters of a periodic effect
+ * struct ff_rumble_effect - defines parameters of a periodic force-feedback effect
  * @strong_magnitude: magnitude of the heavy motor
  * @weak_magnitude: magnitude of the light one
  *
diff --git a/include/linux/serio.h b/include/linux/serio.h
index b99c5ca..0f478a8 100644
--- a/include/linux/serio.h
+++ b/include/linux/serio.h
@@ -85,18 +85,8 @@
 
 void serio_unregister_port(struct serio *serio);
 void serio_unregister_child_port(struct serio *serio);
-void __serio_unregister_port_delayed(struct serio *serio, struct module *owner);
-static inline void serio_unregister_port_delayed(struct serio *serio)
-{
-	__serio_unregister_port_delayed(serio, THIS_MODULE);
-}
 
-void __serio_register_driver(struct serio_driver *drv, struct module *owner);
-static inline void serio_register_driver(struct serio_driver *drv)
-{
-	__serio_register_driver(drv, THIS_MODULE);
-}
-
+int serio_register_driver(struct serio_driver *drv);
 void serio_unregister_driver(struct serio_driver *drv);
 
 static inline int serio_write(struct serio *serio, unsigned char data)