Return-Path: X-Spam-Checker-Version: SpamAssassin X-Spam-Level: X-Spam-Status: No, score=0.6 required=5.0 Received: from smtp111.sbc.mail.mud.yahoo.com (smtp111.sbc.mail.mud.yahoo.com [68.142.198.210]) by zone4.gcu-squad.org (8.13.8/8.12.11) with SMTP id kBT93qSY025680 for ; Fri, 29 Dec 2006 10:03:53 +0100 (CET) Received: (qmail 80333 invoked from network); 29 Dec 2006 09:13:53 -0000 DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=s1024; d=pacbell.net; h=Received:X-YMail-OSG:From:To:Subject:Date:User-Agent:Cc:References:In-Reply-To:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-Disposition:Message-Id; b=I6x4k7V8G5t6b2k/9Exjs5zK7aJofgGsdlTREWOxaVXhiqe5pBL8kg22vPVyUr+YNifgsH3Pbxzkt8i2Y/NZpCQL82bl7ru+VwbqPH1d1UNrqzioNprlKe/+JySpvZEkFTt5D/HfDJ8SH5ImDW/bcWpeaYGoM7FgU6Z3OYrI/Rg= ; Received: from unknown (HELO ascent) (david-b@pacbell.net@69.226.255.247 with plain) by smtp111.sbc.mail.mud.yahoo.com with SMTP; 29 Dec 2006 09:13:52 -0000 X-YMail-OSG: nxu_LzUVM1nwA7eMgB8r_7phlDm47gr2VEjnXlDsWacUitwfuAnYghvwmxZY1T5giLqoYAD3NDv6LAvJKXBaa8407YEradnzYKfKDq8fXUhz5EE2sdeFO9vGl4TfWlSdLHAI97zaXUc6C9PBqO_gwYTV9QgDg4Mvw56IBgKhxcNOLie4OUeQ0.6GiVYl From: David Brownell To: Jean Delvare Subject: Re: [patch 2.6.20-rc1 6/6] i2c driver suspend()/resume()/shutdown() support Date: Fri, 29 Dec 2006 01:13:50 -0800 User-Agent: KMail/1.7.1 Cc: i2c@lm-sensors.org References: <200611251340.41858.david-b@pacbell.net> <20061215200650.b7f5212c.khali@linux-fr.org> <200612171053.55409.david-b@pacbell.net> In-Reply-To: <200612171053.55409.david-b@pacbell.net> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline Message-Id: <200612290113.51190.david-b@pacbell.net> Status: RO 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 --- This is a refresh of the previous version of this patch, updated to apply without the "good parts" of patch #5 ("#5B") ... so it applies to a 2.6.20-rc2 kernel. Documentation/i2c/porting-clients | 6 +++ Documentation/i2c/writing-clients | 58 ++++++++++++++++++++++++++++----- drivers/i2c/i2c-core.c | 65 +++++++++++++++++++++++++------------ include/linux/i2c.h | 7 +++- 4 files changed, 105 insertions(+), 31 deletions(-) --- linux-2.6.20-rc5.orig/Documentation/i2c/writing-clients 2007-01-17 19:11:35.000000000 +0100 +++ linux-2.6.20-rc5/Documentation/i2c/writing-clients 2007-01-20 15:12:49.000000000 +0100 @@ -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 st 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 ================ --- linux-2.6.20-rc5.orig/Documentation/i2c/porting-clients 2007-01-17 19:11:35.000000000 +0100 +++ linux-2.6.20-rc5/Documentation/i2c/porting-clients 2007-01-20 15:12:49.000000000 +0100 @@ -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. --- linux-2.6.20-rc5.orig/include/linux/i2c.h 2007-01-17 19:11:35.000000000 +0100 +++ linux-2.6.20-rc5/include/linux/i2c.h 2007-01-20 15:12:49.000000000 +0100 @@ -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. */ --- linux-2.6.20-rc5.orig/drivers/i2c/i2c-core.c 2007-01-18 14:10:06.000000000 +0100 +++ linux-2.6.20-rc5/drivers/i2c/i2c-core.c 2007-01-23 10:00:34.000000000 +0100 @@ -41,49 +41,72 @@ 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) +static int i2c_device_probe(struct device *dev) { - int rc = 0; + return -ENODEV; +} - if (dev->driver && dev->driver->suspend) - rc = dev->driver->suspend(dev, state); - return rc; +static int i2c_device_remove(struct device *dev) +{ + return 0; } -static int i2c_bus_resume(struct device * dev) +static void i2c_device_shutdown(struct device *dev) { - int rc = 0; - - if (dev->driver && dev->driver->resume) - rc = dev->driver->resume(dev); - return rc; + 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_probe(struct device *dev) +static int i2c_device_suspend(struct device * dev, pm_message_t mesg) { - return -ENODEV; + 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_remove(struct device *dev) +static int i2c_device_resume(struct device * dev) { - return 0; + 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);