i2c-isa.c 5.73 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
/*
2 3 4 5
    i2c-isa.c - an i2c-core-like thing for ISA hardware monitoring chips
    Copyright (C) 2005  Jean Delvare <khali@linux-fr.org>

    Based on the i2c-isa pseudo-adapter from the lm_sensors project
Linus Torvalds's avatar
Linus Torvalds committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl> 

    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.
*/

23 24 25 26 27 28 29 30 31 32 33
/* This implements an i2c-core-like thing for ISA hardware monitoring
   chips. Such chips are linked to the i2c subsystem for historical
   reasons (because the early ISA hardware monitoring chips such as the
   LM78 had both an I2C and an ISA interface). They used to be
   registered with the main i2c-core, but as a first step in the
   direction of a clean separation between I2C and ISA chip drivers,
   we now have this separate core for ISA ones. It is significantly
   more simple than the real one, of course, because we don't have to
   handle multiple busses: there is only one (fake) ISA adapter.
   It is worth noting that we still rely on i2c-core for some things
   at the moment - but hopefully this won't last. */
Linus Torvalds's avatar
Linus Torvalds committed
34 35 36 37 38 39

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/i2c.h>
40
#include <linux/i2c-isa.h>
41
#include <linux/platform_device.h>
42
#include <linux/completion.h>
Linus Torvalds's avatar
Linus Torvalds committed
43

44 45 46 47
/* Exported by i2c-core for i2c-isa only */
extern void i2c_adapter_dev_release(struct device *dev);
extern struct class i2c_adapter_class;

Linus Torvalds's avatar
Linus Torvalds committed
48 49 50
static u32 isa_func(struct i2c_adapter *adapter);

/* This is the actual algorithm we define */
51
static const struct i2c_algorithm isa_algorithm = {
Linus Torvalds's avatar
Linus Torvalds committed
52 53 54 55 56 57
	.functionality	= isa_func,
};

/* There can only be one... */
static struct i2c_adapter isa_adapter = {
	.owner		= THIS_MODULE,
58
	.id		= I2C_HW_ISA,
Linus Torvalds's avatar
Linus Torvalds committed
59 60 61 62 63 64 65 66 67 68 69
	.class          = I2C_CLASS_HWMON,
	.algo		= &isa_algorithm,
	.name		= "ISA main adapter",
};

/* We can't do a thing... */
static u32 isa_func(struct i2c_adapter *adapter)
{
	return 0;
}

70 71 72 73 74 75 76 77 78 79 80 81 82 83

/* We implement an interface which resembles i2c_{add,del}_driver,
   but for i2c-isa drivers. We don't have to remember and handle lists
   of drivers and adapters so this is much more simple, of course. */

int i2c_isa_add_driver(struct i2c_driver *driver)
{
	int res;

	/* Add the driver to the list of i2c drivers in the driver core */
	driver->driver.bus = &i2c_bus_type;
	res = driver_register(&driver->driver);
	if (res)
		return res;
84
	dev_dbg(&isa_adapter.dev, "Driver %s registered\n", driver->driver.name);
85 86

	/* Now look for clients */
87 88
	res = driver->attach_adapter(&isa_adapter);
	if (res) {
89
		dev_dbg(&isa_adapter.dev,
90 91 92 93 94
			"Driver %s failed to attach adapter, unregistering\n",
			driver->driver.name);
		driver_unregister(&driver->driver);
	}
	return res;
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
}

int i2c_isa_del_driver(struct i2c_driver *driver)
{
	struct list_head *item, *_n;
	struct i2c_client *client;
	int res;

	/* Detach all clients belonging to this one driver */
	list_for_each_safe(item, _n, &isa_adapter.clients) {
		client = list_entry(item, struct i2c_client, list);
		if (client->driver != driver)
			continue;
		dev_dbg(&isa_adapter.dev, "Detaching client %s at 0x%x\n",
			client->name, client->addr);
		if ((res = driver->detach_client(client))) {
			dev_err(&isa_adapter.dev, "Failed, driver "
				"%s not unregistered!\n",
113
				driver->driver.name);
114 115 116 117 118 119
			return res;
		}
	}

	/* Get the driver off the core list */
	driver_unregister(&driver->driver);
120
	dev_dbg(&isa_adapter.dev, "Driver %s unregistered\n", driver->driver.name);
121 122 123 124 125

	return 0;
}


Linus Torvalds's avatar
Linus Torvalds committed
126 127
static int __init i2c_isa_init(void)
{
128 129
	int err;

130
	mutex_init(&isa_adapter.clist_lock);
131 132 133 134 135 136
	INIT_LIST_HEAD(&isa_adapter.clients);

	isa_adapter.nr = ANY_I2C_ISA_BUS;
	isa_adapter.dev.parent = &platform_bus;
	sprintf(isa_adapter.dev.bus_id, "i2c-%d", isa_adapter.nr);
	isa_adapter.dev.release = &i2c_adapter_dev_release;
137
	isa_adapter.dev.class = &i2c_adapter_class;
138 139 140 141 142
	err = device_register(&isa_adapter.dev);
	if (err) {
		printk(KERN_ERR "i2c-isa: Failed to register device\n");
		goto exit;
	}
143 144 145 146

	dev_dbg(&isa_adapter.dev, "%s registered\n", isa_adapter.name);

	return 0;
147 148 149

exit:
	return err;
Linus Torvalds's avatar
Linus Torvalds committed
150 151 152 153
}

static void __exit i2c_isa_exit(void)
{
154 155 156 157 158 159 160 161 162 163 164
#ifdef DEBUG
	struct list_head  *item, *_n;
	struct i2c_client *client = NULL;
#endif

	/* There should be no more active client */
#ifdef DEBUG
	dev_dbg(&isa_adapter.dev, "Looking for clients\n");
	list_for_each_safe(item, _n, &isa_adapter.clients) {
		client = list_entry(item, struct i2c_client, list);
		dev_err(&isa_adapter.dev, "Driver %s still has an active "
165
			"ISA client at 0x%x\n", client->driver->driver.name,
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
			client->addr);
	}
	if (client != NULL)
		return;
#endif

	/* Clean up the sysfs representation */
	dev_dbg(&isa_adapter.dev, "Unregistering from sysfs\n");
	init_completion(&isa_adapter.dev_released);
	device_unregister(&isa_adapter.dev);

	/* Wait for sysfs to drop all references */
	dev_dbg(&isa_adapter.dev, "Waiting for sysfs completion\n");
	wait_for_completion(&isa_adapter.dev_released);

	dev_dbg(&isa_adapter.dev, "%s unregistered\n", isa_adapter.name);
Linus Torvalds's avatar
Linus Torvalds committed
182 183
}

184 185 186 187
EXPORT_SYMBOL(i2c_isa_add_driver);
EXPORT_SYMBOL(i2c_isa_del_driver);

MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
Linus Torvalds's avatar
Linus Torvalds committed
188 189 190 191 192
MODULE_DESCRIPTION("ISA bus access through i2c");
MODULE_LICENSE("GPL");

module_init(i2c_isa_init);
module_exit(i2c_isa_exit);