📄 i2c-core.c
字号:
/* i2c-core.c - a device driver for the iic-bus interface *//* ------------------------------------------------------------------------- *//* Copyright (C) 1995-99 Simon G. Vogl This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *//* ------------------------------------------------------------------------- *//* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi>. All SMBus-related things are written by Frodo Looijaard <frodol@dds.nl> SMBus 2.0 support by Mark Studebaker <mdsxyz123@yahoo.com> and Jean Delvare <khali@linux-fr.org> */#include <linux/module.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/slab.h>#include <linux/i2c.h>#include <linux/init.h>#include <linux/idr.h>#include <linux/seq_file.h>#include <linux/platform_device.h>#include <linux/mutex.h>#include <linux/completion.h>#include <asm/uaccess.h>#include "i2c-core.h"static LIST_HEAD(adapters);static LIST_HEAD(drivers);static DEFINE_MUTEX(core_lists);static DEFINE_IDR(i2c_adapter_idr);#define is_newstyle_driver(d) ((d)->probe || (d)->remove)/* ------------------------------------------------------------------------- */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); /* make legacy i2c drivers bypass driver model probing entirely; * such drivers scan each i2c adapter/bus themselves. */ if (!is_newstyle_driver(driver)) 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, struct kobj_uevent_env *env){ struct i2c_client *client = to_i2c_client(dev); /* by definition, legacy drivers can't hotplug */ if (dev->driver || !client->driver_name) return 0; if (add_uevent_var(env, "MODALIAS=%s", client->driver_name)) return -ENOMEM; dev_dbg(dev, "uevent\n"); return 0;}#else#define i2c_device_uevent NULL#endif /* CONFIG_HOTPLUG */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); 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){ 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){ 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));}static void i2c_client_release(struct device *dev){ struct i2c_client *client = to_i2c_client(dev); complete(&client->released);}static void i2c_client_dev_release(struct device *dev){ kfree(to_i2c_client(dev));}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), { },};static 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, .suspend = i2c_device_suspend, .resume = i2c_device_resume,};/** * i2c_new_device - instantiate an i2c device for use with a new style driver * @adap: the adapter managing the device * @info: describes one I2C device; bus_num is ignored * Context: can sleep * * Create a device to work with a new style i2c driver, where binding is * handled through driver model probe()/remove() methods. This call is not * appropriate for use by mainboad initialization logic, which usually runs * during an arch_initcall() long before any i2c_adapter could exist. * * This returns the new i2c client, which may be saved for later use with * i2c_unregister_device(); or NULL to indicate an error. */struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info){ struct i2c_client *client; int status; client = kzalloc(sizeof *client, GFP_KERNEL); if (!client) return NULL; client->adapter = adap; client->dev.platform_data = info->platform_data; device_init_wakeup(&client->dev, info->flags & I2C_CLIENT_WAKE); client->flags = info->flags & ~I2C_CLIENT_WAKE; client->addr = info->addr; client->irq = info->irq; strlcpy(client->driver_name, info->driver_name, sizeof(client->driver_name)); strlcpy(client->name, info->type, sizeof(client->name)); /* a new style driver may be bound to this device when we * return from this function, or any later moment (e.g. maybe * hotplugging will load the driver module). and the device * refcount model is the standard driver model one. */ status = i2c_attach_client(client); if (status < 0) { kfree(client); client = NULL; } return client;}EXPORT_SYMBOL_GPL(i2c_new_device);/** * i2c_unregister_device - reverse effect of i2c_new_device() * @client: value returned from i2c_new_device() * Context: can sleep */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);}EXPORT_SYMBOL_GPL(i2c_unregister_device);/* ------------------------------------------------------------------------- *//* I2C bus adapters -- one roots each I2C or SMBUS segment */static void i2c_adapter_dev_release(struct device *dev){ struct i2c_adapter *adap = to_i2c_adapter(dev); complete(&adap->dev_released);}static ssize_tshow_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);}static struct device_attribute i2c_adapter_attrs[] = { __ATTR(name, S_IRUGO, show_adapter_name, NULL), { },};static struct class i2c_adapter_class = { .owner = THIS_MODULE, .name = "i2c-adapter", .dev_attrs = i2c_adapter_attrs,};static void i2c_scan_static_board_info(struct i2c_adapter *adapter){ struct i2c_devinfo *devinfo; mutex_lock(&__i2c_board_lock); list_for_each_entry(devinfo, &__i2c_board_list, list) { if (devinfo->busnum == adapter->nr && !i2c_new_device(adapter, &devinfo->board_info)) printk(KERN_ERR "i2c-core: can't create i2c%d-%04x\n", i2c_adapter_id(adapter), devinfo->board_info.addr); } mutex_unlock(&__i2c_board_lock);}static int i2c_register_adapter(struct i2c_adapter *adap){ int res = 0; struct list_head *item; struct i2c_driver *driver; mutex_init(&adap->bus_lock); mutex_init(&adap->clist_lock); INIT_LIST_HEAD(&adap->clients); mutex_lock(&core_lists); list_add_tail(&adap->list, &adapters); /* Add the adapter to the driver core. * If the parent pointer is not set up, * we add this adapter to the host bus. */ if (adap->dev.parent == NULL) { adap->dev.parent = &platform_bus; pr_debug("I2C adapter driver [%s] forgot to specify " "physical device\n", adap->name); } sprintf(adap->dev.bus_id, "i2c-%d", adap->nr); adap->dev.release = &i2c_adapter_dev_release; adap->dev.class = &i2c_adapter_class; res = device_register(&adap->dev); if (res) goto out_list; dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name); /* create pre-declared device nodes for new-style drivers */ if (adap->nr < __i2c_first_dynamic_bus_num) i2c_scan_static_board_info(adap); /* 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) /* We ignore the return code; if it fails, too bad */ driver->attach_adapter(adap); }out_unlock: mutex_unlock(&core_lists); return res;out_list: list_del(&adap->list); idr_remove(&i2c_adapter_idr, adap->nr); goto out_unlock;}/** * i2c_add_adapter - declare i2c adapter, use dynamic bus number * @adapter: the adapter to add * Context: can sleep * * This routine is used to declare an I2C adapter when its bus number * doesn't matter. Examples: for I2C adapters dynamically added by * USB links or PCI plugin cards. * * When this returns zero, a new bus number was allocated and stored * in adap->nr, and the specified adapter became available for clients. * Otherwise, a negative errno value is returned. */int i2c_add_adapter(struct i2c_adapter *adapter){ int id, res = 0;retry: if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0) return -ENOMEM; mutex_lock(&core_lists); /* "above" here means "above or equal to", sigh */ res = idr_get_new_above(&i2c_adapter_idr, adapter, __i2c_first_dynamic_bus_num, &id); mutex_unlock(&core_lists); if (res < 0) { if (res == -EAGAIN) goto retry; return res; } adapter->nr = id; return i2c_register_adapter(adapter);}EXPORT_SYMBOL(i2c_add_adapter);/** * i2c_add_numbered_adapter - declare i2c adapter, use static bus number * @adap: the adapter to register (with adap->nr initialized) * Context: can sleep * * This routine is used to declare an I2C adapter when its bus number * matters. Example: for I2C adapters from system-on-chip CPUs, or * otherwise built in to the system's mainboard, and where i2c_board_info * is used to properly configure I2C devices. * * If no devices have pre-been declared for this bus, then be sure to * register the adapter before any dynamically allocated ones. Otherwise * the required bus ID may not be available. * * When this returns zero, the specified adapter became available for * clients using the bus number provided in adap->nr. Also, the table * of I2C devices pre-declared using i2c_register_board_info() is scanned, * and the appropriate driver model device nodes are created. Otherwise, a * negative errno value is returned. */int i2c_add_numbered_adapter(struct i2c_adapter *adap){ int id; int status; if (adap->nr & ~MAX_ID_MASK) return -EINVAL;retry: if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0) return -ENOMEM; mutex_lock(&core_lists); /* "above" here means "above or equal to", sigh; * we need the "equal to" result to force the result */ status = idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id); if (status == 0 && id != adap->nr) { status = -EBUSY; idr_remove(&i2c_adapter_idr, id); } mutex_unlock(&core_lists); if (status == -EAGAIN) goto retry; if (status == 0) status = i2c_register_adapter(adap); return status;}EXPORT_SYMBOL_GPL(i2c_add_numbered_adapter);/** * i2c_del_adapter - unregister I2C adapter * @adap: the adapter being unregistered * Context: can sleep * * This unregisters an I2C adapter which was previously registered * by @i2c_add_adapter or @i2c_add_numbered_adapter. */int i2c_del_adapter(struct i2c_adapter *adap){ struct list_head *item, *_n; struct i2c_adapter *adap_from_list; struct i2c_driver *driver; struct i2c_client *client; int res = 0; mutex_lock(&core_lists); /* First make sure that this adapter was ever added */ list_for_each_entry(adap_from_list, &adapters, list) { if (adap_from_list == adap) break; } if (adap_from_list != adap) { pr_debug("i2c-core: attempting to delete unregistered " "adapter [%s]\n", adap->name); res = -EINVAL; goto out_unlock; } list_for_each(item,&drivers) { driver = list_entry(item, struct i2c_driver, list); if (driver->detach_adapter) if ((res = driver->detach_adapter(adap))) { dev_err(&adap->dev, "detach_adapter failed " "for driver [%s]\n", driver->driver.name); goto out_unlock; } } /* detach any active clients. This must be done first, because * it can fail; in which case we give up. */ list_for_each_safe(item, _n, &adap->clients) { struct i2c_driver *driver; 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; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -