Commit 400c455eaa0d0819d18cd42a74070e0e238a73dc

Authored by Jean Delvare
Committed by Greg Kroah-Hartman
1 parent efde723fda

[PATCH] I2C: Separate non-i2c hwmon drivers from i2c-core (2/9)

Convert i2c-isa from a dumb i2c_adapter into a pseudo i2c-core for ISA
hardware monitoring drivers. The isa i2c_adapter is no more registered
with i2c-core, drivers have to explicitely connect to it using the new
i2c_isa_{add,del}_driver interface.

At this point, all ISA chip drivers are useless, because they still
register with i2c-core in the hope i2c-isa is registered there as well,
but it isn't anymore.

The fake bus will be named i2c-9191 in sysfs. This is the number it
already had internally in various places, so it's not exactly new,
except that now the number is seen in userspace as well. This shouldn't
be a problem until someone really has 9192 I2C busses in a given system
;)

The fake bus will no more show in "i2cdetect -l", as it won't be seen by
i2c-dev anymore (not being registered with i2c-core), which is a good
thing, as i2cdetect/i2cdump/i2cset cannot operate on this fake bus
anyway.

Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

Showing 2 changed files with 177 additions and 10 deletions Side-by-side Diff

drivers/i2c/busses/i2c-isa.c
1 1 /*
2   - i2c-isa.c - Part of lm_sensors, Linux kernel modules for hardware
3   - monitoring
  2 + i2c-isa.c - an i2c-core-like thing for ISA hardware monitoring chips
  3 + Copyright (C) 2005 Jean Delvare <khali@linux-fr.org>
  4 +
  5 + Based on the i2c-isa pseudo-adapter from the lm_sensors project
4 6 Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
5 7  
6 8 This program is free software; you can redistribute it and/or modify
7 9  
... ... @@ -18,17 +20,24 @@
18 20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 21 */
20 22  
21   -/* This implements an i2c algorithm/adapter for ISA bus. Not that this is
22   - on first sight very useful; almost no functionality is preserved.
23   - Except that it makes writing drivers for chips which can be on both
24   - the SMBus and the ISA bus very much easier. See lm78.c for an example
25   - of this. */
  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. */
26 34  
27 35 #include <linux/init.h>
28 36 #include <linux/module.h>
29 37 #include <linux/kernel.h>
30 38 #include <linux/errno.h>
31 39 #include <linux/i2c.h>
  40 +#include <linux/i2c-isa.h>
32 41  
33 42 static u32 isa_func(struct i2c_adapter *adapter);
34 43  
35 44  
36 45  
37 46  
... ... @@ -53,17 +62,146 @@
53 62 return 0;
54 63 }
55 64  
  65 +
  66 +/* Copied from i2c-core */
  67 +static ssize_t show_adapter_name(struct device *dev,
  68 + struct device_attribute *attr, char *buf)
  69 +{
  70 + struct i2c_adapter *adap = dev_to_i2c_adapter(dev);
  71 + return sprintf(buf, "%s\n", adap->name);
  72 +}
  73 +static DEVICE_ATTR(name, S_IRUGO, show_adapter_name, NULL);
  74 +
  75 +static int i2c_isa_device_probe(struct device *dev)
  76 +{
  77 + return -ENODEV;
  78 +}
  79 +
  80 +static int i2c_isa_device_remove(struct device *dev)
  81 +{
  82 + return 0;
  83 +}
  84 +
  85 +
  86 +/* We implement an interface which resembles i2c_{add,del}_driver,
  87 + but for i2c-isa drivers. We don't have to remember and handle lists
  88 + of drivers and adapters so this is much more simple, of course. */
  89 +
  90 +int i2c_isa_add_driver(struct i2c_driver *driver)
  91 +{
  92 + int res;
  93 +
  94 + /* Add the driver to the list of i2c drivers in the driver core */
  95 + driver->driver.name = driver->name;
  96 + driver->driver.bus = &i2c_bus_type;
  97 + driver->driver.probe = i2c_isa_device_probe;
  98 + driver->driver.remove = i2c_isa_device_remove;
  99 + res = driver_register(&driver->driver);
  100 + if (res)
  101 + return res;
  102 + dev_dbg(&isa_adapter.dev, "Driver %s registered\n", driver->name);
  103 +
  104 + /* Now look for clients */
  105 + driver->attach_adapter(&isa_adapter);
  106 +
  107 + return 0;
  108 +}
  109 +
  110 +int i2c_isa_del_driver(struct i2c_driver *driver)
  111 +{
  112 + struct list_head *item, *_n;
  113 + struct i2c_client *client;
  114 + int res;
  115 +
  116 + /* Detach all clients belonging to this one driver */
  117 + list_for_each_safe(item, _n, &isa_adapter.clients) {
  118 + client = list_entry(item, struct i2c_client, list);
  119 + if (client->driver != driver)
  120 + continue;
  121 + dev_dbg(&isa_adapter.dev, "Detaching client %s at 0x%x\n",
  122 + client->name, client->addr);
  123 + if ((res = driver->detach_client(client))) {
  124 + dev_err(&isa_adapter.dev, "Failed, driver "
  125 + "%s not unregistered!\n",
  126 + driver->name);
  127 + return res;
  128 + }
  129 + }
  130 +
  131 + /* Get the driver off the core list */
  132 + driver_unregister(&driver->driver);
  133 + dev_dbg(&isa_adapter.dev, "Driver %s unregistered\n", driver->name);
  134 +
  135 + return 0;
  136 +}
  137 +
  138 +
