mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-06-06 06:37:59 +00:00
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>
This commit is contained in:
parent
b8d6f45b32
commit
f37dd80ac2
4 changed files with 108 additions and 34 deletions
|
@ -129,6 +129,12 @@ Technical changes:
|
||||||
structure, those name member should be initialized to a driver name
|
structure, those name member should be initialized to a driver name
|
||||||
string. i2c_driver itself has no name member anymore.
|
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:
|
Coding policy:
|
||||||
|
|
||||||
* [Copyright] Use (C), not (c), for copyright.
|
* [Copyright] Use (C), not (c), for copyright.
|
||||||
|
|
|
@ -21,20 +21,26 @@ The driver structure
|
||||||
|
|
||||||
Usually, you will implement a single driver structure, and instantiate
|
Usually, you will implement a single driver structure, and instantiate
|
||||||
all clients from it. Remember, a driver structure contains general access
|
all clients from it. Remember, a driver structure contains general access
|
||||||
routines, a client structure specific information like the actual I2C
|
routines, and should be zero-initialized except for fields with data you
|
||||||
address.
|
provide. A client structure holds device-specific information like the
|
||||||
|
driver model device node, and its I2C address.
|
||||||
|
|
||||||
static struct i2c_driver foo_driver = {
|
static struct i2c_driver foo_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "foo",
|
.name = "foo",
|
||||||
},
|
},
|
||||||
.attach_adapter = &foo_attach_adapter,
|
.attach_adapter = foo_attach_adapter,
|
||||||
.detach_client = &foo_detach_client,
|
.detach_client = foo_detach_client,
|
||||||
.command = &foo_command /* may be NULL */
|
.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
|
The name field is the driver name, and must not contain spaces. It
|
||||||
contain spaces, and may be up to 31 characters long.
|
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
|
All other fields are for call-back functions which will be explained
|
||||||
below.
|
below.
|
||||||
|
@ -43,11 +49,18 @@ below.
|
||||||
Extra client data
|
Extra client data
|
||||||
=================
|
=================
|
||||||
|
|
||||||
The client structure has a special `data' field that can point to any
|
Each 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
|
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
|
do not always need this, but especially for `sensors' drivers, it can
|
||||||
be very useful.
|
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.
|
An example structure is below.
|
||||||
|
|
||||||
struct foo_data {
|
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.
|
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
|
Command function
|
||||||
================
|
================
|
||||||
|
|
||||||
|
|
|
@ -41,30 +41,15 @@ static LIST_HEAD(drivers);
|
||||||
static DEFINE_MUTEX(core_lists);
|
static DEFINE_MUTEX(core_lists);
|
||||||
static DEFINE_IDR(i2c_adapter_idr);
|
static DEFINE_IDR(i2c_adapter_idr);
|
||||||
|
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
/* match always succeeds, as we want the probe() to tell if we really accept this match */
|
/* 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)
|
static int i2c_device_match(struct device *dev, struct device_driver *drv)
|
||||||
{
|
{
|
||||||
return 1;
|
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)
|
static int i2c_device_probe(struct device *dev)
|
||||||
{
|
{
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
@ -75,15 +60,53 @@ static int i2c_device_remove(struct device *dev)
|
||||||
return 0;
|
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 = {
|
struct bus_type i2c_bus_type = {
|
||||||
.name = "i2c",
|
.name = "i2c",
|
||||||
.match = i2c_device_match,
|
.match = i2c_device_match,
|
||||||
.probe = i2c_device_probe,
|
.probe = i2c_device_probe,
|
||||||
.remove = i2c_device_remove,
|
.remove = i2c_device_remove,
|
||||||
.suspend = i2c_bus_suspend,
|
.shutdown = i2c_device_shutdown,
|
||||||
.resume = i2c_bus_resume,
|
.suspend = i2c_device_suspend,
|
||||||
|
.resume = i2c_device_resume,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
void i2c_adapter_dev_release(struct device *dev)
|
void i2c_adapter_dev_release(struct device *dev)
|
||||||
{
|
{
|
||||||
struct i2c_adapter *adap = dev_to_i2c_adapter(dev);
|
struct i2c_adapter *adap = dev_to_i2c_adapter(dev);
|
||||||
|
|
|
@ -125,7 +125,12 @@ struct i2c_driver {
|
||||||
* it must be freed here.
|
* it must be freed here.
|
||||||
*/
|
*/
|
||||||
int (*detach_client)(struct i2c_client *);
|
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
|
/* a ioctl like command that can be used to perform specific functions
|
||||||
* with the device.
|
* with the device.
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Add table
Reference in a new issue