From f37dd80ac2a67e4e4e921f99d34a1ceeb2488abb Mon Sep 17 00:00:00 2001
From: David Brownell <david-b@pacbell.net>
Date: Tue, 13 Feb 2007 22:09:00 +0100
Subject: [PATCH] i2c: Add driver suspend/resume/shutdown support

Driver model updates for the I2C core:

 - Add new suspend(), resume(), and shutdown() methods.  Use them in the
   standard driver model style; document them.

 - Minor doc updates to highlight zero-initialized fields in drivers, and
   the driver model accessors for "clientdata".

If any i2c drivers were previously using the old suspend/resume calls
in "struct driver", they were getting warning messages ... and will
now no longer work.  Other than that, this patch changes no behaviors;
and it lets I2C drivers use conventional PM and shutdown support.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Jean Delvare <khali@linux-fr.org>
---
 Documentation/i2c/porting-clients |  6 +++
 Documentation/i2c/writing-clients | 58 +++++++++++++++++++++----
 drivers/i2c/i2c-core.c            | 71 ++++++++++++++++++++-----------
 include/linux/i2c.h               |  7 ++-
 4 files changed, 108 insertions(+), 34 deletions(-)

diff --git a/Documentation/i2c/porting-clients b/Documentation/i2c/porting-clients
index f03c2a02f806..ca272b263a92 100644
--- a/Documentation/i2c/porting-clients
+++ b/Documentation/i2c/porting-clients
@@ -129,6 +129,12 @@ Technical changes:
   structure, those name member should be initialized to a driver name
   string. i2c_driver itself has no name member anymore.
 
+* [Driver model] Instead of shutdown or reboot notifiers, provide a
+  shutdown() method in your driver.
+
+* [Power management] Use the driver model suspend() and resume()
+  callbacks instead of the obsolete pm_register() calls.
+
 Coding policy:
 
 * [Copyright] Use (C), not (c), for copyright.
diff --git a/Documentation/i2c/writing-clients b/Documentation/i2c/writing-clients
index 3a057c8e5507..fbcff96f4ca1 100644
--- a/Documentation/i2c/writing-clients
+++ b/Documentation/i2c/writing-clients
@@ -21,20 +21,26 @@ The driver structure
 
 Usually, you will implement a single driver structure, and instantiate
 all clients from it. Remember, a driver structure contains general access 
-routines, a client structure specific information like the actual I2C
-address.
+routines, and should be zero-initialized except for fields with data you
+provide.  A client structure holds device-specific information like the
+driver model device node, and its I2C address.
 
 static struct i2c_driver foo_driver = {
 	.driver = {
 		.name	= "foo",
 	},
-	.attach_adapter	= &foo_attach_adapter,
-	.detach_client	= &foo_detach_client,
-	.command	= &foo_command /* may be NULL */
+	.attach_adapter	= foo_attach_adapter,
+	.detach_client	= foo_detach_client,
+	.shutdown	= foo_shutdown,	/* optional */
+	.suspend	= foo_suspend,	/* optional */
+	.resume		= foo_resume,	/* optional */
+	.command	= foo_command,	/* optional */
 }
  
-The name field must match the driver name, including the case. It must not
-contain spaces, and may be up to 31 characters long.
+The name field is the driver name, and must not contain spaces.  It
+should match the module name (if the driver can be compiled as a module),
+although you can use MODULE_ALIAS (passing "foo" in this example) to add
+another name for the module.
 
 All other fields are for call-back functions which will be explained 
 below.
@@ -43,11 +49,18 @@ below.
 Extra client data
 =================
 
-The client structure has a special `data' field that can point to any
-structure at all. You can use this to keep client-specific data. You
+Each client structure has a special `data' field that can point to any
+structure at all.  You should use this to keep device-specific data,
+especially in drivers that handle multiple I2C or SMBUS devices.  You
 do not always need this, but especially for `sensors' drivers, it can
 be very useful.
 
+	/* store the value */
+	void i2c_set_clientdata(struct i2c_client *client, void *data);
+
+	/* retrieve the value */
+	void *i2c_get_clientdata(struct i2c_client *client);
+
 An example structure is below.
 
   struct foo_data {
@@ -493,6 +506,33 @@ by `__init_data'.  Hose functions and structures can be removed after
 kernel booting (or module loading) is completed.
 
 
