]> err.no Git - linux-2.6/blobdiff - drivers/i2c/i2c-core.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris...
[linux-2.6] / drivers / i2c / i2c-core.c
index cd3fcb85ca7f5cc4efb7812c07c2312e7f56e60e..e186df657119e9af61296b68db2f8ece414371f1 100644 (file)
@@ -35,8 +35,8 @@
 #include <linux/completion.h>
 #include <linux/hardirq.h>
 #include <linux/irqflags.h>
+#include <linux/semaphore.h>
 #include <asm/uaccess.h>
-#include <asm/semaphore.h>
 
 #include "i2c-core.h"
 
@@ -90,12 +90,16 @@ static int i2c_device_probe(struct device *dev)
 {
        struct i2c_client       *client = to_i2c_client(dev);
        struct i2c_driver       *driver = to_i2c_driver(dev->driver);
+       int status;
 
        if (!driver->probe)
                return -ENODEV;
        client->driver = driver;
        dev_dbg(dev, "probe\n");
-       return driver->probe(client);
+       status = driver->probe(client);
+       if (status)
+               client->driver = NULL;
+       return status;
 }
 
 static int i2c_device_remove(struct device *dev)
@@ -296,6 +300,50 @@ void i2c_unregister_device(struct i2c_client *client)
 EXPORT_SYMBOL_GPL(i2c_unregister_device);
 
 
+static int dummy_nop(struct i2c_client *client)
+{
+       return 0;
+}
+
+static struct i2c_driver dummy_driver = {
+       .driver.name    = "dummy",
+       .probe          = dummy_nop,
+       .remove         = dummy_nop,
+};
+
+/**
+ * i2c_new_dummy - return a new i2c device bound to a dummy driver
+ * @adapter: the adapter managing the device
+ * @address: seven bit address to be used
+ * @type: optional label used for i2c_client.name
+ * Context: can sleep
+ *
+ * This returns an I2C client bound to the "dummy" driver, intended for use
+ * with devices that consume multiple addresses.  Examples of such chips
+ * include various EEPROMS (like 24c04 and 24c08 models).
+ *
+ * These dummy devices have two main uses.  First, most I2C and SMBus calls
+ * except i2c_transfer() need a client handle; the dummy will be that handle.
+ * And second, this prevents the specified address from being bound to a
+ * different driver.
+ *
+ * This returns the new i2c client, which should be saved for later use with
+ * i2c_unregister_device(); or NULL to indicate an error.
+ */
+struct i2c_client *
+i2c_new_dummy(struct i2c_adapter *adapter, u16 address, const char *type)
+{
+       struct i2c_board_info info = {
+               .driver_name    = "dummy",
+               .addr           = address,
+       };
+
+       if (type)
+               strlcpy(info.type, type, sizeof info.type);
+       return i2c_new_device(adapter, &info);
+}
+EXPORT_SYMBOL_GPL(i2c_new_dummy);
+
 /* ------------------------------------------------------------------------- */
 
 /* I2C bus adapters -- one roots each I2C or SMBUS segment */
@@ -441,8 +489,8 @@ EXPORT_SYMBOL(i2c_add_adapter);
  * Context: can sleep
  *
  * This routine is used to declare an I2C adapter when its bus number
- * matters.  Example: for I2C adapters from system-on-chip CPUs, or
- * otherwise built in to the system's mainboard, and where i2c_board_info
+ * matters.  For example, use it for I2C adapters from system-on-chip CPUs,
+ * or otherwise built in to the system's mainboard, and where i2c_board_info
  * is used to properly configure I2C devices.
  *
  * If no devices have pre-been declared for this bus, then be sure to
@@ -841,11 +889,24 @@ static int __init i2c_init(void)
        retval = bus_register(&i2c_bus_type);
        if (retval)
                return retval;
-       return class_register(&i2c_adapter_class);
+       retval = class_register(&i2c_adapter_class);
+       if (retval)
+               goto bus_err;
+       retval = i2c_add_driver(&dummy_driver);
+       if (retval)
+               goto class_err;
+       return 0;
+
+class_err:
+       class_unregister(&i2c_adapter_class);
+bus_err:
+       bus_unregister(&i2c_bus_type);
+       return retval;
 }
 
 static void __exit i2c_exit(void)
 {
+       i2c_del_driver(&dummy_driver);
        class_unregister(&i2c_adapter_class);
        bus_unregister(&i2c_bus_type);
 }