i2c: i2c stack can remove()
More update for new style driver support: add a remove() method, and use it in the relevant code paths. Again, nothing will use this yet since there's nothing to create devices feeding this infrastructure. Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Jean Delvare <khali@linux-fr.org>
This commit is contained in:
committed by
Jean Delvare
parent
7b4fbc50fa
commit
a1d9e6e49f
@@ -41,6 +41,7 @@ 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);
|
||||||
|
|
||||||
|
#define is_newstyle_driver(d) ((d)->probe || (d)->remove)
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
@@ -52,7 +53,7 @@ static int i2c_device_match(struct device *dev, struct device_driver *drv)
|
|||||||
/* make legacy i2c drivers bypass driver model probing entirely;
|
/* make legacy i2c drivers bypass driver model probing entirely;
|
||||||
* such drivers scan each i2c adapter/bus themselves.
|
* such drivers scan each i2c adapter/bus themselves.
|
||||||
*/
|
*/
|
||||||
if (!driver->probe)
|
if (!is_newstyle_driver(driver))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* new style drivers use the same kind of driver matching policy
|
/* new style drivers use the same kind of driver matching policy
|
||||||
@@ -100,7 +101,24 @@ static int i2c_device_probe(struct device *dev)
|
|||||||
|
|
||||||
static int i2c_device_remove(struct device *dev)
|
static int i2c_device_remove(struct device *dev)
|
||||||
{
|
{
|
||||||
return 0;
|
struct i2c_client *client = to_i2c_client(dev);
|
||||||
|
struct i2c_driver *driver;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
if (!dev->driver)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
driver = to_i2c_driver(dev->driver);
|
||||||
|
if (driver->remove) {
|
||||||
|
dev_dbg(dev, "remove\n");
|
||||||
|
status = driver->remove(client);
|
||||||
|
} else {
|
||||||
|
dev->driver = NULL;
|
||||||
|
status = 0;
|
||||||
|
}
|
||||||
|
if (status == 0)
|
||||||
|
client->driver = NULL;
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void i2c_device_shutdown(struct device *dev)
|
static void i2c_device_shutdown(struct device *dev)
|
||||||
@@ -177,6 +195,26 @@ struct bus_type i2c_bus_type = {
|
|||||||
.resume = i2c_device_resume,
|
.resume = i2c_device_resume,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void i2c_unregister_device(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
struct i2c_adapter *adapter = client->adapter;
|
||||||
|
struct i2c_driver *driver = client->driver;
|
||||||
|
|
||||||
|
if (driver && !is_newstyle_driver(driver)) {
|
||||||
|
dev_err(&client->dev, "can't unregister devices "
|
||||||
|
"with legacy drivers\n");
|
||||||
|
WARN_ON(1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&adapter->clist_lock);
|
||||||
|
list_del(&client->list);
|
||||||
|
mutex_unlock(&adapter->clist_lock);
|
||||||
|
|
||||||
|
device_unregister(&client->dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
/* I2C bus adapters -- one roots each I2C or SMBUS segment */
|
/* I2C bus adapters -- one roots each I2C or SMBUS segment */
|
||||||
@@ -310,9 +348,19 @@ int i2c_del_adapter(struct i2c_adapter *adap)
|
|||||||
/* detach any active clients. This must be done first, because
|
/* detach any active clients. This must be done first, because
|
||||||
* it can fail; in which case we give up. */
|
* it can fail; in which case we give up. */
|
||||||
list_for_each_safe(item, _n, &adap->clients) {
|
list_for_each_safe(item, _n, &adap->clients) {
|
||||||
client = list_entry(item, struct i2c_client, list);
|
struct i2c_driver *driver;
|
||||||
|
|
||||||
if ((res=client->driver->detach_client(client))) {
|
client = list_entry(item, struct i2c_client, list);
|
||||||
|
driver = client->driver;
|
||||||
|
|
||||||
|
/* new style, follow standard driver model */
|
||||||
|
if (!driver || is_newstyle_driver(driver)) {
|
||||||
|
i2c_unregister_device(client);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* legacy drivers create and remove clients themselves */
|
||||||
|
if ((res = driver->detach_client(client))) {
|
||||||
dev_err(&adap->dev, "detach_client failed for client "
|
dev_err(&adap->dev, "detach_client failed for client "
|
||||||
"[%s] at address 0x%02x\n", client->name,
|
"[%s] at address 0x%02x\n", client->name,
|
||||||
client->addr);
|
client->addr);
|
||||||
@@ -355,7 +403,7 @@ int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
|
|||||||
int res;
|
int res;
|
||||||
|
|
||||||
/* new style driver methods can't mix with legacy ones */
|
/* new style driver methods can't mix with legacy ones */
|
||||||
if (driver->probe) {
|
if (is_newstyle_driver(driver)) {
|
||||||
if (driver->attach_adapter || driver->detach_adapter
|
if (driver->attach_adapter || driver->detach_adapter
|
||||||
|| driver->detach_client) {
|
|| driver->detach_client) {
|
||||||
printk(KERN_WARNING
|
printk(KERN_WARNING
|
||||||
@@ -392,6 +440,10 @@ int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(i2c_register_driver);
|
EXPORT_SYMBOL(i2c_register_driver);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* i2c_del_driver - unregister I2C driver
|
||||||
|
* @driver: the driver being unregistered
|
||||||
|
*/
|
||||||
int i2c_del_driver(struct i2c_driver *driver)
|
int i2c_del_driver(struct i2c_driver *driver)
|
||||||
{
|
{
|
||||||
struct list_head *item1, *item2, *_n;
|
struct list_head *item1, *item2, *_n;
|
||||||
@@ -402,6 +454,10 @@ int i2c_del_driver(struct i2c_driver *driver)
|
|||||||
|
|
||||||
mutex_lock(&core_lists);
|
mutex_lock(&core_lists);
|
||||||
|
|
||||||
|
/* new-style driver? */
|
||||||
|
if (is_newstyle_driver(driver))
|
||||||
|
goto unregister;
|
||||||
|
|
||||||
/* Have a look at each adapter, if clients of this driver are still
|
/* Have a look at each adapter, if clients of this driver are still
|
||||||
* attached. If so, detach them to be able to kill the driver
|
* attached. If so, detach them to be able to kill the driver
|
||||||
* afterwards.
|
* afterwards.
|
||||||
@@ -434,6 +490,7 @@ int i2c_del_driver(struct i2c_driver *driver)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unregister:
|
||||||
driver_unregister(&driver->driver);
|
driver_unregister(&driver->driver);
|
||||||
list_del(&driver->list);
|
list_del(&driver->list);
|
||||||
pr_debug("i2c-core: driver [%s] unregistered\n", driver->driver.name);
|
pr_debug("i2c-core: driver [%s] unregistered\n", driver->driver.name);
|
||||||
|
@@ -130,6 +130,7 @@ struct i2c_driver {
|
|||||||
* it's done by infrastructure. (NEW STYLE DRIVERS ONLY)
|
* it's done by infrastructure. (NEW STYLE DRIVERS ONLY)
|
||||||
*/
|
*/
|
||||||
int (*probe)(struct i2c_client *);
|
int (*probe)(struct i2c_client *);
|
||||||
|
int (*remove)(struct i2c_client *);
|
||||||
|
|
||||||
/* driver model interfaces that don't relate to enumeration */
|
/* driver model interfaces that don't relate to enumeration */
|
||||||
void (*shutdown)(struct i2c_client *);
|
void (*shutdown)(struct i2c_client *);
|
||||||
|
Reference in New Issue
Block a user