2     i2c-isa.c - an i2c-core-like thing for ISA hardware monitoring chips
 
   3     Copyright (C) 2005  Jean Delvare <khali@linux-fr.org>
 
   5     Based on the i2c-isa pseudo-adapter from the lm_sensors project
 
   6     Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl> 
 
   8     This program is free software; you can redistribute it and/or modify
 
   9     it under the terms of the GNU General Public License as published by
 
  10     the Free Software Foundation; either version 2 of the License, or
 
  11     (at your option) any later version.
 
  13     This program is distributed in the hope that it will be useful,
 
  14     but WITHOUT ANY WARRANTY; without even the implied warranty of
 
  15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
  16     GNU General Public License for more details.
 
  18     You should have received a copy of the GNU General Public License
 
  19     along with this program; if not, write to the Free Software
 
  20     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
  23 /* This implements an i2c-core-like thing for ISA hardware monitoring
 
  24    chips. Such chips are linked to the i2c subsystem for historical
 
  25    reasons (because the early ISA hardware monitoring chips such as the
 
  26    LM78 had both an I2C and an ISA interface). They used to be
 
  27    registered with the main i2c-core, but as a first step in the
 
  28    direction of a clean separation between I2C and ISA chip drivers,
 
  29    we now have this separate core for ISA ones. It is significantly
 
  30    more simple than the real one, of course, because we don't have to
 
  31    handle multiple busses: there is only one (fake) ISA adapter.
 
  32    It is worth noting that we still rely on i2c-core for some things
 
  33    at the moment - but hopefully this won't last. */
 
  35 #include <linux/init.h>
 
  36 #include <linux/module.h>
 
  37 #include <linux/kernel.h>
 
  38 #include <linux/errno.h>
 
  39 #include <linux/i2c.h>
 
  40 #include <linux/i2c-isa.h>
 
  41 #include <linux/platform_device.h>
 
  42 #include <linux/completion.h>
 
  44 static u32 isa_func(struct i2c_adapter *adapter);
 
  46 /* This is the actual algorithm we define */
 
  47 static const struct i2c_algorithm isa_algorithm = {
 
  48         .functionality  = isa_func,
 
  51 /* There can only be one... */
 
  52 static struct i2c_adapter isa_adapter = {
 
  55         .class          = I2C_CLASS_HWMON,
 
  56         .algo           = &isa_algorithm,
 
  57         .name           = "ISA main adapter",
 
  60 /* We can't do a thing... */
 
  61 static u32 isa_func(struct i2c_adapter *adapter)
 
  67 /* Copied from i2c-core */
 
  68 static ssize_t show_adapter_name(struct device *dev,
 
  69                 struct device_attribute *attr, char *buf)
 
  71         struct i2c_adapter *adap = dev_to_i2c_adapter(dev);
 
  72         return sprintf(buf, "%s\n", adap->name);
 
  74 static DEVICE_ATTR(name, S_IRUGO, show_adapter_name, NULL);
 
  77 /* We implement an interface which resembles i2c_{add,del}_driver,
 
  78    but for i2c-isa drivers. We don't have to remember and handle lists
 
  79    of drivers and adapters so this is much more simple, of course. */
 
  81 int i2c_isa_add_driver(struct i2c_driver *driver)
 
  85         /* Add the driver to the list of i2c drivers in the driver core */
 
  86         driver->driver.bus = &i2c_bus_type;
 
  87         res = driver_register(&driver->driver);
 
  90         dev_dbg(&isa_adapter.dev, "Driver %s registered\n", driver->driver.name);
 
  92         /* Now look for clients */
 
  93         res = driver->attach_adapter(&isa_adapter);
 
  95                 dev_dbg(&isa_adapter.dev,
 
  96                         "Driver %s failed to attach adapter, unregistering\n",
 
  98                 driver_unregister(&driver->driver);
 
 103 int i2c_isa_del_driver(struct i2c_driver *driver)
 
 105         struct list_head *item, *_n;
 
 106         struct i2c_client *client;
 
 109         /* Detach all clients belonging to this one driver */
 
 110         list_for_each_safe(item, _n, &isa_adapter.clients) {
 
 111                 client = list_entry(item, struct i2c_client, list);
 
 112                 if (client->driver != driver)
 
 114                 dev_dbg(&isa_adapter.dev, "Detaching client %s at 0x%x\n",
 
 115                         client->name, client->addr);
 
 116                 if ((res = driver->detach_client(client))) {
 
 117                         dev_err(&isa_adapter.dev, "Failed, driver "
 
 118                                 "%s not unregistered!\n",
 
 119                                 driver->driver.name);
 
 124         /* Get the driver off the core list */
 
 125         driver_unregister(&driver->driver);
 
 126         dev_dbg(&isa_adapter.dev, "Driver %s unregistered\n", driver->driver.name);
 
 132 static int __init i2c_isa_init(void)
 
 136         mutex_init(&isa_adapter.clist_lock);
 
 137         INIT_LIST_HEAD(&isa_adapter.clients);
 
 139         isa_adapter.nr = ANY_I2C_ISA_BUS;
 
 140         isa_adapter.dev.parent = &platform_bus;
 
 141         sprintf(isa_adapter.dev.bus_id, "i2c-%d", isa_adapter.nr);
 
 142         isa_adapter.dev.driver = &i2c_adapter_driver;
 
 143         isa_adapter.dev.release = &i2c_adapter_dev_release;
 
 144         err = device_register(&isa_adapter.dev);
 
 146                 printk(KERN_ERR "i2c-isa: Failed to register device\n");
 
 149         err = device_create_file(&isa_adapter.dev, &dev_attr_name);
 
 151                 printk(KERN_ERR "i2c-isa: Failed to create name file\n");
 
 152                 goto exit_unregister;
 
 155         /* Add this adapter to the i2c_adapter class */
 
 156         memset(&isa_adapter.class_dev, 0x00, sizeof(struct class_device));
 
 157         isa_adapter.class_dev.dev = &isa_adapter.dev;
 
 158         isa_adapter.class_dev.class = &i2c_adapter_class;
 
 159         strlcpy(isa_adapter.class_dev.class_id, isa_adapter.dev.bus_id,
 
 161         err = class_device_register(&isa_adapter.class_dev);
 
 163                 printk(KERN_ERR "i2c-isa: Failed to register class device\n");
 
 164                 goto exit_remove_name;
 
 167         dev_dbg(&isa_adapter.dev, "%s registered\n", isa_adapter.name);
 
 172         device_remove_file(&isa_adapter.dev, &dev_attr_name);
 
 174         init_completion(&isa_adapter.dev_released); /* Needed? */
 
 175         device_unregister(&isa_adapter.dev);
 
 176         wait_for_completion(&isa_adapter.dev_released);
 
 181 static void __exit i2c_isa_exit(void)
 
 184         struct list_head  *item, *_n;
 
 185         struct i2c_client *client = NULL;
 
 188         /* There should be no more active client */
 
 190         dev_dbg(&isa_adapter.dev, "Looking for clients\n");
 
 191         list_for_each_safe(item, _n, &isa_adapter.clients) {
 
 192                 client = list_entry(item, struct i2c_client, list);
 
 193                 dev_err(&isa_adapter.dev, "Driver %s still has an active "
 
 194                         "ISA client at 0x%x\n", client->driver->driver.name,
 
 201         /* Clean up the sysfs representation */
 
 202         dev_dbg(&isa_adapter.dev, "Unregistering from sysfs\n");
 
 203         init_completion(&isa_adapter.dev_released);
 
 204         init_completion(&isa_adapter.class_dev_released);
 
 205         class_device_unregister(&isa_adapter.class_dev);
 
 206         device_remove_file(&isa_adapter.dev, &dev_attr_name);
 
 207         device_unregister(&isa_adapter.dev);
 
 209         /* Wait for sysfs to drop all references */
 
 210         dev_dbg(&isa_adapter.dev, "Waiting for sysfs completion\n");
 
 211         wait_for_completion(&isa_adapter.dev_released);
 
 212         wait_for_completion(&isa_adapter.class_dev_released);
 
 214         dev_dbg(&isa_adapter.dev, "%s unregistered\n", isa_adapter.name);
 
 217 EXPORT_SYMBOL(i2c_isa_add_driver);
 
 218 EXPORT_SYMBOL(i2c_isa_del_driver);
 
 220 MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
 
 221 MODULE_DESCRIPTION("ISA bus access through i2c");
 
 222 MODULE_LICENSE("GPL");
 
 224 module_init(i2c_isa_init);
 
 225 module_exit(i2c_isa_exit);