56 139 static int __init i2c_isa_init(void)
57 140 {
58   - return i2c_add_adapter(&isa_adapter);
  141 + init_MUTEX(&isa_adapter.clist_lock);
  142 + INIT_LIST_HEAD(&isa_adapter.clients);
  143 +
  144 + isa_adapter.nr = ANY_I2C_ISA_BUS;
  145 + isa_adapter.dev.parent = &platform_bus;
  146 + sprintf(isa_adapter.dev.bus_id, "i2c-%d", isa_adapter.nr);
  147 + isa_adapter.dev.driver = &i2c_adapter_driver;
  148 + isa_adapter.dev.release = &i2c_adapter_dev_release;
  149 + device_register(&isa_adapter.dev);
  150 + device_create_file(&isa_adapter.dev, &dev_attr_name);
  151 +
  152 + /* Add this adapter to the i2c_adapter class */
  153 + memset(&isa_adapter.class_dev, 0x00, sizeof(struct class_device));
  154 + isa_adapter.class_dev.dev = &isa_adapter.dev;
  155 + isa_adapter.class_dev.class = &i2c_adapter_class;
  156 + strlcpy(isa_adapter.class_dev.class_id, isa_adapter.dev.bus_id,
  157 + BUS_ID_SIZE);
  158 + class_device_register(&isa_adapter.class_dev);
  159 +
  160 + dev_dbg(&isa_adapter.dev, "%s registered\n", isa_adapter.name);
  161 +
  162 + return 0;
59 163 }
60 164  
61 165 static void __exit i2c_isa_exit(void)
62 166 {
63   - i2c_del_adapter(&isa_adapter);
  167 +#ifdef DEBUG
  168 + struct list_head *item, *_n;
  169 + struct i2c_client *client = NULL;
  170 +#endif
  171 +
  172 + /* There should be no more active client */
  173 +#ifdef DEBUG
  174 + dev_dbg(&isa_adapter.dev, "Looking for clients\n");
  175 + list_for_each_safe(item, _n, &isa_adapter.clients) {
  176 + client = list_entry(item, struct i2c_client, list);
  177 + dev_err(&isa_adapter.dev, "Driver %s still has an active "
  178 + "ISA client at 0x%x\n", client->driver->name,
  179 + client->addr);
  180 + }
  181 + if (client != NULL)
  182 + return;
  183 +#endif
  184 +
  185 + /* Clean up the sysfs representation */
  186 + dev_dbg(&isa_adapter.dev, "Unregistering from sysfs\n");
  187 + init_completion(&isa_adapter.dev_released);
  188 + init_completion(&isa_adapter.class_dev_released);
  189 + class_device_unregister(&isa_adapter.class_dev);
  190 + device_remove_file(&isa_adapter.dev, &dev_attr_name);
  191 + device_unregister(&isa_adapter.dev);
  192 +
  193 + /* Wait for sysfs to drop all references */
  194 + dev_dbg(&isa_adapter.dev, "Waiting for sysfs completion\n");
  195 + wait_for_completion(&isa_adapter.dev_released);
  196 + wait_for_completion(&isa_adapter.class_dev_released);
  197 +
  198 + dev_dbg(&isa_adapter.dev, "%s unregistered\n", isa_adapter.name);
64 199 }
65 200  
66   -MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
  201 +EXPORT_SYMBOL(i2c_isa_add_driver);
  202 +EXPORT_SYMBOL(i2c_isa_del_driver);
  203 +
  204 +MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
67 205 MODULE_DESCRIPTION("ISA bus access through i2c");
68 206 MODULE_LICENSE("GPL");
69 207  
include/linux/i2c-isa.h
  1 +/*
  2 + * i2c-isa.h - definitions for the i2c-isa pseudo-i2c-adapter interface
  3 + *
  4 + * Copyright (C) 2005 Jean Delvare <khali@linux-fr.org>
  5 + *
  6 + * This program is free software; you can redistribute it and/or modify
  7 + * it under the terms of the GNU General Public License as published by
  8 + * the Free Software Foundation; either version 2 of the License, or
  9 + * (at your option) any later version.
  10 + *
  11 + * This program is distributed in the hope that it will be useful,
  12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14 + * GNU General Public License for more details.
  15 + *
  16 + * You should have received a copy of the GNU General Public License
  17 + * along with this program; if not, write to the Free Software
  18 + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  19 + */
  20 +
  21 +#ifndef _LINUX_I2C_ISA_H
  22 +#define _LINUX_I2C_ISA_H
  23 +
  24 +#include <linux/i2c.h>
  25 +
  26 +extern int i2c_isa_add_driver(struct i2c_driver *driver);
  27 +extern int i2c_isa_del_driver(struct i2c_driver *driver);
  28 +
  29 +#endif /* _LINUX_I2C_ISA_H */