1/*
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
6    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
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. */
34
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>
43
44/* Exported by i2c-core for i2c-isa only */
45extern void i2c_adapter_dev_release(struct device *dev);
46extern struct class i2c_adapter_class;
47
48static u32 isa_func(struct i2c_adapter *adapter);
49
50/* This is the actual algorithm we define */
51static const struct i2c_algorithm isa_algorithm = {
52	.functionality	= isa_func,
53};
54
55/* There can only be one... */
56static struct i2c_adapter isa_adapter = {
57	.owner		= THIS_MODULE,
58	.id		= I2C_HW_ISA,
59	.class          = I2C_CLASS_HWMON,
60	.algo		= &isa_algorithm,
61	.name		= "ISA main adapter",
62};
63
64/* We can't do a thing... */
65static u32 isa_func(struct i2c_adapter *adapter)
66{
67	return 0;
68}
69
70
71/* We implement an interface which resembles i2c_{add,del}_driver,
72   but for i2c-isa drivers. We don't have to remember and handle lists
73   of drivers and adapters so this is much more simple, of course. */
74
75int i2c_isa_add_driver(struct i2c_driver *driver)
76{
77	int res;
78
79	/* Add the driver to the list of i2c drivers in the driver core */
80	driver->driver.bus = &i2c_bus_type;
81	res = driver_register(&driver->driver);
82	if (res)
83		return res;
84	dev_dbg(&isa_adapter.dev, "Driver %s registered\n", driver->driver.name);
85
86	/* Now look for clients */
87	res = driver->attach_adapter(&isa_adapter);
88	if (res) {
89		dev_dbg(&isa_adapter.dev,
90			"Driver %s failed to attach adapter, unregistering\n",
91			driver->driver.name);
92		driver_unregister(&driver->driver);
93	}
94	return res;
95}
96
97int i2c_isa_del_driver(struct i2c_driver *driver)
98{
99	struct list_head *item, *_n;
100	struct i2c_client *client;
101	int res;
102
103	/* Detach all clients belonging to this one driver */
104	list_for_each_safe(item, _n, &isa_adapter.clients) {
105		client = list_entry(item, struct i2c_client, list);
106		if (client->driver != driver)
107			continue;
108		dev_dbg(&isa_adapter.dev, "Detaching client %s at 0x%x\n",
109			client->name, client->addr);
110		if ((res = driver->detach_client(client))) {
111			dev_err(&isa_adapter.dev, "Failed, driver "
112				"%s not unregistered!\n",
113				driver->driver.name);
114			return res;
115		}
116	}
117
118	/* Get the driver off the core list */
119	driver_unregister(&driver->driver);
120	dev_dbg(&isa_adapter.dev, "Driver %s unregistered\n", driver->driver.name);
121
122	return 0;
123}
124
125
126static int __init i2c_isa_init(void)
127{
128	int err;
129
130	mutex_init(&isa_adapter.clist_lock);
131	INIT_LIST_HEAD(&isa_adapter.clients);
132
133	isa_adapter.nr = ANY_I2C_ISA_BUS;
134	isa_adapter.dev.parent = &platform_bus;
135	sprintf(isa_adapter.dev.bus_id, "i2c-%d", isa_adapter.nr);
136	isa_adapter.dev.release = &i2c_adapter_dev_release;
137	isa_adapter.dev.class = &i2c_adapter_class;
138	err = device_register(&isa_adapter.dev);
139	if (err) {
140		printk(KERN_ERR "i2c-isa: Failed to register device\n");
141		goto exit;
142	}
143
144	dev_dbg(&isa_adapter.dev, "%s registered\n", isa_adapter.name);
145
146	return 0;
147
148exit:
149	return err;
150}
151
152static void __exit i2c_isa_exit(void)
153{
154#ifdef DEBUG
155	struct list_head  *item, *_n;
156	struct i2c_client *client = NULL;
157#endif
158
159	/* There should be no more active client */
160#ifdef DEBUG
161	dev_dbg(&isa_adapter.dev, "Looking for clients\n");
162	list_for_each_safe(item, _n, &isa_adapter.clients) {
163		client = list_entry(item, struct i2c_client, list);
164		dev_err(&isa_adapter.dev, "Driver %s still has an active "
165			"ISA client at 0x%x\n", client->driver->driver.name,
166			client->addr);
167	}
168	if (client != NULL)
169		return;
170#endif
171
172	/* Clean up the sysfs representation */
173	dev_dbg(&isa_adapter.dev, "Unregistering from sysfs\n");
174	init_completion(&isa_adapter.dev_released);
175	device_unregister(&isa_adapter.dev);
176
177	/* Wait for sysfs to drop all references */
178	dev_dbg(&isa_adapter.dev, "Waiting for sysfs completion\n");
179	wait_for_completion(&isa_adapter.dev_released);
180
181	dev_dbg(&isa_adapter.dev, "%s unregistered\n", isa_adapter.name);
182}
183
184EXPORT_SYMBOL(i2c_isa_add_driver);
185EXPORT_SYMBOL(i2c_isa_del_driver);
186
187MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
188MODULE_DESCRIPTION("ISA bus access through i2c");
189MODULE_LICENSE("GPL");
190
191module_init(i2c_isa_init);
192module_exit(i2c_isa_exit);
193