From: David Brownell Date: Tue, 1 May 2007 21:26:30 +0000 (+0200) Subject: i2c: i2c stack can probe() X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=7b4fbc50fabb810523be522fe7ec5cc40f85c7a1;p=openwrt%2Fstaging%2Fblogic.git i2c: i2c stack can probe() One of a series of I2C infrastructure updates to support enumeration using the standard Linux driver model. This patch updates probe() and associated hotplug/coldplug support, but not remove(). Nothing yet _uses_ it to create I2C devices, so those hotplug/coldplug mechanisms will be the only externally visible change. This patch will be an overall NOP since the I2C stack doesn't yet create clients/devices except as part of binding them to legacy drivers. Some code is moved earlier in the source code, helping group more of the per-device infrastructure in one place and simplifying handling per-device attributes. Terminology being adopted: "legacy drivers" create devices (i2c_client) themselves, while "new style" ones follow the driver model (the i2c_client is handed to the probe routine). It's an either/or thing; the two models don't mix, and drivers that try mixing them won't even be registered. Signed-off-by: David Brownell Signed-off-by: Jean Delvare --- diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index fd921ce0b75b..a2ad83ad0f53 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -44,15 +44,58 @@ 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; + struct i2c_client *client = to_i2c_client(dev); + struct i2c_driver *driver = to_i2c_driver(drv); + + /* make legacy i2c drivers bypass driver model probing entirely; + * such drivers scan each i2c adapter/bus themselves. + */ + if (!driver->probe) + return 0; + + /* new style drivers use the same kind of driver matching policy + * as platform devices or SPI: compare device and driver IDs. + */ + return strcmp(client->driver_name, drv->name) == 0; } +#ifdef CONFIG_HOTPLUG + +/* uevent helps with hotplug: modprobe -q $(MODALIAS) */ +static int i2c_device_uevent(struct device *dev, char **envp, int num_envp, + char *buffer, int buffer_size) +{ + struct i2c_client *client = to_i2c_client(dev); + int i = 0, length = 0; + + /* by definition, legacy drivers can't hotplug */ + if (dev->driver || !client->driver_name) + return 0; + + if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length, + "MODALIAS=%s", client->driver_name)) + return -ENOMEM; + envp[i] = NULL; + dev_dbg(dev, "uevent\n"); + return 0; +} + +#else +#define i2c_device_uevent NULL +#endif /* CONFIG_HOTPLUG */ + static int i2c_device_probe(struct device *dev) { - return -ENODEV; + struct i2c_client *client = to_i2c_client(dev); + struct i2c_driver *driver = to_i2c_driver(dev->driver); + + if (!driver->probe) + return -ENODEV; + client->driver = driver; + dev_dbg(dev, "probe\n"); + return driver->probe(client); } static int i2c_device_remove(struct device *dev) @@ -95,9 +138,38 @@ static int i2c_device_resume(struct device * dev) return driver->resume(to_i2c_client(dev)); } +static void i2c_client_release(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + complete(&client->released); +} + +static ssize_t show_client_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); +} + +static ssize_t show_modalias(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + return client->driver_name + ? sprintf(buf, "%s\n", client->driver_name) + : 0; +} + +static struct device_attribute i2c_dev_attrs[] = { + __ATTR(name, S_IRUGO, show_client_name, NULL), + /* modalias helps coldplug: modprobe $(cat .../modalias) */ + __ATTR(modalias, S_IRUGO, show_modalias, 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, @@ -134,31 +206,6 @@ struct class i2c_adapter_class = { }; -static void i2c_client_release(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - complete(&client->released); -} - -static ssize_t show_client_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); -} - -/* - * We can't use the DEVICE_ATTR() macro here, as we used the same name for - * an i2c adapter attribute (above). - */ -static struct device_attribute dev_attr_client_name = - __ATTR(name, S_IRUGO, &show_client_name, NULL); - - -/* --------------------------------------------------- - * registering functions - * --------------------------------------------------- - */ - /* ----- * i2c_add_adapter is called from within the algorithm layer, * when a new hw adapter registers. A new device is register to be @@ -208,7 +255,7 @@ int i2c_add_adapter(struct i2c_adapter *adap) dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name); - /* inform drivers of new adapters */ + /* let legacy drivers scan this bus for matching devices */ list_for_each(item,&drivers) { driver = list_entry(item, struct i2c_driver, list); if (driver->attach_adapter) @@ -292,16 +339,32 @@ int i2c_del_adapter(struct i2c_adapter *adap) } -/* ----- - * What follows is the "upwards" interface: commands for talking to clients, - * which implement the functions to access the physical information of the - * chips. +/* ------------------------------------------------------------------------- */ + +/* + * An i2c_driver is used with one or more i2c_client (device) nodes to access + * i2c slave chips, on a bus instance associated with some i2c_adapter. There + * are two models for binding the driver to its device: "new style" drivers + * follow the standard Linux driver model and just respond to probe() calls + * issued if the driver core sees they match(); "legacy" drivers create device + * nodes themselves. */ int i2c_register_driver(struct module *owner, struct i2c_driver *driver) { int res; + /* new style driver methods can't mix with legacy ones */ + if (driver->probe) { + if (driver->attach_adapter || driver->detach_adapter + || driver->detach_client) { + printk(KERN_WARNING + "i2c-core: driver [%s] is confused\n", + driver->driver.name); + return -EINVAL; + } + } + /* add the driver to the list of i2c drivers in the driver core */ driver->driver.owner = owner; driver->driver.bus = &i2c_bus_type; @@ -315,7 +378,7 @@ int i2c_register_driver(struct module *owner, struct i2c_driver *driver) list_add_tail(&driver->list,&drivers); pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name); - /* now look for instances of driver on our adapters */ + /* legacy drivers scan i2c busses directly */ if (driver->attach_adapter) { struct i2c_adapter *adapter; @@ -380,6 +443,8 @@ int i2c_del_driver(struct i2c_driver *driver) return 0; } +/* ------------------------------------------------------------------------- */ + static int __i2c_check_addr(struct i2c_adapter *adapter, unsigned int addr) { struct list_head *item; @@ -430,9 +495,6 @@ int i2c_attach_client(struct i2c_client *client) res = device_register(&client->dev); if (res) goto out_list; - res = device_create_file(&client->dev, &dev_attr_client_name); - if (res) - goto out_unregister; mutex_unlock(&adapter->clist_lock); if (adapter->client_register) { @@ -445,10 +507,6 @@ int i2c_attach_client(struct i2c_client *client) return 0; -out_unregister: - init_completion(&client->released); /* Needed? */ - device_unregister(&client->dev); - wait_for_completion(&client->released); out_list: list_del(&client->list); dev_err(&adapter->dev, "Failed to attach i2c client %s at 0x%02x " @@ -483,7 +541,6 @@ int i2c_detach_client(struct i2c_client *client) mutex_lock(&adapter->clist_lock); list_del(&client->list); init_completion(&client->released); - device_remove_file(&client->dev, &dev_attr_client_name); device_unregister(&client->dev); mutex_unlock(&adapter->clist_lock); wait_for_completion(&client->released); diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 563c9651dd37..8dcccc0f4822 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -113,7 +113,7 @@ struct i2c_driver { * can be used by the driver to test if the bus meets its conditions * & seek for the presence of the chip(s) it supports. If found, it * registers the client(s) that are on the bus to the i2c admin. via - * i2c_attach_client. + * i2c_attach_client. (LEGACY I2C DRIVERS ONLY) */ int (*attach_adapter)(struct i2c_adapter *); int (*detach_adapter)(struct i2c_adapter *); @@ -121,10 +121,16 @@ struct i2c_driver { /* tells the driver that a client is about to be deleted & gives it * the chance to remove its private data. Also, if the client struct * has been dynamically allocated by the driver in the function above, - * it must be freed here. + * it must be freed here. (LEGACY I2C DRIVERS ONLY) */ int (*detach_client)(struct i2c_client *); + /* Standard driver model interfaces, for "new style" i2c drivers. + * With the driver model, device enumeration is NEVER done by drivers; + * it's done by infrastructure. (NEW STYLE DRIVERS ONLY) + */ + int (*probe)(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); @@ -148,6 +154,8 @@ struct i2c_driver { * @name: Indicates the type of the device, usually a chip name that's * generic enough to hide second-sourcing and compatible revisions. * @dev: Driver model device node for the slave. + * @driver_name: Identifies new-style driver used with this device; also + * used as the module name for hotplug/coldplug modprobe support. * * An i2c_client identifies a single device (i.e. chip) connected to an * i2c bus. The behaviour is defined by the routines of the driver. @@ -163,6 +171,7 @@ struct i2c_client { int usage_count; /* How many accesses currently */ /* to the client */ struct device dev; /* the device structure */ + char driver_name[KOBJ_NAME_LEN]; struct list_head list; struct completion released; };