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