|
@@ -46,6 +46,7 @@ static DEFINE_MUTEX(core_lock);
|
|
|
static DEFINE_IDR(i2c_adapter_idr);
|
|
|
static LIST_HEAD(userspace_devices);
|
|
|
|
|
|
+static struct device_type i2c_client_type;
|
|
|
static int i2c_check_addr(struct i2c_adapter *adapter, int addr);
|
|
|
static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver);
|
|
|
|
|
@@ -64,9 +65,13 @@ static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
|
|
|
|
|
|
static int i2c_device_match(struct device *dev, struct device_driver *drv)
|
|
|
{
|
|
|
- struct i2c_client *client = to_i2c_client(dev);
|
|
|
- struct i2c_driver *driver = to_i2c_driver(drv);
|
|
|
+ struct i2c_client *client = i2c_verify_client(dev);
|
|
|
+ struct i2c_driver *driver;
|
|
|
+
|
|
|
+ if (!client)
|
|
|
+ return 0;
|
|
|
|
|
|
+ driver = to_i2c_driver(drv);
|
|
|
/* match on an id table if there is one */
|
|
|
if (driver->id_table)
|
|
|
return i2c_match_id(driver->id_table, client) != NULL;
|
|
@@ -94,10 +99,14 @@ static int i2c_device_uevent(struct device *dev, struct kobj_uevent_env *env)
|
|
|
|
|
|
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);
|
|
|
+ struct i2c_client *client = i2c_verify_client(dev);
|
|
|
+ struct i2c_driver *driver;
|
|
|
int status;
|
|
|
|
|
|
+ if (!client)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ driver = to_i2c_driver(dev->driver);
|
|
|
if (!driver->probe || !driver->id_table)
|
|
|
return -ENODEV;
|
|
|
client->driver = driver;
|
|
@@ -114,11 +123,11 @@ static int i2c_device_probe(struct device *dev)
|
|
|
|
|
|
static int i2c_device_remove(struct device *dev)
|
|
|
{
|
|
|
- struct i2c_client *client = to_i2c_client(dev);
|
|
|
+ struct i2c_client *client = i2c_verify_client(dev);
|
|
|
struct i2c_driver *driver;
|
|
|
int status;
|
|
|
|
|
|
- if (!dev->driver)
|
|
|
+ if (!client || !dev->driver)
|
|
|
return 0;
|
|
|
|
|
|
driver = to_i2c_driver(dev->driver);
|
|
@@ -136,37 +145,40 @@ static int i2c_device_remove(struct device *dev)
|
|
|
|
|
|
static void i2c_device_shutdown(struct device *dev)
|
|
|
{
|
|
|
+ struct i2c_client *client = i2c_verify_client(dev);
|
|
|
struct i2c_driver *driver;
|
|
|
|
|
|
- if (!dev->driver)
|
|
|
+ if (!client || !dev->driver)
|
|
|
return;
|
|
|
driver = to_i2c_driver(dev->driver);
|
|
|
if (driver->shutdown)
|
|
|
- driver->shutdown(to_i2c_client(dev));
|
|
|
+ driver->shutdown(client);
|
|
|
}
|
|
|
|
|
|
static int i2c_device_suspend(struct device *dev, pm_message_t mesg)
|
|
|
{
|
|
|
+ struct i2c_client *client = i2c_verify_client(dev);
|
|
|
struct i2c_driver *driver;
|
|
|
|
|
|
- if (!dev->driver)
|
|
|
+ if (!client || !dev->driver)
|
|
|
return 0;
|
|
|
driver = to_i2c_driver(dev->driver);
|
|
|
if (!driver->suspend)
|
|
|
return 0;
|
|
|
- return driver->suspend(to_i2c_client(dev), mesg);
|
|
|
+ return driver->suspend(client, mesg);
|
|
|
}
|
|
|
|
|
|
static int i2c_device_resume(struct device *dev)
|
|
|
{
|
|
|
+ struct i2c_client *client = i2c_verify_client(dev);
|
|
|
struct i2c_driver *driver;
|
|
|
|
|
|
- if (!dev->driver)
|
|
|
+ if (!client || !dev->driver)
|
|
|
return 0;
|
|
|
driver = to_i2c_driver(dev->driver);
|
|
|
if (!driver->resume)
|
|
|
return 0;
|
|
|
- return driver->resume(to_i2c_client(dev));
|
|
|
+ return driver->resume(client);
|
|
|
}
|
|
|
|
|
|
static void i2c_client_dev_release(struct device *dev)
|
|
@@ -175,10 +187,10 @@ static void i2c_client_dev_release(struct device *dev)
|
|
|
}
|
|
|
|
|
|
static ssize_t
|
|
|
-show_client_name(struct device *dev, struct device_attribute *attr, char *buf)
|
|
|
+show_name(struct device *dev, struct device_attribute *attr, char *buf)
|
|
|
{
|
|
|
- struct i2c_client *client = to_i2c_client(dev);
|
|
|
- return sprintf(buf, "%s\n", client->name);
|
|
|
+ return sprintf(buf, "%s\n", dev->type == &i2c_client_type ?
|
|
|
+ to_i2c_client(dev)->name : to_i2c_adapter(dev)->name);
|
|
|
}
|
|
|
|
|
|
static ssize_t
|
|
@@ -188,18 +200,28 @@ show_modalias(struct device *dev, struct device_attribute *attr, char *buf)
|
|
|
return sprintf(buf, "%s%s\n", I2C_MODULE_PREFIX, client->name);
|
|
|
}
|
|
|
|
|
|
-static struct device_attribute i2c_dev_attrs[] = {
|
|
|
- __ATTR(name, S_IRUGO, show_client_name, NULL),
|
|
|
+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
|
|
|
+static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
|
|
|
+
|
|
|
+static struct attribute *i2c_dev_attrs[] = {
|
|
|
+ &dev_attr_name.attr,
|
|
|
/* modalias helps coldplug: modprobe $(cat .../modalias) */
|
|
|
- __ATTR(modalias, S_IRUGO, show_modalias, NULL),
|
|
|
- { },
|
|
|
+ &dev_attr_modalias.attr,
|
|
|
+ NULL
|
|
|
+};
|
|
|
+
|
|
|
+static struct attribute_group i2c_dev_attr_group = {
|
|
|
+ .attrs = i2c_dev_attrs,
|
|
|
+};
|
|
|
+
|
|
|
+static const struct attribute_group *i2c_dev_attr_groups[] = {
|
|
|
+ &i2c_dev_attr_group,
|
|
|
+ NULL
|
|
|
};
|
|
|
|
|
|
struct bus_type i2c_bus_type = {
|
|
|
.name = "i2c",
|
|
|
- .dev_attrs = i2c_dev_attrs,
|
|
|
.match = i2c_device_match,
|
|
|
- .uevent = i2c_device_uevent,
|
|
|
.probe = i2c_device_probe,
|
|
|
.remove = i2c_device_remove,
|
|
|
.shutdown = i2c_device_shutdown,
|
|
@@ -208,6 +230,12 @@ struct bus_type i2c_bus_type = {
|
|
|
};
|
|
|
EXPORT_SYMBOL_GPL(i2c_bus_type);
|
|
|
|
|
|
+static struct device_type i2c_client_type = {
|
|
|
+ .groups = i2c_dev_attr_groups,
|
|
|
+ .uevent = i2c_device_uevent,
|
|
|
+ .release = i2c_client_dev_release,
|
|
|
+};
|
|
|
+
|
|
|
|
|
|
/**
|
|
|
* i2c_verify_client - return parameter as i2c_client, or NULL
|
|
@@ -220,7 +248,7 @@ EXPORT_SYMBOL_GPL(i2c_bus_type);
|
|
|
*/
|
|
|
struct i2c_client *i2c_verify_client(struct device *dev)
|
|
|
{
|
|
|
- return (dev->bus == &i2c_bus_type)
|
|
|
+ return (dev->type == &i2c_client_type)
|
|
|
? to_i2c_client(dev)
|
|
|
: NULL;
|
|
|
}
|
|
@@ -273,7 +301,7 @@ i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
|
|
|
|
|
|
client->dev.parent = &client->adapter->dev;
|
|
|
client->dev.bus = &i2c_bus_type;
|
|
|
- client->dev.release = i2c_client_dev_release;
|
|
|
+ client->dev.type = &i2c_client_type;
|
|
|
|
|
|
dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
|
|
|
client->addr);
|
|
@@ -368,13 +396,6 @@ static void i2c_adapter_dev_release(struct device *dev)
|
|
|
complete(&adap->dev_released);
|
|
|
}
|
|
|
|
|
|
-static ssize_t
|
|
|
-show_adapter_name(struct device *dev, struct device_attribute *attr, char *buf)
|
|
|
-{
|
|
|
- struct i2c_adapter *adap = to_i2c_adapter(dev);
|
|
|
- return sprintf(buf, "%s\n", adap->name);
|
|
|
-}
|
|
|
-
|
|
|
/*
|
|
|
* Let users instantiate I2C devices through sysfs. This can be used when
|
|
|
* platform initialization code doesn't contain the proper data for
|
|
@@ -493,19 +514,34 @@ i2c_sysfs_delete_device(struct device *dev, struct device_attribute *attr,
|
|
|
return res;
|
|
|
}
|
|
|
|
|
|
-static struct device_attribute i2c_adapter_attrs[] = {
|
|
|
- __ATTR(name, S_IRUGO, show_adapter_name, NULL),
|
|
|
- __ATTR(new_device, S_IWUSR, NULL, i2c_sysfs_new_device),
|
|
|
- __ATTR(delete_device, S_IWUSR, NULL, i2c_sysfs_delete_device),
|
|
|
- { },
|
|
|
+static DEVICE_ATTR(new_device, S_IWUSR, NULL, i2c_sysfs_new_device);
|
|
|
+static DEVICE_ATTR(delete_device, S_IWUSR, NULL, i2c_sysfs_delete_device);
|
|
|
+
|
|
|
+static struct attribute *i2c_adapter_attrs[] = {
|
|
|
+ &dev_attr_name.attr,
|
|
|
+ &dev_attr_new_device.attr,
|
|
|
+ &dev_attr_delete_device.attr,
|
|
|
+ NULL
|
|
|
};
|
|
|
|
|
|
-static struct class i2c_adapter_class = {
|
|
|
- .owner = THIS_MODULE,
|
|
|
- .name = "i2c-adapter",
|
|
|
- .dev_attrs = i2c_adapter_attrs,
|
|
|
+static struct attribute_group i2c_adapter_attr_group = {
|
|
|
+ .attrs = i2c_adapter_attrs,
|
|
|
};
|
|
|
|
|
|
+static const struct attribute_group *i2c_adapter_attr_groups[] = {
|
|
|
+ &i2c_adapter_attr_group,
|
|
|
+ NULL
|
|
|
+};
|
|
|
+
|
|
|
+static struct device_type i2c_adapter_type = {
|
|
|
+ .groups = i2c_adapter_attr_groups,
|
|
|
+ .release = i2c_adapter_dev_release,
|
|
|
+};
|
|
|
+
|
|
|
+#ifdef CONFIG_I2C_COMPAT
|
|
|
+static struct class_compat *i2c_adapter_compat_class;
|
|
|
+#endif
|
|
|
+
|
|
|
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
|
|
|
{
|
|
|
struct i2c_devinfo *devinfo;
|
|
@@ -555,14 +591,22 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
|
|
|
adap->timeout = HZ;
|
|
|
|
|
|
dev_set_name(&adap->dev, "i2c-%d", adap->nr);
|
|
|
- adap->dev.release = &i2c_adapter_dev_release;
|
|
|
- adap->dev.class = &i2c_adapter_class;
|
|
|
+ adap->dev.bus = &i2c_bus_type;
|
|
|
+ adap->dev.type = &i2c_adapter_type;
|
|
|
res = device_register(&adap->dev);
|
|
|
if (res)
|
|
|
goto out_list;
|
|
|
|
|
|
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
|
|
|
|
|
|
+#ifdef CONFIG_I2C_COMPAT
|
|
|
+ res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,
|
|
|
+ adap->dev.parent);
|
|
|
+ if (res)
|
|
|
+ dev_warn(&adap->dev,
|
|
|
+ "Failed to create compatibility class link\n");
|
|
|
+#endif
|
|
|
+
|
|
|
/* create pre-declared device nodes */
|
|
|
if (adap->nr < __i2c_first_dynamic_bus_num)
|
|
|
i2c_scan_static_board_info(adap);
|
|
@@ -741,6 +785,11 @@ int i2c_del_adapter(struct i2c_adapter *adap)
|
|
|
checking the returned value. */
|
|
|
res = device_for_each_child(&adap->dev, NULL, __unregister_client);
|
|
|
|
|
|
+#ifdef CONFIG_I2C_COMPAT
|
|
|
+ class_compat_remove_link(i2c_adapter_compat_class, &adap->dev,
|
|
|
+ adap->dev.parent);
|
|
|
+#endif
|
|
|
+
|
|
|
/* clean up the sysfs representation */
|
|
|
init_completion(&adap->dev_released);
|
|
|
device_unregister(&adap->dev);
|
|
@@ -768,9 +817,13 @@ EXPORT_SYMBOL(i2c_del_adapter);
|
|
|
|
|
|
static int __attach_adapter(struct device *dev, void *data)
|
|
|
{
|
|
|
- struct i2c_adapter *adapter = to_i2c_adapter(dev);
|
|
|
+ struct i2c_adapter *adapter;
|
|
|
struct i2c_driver *driver = data;
|
|
|
|
|
|
+ if (dev->type != &i2c_adapter_type)
|
|
|
+ return 0;
|
|
|
+ adapter = to_i2c_adapter(dev);
|
|
|
+
|
|
|
i2c_detect(adapter, driver);
|
|
|
|
|
|
/* Legacy drivers scan i2c busses directly */
|
|
@@ -809,8 +862,7 @@ int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
|
|
|
INIT_LIST_HEAD(&driver->clients);
|
|
|
/* Walk the adapters that are already present */
|
|
|
mutex_lock(&core_lock);
|
|
|
- class_for_each_device(&i2c_adapter_class, NULL, driver,
|
|
|
- __attach_adapter);
|
|
|
+ bus_for_each_dev(&i2c_bus_type, NULL, driver, __attach_adapter);
|
|
|
mutex_unlock(&core_lock);
|
|
|
|
|
|
return 0;
|
|
@@ -819,10 +871,14 @@ EXPORT_SYMBOL(i2c_register_driver);
|
|
|
|
|
|
static int __detach_adapter(struct device *dev, void *data)
|
|
|
{
|
|
|
- struct i2c_adapter *adapter = to_i2c_adapter(dev);
|
|
|
+ struct i2c_adapter *adapter;
|
|
|
struct i2c_driver *driver = data;
|
|
|
struct i2c_client *client, *_n;
|
|
|
|
|
|
+ if (dev->type != &i2c_adapter_type)
|
|
|
+ return 0;
|
|
|
+ adapter = to_i2c_adapter(dev);
|
|
|
+
|
|
|
/* Remove the devices we created ourselves as the result of hardware
|
|
|
* probing (using a driver's detect method) */
|
|
|
list_for_each_entry_safe(client, _n, &driver->clients, detected) {
|
|
@@ -850,8 +906,7 @@ static int __detach_adapter(struct device *dev, void *data)
|
|
|
void i2c_del_driver(struct i2c_driver *driver)
|
|
|
{
|
|
|
mutex_lock(&core_lock);
|
|
|
- class_for_each_device(&i2c_adapter_class, NULL, driver,
|
|
|
- __detach_adapter);
|
|
|
+ bus_for_each_dev(&i2c_bus_type, NULL, driver, __detach_adapter);
|
|
|
mutex_unlock(&core_lock);
|
|
|
|
|
|
driver_unregister(&driver->driver);
|
|
@@ -940,17 +995,23 @@ static int __init i2c_init(void)
|
|
|
retval = bus_register(&i2c_bus_type);
|
|
|
if (retval)
|
|
|
return retval;
|
|
|
- retval = class_register(&i2c_adapter_class);
|
|
|
- if (retval)
|
|
|
+#ifdef CONFIG_I2C_COMPAT
|
|
|
+ i2c_adapter_compat_class = class_compat_register("i2c-adapter");
|
|
|
+ if (!i2c_adapter_compat_class) {
|
|
|
+ retval = -ENOMEM;
|
|
|
goto bus_err;
|
|
|
+ }
|
|
|
+#endif
|
|
|
retval = i2c_add_driver(&dummy_driver);
|
|
|
if (retval)
|
|
|
goto class_err;
|
|
|
return 0;
|
|
|
|
|
|
class_err:
|
|
|
- class_unregister(&i2c_adapter_class);
|
|
|
+#ifdef CONFIG_I2C_COMPAT
|
|
|
+ class_compat_unregister(i2c_adapter_compat_class);
|
|
|
bus_err:
|
|
|
+#endif
|
|
|
bus_unregister(&i2c_bus_type);
|
|
|
return retval;
|
|
|
}
|
|
@@ -958,7 +1019,9 @@ bus_err:
|
|
|
static void __exit i2c_exit(void)
|
|
|
{
|
|
|
i2c_del_driver(&dummy_driver);
|
|
|
- class_unregister(&i2c_adapter_class);
|
|
|
+#ifdef CONFIG_I2C_COMPAT
|
|
|
+ class_compat_unregister(i2c_adapter_compat_class);
|
|
|
+#endif
|
|
|
bus_unregister(&i2c_bus_type);
|
|
|
}
|
|
|
|