+Power Management
+================
+
+If your I2C device needs special handling when entering a system low
+power state -- like putting a transceiver into a low power mode, or
+activating a system wakeup mechanism -- do that in the suspend() method.
+The resume() method should reverse what the suspend() method does.
+
+These are standard driver model calls, and they work just like they
+would for any other driver stack.  The calls can sleep, and can use
+I2C messaging to the device being suspended or resumed (since their
+parent I2C adapter is active when these calls are issued, and IRQs
+are still enabled).
+
+
+System Shutdown
+===============
+
+If your I2C device needs special handling when the system shuts down
+or reboots (including kexec) -- like turning something off -- use a
+shutdown() method.
+
+Again, this is a standard driver model call, working just like it
+would for any other driver stack:  the calls can sleep, and can use
+I2C messaging.
+
+
 Command function
 ================
 
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index 60f6eb194046..9653f7f81561 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -41,30 +41,15 @@ static LIST_HEAD(drivers);
 static DEFINE_MUTEX(core_lists);
 static DEFINE_IDR(i2c_adapter_idr);
 
+
+/* ------------------------------------------------------------------------- */
+
 /* match always succeeds, as we want the probe() to tell if we really accept this match */
 static int i2c_device_match(struct device *dev, struct device_driver *drv)
 {
 	return 1;
 }
 
-static int i2c_bus_suspend(struct device * dev, pm_message_t state)
-{
-	int rc = 0;
-
-	if (dev->driver && dev->driver->suspend)
-		rc = dev->driver->suspend(dev, state);
-	return rc;
-}
-
-static int i2c_bus_resume(struct device * dev)
-{
-	int rc = 0;
-	
-	if (dev->driver && dev->driver->resume)
-		rc = dev->driver->resume(dev);
-	return rc;
-}
-
 static int i2c_device_probe(struct device *dev)
 {
 	return -ENODEV;
@@ -75,15 +60,53 @@ static int i2c_device_remove(struct device *dev)
 	return 0;
 }
 
+static void i2c_device_shutdown(struct device *dev)
+{
+	struct i2c_driver *driver;
+
+	if (!dev->driver)
+		return;
+	driver = to_i2c_driver(dev->driver);
+	if (driver->shutdown)
+		driver->shutdown(to_i2c_client(dev));
+}
+
+static int i2c_device_suspend(struct device * dev, pm_message_t mesg)
+{
+	struct i2c_driver *driver;
+
+	if (!dev->driver)
+		return 0;
+	driver = to_i2c_driver(dev->driver);
+	if (!driver->suspend)
+		return 0;
+	return driver->suspend(to_i2c_client(dev), mesg);
+}
+
+static int i2c_device_resume(struct device * dev)
+{
+	struct i2c_driver *driver;
+
+	if (!dev->driver)
+		return 0;
+	driver = to_i2c_driver(dev->driver);
+	if (!driver->resume)
+		return 0;
+	return driver->resume(to_i2c_client(dev));
+}
+
 struct bus_type i2c_bus_type = {
-	.name =		"i2c",
-	.match =	i2c_device_match,
-	.probe =	i2c_device_probe,
-	.remove =	i2c_device_remove,
-	.suspend =      i2c_bus_suspend,
-	.resume =       i2c_bus_resume,
+	.name		= "i2c",
+	.match		= i2c_device_match,
+	.probe		= i2c_device_probe,
+	.remove		= i2c_device_remove,
+	.shutdown	= i2c_device_shutdown,
+	.suspend	= i2c_device_suspend,
+	.resume		= i2c_device_resume,
 };
 
+/* ------------------------------------------------------------------------- */
+
 void i2c_adapter_dev_release(struct device *dev)
 {
 	struct i2c_adapter *adap = dev_to_i2c_adapter(dev);
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index 71e50d3e492f..9428092017e3 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -125,7 +125,12 @@ struct i2c_driver {
 	 * it must be freed here.
 	 */
 	int (*detach_client)(struct i2c_client *);
-	
+
+	/* driver model interfaces that don't relate to enumeration  */
+	void (*shutdown)(struct i2c_client *);
+	int (*suspend)(struct i2c_client *, pm_message_t mesg);
+	int (*resume)(struct i2c_client *);
+
 	/* a ioctl like command that can be used to perform specific functions
 	 * with the device.
 	 */