blob: a60f4801757e2dbf5ac9d8697ce9a2beee0262db [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Jean Delvare400c4552005-07-19 23:48:43 +02002 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
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
7
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.
12
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.
17
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.
21*/
22
Jean Delvare400c4552005-07-19 23:48:43 +020023/* 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. */
Linus Torvalds1da177e2005-04-16 15:20:36 -070034
Linus Torvalds1da177e2005-04-16 15:20:36 -070035#include <linux/init.h>
36#include <linux/module.h>
37#include <linux/kernel.h>
38#include <linux/errno.h>
39#include <linux/i2c.h>
Jean Delvare400c4552005-07-19 23:48:43 +020040#include <linux/i2c-isa.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070041
42static u32 isa_func(struct i2c_adapter *adapter);
43
44/* This is the actual algorithm we define */
45static struct i2c_algorithm isa_algorithm = {
46 .name = "ISA bus algorithm",
47 .id = I2C_ALGO_ISA,
48 .functionality = isa_func,
49};
50
51/* There can only be one... */
52static struct i2c_adapter isa_adapter = {
53 .owner = THIS_MODULE,
54 .class = I2C_CLASS_HWMON,
55 .algo = &isa_algorithm,
56 .name = "ISA main adapter",
57};
58
59/* We can't do a thing... */
60static u32 isa_func(struct i2c_adapter *adapter)
61{
62 return 0;
63}
64
Jean Delvare400c4552005-07-19 23:48:43 +020065
66/* Copied from i2c-core */
67static 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}
73static DEVICE_ATTR(name, S_IRUGO, show_adapter_name, NULL);
74
75static int i2c_isa_device_probe(struct device *dev)
76{
77 return -ENODEV;
78}
79
80static 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
90int 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
110int 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
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139static int __init i2c_isa_init(void)
140{
Jean Delvare400c4552005-07-19 23:48:43 +0200141 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;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163}
164
165static void __exit i2c_isa_exit(void)
166{
Jean Delvare400c4552005-07-19 23:48:43 +0200167#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);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199}
200
Jean Delvare400c4552005-07-19 23:48:43 +0200201EXPORT_SYMBOL(i2c_isa_add_driver);
202EXPORT_SYMBOL(i2c_isa_del_driver);
203
204MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205MODULE_DESCRIPTION("ISA bus access through i2c");
206MODULE_LICENSE("GPL");
207
208module_init(i2c_isa_init);
209module_exit(i2c_isa_exit);