2
0
mirror of https://github.com/lm-sensors/lm-sensors synced 2025-09-05 16:55:45 +00:00
Files
lm-sensors/i2c/i2c-core.c
Frodo Looijaard af7232145d Bug-fixes: lm78.c is now working!
Yes, our first chip driver is now fully operative. Some parts of it can
still be moved to sensors.o and reused by other drivers, but this can be
done later.

Bug fixes:
* i2c-core.c: Corrected previous patch (a client was now unloaded twice,
  which caused problems, as could be expected).
* lm78.c, sensors.c: some minor typos, which caused the crashes
* lm78.c: FAN_DIV only has two fields (fan 3 is not changable)
* sensors.c: ISA/SMBus addresses are prefixed with zeros in directory names


git-svn-id: http://lm-sensors.org/svn/lm-sensors/trunk@20 7894878c-1315-0410-8ee3-d5d059ff63e0
1998-12-02 00:47:37 +00:00

463 lines
11 KiB
C

/* ------------------------------------------------------------------------- */
/* i2c.c - a device driver for the iic-bus interface */
/* ------------------------------------------------------------------------- */
/* Copyright (C) 1995-98 Simon G. Vogl
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. */
/* ------------------------------------------------------------------------- */
#define RCSID "$Id: i2c-core.c,v 1.5 1998/12/01 21:11:32 frodo Exp $"
/* ------------------------------------------------------------------------- */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/malloc.h>
#include "i2c.h"
/* ----- global defines ---------------------------------------------------- */
/* exclusive access to the bus */
/*#define SPINLOCK*/
#ifdef SPINLOCK
#define I2C_LOCK(adap) spin_lock_irqsave(&adap->lock,adap->lockflags)
#define I2C_UNLOCK(adap) spin_unlock_irqrestore(&adap->lock,adap->lockflags)
#else
#define I2C_LOCK(adap) down(&adap->lock)
#define I2C_UNLOCK(adap) up(&adap->lock)
#endif
#define DEB(x) if (i2c_debug>=1) x;
#define DEB2(x) if (i2c_debug>=2) x;
/* ----- global variables -------------------------------------------------- */
/**** algorithm list */
struct i2c_algorithm *algorithms[I2C_ALGO_MAX];
int algo_count;
/**** adapter list */
struct i2c_adapter *adapters[I2C_ADAP_MAX];
int adap_count;
/**** drivers list */
struct i2c_driver *drivers[I2C_DRIVER_MAX];
int driver_count;
/**** debug level */
int i2c_debug=1;
/* ---------------------------------------------------
* registering functions
* ---------------------------------------------------
*/
/* -----
* Algorithms - used to access groups of similar hw adapters or
* specific interfaces like the PCF8584 et al.
*/
int i2c_add_algorithm(struct i2c_algorithm *algo)
{
int i;
for (i = 0; i < I2C_ALGO_MAX; i++)
if (NULL == algorithms[i])
break;
if (I2C_ALGO_MAX == i) {
printk(KERN_WARNING
" i2c: register_algorithm(%s) - enlarge I2C_ALGO_MAX.\n",
algo->name);
return -ENOMEM;
}
algorithms[i] = algo;
algo_count++;
DEB(printk("i2c: algorithm %s registered.\n",algo->name));
return 0;
}
int i2c_del_algorithm(struct i2c_algorithm *algo)
{
int i;
for (i = 0; i < I2C_ALGO_MAX; i++)
if (algo == algorithms[i])
break;
if (I2C_ALGO_MAX == i) {
printk(KERN_WARNING
" i2c: unregister_algorithm: [%s] not found.\n",
algo->name);
return -ENODEV;
}
algorithms[i] = NULL;
algo_count--;
DEB(printk("i2c: algorithm unregistered: %s\n",algo->name));
return 0;
}
/* -----
* i2c_add_adapter is called from within the algorithm layer,
* when a new hw adapter registers. A new device is register to be
* available for clients.
*/
int i2c_add_adapter(struct i2c_adapter *adap)
{
int i;
for (i = 0; i < I2C_ADAP_MAX; i++)
if (NULL == adapters[i])
break;
if (I2C_ADAP_MAX == i) {
printk(KERN_WARNING
" i2c: register_adapter(%s) - enlarge I2C_ADAP_MAX.\n",
adap->name);
return -ENOMEM;
}
adapters[i] = adap;
adap_count++;
/* init data types */
#ifdef SPINLOCK
adap->lock = (spinlock_t)SPIN_LOCK_UNLOCKED;
#else
adap->lock = MUTEX;
#endif
/* inform drivers of new adapters */
for (i=0;i<I2C_DRIVER_MAX;i++)
if (drivers[i]!=NULL && drivers[i]->flags&DF_NOTIFY)
drivers[i]->attach_adapter(adap);
DEB(printk("i2c: adapter %s registered.\n",adap->name));
return 0;
}
int i2c_del_adapter(struct i2c_adapter *adap)
{
int i,j;
for (i = 0; i < I2C_ADAP_MAX; i++)
if (adap == adapters[i])
break;
if (I2C_ADAP_MAX == i) {
printk(KERN_WARNING
" i2c: unregister_adapter adap [%s] not found.\n",
adap->name);
return -ENODEV;
}
/* detach any active clients */
for (j=0;j<I2C_CLIENT_MAX;j++) {
struct i2c_client *client = adap->clients[j];
if ( (client!=NULL)
/* && (client->driver->flags & DF_NOTIFY) */ )
/* detaching devices is unconditional of the set notify
* flag, as _all_ clients that reside on the adapter
* must be deleted, as this would cause invalid states.
*/
client->driver->detach_client(client);
}
/* all done, now unregister */
adapters[i] = NULL;
adap_count--;
DEB(printk("i2c: adapter unregistered: %s\n",adap->name));
return 0;
}
/* -----
* What follows is the "upwards" interface: commands for talking to clients,
* which implement the functions to access the physical information of the
* chips.
*/
int i2c_add_driver(struct i2c_driver *driver)
{
int i;
for (i = 0; i < I2C_DRIVER_MAX; i++)
if (NULL == drivers[i])
break;
if (I2C_DRIVER_MAX == i) {
printk(KERN_WARNING
" i2c: register_driver(%s) - enlarge I2C_DRIVER_MAX.\n",
driver->name);
return -ENOMEM;
}
drivers[i] = driver;
driver_count++;
DEB(printk("i2c: driver %s registered.\n",driver->name));
/* now look for instances of driver on our adapters
*/
if ( driver->flags&DF_NOTIFY )
for (i=0;i<I2C_ADAP_MAX;i++)
if (adapters[i]!=NULL)
driver->attach_adapter(adapters[i]);
return 0;
}
int i2c_del_driver(struct i2c_driver *driver)
{
int i,j,k;
for (i = 0; i < I2C_DRIVER_MAX; i++)
if (driver == drivers[i])
break;
if (I2C_DRIVER_MAX == i) {
printk(KERN_WARNING " i2c: unregister_driver: [%s] not found\n",
driver->name);
return -ENODEV;
}
/* Have a look at each adapter, if clients of this driver are still
* attached. If so, detach them to be able to kill the driver afterwards.
*/
DEB2(printk("i2c: unregister_driver - looking for clients.\n"));
/* removing clients does not depend on the notify flag, else
* invalid operation might (will!) result, when using stale client
* pointers.
*/
for (k=0;k<I2C_ADAP_MAX;k++) {
struct i2c_adapter *adap = adapters[k];
if (adap == NULL) /* skip empty entries. */
continue;
DEB2(printk("i2c: examining adapter %s:\n",adap->name));
for (j=0;j<I2C_CLIENT_MAX;j++) {
struct i2c_client *client = adap->clients[j];
if (client != NULL && client->driver == driver) {
DEB2(printk("i2c: detaching client %s:\n",
client->name));
/* i2c_detach_client(client); */
driver->detach_client(client);
}
}
}
drivers[i] = NULL;
driver_count--;
DEB(printk("i2c: driver unregistered: %s\n",driver->name));
return 0;
}
int i2c_attach_client(struct i2c_client *client)
{
struct i2c_adapter *adapter = client->adapter;
struct i2c_algorithm *algo = adapter->algo;
int i;
for (i = 0; i < I2C_CLIENT_MAX; i++)
if (NULL == adapter->clients[i])
break;
if (I2C_CLIENT_MAX == i) {
printk(KERN_WARNING
" i2c: attach_client(%s) - enlarge I2C_CLIENT_MAX.\n",
client->name);
return -ENOMEM;
}
adapter->clients[i] = client;
adapter->client_count++;
if (algo->client_register != NULL)
algo->client_register(client);
DEB(printk("i2c: client [%s] registered to adapter [%s](pos. %d).\n",
client->name, adapter->name,i));
return 0;
}
int i2c_detach_client(struct i2c_client *client)
{
struct i2c_adapter *adapter = client->adapter;
struct i2c_algorithm *algo = adapter->algo;
int i;
for (i = 0; i < I2C_CLIENT_MAX; i++)
if (client == adapter->clients[i])
break;
if (I2C_CLIENT_MAX == i) {
printk(KERN_WARNING " i2c: unregister_client [%s] not found\n",
client->name);
return -ENODEV;
}
if (algo->client_unregister != NULL)
algo->client_unregister(client);
/* client->driver->detach_client(client);*/
adapter->clients[i] = NULL;
adapter->client_count--;
DEB(printk("i2c: client [%s] unregistered.\n",client->name));
return 0;
}
int i2c_init(void)
{
/* clear algorithms */
memset(algorithms,0,sizeof(algorithms));
memset(adapters,0,sizeof(adapters));
memset(drivers,0,sizeof(drivers));
algo_count=0;
adap_count=0;
driver_count=0;
printk("i2c module initialized.\n");
return 0;
}
/* ----------------------------------------------------
* the functional interface to the i2c busses.
* ----------------------------------------------------
*/
int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg msgs[],int num)
{
int ret;
DEB(printk("master_xfer: %s with %d msgs.\n",adap->name,num));
I2C_LOCK(adap);
ret = adap->algo->master_xfer(adap,msgs,num);
I2C_UNLOCK(adap);
return ret;
}
int i2c_master_send(struct i2c_client *client,const char *buf ,int count)
{
int ret;
struct i2c_adapter *adap=client->adapter;
struct i2c_msg msg;
msg.addr = client->addr;
msg.flags = client->flags & ( I2C_M_TEN|I2C_M_TENMASK );
msg.len = count;
(const char *)msg.buf = buf;
DEB(printk("master_send: writing %d bytes on %s.\n",
count,client->adapter->name));
I2C_LOCK(adap);
ret = adap->algo->master_xfer(adap,&msg,1);
I2C_UNLOCK(adap);
/* if everything went ok (i.e. 1 msg transmitted), return #bytes
* transmitted, else error code.
*/
return (ret == 1 )? count : ret;
}
int i2c_master_recv(struct i2c_client *client, char *buf ,int count)
{
struct i2c_adapter *adap=client->adapter;
struct i2c_msg msg;
int ret;
msg.addr = client->addr;
msg.flags = client->flags & ( I2C_M_TEN|I2C_M_TENMASK );
msg.flags |= I2C_M_RD;
msg.len = count;
msg.buf = buf;
DEB(printk("master_recv: reading %d bytes on %s.\n",
count,client->adapter->name));
I2C_LOCK(adap);
ret = adap->algo->master_xfer(adap,&msg,1);
I2C_UNLOCK(adap);
/* if everything went ok (i.e. 1 msg transmitted), return #bytes
* transmitted, else error code.
*/
return (ret == 1 )? count : ret;
}
int i2c_control(struct i2c_client *client,
unsigned int cmd, unsigned long arg)
{
int ret = 0;
struct i2c_adapter *adap = client->adapter;
DEB2(printk("i2c ioctl, cmd: 0x%x, arg: %#lx\n", cmd, arg));
switch ( cmd ) {
case I2C_RETRIES:
adap->retries = arg;
break;
case I2C_TIMEOUT:
adap->timeout = arg;
break;
default:
if (adap->algo->algo_control!=NULL)
ret = adap->algo->algo_control(adap,cmd,arg);
}
return ret;
}
int i2c_probe(struct i2c_client *client, int low_addr, int hi_addr)
{
int i;
struct i2c_msg msg;
struct i2c_msg *pmsg = &msg;
msg.flags=client->flags & (I2C_M_TENMASK | I2C_M_TEN );
msg.buf = NULL;
msg.len = 0;
I2C_LOCK(client->adapter);
for (i = low_addr; i <= hi_addr; i++) {
client->addr=i;
/* TODO: implement a control statement in the algo layer
* that does address lookup only.
*/
if (1 == client->adapter->
algo->master_xfer(client->adapter,pmsg,1))
break;
}
I2C_UNLOCK(client->adapter);
return (i <= hi_addr) ? i : -1;
}
int i2c_adapter_id(struct i2c_adapter *adap)
{
int i;
for (i = 0; i < I2C_ADAP_MAX; i++)
if (adap == adapters[i])
return i;
return -1;
}
#ifdef MODULE
MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>");
MODULE_DESCRIPTION("I2C-Bus main module");
MODULE_PARM(i2c_debug, "i");
MODULE_PARM_DESC(i2c_debug,"debug level");
int init_module(void)
{
return i2c_init();
}
void cleanup_module(void)
{
}
#endif