diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-02-05 06:47:02 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-02-05 06:47:02 +0000 |
commit | 99a7e12f34b3661a0d1354eef83a0eef4df5e34c (patch) | |
tree | 3560aca9ca86792f9ab7bd87861ea143a1b3c7a3 /drivers/i2c | |
parent | e73a04659c0b8cdee4dd40e58630e2cf63afb316 (diff) |
Merge with Linux 2.3.38.
Diffstat (limited to 'drivers/i2c')
-rw-r--r-- | drivers/i2c/.cvsignore | 2 | ||||
-rw-r--r-- | drivers/i2c/Config.in | 29 | ||||
-rw-r--r-- | drivers/i2c/Makefile | 98 | ||||
-rw-r--r-- | drivers/i2c/i2c-algo-bit.c | 655 | ||||
-rw-r--r-- | drivers/i2c/i2c-algo-pcf.c | 609 | ||||
-rw-r--r-- | drivers/i2c/i2c-core.c | 1369 | ||||
-rw-r--r-- | drivers/i2c/i2c-dev.c | 557 | ||||
-rw-r--r-- | drivers/i2c/i2c-elektor.c | 327 | ||||
-rw-r--r-- | drivers/i2c/i2c-elv.c | 236 | ||||
-rw-r--r-- | drivers/i2c/i2c-pcf8584.h | 78 | ||||
-rw-r--r-- | drivers/i2c/i2c-philips-par.c | 232 | ||||
-rw-r--r-- | drivers/i2c/i2c-velleman.c | 219 |
12 files changed, 4411 insertions, 0 deletions
diff --git a/drivers/i2c/.cvsignore b/drivers/i2c/.cvsignore new file mode 100644 index 000000000..857dd22e9 --- /dev/null +++ b/drivers/i2c/.cvsignore @@ -0,0 +1,2 @@ +.depend +.*.flags diff --git a/drivers/i2c/Config.in b/drivers/i2c/Config.in new file mode 100644 index 000000000..ba533aac0 --- /dev/null +++ b/drivers/i2c/Config.in @@ -0,0 +1,29 @@ +# +# Character device configuration +# +mainmenu_option next_comment +comment 'I2C support' + +tristate 'I2C support' CONFIG_I2C + +if [ "$CONFIG_I2C" != "n" ]; then + + dep_tristate 'I2C bit-banging interfaces' CONFIG_I2C_ALGOBIT $CONFIG_I2C + if [ "$CONFIG_I2C_ALGOBIT" != "n" ]; then + dep_tristate ' Philips style parallel port adapter' CONFIG_I2C_PHILIPSPAR $CONFIG_I2C_ALGOBIT + dep_tristate ' ELV adapter' CONFIG_I2C_ELV $CONFIG_I2C_ALGOBIT + dep_tristate ' Velleman K9000 adapter' CONFIG_I2C_VELLEMAN $CONFIG_I2C_ALGOBIT + fi + + dep_tristate 'I2C PCF 8584 interfaces' CONFIG_I2C_ALGOPCF $CONFIG_I2C + if [ "$CONFIG_I2C_ALGOPCF" != "n" ]; then + dep_tristate ' Elektor ISA card' CONFIG_I2C_ELEKTOR $CONFIG_I2C_ALGOPCF + fi + +# This is needed for automatic patch generation: sensors code starts here +# This is needed for automatic patch generation: sensors code ends here + + dep_tristate 'I2C device interface' CONFIG_I2C_CHARDEV $CONFIG_I2C + +fi +endmenu diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile new file mode 100644 index 000000000..7c0abfc24 --- /dev/null +++ b/drivers/i2c/Makefile @@ -0,0 +1,98 @@ +# +# Makefile for the kernel i2c bus driver. +# + +SUB_DIRS := +MOD_SUB_DIRS := $(SUB_DIRS) +ALL_SUB_DIRS := $(SUB_DIRS) +MOD_LIST_NAME := I2C_MODULES + +L_TARGET := i2c.a +MX_OBJS := +M_OBJS := +LX_OBJS := +L_OBJS := + +# ----- +# i2c core components +# ----- + +ifeq ($(CONFIG_I2C),y) + LX_OBJS += i2c-core.o +else + ifeq ($(CONFIG_I2C),m) + MX_OBJS += i2c-core.o + endif +endif + +ifeq ($(CONFIG_I2C_CHARDEV),y) + L_OBJS += i2c-dev.o +else + ifeq ($(CONFIG_I2C_CHARDEV),m) + M_OBJS += i2c-dev.o + endif +endif + +# ----- +# Bit banging adapters... +# ----- + +ifeq ($(CONFIG_I2C_ALGOBIT),y) + LX_OBJS += i2c-algo-bit.o +else + ifeq ($(CONFIG_I2C_ALGOBIT),m) + MX_OBJS += i2c-algo-bit.o + endif +endif + +ifeq ($(CONFIG_I2C_PHILIPSPAR),y) + L_OBJS += i2c-philips-par.o +else + ifeq ($(CONFIG_I2C_PHILIPSPAR),m) + M_OBJS += i2c-philips-par.o + endif +endif + +ifeq ($(CONFIG_I2C_ELV),y) + L_OBJS += i2c-elv.o +else + ifeq ($(CONFIG_I2C_ELV),m) + M_OBJS += i2c-elv.o + endif +endif + +ifeq ($(CONFIG_I2C_VELLEMAN),y) + L_OBJS += i2c-velleman.o +else + ifeq ($(CONFIG_I2C_VELLEMAN),m) + M_OBJS += i2c-velleman.o + endif +endif + + + +# ----- +# PCF components +# ----- + +ifeq ($(CONFIG_I2C_ALGOPCF),y) + LX_OBJS += i2c-algo-pcf.o +else + ifeq ($(CONFIG_I2C_ALGOPCF),m) + MX_OBJS += i2c-algo-pcf.o + endif +endif + +ifeq ($(CONFIG_I2C_ELEKTOR),y) + L_OBJS += i2c-elektor.o +else + ifeq ($(CONFIG_I2C_ELEKTOR),m) + M_OBJS += i2c-elektor.o + endif +endif + +# This is needed for automatic patch generation: sensors code starts here +# This is needed for automatic patch generation: sensors code ends here + +include $(TOPDIR)/Rules.make + diff --git a/drivers/i2c/i2c-algo-bit.c b/drivers/i2c/i2c-algo-bit.c new file mode 100644 index 000000000..32a8514ee --- /dev/null +++ b/drivers/i2c/i2c-algo-bit.c @@ -0,0 +1,655 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-algo-bit.c i2c driver algorithms for bit-shift adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-99 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. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even + Frodo Looijaard <frodol@dds.nl> */ + +/* $Id: i2c-algo-bit.c,v 1.21 1999/12/21 23:45:58 frodo Exp $ */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/malloc.h> +#include <linux/version.h> +#if LINUX_VERSION_CODE >= 0x020135 +#include <linux/init.h> +#else +#define __init +#endif + +#if LINUX_VERSION_CODE >= 0x020100 +# include <asm/uaccess.h> +#else +# include <asm/segment.h> +#endif + +/* 2.0.0 kernel compatibility */ +#if LINUX_VERSION_CODE < 0x020100 +#define MODULE_AUTHOR(noone) +#define MODULE_DESCRIPTION(none) +#define MODULE_PARM(no,param) +#define MODULE_PARM_DESC(no,description) +#define EXPORT_SYMBOL(noexport) +#define EXPORT_NO_SYMBOLS +#endif + +#include <linux/ioport.h> +#include <linux/errno.h> +#include <linux/sched.h> + +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) if (i2c_debug>=1) x; +#define DEB2(x) if (i2c_debug>=2) x; +#define DEBSTAT(x) if (i2c_debug>=3) x; /* print several statistical values*/ +#define DEBPROTO(x) if (i2c_debug>=9) { x; } + /* debug the protocol by showing transferred bits */ + +/* debugging - slow down transfer to have a look at the data .. */ +/* I use this with two leds&resistors, each one connected to sda,scl */ +/* respectively. This makes sure that the algorithm works. Some chips */ +/* might not like this, as they have an internal timeout of some mils */ +/* +#if LINUX_VERSION_CODE >= 0x02016e +#define SLO_IO jif=jiffies;while(jiffies<=jif+i2c_table[minor].veryslow)\ + if (need_resched) schedule(); +#else +#define SLO_IO jif=jiffies;while(jiffies<=jif+i2c_table[minor].veryslow)\ + if (need_resched) schedule(); +#endif +*/ + + +/* ----- global variables --------------------------------------------- */ + +#ifdef SLO_IO + int jif; +#endif + +/* module parameters: + */ +static int i2c_debug=0; +static int bit_test=0; /* see if the line-setting functions work */ +static int bit_scan=0; /* have a look at what's hanging 'round */ + +/* --- setting states on the bus with the right timing: --------------- */ + +#define setsda(adap,val) adap->setsda(adap->data, val) +#define setscl(adap,val) adap->setscl(adap->data, val) +#define getsda(adap) adap->getsda(adap->data) +#define getscl(adap) adap->getscl(adap->data) + +static inline void sdalo(struct i2c_algo_bit_data *adap) +{ + setsda(adap,0); + udelay(adap->udelay); +} + +static inline void sdahi(struct i2c_algo_bit_data *adap) +{ + setsda(adap,1); + udelay(adap->udelay); +} + +static inline void scllo(struct i2c_algo_bit_data *adap) +{ + setscl(adap,0); + udelay(adap->udelay); +#ifdef SLO_IO + SLO_IO +#endif +} + +/* + * Raise scl line, and do checking for delays. This is necessary for slower + * devices. + */ +static inline int sclhi(struct i2c_algo_bit_data *adap) +{ + int start=jiffies; + + setscl(adap,1); + + udelay(adap->udelay); + + /* Not all adapters have scl sense line... */ + if (adap->getscl == NULL ) + return 0; + + while (! getscl(adap) ) { + /* the hw knows how to read the clock line, + * so we wait until it actually gets high. + * This is safer as some chips may hold it low + * while they are processing data internally. + */ + setscl(adap,1); + if (start+adap->timeout <= jiffies) { + return -ETIMEDOUT; + } +#if LINUX_VERSION_CODE >= 0x02016e + if (current->need_resched) + schedule(); +#else + if (need_resched) + schedule(); +#endif + } + DEBSTAT(printk("needed %ld jiffies\n", jiffies-start)); +#ifdef SLO_IO + SLO_IO +#endif + return 0; +} + + +/* --- other auxiliary functions -------------------------------------- */ +static void i2c_start(struct i2c_algo_bit_data *adap) +{ + /* assert: scl, sda are high */ + DEBPROTO(printk("S ")); + sdalo(adap); + scllo(adap); +} + +static void i2c_repstart(struct i2c_algo_bit_data *adap) +{ + /* scl, sda may not be high */ + DEBPROTO(printk(" Sr ")); + setsda(adap,1); + setscl(adap,1); + udelay(adap->udelay); + + sdalo(adap); + scllo(adap); +} + + +static void i2c_stop(struct i2c_algo_bit_data *adap) +{ + DEBPROTO(printk("P\n")); + /* assert: scl is low */ + sdalo(adap); + sclhi(adap); + sdahi(adap); +} + + + +/* send a byte without start cond., look for arbitration, + check ackn. from slave */ +/* returns: + * 1 if the device acknowledged + * 0 if the device did not ack + * -ETIMEDOUT if an error occured (while raising the scl line) + */ +static int i2c_outb(struct i2c_adapter *i2c_adap, char c) +{ + int i; + int sb; + int ack; + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + /* assert: scl is low */ + DEB2(printk(" i2c_outb:%2.2X\n",c&0xff)); + for ( i=7 ; i>=0 ; i-- ) { + sb = c & ( 1 << i ); + setsda(adap,sb); + udelay(adap->udelay); + DEBPROTO(printk("%d",sb!=0)); + if (sclhi(adap)<0) { /* timed out */ + sdahi(adap); /* we don't want to block the net */ + return -ETIMEDOUT; + }; + /* do arbitration here: + * if ( sb && ! getsda(adap) ) -> ouch! Get out of here. + */ + setscl(adap, 0 ); + udelay(adap->udelay); + } + sdahi(adap); + if (sclhi(adap)<0){ /* timeout */ + return -ETIMEDOUT; + }; + /* read ack: SDA should be pulled down by slave */ + ack=getsda(adap); /* ack: sda is pulled low ->success. */ + DEB2(printk(" i2c_outb: getsda() = 0x%2.2x\n", ~ack )); + + DEBPROTO( printk("[%2.2x]",c&0xff) ); + DEBPROTO(if (0==ack){ printk(" A ");} else printk(" NA ") ); + scllo(adap); + return 0==ack; /* return 1 if device acked */ + /* assert: scl is low (sda undef) */ +} + + +static int i2c_inb(struct i2c_adapter *i2c_adap) +{ + /* read byte via i2c port, without start/stop sequence */ + /* acknowledge is sent in i2c_read. */ + int i; + unsigned char indata=0; + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + /* assert: scl is low */ + DEB2(printk("i2c_inb.\n")); + + sdahi(adap); + for (i=0;i<8;i++) { + if (sclhi(adap)<0) { /* timeout */ + return -ETIMEDOUT; + }; + indata *= 2; + if ( getsda(adap) ) + indata |= 0x01; + scllo(adap); + } + /* assert: scl is low */ + DEBPROTO(printk(" %2.2x", indata & 0xff)); + return (int) (indata & 0xff); +} + +/* + * Sanity check for the adapter hardware - check the reaction of + * the bus lines only if it seems to be idle. + */ +static int test_bus(struct i2c_algo_bit_data *adap, char* name) { + int scl,sda; + sda=getsda(adap); + if (adap->getscl==NULL) { + printk("i2c-algo-bit.o: Warning: Adapter can't read from clock line - skipping test.\n"); + return 0; + } + scl=getscl(adap); + printk("i2c-algo-bit.o: Adapter: %s scl: %d sda: %d -- testing...\n", + name,getscl(adap),getsda(adap)); + if (!scl || !sda ) { + printk("i2c-algo-bit.o: %s seems to be busy.\n",name); + goto bailout; + } + sdalo(adap); + printk("i2c-algo-bit.o:1 scl: %d sda: %d \n",getscl(adap),getsda(adap)); + if ( 0 != getsda(adap) ) { + printk("i2c-algo-bit.o: %s SDA stuck high!\n",name); + sdahi(adap); + goto bailout; + } + if ( 0 == getscl(adap) ) { + printk("i2c-algo-bit.o: %s SCL unexpected low while pulling SDA low!\n", + name); + goto bailout; + } + sdahi(adap); + printk("i2c-algo-bit.o:2 scl: %d sda: %d \n",getscl(adap),getsda(adap)); + if ( 0 == getsda(adap) ) { + printk("i2c-algo-bit.o: %s SDA stuck low!\n",name); + sdahi(adap); + goto bailout; + } + if ( 0 == getscl(adap) ) { + printk("i2c-algo-bit.o: %s SCL unexpected low while SDA high!\n",name); + goto bailout; + } + scllo(adap); + printk("i2c-algo-bit.o:3 scl: %d sda: %d \n",getscl(adap),getsda(adap)); + if ( 0 != getscl(adap) ) { + printk("i2c-algo-bit.o: %s SCL stuck high!\n",name); + sclhi(adap); + goto bailout; + } + if ( 0 == getsda(adap) ) { + printk("i2c-algo-bit.o: %s SDA unexpected low while pulling SCL low!\n", + name); + goto bailout; + } + sclhi(adap); + printk("i2c-algo-bit.o:4 scl: %d sda: %d \n",getscl(adap),getsda(adap)); + if ( 0 == getscl(adap) ) { + printk("i2c-algo-bit.o: %s SCL stuck low!\n",name); + sclhi(adap); + goto bailout; + } + if ( 0 == getsda(adap) ) { + printk("i2c-algo-bit.o: %s SDA unexpected low while SCL high!\n", + name); + goto bailout; + } + printk("i2c-algo-bit.o: %s passed test.\n",name); + return 0; +bailout: + sdahi(adap); + sclhi(adap); + return -ENODEV; +} + +/* ----- Utility functions + */ + +/* try_address tries to contact a chip for a number of + * times before it gives up. + * return values: + * 1 chip answered + * 0 chip did not answer + * -x transmission error + */ +static inline int try_address(struct i2c_adapter *i2c_adap, + unsigned char addr, int retries) +{ + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + int i,ret = -1; + for (i=0;i<=retries;i++) { + ret = i2c_outb(i2c_adap,addr); + if (ret==1) + break; /* success! */ + i2c_stop(adap); + udelay(5/*adap->udelay*/); + if (i==retries) /* no success */ + break; + i2c_start(adap); + udelay(adap->udelay); + } + DEB2(if (i) printk("i2c-algo-bit.o: needed %d retries for %d\n",i,addr)); + return ret; +} + +static int sendbytes(struct i2c_adapter *i2c_adap,const char *buf, int count) +{ + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + char c; + const char *temp = buf; + int retval; + int wrcount=0; + + while (count > 0) { + c = *temp; + DEB2(printk("i2c-algo-bit.o: %s i2c_write: writing %2.2X\n", + i2c_adap->name, c&0xff)); + retval = i2c_outb(i2c_adap,c); + if (retval>0) { + count--; + temp++; + wrcount++; + } else { /* arbitration or no acknowledge */ + printk("i2c-algo-bit.o: %s i2c_write: error - bailout.\n", + i2c_adap->name); + i2c_stop(adap); + return (retval<0)? retval : -EFAULT; /* got a better one ?? */ + } +#if 0 + /* from asm/delay.h */ + __delay(adap->mdelay * (loops_per_sec / 1000) ); +#endif + } + return wrcount; +} + +static inline int readbytes(struct i2c_adapter *i2c_adap,char *buf,int count) +{ + char *temp = buf; + int inval; + int rdcount=0; /* counts bytes read */ + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + while (count > 0) { + inval = i2c_inb(i2c_adap); +/*printk("%#02x ",inval); if ( ! (count % 16) ) printk("\n"); */ + if (inval>=0) { + *temp = inval; + rdcount++; + } else { /* read timed out */ + printk("i2c-algo-bit.o: i2c_read: i2c_inb timed out.\n"); + break; + } + + if ( count > 1 ) { /* send ack */ + sdalo(adap); + DEBPROTO(printk(" Am ")); + } else { + sdahi(adap); /* neg. ack on last byte */ + DEBPROTO(printk(" NAm ")); + } + if (sclhi(adap)<0) { /* timeout */ + sdahi(adap); + printk("i2c-algo-bit.o: i2c_read: Timeout at ack\n"); + return -ETIMEDOUT; + }; + scllo(adap); + sdahi(adap); + temp++; + count--; + } + return rdcount; +} + +/* doAddress initiates the transfer by generating the start condition (in + * try_address) and transmits the address in the necessary format to handle + * reads, writes as well as 10bit-addresses. + * returns: + * 0 everything went okay, the chip ack'ed + * -x an error occured (like: -EREMOTEIO if the device did not answer, or + * -ETIMEDOUT, for example if the lines are stuck...) + */ +static inline int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg, + int retries) +{ + unsigned short flags = msg->flags; + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + unsigned char addr; + int ret; + if ( (flags & I2C_M_TEN) ) { + /* a ten bit address */ + addr = 0xf0 | (( msg->addr >> 7) & 0x03); + DEB2(printk("addr0: %d\n",addr)); + /* try extended address code...*/ + ret = try_address(i2c_adap, addr, retries); + if (ret!=1) { + printk("died at extended address code.\n"); + return -EREMOTEIO; + } + /* the remaining 8 bit address */ + ret = i2c_outb(i2c_adap,msg->addr & 0x7f); + if (ret != 1) { + /* the chip did not ack / xmission error occured */ + printk("died at 2nd address code.\n"); + return -EREMOTEIO; + } + if ( flags & I2C_M_RD ) { + i2c_repstart(adap); + /* okay, now switch into reading mode */ + addr |= 0x01; + ret = try_address(i2c_adap, addr, retries); + if (ret!=1) { + printk("died at extended address code.\n"); + return -EREMOTEIO; + } + } + } else { /* normal 7bit address */ + addr = ( msg->addr << 1 ); + if (flags & I2C_M_RD ) + addr |= 1; + ret = try_address(i2c_adap, addr, retries); + if (ret!=1) { + return -EREMOTEIO; + } + } + return 0; +} + +static int bit_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], int num) +{ + struct i2c_msg *pmsg; + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + int i,ret; + + i2c_start(adap); + for (i=0;i<num;i++) { + pmsg = &msgs[i]; + ret = bit_doAddress(i2c_adap,pmsg,i2c_adap->retries); + if (ret != 0) { + DEB2(printk("i2c-algo-bit.o: NAK from device adr %#2x msg #%d\n" + ,msgs[i].addr,i)); + return (ret<0) ? ret : -EREMOTEIO; + } + if (pmsg->flags & I2C_M_RD ) { + /* read bytes into buffer*/ + ret = readbytes(i2c_adap,pmsg->buf,pmsg->len); + DEB2(printk("i2c-algo-bit.o: read %d bytes.\n",ret)); + if (ret < pmsg->len ) { + return (ret<0)? ret : -EREMOTEIO; + } + } else { + /* write bytes from buffer */ + ret = sendbytes(i2c_adap,pmsg->buf,pmsg->len); + DEB2(printk("i2c-algo-bit.o: wrote %d bytes.\n",ret)); + if (ret < pmsg->len ) { + return (ret<0) ? ret : -EREMOTEIO; + } + } + if (i<num-1) { + i2c_repstart(adap); + } + } + i2c_stop(adap); + return num; +} + +static int algo_control(struct i2c_adapter *adapter, + unsigned int cmd, unsigned long arg) +{ + return 0; +} + +static u32 bit_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR; +} + + +/* -----exported algorithm data: ------------------------------------- */ + +static struct i2c_algorithm i2c_bit_algo = { + "Bit-shift algorithm", + I2C_ALGO_BIT, + bit_xfer, + NULL, + NULL, /* slave_xmit */ + NULL, /* slave_recv */ + algo_control, /* ioctl */ + bit_func, /* functionality */ +}; + +/* + * registering functions to load algorithms at runtime + */ +int i2c_bit_add_bus(struct i2c_adapter *adap) +{ + int i; + struct i2c_algo_bit_data *bit_adap = adap->algo_data; + + if (bit_test) { + int ret = test_bus(bit_adap, adap->name); + if (ret<0) + return -ENODEV; + } + + DEB2(printk("i2c-algo-bit.o: hw routines for %s registered.\n",adap->name)); + + /* register new adapter to i2c module... */ + + adap->id |= i2c_bit_algo.id; + adap->algo = &i2c_bit_algo; + + adap->timeout = 100; /* default values, should */ + adap->retries = 3; /* be replaced by defines */ + + /* scan bus */ + if (bit_scan) { + int ack; + printk(KERN_INFO " i2c-algo-bit.o: scanning bus %s.\n", adap->name); + for (i = 0x00; i < 0xff; i+=2) { + i2c_start(bit_adap); + ack = i2c_outb(adap,i); + i2c_stop(bit_adap); + if (ack>0) { + printk("(%02x)",i>>1); + } else + printk("."); + } + printk("\n"); + } + +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif + i2c_add_adapter(adap); + + return 0; +} + + +int i2c_bit_del_bus(struct i2c_adapter *adap) +{ + + i2c_del_adapter(adap); + + DEB2(printk("i2c-algo-bit.o: adapter unregistered: %s\n",adap->name)); + +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + return 0; +} + +int __init i2c_algo_bit_init (void) +{ + printk("i2c-algo-bit.o: i2c bit algorithm module\n"); + return 0; +} + + + +EXPORT_SYMBOL(i2c_bit_add_bus); +EXPORT_SYMBOL(i2c_bit_del_bus); + +#ifdef MODULE +MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>"); +MODULE_DESCRIPTION("I2C-Bus bit-banging algorithm"); + +MODULE_PARM(bit_test, "i"); +MODULE_PARM(bit_scan, "i"); +MODULE_PARM(i2c_debug,"i"); + +MODULE_PARM_DESC(bit_test, "Test the lines of the bus to see if it is stuck"); +MODULE_PARM_DESC(bit_scan, "Scan for active chips on the bus"); +MODULE_PARM_DESC(i2c_debug,"debug level - 0 off; 1 normal; 2,3 more verbose; 9 bit-protocol"); + +int init_module(void) +{ + return i2c_algo_bit_init(); +} + +void cleanup_module(void) +{ +} +#endif diff --git a/drivers/i2c/i2c-algo-pcf.c b/drivers/i2c/i2c-algo-pcf.c new file mode 100644 index 000000000..2edf21f37 --- /dev/null +++ b/drivers/i2c/i2c-algo-pcf.c @@ -0,0 +1,609 @@ + +/* ------------------------------------------------------------------------- */ +/* i2c-algo-pcf.c i2c driver algorithms for PCF8584 adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-97 Simon G. Vogl + 1998-99 Hans Berglund + + 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. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even + Frodo Looijaard <frodol@dds.nl> */ + +/* $Id: i2c-algo-pcf.c,v 1.15 1999/12/21 23:45:58 frodo Exp $ */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/malloc.h> +#include <linux/version.h> +#if LINUX_VERSION_CODE >= 0x020135 +#include <linux/init.h> +#else +#define __init +#endif + +#if LINUX_VERSION_CODE >= 0x020100 +# include <asm/uaccess.h> +#else +# include <asm/segment.h> +#endif + + +#include <linux/ioport.h> +#include <linux/errno.h> +#include <linux/sched.h> + +/* 2.0.0 kernel compatibility */ +#if LINUX_VERSION_CODE < 0x020100 +#define MODULE_AUTHOR(noone) +#define MODULE_DESCRIPTION(none) +#define MODULE_PARM(no,param) +#define MODULE_PARM_DESC(no,description) +#define EXPORT_SYMBOL(noexport) +#define EXPORT_NO_SYMBOLS +#endif + +#include <linux/i2c.h> +#include <linux/i2c-algo-pcf.h> +#include "i2c-pcf8584.h" + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) if (i2c_debug>=1) x +#define DEB2(x) if (i2c_debug>=2) x +#define DEB3(x) if (i2c_debug>=3) x /* print several statistical values*/ +#define DEBPROTO(x) if (i2c_debug>=9) x; + /* debug the protocol by showing transferred bits */ +#define DEF_TIMEOUT 16 + +/* debugging - slow down transfer to have a look at the data .. */ +/* I use this with two leds&resistors, each one connected to sda,scl */ +/* respectively. This makes sure that the algorithm works. Some chips */ +/* might not like this, as they have an internal timeout of some mils */ +/* +#if LINUX_VERSION_CODE >= 0x02016e +#define SLO_IO jif=jiffies;while(jiffies<=jif+i2c_table[minor].veryslow)\ + if (need_resched) schedule(); +#else +#define SLO_IO jif=jiffies;while(jiffies<=jif+i2c_table[minor].veryslow)\ + if (need_resched) schedule(); +#endif +*/ + + +/* ----- global variables --------------------------------------------- */ + +#ifdef SLO_IO + int jif; +#endif + +/* module parameters: + */ +static int i2c_debug=1; +static int pcf_test=0; /* see if the line-setting functions work */ +static int pcf_scan=0; /* have a look at what's hanging 'round */ + +/* --- setting states on the bus with the right timing: --------------- */ + +#define set_pcf(adap, ctl, val) adap->setpcf(adap->data, ctl, val) +#define get_pcf(adap, ctl) adap->getpcf(adap->data, ctl) +#define get_own(adap) adap->getown(adap->data) +#define get_clock(adap) adap->getclock(adap->data) +#define i2c_outb(adap, val) adap->setpcf(adap->data, 0, val) +#define i2c_inb(adap) adap->getpcf(adap->data, 0) + + +/* --- other auxiliary functions -------------------------------------- */ + +#if LINUX_VERSION_CODE < 0x02017f +static void schedule_timeout(int j) +{ + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + j; + schedule(); +} +#endif + + +static void i2c_start(struct i2c_algo_pcf_data *adap) +{ + DEBPROTO(printk("S ")); + set_pcf(adap, 1, I2C_PCF_START); +} + +static void i2c_repstart(struct i2c_algo_pcf_data *adap) +{ + DEBPROTO(printk(" Sr ")); + set_pcf(adap, 1, I2C_PCF_REPSTART); +} + + +static void i2c_stop(struct i2c_algo_pcf_data *adap) +{ + DEBPROTO(printk("P\n")); + set_pcf(adap, 1, I2C_PCF_STOP); +} + + +static int wait_for_bb(struct i2c_algo_pcf_data *adap) { + + int timeout = DEF_TIMEOUT; + int status; + + status = get_pcf(adap, 1); + while (timeout-- && !(status & I2C_PCF_BB)) { + udelay(1000); /* How much is this? */ + status = get_pcf(adap, 1); + } + if (timeout<=0) + printk("Timeout waiting for Bus Busy\n"); + /* + set_pcf(adap, 1, I2C_PCF_STOP); + */ + return(timeout<=0); +} + + +static inline void pcf_sleep(unsigned long timeout) +{ + schedule_timeout( timeout * HZ); +} + + +static int wait_for_pin(struct i2c_algo_pcf_data *adap, int *status) { + + int timeout = DEF_TIMEOUT; + + *status = get_pcf(adap, 1); + while (timeout-- && (*status & I2C_PCF_PIN)) { + adap->waitforpin(); + *status = get_pcf(adap, 1); + } + if (timeout <= 0) + return(-1); + else + return(0); +} + + +/* + * This should perform the 'PCF8584 initialization sequence' as described + * in the Philips IC12 data book (1995, Aug 29). + * There should be a 30 clock cycle wait after reset, I assume this + * has been fulfilled. + * There should be a delay at the end equal to the longest I2C message + * to synchronize the BB-bit (in multimaster systems). How long is + * this? I assume 1 second is always long enough. + */ +static int pcf_init_8584 (struct i2c_algo_pcf_data *adap) +{ + + /* S1=0x80: S0 selected, serial interface off */ + set_pcf(adap, 1, I2C_PCF_PIN); + + /* load own address in S0, effective address is (own << 1) */ + i2c_outb(adap, get_own(adap)); + + /* S1=0xA0, next byte in S2 */ + set_pcf(adap, 1, I2C_PCF_PIN | I2C_PCF_ES1); + + /* load clock register S2 */ + i2c_outb(adap, get_clock(adap)); + + /* Enable serial interface, idle, S0 selected */ + set_pcf(adap, 1, I2C_PCF_IDLE); + + DEB2(printk("i2c-algo-pcf.o: irq: Initialized 8584.\n")); + return 0; +} + + +/* + * Sanity check for the adapter hardware - check the reaction of + * the bus lines only if it seems to be idle. + */ +static int test_bus(struct i2c_algo_pcf_data *adap, char *name) { +#if 0 + int scl,sda; + sda=getsda(adap); + if (adap->getscl==NULL) { + printk("i2c-algo-pcf.o: Warning: Adapter can't read from clock line - skipping test.\n"); + return 0; + } + scl=getscl(adap); + printk("i2c-algo-pcf.o: Adapter: %s scl: %d sda: %d -- testing...\n", + name,getscl(adap),getsda(adap)); + if (!scl || !sda ) { + printk("i2c-algo-pcf.o: %s seems to be busy.\n",adap->name); + goto bailout; + } + sdalo(adap); + printk("i2c-algo-pcf.o:1 scl: %d sda: %d \n",getscl(adap),getsda(adap)); + if ( 0 != getsda(adap) ) { + printk("i2c-algo-pcf.o: %s SDA stuck high!\n",name); + sdahi(adap); + goto bailout; + } + if ( 0 == getscl(adap) ) { + printk("i2c-algo-pcf.o: %s SCL unexpected low while pulling SDA low!\n", + name); + goto bailout; + } + sdahi(adap); + printk("i2c-algo-pcf.o:2 scl: %d sda: %d \n",getscl(adap),getsda(adap)); + if ( 0 == getsda(adap) ) { + printk("i2c-algo-pcf.o: %s SDA stuck low!\n",name); + sdahi(adap); + goto bailout; + } + if ( 0 == getscl(adap) ) { + printk("i2c-algo-pcf.o: %s SCL unexpected low while SDA high!\n",adap->name); + goto bailout; + } + scllo(adap); + printk("i2c-algo-pcf.o:3 scl: %d sda: %d \n",getscl(adap),getsda(adap)); + if ( 0 != getscl(adap) ) { + printk("i2c-algo-pcf.o: %s SCL stuck high!\n",name); + sclhi(adap); + goto bailout; + } + if ( 0 == getsda(adap) ) { + printk("i2c-algo-pcf.o: %s SDA unexpected low while pulling SCL low!\n", + name); + goto bailout; + } + sclhi(adap); + printk("i2c-algo-pcf.o:4 scl: %d sda: %d \n",getscl(adap),getsda(adap)); + if ( 0 == getscl(adap) ) { + printk("i2c-algo-pcf.o: %s SCL stuck low!\n",name); + sclhi(adap); + goto bailout; + } + if ( 0 == getsda(adap) ) { + printk("i2c-algo-pcf.o: %s SDA unexpected low while SCL high!\n", + name); + goto bailout; + } + printk("i2c-algo-pcf.o: %s passed test.\n",name); + return 0; +bailout: + sdahi(adap); + sclhi(adap); + return -ENODEV; +#endif + return (0); +} + +/* ----- Utility functions + */ + +static inline int try_address(struct i2c_algo_pcf_data *adap, + unsigned char addr, int retries) +{ + int i, status, ret = -1; + for (i=0;i<retries;i++) { + i2c_outb(adap, addr); + i2c_start(adap); + status = get_pcf(adap, 1); + if (wait_for_pin(adap, &status) >= 0) { + if ((status && I2C_PCF_LRB) == 0) { + i2c_stop(adap); + break; /* success! */ + } + } + i2c_stop(adap); + udelay(adap->udelay); + } + DEB2(if (i) printk("i2c-algo-pcf.o: needed %d retries for %d\n",i,addr)); + return ret; +} + + +static int pcf_sendbytes(struct i2c_adapter *i2c_adap,const char *buf, int count) +{ + struct i2c_algo_pcf_data *adap = i2c_adap->algo_data; + int wrcount, status, timeout; + + for (wrcount=0; wrcount<count; ++wrcount) { + DEB2(printk("i2c-algo-pcf.o: %s i2c_write: writing %2.2X\n", + i2c_adap->name, buf[wrcount]&0xff)); + i2c_outb(adap, buf[wrcount]); + timeout = wait_for_pin(adap, &status); + if (timeout) { + printk("i2c-algo-pcf.o: %s i2c_write: error - timeout.\n", + i2c_adap->name); + i2c_stop(adap); + return -EREMOTEIO; /* got a better one ?? */ + } + if (status & I2C_PCF_LRB) { + printk("i2c-algo-pcf.o: %s i2c_write: error - no ack.\n", + i2c_adap->name); + i2c_stop(adap); + return -EREMOTEIO; /* got a better one ?? */ + } + } + return (wrcount); +} + + +static int pcf_readbytes(struct i2c_adapter *i2c_adap, char *buf, int count) +{ + int rdcount=0, i, status, timeout, dummy=1; + struct i2c_algo_pcf_data *adap = i2c_adap->algo_data; + + for (i=0; i<count-1; ++i) { + buf[rdcount] = i2c_inb(adap); + if (dummy) { + dummy = 0; + } + else + rdcount++; + timeout = wait_for_pin(adap, &status); + if (timeout) { + printk("i2c-algo-pcf.o: i2c_read: i2c_inb timed out.\n"); + return (-1); + } + if (status & I2C_PCF_LRB) { + printk("i2c-algo-pcf.o: i2c_read: i2c_inb, No ack.\n"); + return (-1); + } + } + set_pcf(adap, 1, I2C_PCF_ESO); + buf[rdcount] = i2c_inb(adap); + if (dummy) { + dummy = 0; + } + else + rdcount++; + timeout = wait_for_pin(adap, &status); + if (timeout) { + printk("i2c-algo-pcf.o: i2c_read: i2c_inb timed out.\n"); + return (-1); + } + return (rdcount); +} + + +static inline int pcf_doAddress(struct i2c_algo_pcf_data *adap, struct i2c_msg *msg, + int retries) +{ + unsigned short flags = msg->flags; + unsigned char addr; + int ret; + if ( (flags & I2C_M_TEN) ) { + /* a ten bit address */ + addr = 0xf0 | (( msg->addr >> 7) & 0x03); + DEB2(printk("addr0: %d\n",addr)); + /* try extended address code...*/ + ret = try_address(adap, addr, retries); + if (ret!=1) { + printk("died at extended address code.\n"); + return -EREMOTEIO; + } + /* the remaining 8 bit address */ + i2c_outb(adap,msg->addr & 0x7f); +/* Status check comes here */ + if (ret != 1) { + printk("died at 2nd address code.\n"); + return -EREMOTEIO; + } + if ( flags & I2C_M_RD ) { + i2c_repstart(adap); + /* okay, now switch into reading mode */ + addr |= 0x01; + ret = try_address(adap, addr, retries); + if (ret!=1) { + printk("died at extended address code.\n"); + return -EREMOTEIO; + } + } + } else { /* normal 7bit address */ + addr = ( msg->addr << 1 ); + if (flags & I2C_M_RD ) + addr |= 1; + i2c_outb(adap, addr); + } + return 0; +} + +static int pcf_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], + int num) +{ + struct i2c_algo_pcf_data *adap = i2c_adap->algo_data; + struct i2c_msg *pmsg; + int i, ret, timeout, status; + + timeout = wait_for_bb(adap); + if (timeout) { + DEB2(printk("i2c-algo-pcf.o: Timeout waiting for BB in pcf_xfer\n");) + return -EIO; + } + pmsg = &msgs[0]; + ret = pcf_doAddress(adap, pmsg, i2c_adap->retries); + i2c_start(adap); + + for (i=0; i<num; i++) { + DEB3(printk("i2c-algo-pcf.o: Msg %d, addr=0x%x, flags=0x%x, len=%d\n", + i, msgs[i].addr, msgs[i].flags, msgs[i].len);) + timeout = wait_for_pin(adap, &status); + if (timeout) { + DEB2(printk("i2c-algo-pcf.o: Timeout waiting for PIN(1) in pcf_xfer\n");) + i2c_stop(adap); + return (-EREMOTEIO); + } + if (status & I2C_PCF_LRB) { + i2c_stop(adap); + DEB2(printk("i2c-algo-pcf.o: NAK from device adr %#2x msg #%d\n" + ,msgs[i].addr,i)); + return -EREMOTEIO; + } + if (pmsg->flags & I2C_M_RD ) { + /* read bytes into buffer*/ + ret = pcf_readbytes(i2c_adap, pmsg->buf, pmsg->len); + DEB2(printk("i2c-algo-pcf.o: read %d bytes.\n",ret)); + } else { + /* write bytes from buffer */ + ret = pcf_sendbytes(i2c_adap, pmsg->buf, pmsg->len); + DEB2(printk("i2c-algo-pcf.o: wrote %d bytes.\n",ret)); + } + if (i == (num-1)) { + i2c_stop(adap); + } + else { + i2c_repstart(adap); + } + if (pmsg->flags & I2C_M_RD ) { + pmsg->buf[pmsg->len-1] = i2c_inb(adap); + } + if (i != (num-1)) { + pmsg = &msgs[0]; + ret = pcf_doAddress(adap, pmsg, i2c_adap->retries); + timeout = wait_for_pin(adap, &status); + if (timeout) { + DEB2(printk("i2c-algo-pcf.o: Timeout waiting for PIN(2) in pcf_xfer\n");) + return (-EREMOTEIO); + } + if (status & I2C_PCF_LRB) { + i2c_stop(adap); + DEB2(printk("i2c-algo-pcf.o: No LRB(2) in pcf_xfer\n");) + return (-EREMOTEIO); + } + } + } + return (num); +} + + +static int algo_control(struct i2c_adapter *adapter, + unsigned int cmd, unsigned long arg) +{ + return 0; +} + +static u32 pcf_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR; +} + +/* -----exported algorithm data: ------------------------------------- */ + +static struct i2c_algorithm pcf_algo = { + "PCF8584 algorithm", + I2C_ALGO_PCF, + pcf_xfer, + NULL, + NULL, /* slave_xmit */ + NULL, /* slave_recv */ + algo_control, /* ioctl */ + pcf_func, /* functionality */ +}; + +/* + * registering functions to load algorithms at runtime + */ +int i2c_pcf_add_bus(struct i2c_adapter *adap) +{ + int i, status; + struct i2c_algo_pcf_data *pcf_adap = adap->algo_data; + + if (pcf_test) { + int ret = test_bus(pcf_adap, adap->name); + if (ret<0) + return -ENODEV; + } + + DEB2(printk("i2c-algo-pcf.o: hw routines for %s registered.\n",adap->name)); + + /* register new adapter to i2c module... */ + + adap->id |= pcf_algo.id; + adap->algo = &pcf_algo; + + adap->timeout = 100; /* default values, should */ + adap->retries = 3; /* be replaced by defines */ + +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif + + i2c_add_adapter(adap); + pcf_init_8584(pcf_adap); + + /* scan bus */ + if (pcf_scan) { + printk(KERN_INFO " i2c-algo-pcf.o: scanning bus %s.\n", adap->name); + for (i = 0x00; i < 0xff; i+=2) { + i2c_outb(pcf_adap, i); + i2c_start(pcf_adap); + if ((wait_for_pin(pcf_adap, &status) >= 0) && + ((status && I2C_PCF_LRB) == 0)) { + printk("(%02x)",i>>1); + } else { + printk("."); + } + i2c_stop(pcf_adap); + udelay(pcf_adap->udelay); + } + printk("\n"); + } + return 0; +} + + +int i2c_pcf_del_bus(struct i2c_adapter *adap) +{ + i2c_del_adapter(adap); + DEB2(printk("i2c-algo-pcf.o: adapter unregistered: %s\n",adap->name)); + +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + return 0; +} + +int __init i2c_algo_pcf_init (void) +{ + printk("i2c-algo-pcf.o: i2c pcf8584 algorithm module\n"); + return 0; +} + + +EXPORT_SYMBOL(i2c_pcf_add_bus); +EXPORT_SYMBOL(i2c_pcf_del_bus); + +#ifdef MODULE +MODULE_AUTHOR("Hans Berglund <hb@spacetec.no>"); +MODULE_DESCRIPTION("I2C-Bus PCF8584 algorithm"); + +MODULE_PARM(pcf_test, "i"); +MODULE_PARM(pcf_scan, "i"); +MODULE_PARM(i2c_debug,"i"); + +MODULE_PARM_DESC(pcf_test, "Test if the I2C bus is available"); +MODULE_PARM_DESC(pcf_scan, "Scan for active chips on the bus"); +MODULE_PARM_DESC(i2c_debug,"debug level - 0 off; 1 normal; 2,3 more verbose; 9 pcf-protocol"); + + +int init_module(void) +{ + return i2c_algo_pcf_init(); +} + +void cleanup_module(void) +{ +} +#endif diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c new file mode 100644 index 000000000..3d88b6c39 --- /dev/null +++ b/drivers/i2c/i2c-core.c @@ -0,0 +1,1369 @@ +/* i2c-core.c - a device driver for the iic-bus interface */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-99 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. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi>. + All SMBus-related things are written by Frodo Looijaard <frodol@dds.nl> */ + +/* $Id: i2c-core.c,v 1.44 1999/12/21 23:45:58 frodo Exp $ */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/malloc.h> +#include <linux/proc_fs.h> +#include <linux/config.h> + +#include <linux/i2c.h> + +/* ----- compatibility stuff ----------------------------------------------- */ + +/* 2.0.0 kernel compatibility */ +#if LINUX_VERSION_CODE < 0x020100 +#define MODULE_AUTHOR(noone) +#define MODULE_DESCRIPTION(none) +#define MODULE_PARM(no,param) +#define MODULE_PARM_DESC(no,description) +#define EXPORT_SYMBOL(noexport) +#define EXPORT_NO_SYMBOLS +#endif + +#include <linux/version.h> +#ifndef KERNEL_VERSION +#define KERNEL_VERSION(a,b,c) (((a) << 16) | ((b) << 8) | (c)) +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,53) +#include <linux/init.h> +#else +#define __init +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,4)) +#define copy_from_user memcpy_fromfs +#define copy_to_user memcpy_tofs +#else +#include <asm/uaccess.h> +#endif + +/* ----- global defines ---------------------------------------------------- */ + +/* exclusive access to the bus */ +#define I2C_LOCK(adap) down(&adap->lock) +#define I2C_UNLOCK(adap) up(&adap->lock) + +#define ADAP_LOCK() down(&adap_lock) +#define ADAP_UNLOCK() up(&adap_lock) + +#define DRV_LOCK() down(&driver_lock) +#define DRV_UNLOCK() up(&driver_lock) + +#define DEB(x) if (i2c_debug>=1) x; +#define DEB2(x) if (i2c_debug>=2) x; + +/* ----- global variables -------------------------------------------------- */ + +/**** lock for writing to global variables: the adapter & driver list */ +struct semaphore adap_lock; +struct semaphore driver_lock; + +/**** adapter list */ +static struct i2c_adapter *adapters[I2C_ADAP_MAX]; +static int adap_count; + +/**** drivers list */ +static struct i2c_driver *drivers[I2C_DRIVER_MAX]; +static int driver_count; + +/**** debug level */ +static int i2c_debug=1; +static void i2c_dummy_adapter(struct i2c_adapter *adapter); +static void i2c_dummy_client(struct i2c_client *client); + +/* --------------------------------------------------- + * /proc entry declarations + *---------------------------------------------------- + */ + +/* Note that quite some things changed within the 2.1 kernel series. + Some things below are somewhat difficult to read because of this. */ + +#ifdef CONFIG_PROC_FS + +static int i2cproc_init(void); +static int i2cproc_cleanup(void); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) && \ + (LINUX_VERSION_CODE <= KERNEL_VERSION(2,3,27)) +static void monitor_bus_i2c(struct inode *inode, int fill); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) + +static ssize_t i2cproc_bus_read(struct file * file, char * buf,size_t count, + loff_t *ppos); +static int read_bus_i2c(char *buf, char **start, off_t offset, int len, + int *eof , void *private); + +#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */ + +static int i2cproc_bus_read(struct inode * inode, struct file * file, + char * buf, int count); +static int read_bus_i2c(char *buf, char **start, off_t offset, int len, + int unused); + +static struct proc_dir_entry proc_bus_dir = + { + /* low_ino */ 0, /* Set by proc_register_dynamic */ + /* namelen */ 3, + /* name */ "bus", + /* mode */ S_IRUGO | S_IXUGO | S_IFDIR, + /* nlink */ 2, /* Corrected by proc_register[_dynamic] */ + /* uid */ 0, + /* gid */ 0, + /* size */ 0, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,0,36)) + /* ops */ &proc_dir_inode_operations, +#endif + }; + +static struct proc_dir_entry proc_bus_i2c_dir = + { + /* low_ino */ 0, /* Set by proc_register_dynamic */ + /* namelen */ 3, + /* name */ "i2c", + /* mode */ S_IRUGO | S_IFREG, + /* nlink */ 1, + /* uid */ 0, + /* gid */ 0, + /* size */ 0, + /* ops */ NULL, + /* get_info */ &read_bus_i2c + }; + +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ + +/* To implement the dynamic /proc/bus/i2c-? files, we need our own + implementation of the read hook */ +static struct file_operations i2cproc_operations = { + NULL, + i2cproc_bus_read, +}; + +static struct inode_operations i2cproc_inode_operations = { + &i2cproc_operations +}; + +static int i2cproc_initialized = 0; + +#else /* undef CONFIG_PROC_FS */ + +#define i2cproc_init() +#define i2cproc_cleanup() + +#endif /* CONFIG_PROC_FS */ + + +/* --------------------------------------------------- + * registering functions + * --------------------------------------------------- + */ + +/* ----- + * 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,j; + + ADAP_LOCK(); + for (i = 0; i < I2C_ADAP_MAX; i++) + if (NULL == adapters[i]) + break; + if (I2C_ADAP_MAX == i) { + printk(KERN_WARNING + " i2c-core.o: register_adapter(%s) - enlarge I2C_ADAP_MAX.\n", + adap->name); + ADAP_UNLOCK(); + return -ENOMEM; + } + + adapters[i] = adap; + adap_count++; + ADAP_UNLOCK(); + + /* init data types */ + init_MUTEX(&adap->lock); + + i2c_dummy_adapter(adap); /* actually i2c_dummy->add_adapter */ +#ifdef CONFIG_PROC_FS + + if (i2cproc_initialized) { + char name[8]; + struct proc_dir_entry *proc_entry; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) + int res; +#endif + + sprintf(name,"i2c-%d", i); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) + proc_entry = create_proc_entry(name,0,proc_bus); + if (! proc_entry) { + printk("i2c-core.o: Could not create /proc/bus/%s\n", + name); + return -ENOENT; + } + proc_entry->ops = &i2cproc_inode_operations; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,27)) + proc_entry->owner = THIS_MODULE; +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) + proc_entry->fill_inode = &monitor_bus_i2c; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */ +#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */ + adap->proc_entry = NULL; + if (!(proc_entry = kmalloc(sizeof(struct proc_dir_entry)+ + strlen(name)+1, GFP_KERNEL))) { + printk("i2c-core.o: Out of memory!\n"); + return -ENOMEM; + } + memset(proc_entry,0,sizeof(struct proc_dir_entry)); + proc_entry->namelen = strlen(name); + proc_entry->name = (char *) (proc_entry + 1); + proc_entry->mode = S_IRUGO | S_IFREG; + proc_entry->nlink = 1; + proc_entry->ops = &i2cproc_inode_operations; + + /* Nasty stuff to keep GCC satisfied */ + { + char *procname; + (const char *) procname = proc_entry->name; + strcpy (procname,name); + } + + if ((res = proc_register_dynamic(&proc_bus_dir, proc_entry))) { + printk("i2c-core.o: Could not create %s.\n",name); + kfree(proc_entry); + return res; + } + + adap->proc_entry = proc_entry; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ + + adap->inode = proc_entry->low_ino; + } + +#endif /* def CONFIG_PROC_FS */ + + /* inform drivers of new adapters */ + DRV_LOCK(); + for (j=0;j<I2C_DRIVER_MAX;j++) + if (drivers[j]!=NULL && drivers[j]->flags&I2C_DF_NOTIFY) + drivers[j]->attach_adapter(adap); + DRV_UNLOCK(); + + DEB(printk("i2c-core.o: adapter %s registered as adapter %d.\n",adap->name,i)); + + return 0; +} + + +int i2c_del_adapter(struct i2c_adapter *adap) +{ + int i,j; + ADAP_LOCK(); + for (i = 0; i < I2C_ADAP_MAX; i++) + if (adap == adapters[i]) + break; + if (I2C_ADAP_MAX == i) { + printk( " i2c-core.o: unregister_adapter adap [%s] not found.\n", + adap->name); + ADAP_UNLOCK(); + return -ENODEV; + } + + i2c_dummy_adapter(adap); /* actually i2c_dummy->del_adapter */ +#ifdef CONFIG_PROC_FS + if (i2cproc_initialized) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) + char name[8]; + sprintf(name,"i2c-%d", i); + remove_proc_entry(name,proc_bus); +#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */ + int res; + if (adapters[i]->proc_entry) { + if ((res = proc_unregister(&proc_bus_dir, + adapters[i]->proc_entry->low_ino))) { + printk("i2c-core.o: Deregistration of /proc " + "entry failed\n"); + ADAP_UNLOCK(); + return res; + } + kfree(adapters[i]->proc_entry); + } +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ + } +#endif /* def CONFIG_PROC_FS */ + + /* 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 & I2C_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); + /* i2c_detach_client(client); --- frodo */ + } + /* all done, now unregister */ + adapters[i] = NULL; + adap_count--; + + ADAP_UNLOCK(); + DEB(printk("i2c-core.o: 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,j; + DRV_LOCK(); + for (i = 0; i < I2C_DRIVER_MAX; i++) + if (NULL == drivers[i]) + break; + if (I2C_DRIVER_MAX == i) { + printk(KERN_WARNING + " i2c-core.o: register_driver(%s) - enlarge I2C_DRIVER_MAX.\n", + driver->name); + DRV_UNLOCK(); + return -ENOMEM; + } + + drivers[i] = driver; + driver_count++; + + DRV_UNLOCK(); /* driver was successfully added */ + + DEB(printk("i2c-core.o: driver %s registered.\n",driver->name)); + + /* Notify all existing adapters and clients to dummy driver */ + ADAP_LOCK(); + if (driver->flags&I2C_DF_DUMMY) { + for (i=0; i<I2C_ADAP_MAX; i++) { + if (adapters[i]) { + driver->attach_adapter(adapters[i]); + for (j=0; j<I2C_CLIENT_MAX; j++) + if (adapters[i]->clients[j]) + driver->detach_client(adapters[i]->clients[j]); + } + } + ADAP_UNLOCK(); + return 0; + } + + /* now look for instances of driver on our adapters + */ + if ( driver->flags&I2C_DF_NOTIFY ) { + for (i=0;i<I2C_ADAP_MAX;i++) + if (adapters[i]!=NULL) + driver->attach_adapter(adapters[i]); + } + ADAP_UNLOCK(); + return 0; +} + +int i2c_del_driver(struct i2c_driver *driver) +{ + int i,j,k; + + DRV_LOCK(); + for (i = 0; i < I2C_DRIVER_MAX; i++) + if (driver == drivers[i]) + break; + if (I2C_DRIVER_MAX == i) { + printk(KERN_WARNING " i2c-core.o: unregister_driver: [%s] not found\n", + driver->name); + DRV_UNLOCK(); + 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-core.o: 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. + */ + ADAP_LOCK(); /* should be moved inside the if statement... */ + if ((driver->flags&I2C_DF_DUMMY)==0) + for (k=0;k<I2C_ADAP_MAX;k++) { + struct i2c_adapter *adap = adapters[k]; + if (adap == NULL) /* skip empty entries. */ + continue; + DEB2(printk("i2c-core.o: 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-core.o: detaching client %s:\n", + client->name)); + /*i2c_detach_client(client);*/ + driver->detach_client(client); + } + } + } + ADAP_UNLOCK(); + drivers[i] = NULL; + driver_count--; + DRV_UNLOCK(); + + DEB(printk("i2c-core.o: driver unregistered: %s\n",driver->name)); + return 0; +} + +int i2c_check_addr (struct i2c_adapter *adapter, int addr) +{ + int i; + for (i = 0; i < I2C_CLIENT_MAX ; i++) + if (adapter->clients[i] && (adapter->clients[i]->addr == addr)) + return -EBUSY; + return 0; +} + +int i2c_attach_client(struct i2c_client *client) +{ + struct i2c_adapter *adapter = client->adapter; + int i; + + if (i2c_check_addr(client->adapter,client->addr)) + return -EBUSY; + + for (i = 0; i < I2C_CLIENT_MAX; i++) + if (NULL == adapter->clients[i]) + break; + if (I2C_CLIENT_MAX == i) { + printk(KERN_WARNING + " i2c-core.o: attach_client(%s) - enlarge I2C_CLIENT_MAX.\n", + client->name); + return -ENOMEM; + } + + adapter->clients[i] = client; + adapter->client_count++; + i2c_dummy_client(client); + + if (adapter->client_register != NULL) + adapter->client_register(client); + DEB(printk("i2c-core.o: 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; + 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-core.o: unregister_client [%s] not found\n", + client->name); + return -ENODEV; + } + + if (adapter->client_unregister != NULL) + adapter->client_unregister(client); + /* client->driver->detach_client(client);*/ + + adapter->clients[i] = NULL; + adapter->client_count--; + i2c_dummy_client(client); + + DEB(printk("i2c-core.o: client [%s] unregistered.\n",client->name)); + return 0; +} + +void i2c_inc_use_client(struct i2c_client *client) +{ + + if (client->driver->inc_use != NULL) + client->driver->inc_use(client); + + if (client->adapter->inc_use != NULL) + client->adapter->inc_use(client->adapter); +} + +void i2c_dec_use_client(struct i2c_client *client) +{ + + if (client->driver->dec_use != NULL) + client->driver->dec_use(client); + + if (client->adapter->dec_use != NULL) + client->adapter->dec_use(client->adapter); +} + +/* ---------------------------------------------------- + * The /proc functions + * ---------------------------------------------------- + */ + +#ifdef CONFIG_PROC_FS + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) && \ + (LINUX_VERSION_CODE <= KERNEL_VERSION(2,3,27)) +/* Monitor access to /proc/bus/i2c*; make unloading i2c-proc impossible + if some process still uses it or some file in it */ +void monitor_bus_i2c(struct inode *inode, int fill) +{ + if (fill) + MOD_INC_USE_COUNT; + else + MOD_DEC_USE_COUNT; +} +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */ + +/* This function generates the output for /proc/bus/i2c */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) +int read_bus_i2c(char *buf, char **start, off_t offset, int len, int *eof, + void *private) +#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */ +int read_bus_i2c(char *buf, char **start, off_t offset, int len, int unused) +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ +{ + int i; + int nr = 0; + /* Note that it is safe to write a `little' beyond len. Yes, really. */ + for (i = 0; (i < I2C_ADAP_MAX) && (nr < len); i++) + if (adapters[i]) { + nr += sprintf(buf+nr, "i2c-%d\t", i); + if (adapters[i]->algo->smbus_xfer) { + if (adapters[i]->algo->master_xfer) + nr += sprintf(buf+nr,"smbus/i2c"); + else + nr += sprintf(buf+nr,"smbus "); + } else if (adapters[i]->algo->master_xfer) + nr += sprintf(buf+nr,"i2c "); + else + nr += sprintf(buf+nr,"dummy "); + nr += sprintf(buf+nr,"\t%-32s\t%-32s\n", + adapters[i]->name, + adapters[i]->algo->name); + } + return nr; +} + +/* This function generates the output for /proc/bus/i2c-? */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) +ssize_t i2cproc_bus_read(struct file * file, char * buf,size_t count, + loff_t *ppos) +{ + struct inode * inode = file->f_dentry->d_inode; +#else (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) +int i2cproc_bus_read(struct inode * inode, struct file * file,char * buf, + int count) +{ +#endif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) + char *kbuf; + struct i2c_client *client; + int i,j,len=0; + + if (count < 0) + return -EINVAL; + if (count > 4000) + count = 4000; + for (i = 0; i < I2C_ADAP_MAX; i++) + if (adapters[i]->inode == inode->i_ino) { + /* We need a bit of slack in the kernel buffer; this makes the + sprintf safe. */ + if (! (kbuf = kmalloc(count + 80,GFP_KERNEL))) + return -ENOMEM; + for (j = 0; j < I2C_CLIENT_MAX; j++) + if ((client = adapters[i]->clients[j])) + /* Filter out dummy clients */ + if (client->driver->id != I2C_DRIVERID_I2CDEV) + len += sprintf(kbuf+len,"%02x\t%-32s\t%-32s\n", + client->addr, + client->name,client->driver->name); + if (file->f_pos+len > count) + len = count - file->f_pos; + len = len - file->f_pos; + if (len < 0) + len = 0; + copy_to_user (buf,kbuf+file->f_pos,len); + file->f_pos += len; + kfree(kbuf); + return len; + } + return -ENOENT; +} + +int i2cproc_init(void) +{ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) + struct proc_dir_entry *proc_bus_i2c; +#else + int res; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ + + i2cproc_initialized = 0; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) + if (! proc_bus) { + printk("i2c-core.o: /proc/bus/ does not exist"); + i2cproc_cleanup(); + return -ENOENT; + } + proc_bus_i2c = create_proc_entry("i2c",0,proc_bus); + if (!proc_bus_i2c) { + printk("i2c-core.o: Could not create /proc/bus/i2c"); + i2cproc_cleanup(); + return -ENOENT; + } + proc_bus_i2c->read_proc = &read_bus_i2c; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,27)) + proc_bus_i2c->owner = THIS_MODULE; +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) + proc_bus_i2c->fill_inode = &monitor_bus_i2c; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ + i2cproc_initialized += 2; +#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */ + /* In Linux 2.0.x, there is no /proc/bus! But I hope no other module + introduced it, or we are fucked. And 2.0.35 and earlier does not + export proc_dir_inode_operations, so we grab it from proc_net, + which also uses it. Not nice. */ + proc_bus_dir.ops = proc_net.ops; + if ((res = proc_register_dynamic(&proc_root, &proc_bus_dir))) { + printk("i2c-core.o: Could not create /proc/bus/"); + i2cproc_cleanup(); + return res; + } + i2cproc_initialized ++; + if ((res = proc_register_dynamic(&proc_bus_dir, &proc_bus_i2c_dir))) { + printk("i2c-core.o: Could not create /proc/bus/i2c\n"); + i2cproc_cleanup(); + return res; + } + i2cproc_initialized ++; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ + return 0; +} + +int i2cproc_cleanup(void) +{ + + if (i2cproc_initialized >= 1) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) + remove_proc_entry("i2c",proc_bus); + i2cproc_initialized -= 2; +#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */ + int res; + if (i2cproc_initialized >= 2) { + if ((res = proc_unregister(&proc_bus_dir, + proc_bus_i2c_dir.low_ino))) { + printk("i2c-core.o: could not delete " + "/proc/bus/i2c, module not removed."); + return res; + } + i2cproc_initialized --; + } + if ((res = proc_unregister(&proc_root,proc_bus_dir.low_ino))) { + printk("i2c-core.o: could not delete /proc/bus/, " + "module not removed."); + return res; + } + i2cproc_initialized --; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ + } + return 0; +} + + +#endif /* def CONFIG_PROC_FS */ + +/* --------------------------------------------------- + * dummy driver notification + * --------------------------------------------------- + */ + +static void i2c_dummy_adapter(struct i2c_adapter *adap) +{ + int i; + for (i=0; i<I2C_DRIVER_MAX; i++) + if (drivers[i] && (drivers[i]->flags & I2C_DF_DUMMY)) + drivers[i]->attach_adapter(adap); +} + +static void i2c_dummy_client(struct i2c_client *client) +{ + int i; + for (i=0; i<I2C_DRIVER_MAX; i++) + if (drivers[i] && (drivers[i]->flags & I2C_DF_DUMMY)) + drivers[i]->detach_client(client); +} + + +/* ---------------------------------------------------- + * the functional interface to the i2c busses. + * ---------------------------------------------------- + */ + +int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg msgs[],int num) +{ + int ret; + + if (adap->algo->master_xfer) { + DEB2(printk("i2c-core.o: 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; + } else { + printk("i2c-core.o: I2C adapter %04x: I2C level transfers not supported\n", + adap->id); + return -ENOSYS; + } +} + +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; + + if (client->adapter->algo->master_xfer) { + msg.addr = client->addr; + msg.flags = client->flags & I2C_M_TEN; + msg.len = count; + (const char *)msg.buf = buf; + + DEB2(printk("i2c-core.o: 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; + } else { + printk("i2c-core.o: I2C adapter %04x: I2C level transfers not supported\n", + client->adapter->id); + return -ENOSYS; + } +} + +int i2c_master_recv(struct i2c_client *client, char *buf ,int count) +{ + struct i2c_adapter *adap=client->adapter; + struct i2c_msg msg; + int ret; + if (client->adapter->algo->master_xfer) { + msg.addr = client->addr; + msg.flags = client->flags & I2C_M_TEN; + msg.flags |= I2C_M_RD; + msg.len = count; + msg.buf = buf; + + DEB2(printk("i2c-core.o: 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); + + DEB2(printk("i2c-core.o: master_recv: return:%d (count:%d, addr:0x%02x)\n", + ret, count, client->addr)); + + /* if everything went ok (i.e. 1 msg transmitted), return #bytes + * transmitted, else error code. + */ + return (ret == 1 )? count : ret; + } else { + printk("i2c-core.o: I2C adapter %04x: I2C level transfers not supported\n", + client->adapter->id); + return -ENOSYS; + } +} + + +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-core.o: 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; +} + +/* ---------------------------------------------------- + * the i2c address scanning function + * Will not work for 10-bit addresses! + * ---------------------------------------------------- + */ +int i2c_probe(struct i2c_adapter *adapter, + struct i2c_client_address_data *address_data, + i2c_client_found_addr_proc *found_proc) +{ + int addr,i,found,err; + int adap_id = i2c_adapter_id(adapter); + + /* Forget it if we can't probe using SMBUS_QUICK */ + if (! i2c_check_functionality(adapter,I2C_FUNC_SMBUS_QUICK)) + return -1; + + for (addr = 0x00; + addr <= 0x7f; + addr++) { + + /* Skip if already in use */ + if (i2c_check_addr(adapter,addr)) + continue; + + /* If it is in one of the force entries, we don't do any detection + at all */ + found = 0; + + for (i = 0; + !found && (address_data->force[i] != I2C_CLIENT_END); + i += 3) { + if (((adap_id == address_data->force[i]) || + (address_data->force[i] == ANY_I2C_BUS)) && + (addr == address_data->force[i+1])) { + DEB2(printk("i2c-core.o: found force parameter for adapter %d, addr %04x\n", + adap_id,addr)); + if ((err = found_proc(adapter,addr,0,0))) + return err; + found = 1; + } + } + if (found) + continue; + + /* If this address is in one of the ignores, we can forget about it + right now */ + for (i = 0; + !found && (address_data->ignore[i] != I2C_CLIENT_END); + i += 2) { + if (((adap_id == address_data->ignore[i]) || + ((address_data->ignore[i] == ANY_I2C_BUS))) && + (addr == address_data->ignore[i+1])) { + DEB2(printk("i2c-core.o: found ignore parameter for adapter %d, " + "addr %04x\n", adap_id ,addr)); + found = 1; + } + } + for (i = 0; + !found && (address_data->ignore_range[i] != I2C_CLIENT_END); + i += 3) { + if (((adap_id == address_data->ignore_range[i]) || + ((address_data->ignore_range[i]==ANY_I2C_BUS))) && + (addr >= address_data->ignore_range[i+1]) && + (addr <= address_data->ignore_range[i+2])) { + DEB2(printk("i2c-core.o: found ignore_range parameter for adapter %d, " + "addr %04x\n", adap_id,addr)); + found = 1; + } + } + if (found) + continue; + + /* Now, we will do a detection, but only if it is in the normal or + probe entries */ + for (i = 0; + !found && (address_data->normal_i2c[i] != I2C_CLIENT_END); + i += 1) { + if (addr == address_data->normal_i2c[i]) { + found = 1; + DEB2(printk("i2c-core.o: found normal i2c entry for adapter %d, " + "addr %02x", adap_id,addr)); + } + } + + for (i = 0; + !found && (address_data->normal_i2c_range[i] != I2C_CLIENT_END); + i += 2) { + if ((addr >= address_data->normal_i2c_range[i]) && + (addr <= address_data->normal_i2c_range[i+1])) { + found = 1; + DEB2(printk("i2c-core.o: found normal i2c_range entry for adapter %d, " + "addr %04x\n", adap_id,addr)); + } + } + + for (i = 0; + !found && (address_data->probe[i] != I2C_CLIENT_END); + i += 2) { + if (((adap_id == address_data->probe[i]) || + ((address_data->probe[i] == ANY_I2C_BUS))) && + (addr == address_data->probe[i+1])) { + found = 1; + DEB2(printk("i2c-core.o: found probe parameter for adapter %d, " + "addr %04x\n", adap_id,addr)); + } + } + for (i = 0; + !found && (address_data->probe_range[i] != I2C_CLIENT_END); + i += 3) { + if (((adap_id == address_data->probe_range[i]) || + (address_data->probe_range[i] == ANY_I2C_BUS)) && + (addr >= address_data->probe_range[i+1]) && + (addr <= address_data->probe_range[i+2])) { + found = 1; + DEB2(printk("i2c-core.o: found probe_range parameter for adapter %d, " + "addr %04x\n", adap_id,addr)); + } + } + if (!found) + continue; + + /* OK, so we really should examine this address. First check + whether there is some client here at all! */ + if (i2c_smbus_xfer(adapter,addr,0,0,0,I2C_SMBUS_QUICK,NULL) >= 0) + if ((err = found_proc(adapter,addr,0,-1))) + return err; + } + return 0; +} + +/* +++ frodo + * return id number for a specific adapter + */ +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; +} + +/* The SMBus parts */ + +extern s32 i2c_smbus_write_quick(struct i2c_client * client, u8 value) +{ + return i2c_smbus_xfer(client->adapter,client->addr,client->flags, + value,0,I2C_SMBUS_QUICK,NULL); +} + +extern s32 i2c_smbus_read_byte(struct i2c_client * client) +{ + union i2c_smbus_data data; + if (i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_READ,0,I2C_SMBUS_BYTE, &data)) + return -1; + else + return 0x0FF & data.byte; +} + +extern s32 i2c_smbus_write_byte(struct i2c_client * client, u8 value) +{ + return i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_WRITE,value, I2C_SMBUS_BYTE,NULL); +} + +extern s32 i2c_smbus_read_byte_data(struct i2c_client * client, u8 command) +{ + union i2c_smbus_data data; + if (i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_READ,command, I2C_SMBUS_BYTE_DATA,&data)) + return -1; + else + return 0x0FF & data.byte; +} + +extern s32 i2c_smbus_write_byte_data(struct i2c_client * client, + u8 command, u8 value) +{ + union i2c_smbus_data data; + data.byte = value; + return i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_WRITE,command, + I2C_SMBUS_BYTE_DATA,&data); +} + +extern s32 i2c_smbus_read_word_data(struct i2c_client * client, u8 command) +{ + union i2c_smbus_data data; + if (i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_READ,command, I2C_SMBUS_WORD_DATA, &data)) + return -1; + else + return 0x0FFFF & data.word; +} + +extern s32 i2c_smbus_write_word_data(struct i2c_client * client, + u8 command, u16 value) +{ + union i2c_smbus_data data; + data.word = value; + return i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_WRITE,command, + I2C_SMBUS_WORD_DATA,&data); +} + +extern s32 i2c_smbus_process_call(struct i2c_client * client, + u8 command, u16 value) +{ + union i2c_smbus_data data; + data.word = value; + if (i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_WRITE,command, + I2C_SMBUS_PROC_CALL, &data)) + return -1; + else + return 0x0FFFF & data.word; +} + +/* Returns the number of read bytes */ +extern s32 i2c_smbus_read_block_data(struct i2c_client * client, + u8 command, u8 *values) +{ + union i2c_smbus_data data; + int i; + if (i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_READ,command, + I2C_SMBUS_BLOCK_DATA,&data)) + return -1; + else { + for (i = 1; i <= data.block[0]; i++) + values[i-1] = data.block[i]; + return data.block[0]; + } +} + +extern s32 i2c_smbus_write_block_data(struct i2c_client * client, + u8 command, u8 length, u8 *values) +{ + union i2c_smbus_data data; + int i; + if (length > 32) + length = 32; + for (i = 1; i <= length; i++) + data.block[i] = values[i-1]; + data.block[0] = length; + return i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_WRITE,command, + I2C_SMBUS_BLOCK_DATA,&data); +} + +/* Simulate a SMBus command using the i2c protocol + No checking of parameters is done! */ +static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr, + unsigned short flags, + char read_write, u8 command, int size, + union i2c_smbus_data * data) +{ + /* So we need to generate a series of msgs. In the case of writing, we + need to use only one message; when reading, we need two. We initialize + most things with sane defaults, to keep the code below somewhat + simpler. */ + unsigned char msgbuf0[33]; + unsigned char msgbuf1[33]; + int num = read_write == I2C_SMBUS_READ?2:1; + struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 }, + { addr, flags | I2C_M_RD, 0, msgbuf1 } + }; + int i; + + msgbuf0[0] = command; + switch(size) { + case I2C_SMBUS_QUICK: + msg[0].len = 0; + /* Special case: The read/write field is used as data */ + msg[0].flags = flags | (read_write==I2C_SMBUS_READ)?I2C_M_RD:0; + num = 1; + break; + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_READ) { + /* Special case: only a read! */ + msg[0].flags = I2C_M_RD | flags; + num = 1; + } + break; + case I2C_SMBUS_BYTE_DATA: + if (read_write == I2C_SMBUS_READ) + msg[1].len = 1; + else { + msg[0].len = 2; + msgbuf0[1] = data->byte; + } + break; + case I2C_SMBUS_WORD_DATA: + if (read_write == I2C_SMBUS_READ) + msg[1].len = 2; + else { + msg[0].len=3; + msgbuf0[1] = data->word & 0xff; + msgbuf0[2] = (data->word >> 8) & 0xff; + } + break; + case I2C_SMBUS_PROC_CALL: + num = 2; /* Special case */ + msg[0].len = 3; + msg[1].len = 2; + msgbuf0[1] = data->word & 0xff; + msgbuf0[2] = (data->word >> 8) & 0xff; + break; + case I2C_SMBUS_BLOCK_DATA: + if (read_write == I2C_SMBUS_READ) { + printk("i2c-core.o: Block read not supported under " + "I2C emulation!\n"); + return -1; + } else { + msg[1].len = data->block[0] + 1; + if (msg[1].len > 32) { + printk("i2c-core.o: smbus_access called with " + "invalid block write size (%d)\n", + msg[1].len); + return -1; + } + for (i = 1; i <= msg[1].len; i++) + msgbuf0[i] = data->block[i]; + } + break; + default: + printk("i2c-core.o: smbus_access called with invalid size (%d)\n", + size); + return -1; + } + + if (i2c_transfer(adapter, msg, num) < 0) + return -1; + + if (read_write == I2C_SMBUS_READ) + switch(size) { + case I2C_SMBUS_BYTE: + data->byte = msgbuf0[0]; + break; + case I2C_SMBUS_BYTE_DATA: + data->byte = msgbuf1[0]; + break; + case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_PROC_CALL: + data->word = msgbuf1[0] | (msgbuf1[1] << 8); + break; + } + return 0; +} + + +s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags, + char read_write, u8 command, int size, + union i2c_smbus_data * data) +{ + s32 res; + flags = flags & I2C_M_TEN; + if (adapter->algo->smbus_xfer) { + I2C_LOCK(adapter); + res = adapter->algo->smbus_xfer(adapter,addr,flags,read_write, + command,size,data); + I2C_UNLOCK(adapter); + } else + res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write, + command,size,data); + return res; +} + + +/* You should always define `functionality'; the 'else' is just for + backward compatibility. */ +u32 i2c_get_functionality (struct i2c_adapter *adap) +{ + if (adap->algo->functionality) + return adap->algo->functionality(adap); + else + return 0xffffffff; +} + +int i2c_check_functionality (struct i2c_adapter *adap, u32 func) +{ + u32 adap_func = i2c_get_functionality (adap); + return (func & adap_func) == func; +} + + +static int __init i2c_init(void) +{ + printk("i2c-core.o: i2c core module\n"); + memset(adapters,0,sizeof(adapters)); + memset(drivers,0,sizeof(drivers)); + adap_count=0; + driver_count=0; + + init_MUTEX(&adap_lock); + init_MUTEX(&driver_lock); + + i2cproc_init(); + + return 0; +} + +#ifndef MODULE +#ifdef CONFIG_I2C_CHARDEV + extern int i2c_dev_init(void); +#endif +#ifdef CONFIG_I2C_ALGOBIT + extern int algo_bit_init(void); +#endif +#ifdef CONFIG_I2C_BITLP + extern int bitlp_init(void); +#endif +#ifdef CONFIG_I2C_BITELV + extern int bitelv_init(void); +#endif +#ifdef CONFIG_I2C_BITVELLE + extern int bitvelle_init(void); +#endif +#ifdef CONFIG_I2C_BITVIA + extern int bitvia_init(void); +#endif + +#ifdef CONFIG_I2C_ALGOPCF + extern int algo_pcf_init(void); +#endif +#ifdef CONFIG_I2C_PCFISA + extern int pcfisa_init(void); +#endif + +/* This is needed for automatic patch generation: sensors code starts here */ +/* This is needed for automatic patch generation: sensors code ends here */ + +int __init i2c_init_all(void) +{ + /* --------------------- global ----- */ + i2c_init(); + +#ifdef CONFIG_I2C_CHARDEV + i2c_dev_init(); +#endif + /* --------------------- bit -------- */ +#ifdef CONFIG_I2C_ALGOBIT + i2c_algo_bit_init(); +#endif +#ifdef CONFIG_I2C_PHILIPSPAR + i2c_bitlp_init(); +#endif +#ifdef CONFIG_I2C_ELV + i2c_bitelv_init(); +#endif +#ifdef CONFIG_I2C_VELLEMAN + i2c_bitvelle_init(); +#endif + + /* --------------------- pcf -------- */ +#ifdef CONFIG_I2C_ALGOPCF + i2c_algo_pcf_init(); +#endif +#ifdef CONFIG_I2C_ELEKTOR + i2c_pcfisa_init(); +#endif +/* This is needed for automatic patch generation: sensors code starts here */ +/* This is needed for automatic patch generation: sensors code ends here */ + + return 0; +} + +#endif + + + +EXPORT_SYMBOL(i2c_add_adapter); +EXPORT_SYMBOL(i2c_del_adapter); +EXPORT_SYMBOL(i2c_add_driver); +EXPORT_SYMBOL(i2c_del_driver); +EXPORT_SYMBOL(i2c_attach_client); +EXPORT_SYMBOL(i2c_detach_client); +EXPORT_SYMBOL(i2c_inc_use_client); +EXPORT_SYMBOL(i2c_dec_use_client); +EXPORT_SYMBOL(i2c_check_addr); + + +EXPORT_SYMBOL(i2c_master_send); +EXPORT_SYMBOL(i2c_master_recv); +EXPORT_SYMBOL(i2c_control); +EXPORT_SYMBOL(i2c_transfer); +EXPORT_SYMBOL(i2c_adapter_id); +EXPORT_SYMBOL(i2c_probe); + +EXPORT_SYMBOL(i2c_smbus_xfer); +EXPORT_SYMBOL(i2c_smbus_write_quick); +EXPORT_SYMBOL(i2c_smbus_read_byte); +EXPORT_SYMBOL(i2c_smbus_write_byte); +EXPORT_SYMBOL(i2c_smbus_read_byte_data); +EXPORT_SYMBOL(i2c_smbus_write_byte_data); +EXPORT_SYMBOL(i2c_smbus_read_word_data); +EXPORT_SYMBOL(i2c_smbus_write_word_data); +EXPORT_SYMBOL(i2c_smbus_process_call); +EXPORT_SYMBOL(i2c_smbus_read_block_data); +EXPORT_SYMBOL(i2c_smbus_write_block_data); + +EXPORT_SYMBOL(i2c_get_functionality); +EXPORT_SYMBOL(i2c_check_functionality); + +#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) +{ + i2cproc_cleanup(); +} +#endif diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c new file mode 100644 index 000000000..088d730fc --- /dev/null +++ b/drivers/i2c/i2c-dev.c @@ -0,0 +1,557 @@ +/* + i2c-dev.c - i2c-bus driver, char device interface + + Copyright (C) 1995-97 Simon G. Vogl + Copyright (C) 1998-99 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. +*/ + +/* Note that this is a complete rewrite of Simon Vogl's i2c-dev module. + But I have used so much of his original code and ideas that it seems + only fair to recognize him as co-author -- Frodo */ + +/* $Id: i2c-dev.c,v 1.18 1999/12/21 23:45:58 frodo Exp $ */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/malloc.h> +#include <linux/version.h> + +/* If you want debugging uncomment: */ +/* #define DEBUG */ + +#ifndef KERNEL_VERSION +#define KERNEL_VERSION(a,b,c) (((a) << 16) | ((b) << 8) | (c)) +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,51) +#include <linux/init.h> +#else +#define __init +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,4)) +#define copy_from_user memcpy_fromfs +#define copy_to_user memcpy_tofs +#define get_user_data(to,from) ((to) = get_user(from),0) +#else +#include <asm/uaccess.h> +#define get_user_data(to,from) get_user(to,from) +#endif + +/* 2.0.0 kernel compatibility */ +#if LINUX_VERSION_CODE < 0x020100 +#define MODULE_AUTHOR(noone) +#define MODULE_DESCRIPTION(none) +#define MODULE_PARM(no,param) +#define MODULE_PARM_DESC(no,description) +#define EXPORT_SYMBOL(noexport) +#define EXPORT_NO_SYMBOLS +#endif + +#include <linux/i2c.h> +#include <linux/i2c-dev.h> + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* def MODULE */ + +/* struct file_operations changed too often in the 2.1 series for nice code */ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) +static loff_t i2cdev_lseek (struct file *file, loff_t offset, int origin); +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,56)) +static long long i2cdev_lseek (struct file *file, long long offset, int origin); +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) +static long long i2cdev_llseek (struct inode *inode, struct file *file, + long long offset, int origin); +#else +static int i2cdev_lseek (struct inode *inode, struct file *file, off_t offset, + int origin); +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) +static ssize_t i2cdev_read (struct file *file, char *buf, size_t count, + loff_t *offset); +static ssize_t i2cdev_write (struct file *file, const char *buf, size_t count, + loff_t *offset); +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) +static long i2cdev_read (struct inode *inode, struct file *file, char *buf, + unsigned long count); +static long i2cdev_write (struct inode *inode, struct file *file, + const char *buf, unsigned long offset); +#else +static int i2cdev_read(struct inode *inode, struct file *file, char *buf, + int count); +static int i2cdev_write(struct inode *inode, struct file *file, + const char *buf, int count); +#endif + +static int i2cdev_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +static int i2cdev_open (struct inode *inode, struct file *file); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,31)) +static int i2cdev_release (struct inode *inode, struct file *file); +#else +static void i2cdev_release (struct inode *inode, struct file *file); +#endif + + +static int i2cdev_attach_adapter(struct i2c_adapter *adap); +static int i2cdev_detach_client(struct i2c_client *client); +static int i2cdev_command(struct i2c_client *client, unsigned int cmd, + void *arg); + +#ifdef MODULE +static +#else +extern +#endif + int __init i2c_dev_init(void); +static int i2cdev_cleanup(void); + +static struct file_operations i2cdev_fops = { + i2cdev_lseek, + i2cdev_read, + i2cdev_write, + NULL, /* i2cdev_readdir */ + NULL, /* i2cdev_select */ + i2cdev_ioctl, + NULL, /* i2cdev_mmap */ + i2cdev_open, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,118) + NULL, /* i2cdev_flush */ +#endif + i2cdev_release, +}; + +#define I2CDEV_ADAPS_MAX I2C_ADAP_MAX +static struct i2c_adapter *i2cdev_adaps[I2CDEV_ADAPS_MAX]; + +static struct i2c_driver i2cdev_driver = { + /* name */ "i2c-dev dummy driver", + /* id */ I2C_DRIVERID_I2CDEV, + /* flags */ I2C_DF_DUMMY, + /* attach_adapter */ i2cdev_attach_adapter, + /* detach_client */ i2cdev_detach_client, + /* command */ i2cdev_command, + /* inc_use */ NULL, + /* dec_use */ NULL, +}; + +static struct i2c_client i2cdev_client_template = { + /* name */ "I2C /dev entry", + /* id */ 1, + /* flags */ 0, + /* addr */ -1, + /* adapter */ NULL, + /* driver */ &i2cdev_driver, + /* data */ NULL +}; + +static int i2cdev_initialized; + +/* Note that the lseek function is called llseek in 2.1 kernels. But things + are complicated enough as is. */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) +loff_t i2cdev_lseek (struct file *file, loff_t offset, int origin) +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,56)) +long long i2cdev_lseek (struct file *file, long long offset, int origin) +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) +long long i2cdev_llseek (struct inode *inode, struct file *file, + long long offset, int origin) +#else +int i2cdev_lseek (struct inode *inode, struct file *file, off_t offset, + int origin) +#endif +{ +#ifdef DEBUG +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,56)) + struct inode *inode = file->f_dentry->d_inode; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) */ + printk("i2c-dev,o: i2c-%d lseek to %ld bytes relative to %d.\n", + MINOR(inode->i_rdev),(long) offset,origin); +#endif /* DEBUG */ + return -ESPIPE; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) +static ssize_t i2cdev_read (struct file *file, char *buf, size_t count, + loff_t *offset) +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) +static long i2cdev_read (struct inode *inode, struct file *file, char *buf, + unsigned long count) +#else +static int i2cdev_read(struct inode *inode, struct file *file, char *buf, + int count) +#endif +{ + char *tmp; + int ret; + +#ifdef DEBUG +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) + struct inode *inode = file->f_dentry->d_inode; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) */ +#endif /* DEBUG */ + + struct i2c_client *client = (struct i2c_client *)file->private_data; + + /* copy user space data to kernel space. */ + tmp = kmalloc(count,GFP_KERNEL); + if (tmp==NULL) + return -ENOMEM; + +#ifdef DEBUG + printk("i2c-dev,o: i2c-%d reading %d bytes.\n",MINOR(inode->i_rdev),count); +#endif + + ret = i2c_master_recv(client,tmp,count); + copy_to_user(buf,tmp,count); + kfree(tmp); + return ret; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) +static ssize_t i2cdev_write (struct file *file, const char *buf, size_t count, + loff_t *offset) +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) +static long i2cdev_write (struct inode *inode, struct file *file, + const char *buf, unsigned long offset) +#else +static int i2cdev_write(struct inode *inode, struct file *file, + const char *buf, int count) +#endif +{ + int ret; + char *tmp; + struct i2c_client *client = (struct i2c_client *)file->private_data; + +#ifdef DEBUG +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) + struct inode *inode = file->f_dentry->d_inode; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) */ +#endif /* DEBUG */ + + /* copy user space data to kernel space. */ + tmp = kmalloc(count,GFP_KERNEL); + if (tmp==NULL) + return -ENOMEM; + copy_from_user(tmp,buf,count); + +#ifdef DEBUG + printk("i2c-dev,o: i2c-%d writing %d bytes.\n",MINOR(inode->i_rdev),count); +#endif + ret = i2c_master_send(client,tmp,count); + kfree(tmp); + return ret; +} + +int i2cdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct i2c_client *client = (struct i2c_client *)file->private_data; + struct i2c_smbus_ioctl_data data_arg; + union i2c_smbus_data temp; + int ver,datasize,res; + unsigned long funcs; + +#ifdef DEBUG + printk("i2c-dev.o: i2c-%d ioctl, cmd: 0x%x, arg: %lx.\n", + MINOR(inode->i_rdev),cmd, arg); +#endif /* DEBUG */ + + switch ( cmd ) { + case I2C_SLAVE: + case I2C_SLAVE_FORCE: + if ((arg > 0x3ff) || (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f)) + return -EINVAL; + if ((cmd == I2C_SLAVE) && i2c_check_addr(client->adapter,arg)) + return -EBUSY; + client->addr = arg; + return 0; + case I2C_TENBIT: + if (arg) + client->flags |= I2C_M_TEN; + else + client->flags &= ~I2C_M_TEN; + return 0; + case I2C_FUNCS: + if (! arg) { +#ifdef DEBUG + printk("i2c-dev.o: NULL argument pointer in ioctl I2C_SMBUS.\n"); +#endif + return -EINVAL; + } + if (verify_area(VERIFY_WRITE,(unsigned long *) arg, + sizeof(unsigned long))) { +#ifdef DEBUG + printk("i2c-dev.o: invalid argument pointer (%ld) " + "in IOCTL I2C_SMBUS.\n", arg); +#endif + return -EINVAL; + } + + funcs = i2c_get_functionality(client->adapter); + copy_to_user((unsigned long *)arg,&funcs,sizeof(unsigned long)); + return 0; + case I2C_SMBUS: + if (! arg) { +#ifdef DEBUG + printk("i2c-dev.o: NULL argument pointer in ioctl I2C_SMBUS.\n"); +#endif + return -EINVAL; + } + if (verify_area(VERIFY_READ,(struct i2c_smbus_ioctl_data *) arg, + sizeof(struct i2c_smbus_ioctl_data))) { +#ifdef DEBUG + printk("i2c-dev.o: invalid argument pointer (%ld) " + "in IOCTL I2C_SMBUS.\n", arg); +#endif + return -EINVAL; + } + copy_from_user(&data_arg,(struct i2c_smbus_ioctl_data *) arg, + sizeof(struct i2c_smbus_ioctl_data)); + if ((data_arg.size != I2C_SMBUS_BYTE) && + (data_arg.size != I2C_SMBUS_QUICK) && + (data_arg.size != I2C_SMBUS_BYTE_DATA) && + (data_arg.size != I2C_SMBUS_WORD_DATA) && + (data_arg.size != I2C_SMBUS_PROC_CALL) && + (data_arg.size != I2C_SMBUS_BLOCK_DATA)) { +#ifdef DEBUG + printk("i2c-dev.o: size out of range (%x) in ioctl I2C_SMBUS.\n", + data_arg.size); +#endif + return -EINVAL; + } + /* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1, + so the check is valid if size==I2C_SMBUS_QUICK too. */ + if ((data_arg.read_write != I2C_SMBUS_READ) && + (data_arg.read_write != I2C_SMBUS_WRITE)) { +#ifdef DEBUG + printk("i2c-dev.o: read_write out of range (%x) in ioctl I2C_SMBUS.\n", + data_arg.read_write); +#endif + return -EINVAL; + } + + /* Note that command values are always valid! */ + + if ((data_arg.size == I2C_SMBUS_QUICK) || + ((data_arg.size == I2C_SMBUS_BYTE) && + (data_arg.read_write == I2C_SMBUS_WRITE))) + /* These are special: we do not use data */ + return i2c_smbus_xfer(client->adapter, client->addr, client->flags, + data_arg.read_write, data_arg.command, + data_arg.size, NULL); + + if (data_arg.data == NULL) { +#ifdef DEBUG + printk("i2c-dev.o: data is NULL pointer in ioctl I2C_SMBUS.\n"); +#endif + return -EINVAL; + } + + /* This seems unlogical but it is not: if the user wants to read a + value, we must write that value to user memory! */ + ver = ((data_arg.read_write == I2C_SMBUS_WRITE) && + (data_arg.size != I2C_SMBUS_PROC_CALL))?VERIFY_READ:VERIFY_WRITE; + + if ((data_arg.size == I2C_SMBUS_BYTE_DATA) || (data_arg.size == I2C_SMBUS_BYTE)) + datasize = sizeof(data_arg.data->byte); + else if ((data_arg.size == I2C_SMBUS_WORD_DATA) || + (data_arg.size == I2C_SMBUS_PROC_CALL)) + datasize = sizeof(data_arg.data->word); + else /* size == I2C_SMBUS_BLOCK_DATA */ + datasize = sizeof(data_arg.data->block); + + if (verify_area(ver,data_arg.data,datasize)) { +#ifdef DEBUG + printk("i2c-dev.o: invalid pointer data (%p) in ioctl I2C_SMBUS.\n", + data_arg.data); +#endif + return -EINVAL; + } + + if ((data_arg.size == I2C_SMBUS_PROC_CALL) || + (data_arg.read_write == I2C_SMBUS_WRITE)) + copy_from_user(&temp,data_arg.data,datasize); + res = i2c_smbus_xfer(client->adapter,client->addr,client->flags, + data_arg.read_write, + data_arg.command,data_arg.size,&temp); + if (! res && ((data_arg.size == I2C_SMBUS_PROC_CALL) || + (data_arg.read_write == I2C_SMBUS_READ))) + copy_to_user(data_arg.data,&temp,datasize); + return res; + + default: + return i2c_control(client,cmd,arg); + } + return 0; +} + +int i2cdev_open (struct inode *inode, struct file *file) +{ + unsigned int minor = MINOR(inode->i_rdev); + struct i2c_client *client; + + if ((minor >= I2CDEV_ADAPS_MAX) || ! (i2cdev_adaps[minor])) { +#ifdef DEBUG + printk("i2c-dev.o: Trying to open unattached adapter i2c-%d\n",minor); +#endif + return -ENODEV; + } + + /* Note that we here allocate a client for later use, but we will *not* + register this client! Yes, this is safe. No, it is not very clean. */ + if(! (client = kmalloc(sizeof(struct i2c_client),GFP_KERNEL))) + return -ENOMEM; + memcpy(client,&i2cdev_client_template,sizeof(struct i2c_client)); + client->adapter = i2cdev_adaps[minor]; + file->private_data = client; + + i2cdev_adaps[minor]->inc_use(i2cdev_adaps[minor]); + MOD_INC_USE_COUNT; + +#ifdef DEBUG + printk("i2c-dev.o: opened i2c-%d\n",minor); +#endif + return 0; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,31)) +static int i2cdev_release (struct inode *inode, struct file *file) +#else +static void i2cdev_release (struct inode *inode, struct file *file) +#endif +{ + unsigned int minor = MINOR(inode->i_rdev); + kfree(file->private_data); + file->private_data=NULL; +#ifdef DEBUG + printk("i2c-dev.o: Closed: i2c-%d\n", minor); +#endif + MOD_DEC_USE_COUNT; + i2cdev_adaps[minor]->dec_use(i2cdev_adaps[minor]); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,31)) + return 0; +#endif +} + +int i2cdev_attach_adapter(struct i2c_adapter *adap) +{ + int i; + + if ((i = i2c_adapter_id(adap)) < 0) { + printk("i2c-dev.o: Unknown adapter ?!?\n"); + return -ENODEV; + } + if (i >= I2CDEV_ADAPS_MAX) { + printk("i2c-dev.o: Adapter number too large?!? (%d)\n",i); + return -ENODEV; + } + + if (! i2cdev_adaps[i]) { + i2cdev_adaps[i] = adap; + printk("i2c-dev.o: Registered '%s' as minor %d\n",adap->name,i); + } else { + i2cdev_adaps[i] = NULL; +#ifdef DEBUG + printk("i2c-dev.o: Adapter unregistered: %s\n",adap->name); +#endif + } + + return 0; +} + +int i2cdev_detach_client(struct i2c_client *client) +{ + return 0; +} + +static int i2cdev_command(struct i2c_client *client, unsigned int cmd, + void *arg) +{ + return -1; +} + +int __init i2c_dev_init(void) +{ + int res; + + printk("i2c-dev.o: i2c /dev entries driver module\n"); + + i2cdev_initialized = 0; + if (register_chrdev(I2C_MAJOR,"i2c",&i2cdev_fops)) { + printk("i2c-dev.o: unable to get major %d for i2c bus\n",I2C_MAJOR); + return -EIO; + } + i2cdev_initialized ++; + + if ((res = i2c_add_driver(&i2cdev_driver))) { + printk("i2c-dev.o: Driver registration failed, module not inserted.\n"); + i2cdev_cleanup(); + return res; + } + i2cdev_initialized ++; + return 0; +} + +int i2cdev_cleanup(void) +{ + int res; + + if (i2cdev_initialized >= 2) { + if ((res = i2c_del_driver(&i2cdev_driver))) { + printk("i2c-dev.o: Driver deregistration failed, " + "module not removed.\n"); + return res; + } + i2cdev_initialized ++; + } + + if (i2cdev_initialized >= 1) { + if ((res = unregister_chrdev(I2C_MAJOR,"i2c"))) { + printk("i2c-dev.o: unable to release major %d for i2c bus\n",I2C_MAJOR); + return res; + } + i2cdev_initialized --; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and Simon G. Vogl <simon@tk.uni-linz.ac.at>"); +MODULE_DESCRIPTION("I2C /dev entries driver"); + +int init_module(void) +{ + return i2c_dev_init(); +} + +int cleanup_module(void) +{ + return i2cdev_cleanup(); +} + +#endif /* def MODULE */ + diff --git a/drivers/i2c/i2c-elektor.c b/drivers/i2c/i2c-elektor.c new file mode 100644 index 000000000..fb965df0f --- /dev/null +++ b/drivers/i2c/i2c-elektor.c @@ -0,0 +1,327 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-elektor.c i2c-hw access for PCF8584 style isa bus adaptes */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-97 Simon G. Vogl + 1998-99 Hans Berglund + + 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. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even + Frodo Looijaard <frodol@dds.nl> */ + +/* $Id: i2c-elektor.c,v 1.13 1999/12/21 23:45:58 frodo Exp $ */ + +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/malloc.h> +#include <linux/version.h> +#if LINUX_VERSION_CODE >= 0x020135 +#include <linux/init.h> +#else +#define __init +#endif +#include <asm/irq.h> +#include <asm/io.h> + +/* 2.0.0 kernel compatibility */ +#if LINUX_VERSION_CODE < 0x020100 +#define MODULE_AUTHOR(noone) +#define MODULE_DESCRIPTION(none) +#define MODULE_PARM(no,param) +#define MODULE_PARM_DESC(no,description) +#define EXPORT_SYMBOL(noexport) +#define EXPORT_NO_SYMBOLS +#endif + +#include <linux/i2c.h> +#include <linux/i2c-algo-pcf.h> +#include <linux/i2c-elektor.h> +#include "i2c-pcf8584.h" + +#define DEFAULT_BASE 0x300 +#define DEFAULT_IRQ 0 +#define DEFAULT_CLOCK 0x1c +#define DEFAULT_OWN 0x55 + +static int base = 0; +static int irq = 0; +static int clock = 0; +static int own = 0; +static int i2c_debug=0; +static struct i2c_pcf_isa gpi; +#if (LINUX_VERSION_CODE < 0x020301) +static struct wait_queue *pcf_wait = NULL; +#else +static wait_queue_head_t pcf_wait; +#endif +static int pcf_pending; + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) if (i2c_debug>=1) x +#define DEB2(x) if (i2c_debug>=2) x +#define DEB3(x) if (i2c_debug>=3) x +#define DEBE(x) x /* error messages */ + + +/* --- Convenience defines for the i2c port: */ +#define BASE ((struct i2c_pcf_isa *)(data))->pi_base +#define DATA BASE /* Adapter data port */ +#define CTRL (BASE+1) /* Adapter control port */ + +/* ----- local functions ---------------------------------------------- */ + +static void pcf_isa_setbyte(void *data, int ctl, int val) +{ + if (ctl) { + if (gpi.pi_irq > 0) { + DEB3(printk("i2c-elektor.o: Write control 0x%x\n", val|I2C_PCF_ENI)); + outb(val | I2C_PCF_ENI, CTRL); + } else { + DEB3(printk("i2c-elektor.o: Write control 0x%x\n", val)); + outb(val, CTRL); + } + } else { + DEB3(printk("i2c-elektor.o: Write data 0x%x\n", val)); + outb(val, DATA); + } +} + +static int pcf_isa_getbyte(void *data, int ctl) +{ + int val; + + if (ctl) { + val = inb(CTRL); + DEB3(printk("i2c-elektor.o: Read control 0x%x\n", val)); + } else { + val = inb(DATA); + DEB3(printk("i2c-elektor.o: Read data 0x%x\n", val)); + } + return (val); +} + +static int pcf_isa_getown(void *data) +{ + return (gpi.pi_own); +} + + +static int pcf_isa_getclock(void *data) +{ + return (gpi.pi_clock); +} + + + +#if LINUX_VERSION_CODE < 0x02017f +static void schedule_timeout(int j) +{ + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + j; + schedule(); +} +#endif + +#if 0 +static void pcf_isa_sleep(unsigned long timeout) +{ + schedule_timeout( timeout * HZ); +} +#endif + + +static void pcf_isa_waitforpin(void) { + + int timeout = 2; + + if (gpi.pi_irq > 0) { + cli(); + if (pcf_pending == 0) { +#if LINUX_VERSION_CODE < 0x02017f + current->timeout = jiffies + timeout * HZ; + interruptible_sleep_on(&pcf_wait); +#else + interruptible_sleep_on_timeout(&pcf_wait, timeout*HZ ); +#endif + } + else + pcf_pending = 0; + sti(); +#if LINUX_VERSION_CODE < 0x02017f + current->timeout = 0; +#endif + } + else { + udelay(100); + } +} + + +static void pcf_isa_handler(int this_irq, void *dev_id, struct pt_regs *regs) { + + pcf_pending = 1; + wake_up_interruptible(&pcf_wait); +} + + +static int pcf_isa_init(void) +{ + if (check_region(gpi.pi_base, 2) < 0 ) { + return -ENODEV; + } else { + request_region(gpi.pi_base, 2, "i2c (isa bus adapter)"); + } + if (gpi.pi_irq > 0) { + if (request_irq(gpi.pi_irq, pcf_isa_handler, 0, "PCF8584", 0) < 0) { + printk("i2c-elektor.o: Request irq%d failed\n", gpi.pi_irq); + gpi.pi_irq = 0; + } + else + enable_irq(gpi.pi_irq); + } + return 0; +} + + +static void pcf_isa_exit(void) +{ + if (gpi.pi_irq > 0) { + disable_irq(gpi.pi_irq); + free_irq(gpi.pi_irq, 0); + } + release_region(gpi.pi_base , 2); +} + + +static int pcf_isa_reg(struct i2c_client *client) +{ + return 0; +} + + +static int pcf_isa_unreg(struct i2c_client *client) +{ + return 0; +} + +static void pcf_isa_inc_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +static void pcf_isa_dec_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ +static struct i2c_algo_pcf_data pcf_isa_data = { + NULL, + pcf_isa_setbyte, + pcf_isa_getbyte, + pcf_isa_getown, + pcf_isa_getclock, + pcf_isa_waitforpin, + 80, 80, 100, /* waits, timeout */ +}; + +static struct i2c_adapter pcf_isa_ops = { + "PCF8584 ISA adapter", + I2C_HW_P_ELEK, + NULL, + &pcf_isa_data, + pcf_isa_inc_use, + pcf_isa_dec_use, + pcf_isa_reg, + pcf_isa_unreg, +}; + +int __init i2c_pcfisa_init(void) +{ + + struct i2c_pcf_isa *pisa = &gpi; + + printk("i2c-elektor.o: i2c pcf8584-isa adapter module\n"); + if (base == 0) + pisa->pi_base = DEFAULT_BASE; + else + pisa->pi_base = base; + + if (irq == 0) + pisa->pi_irq = DEFAULT_IRQ; + else + pisa->pi_irq = irq; + + if (clock == 0) + pisa->pi_clock = DEFAULT_CLOCK; + else + pisa->pi_clock = clock; + + if (own == 0) + pisa->pi_own = DEFAULT_OWN; + else + pisa->pi_own = own; + + pcf_isa_data.data = (void *)pisa; +#if (LINUX_VERSION_CODE >= 0x020301) + init_waitqueue_head(&pcf_wait); +#endif + if (pcf_isa_init() == 0) { + if (i2c_pcf_add_bus(&pcf_isa_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + printk("i2c-elektor.o: found device at %#x.\n", pisa->pi_base); + return 0; +} + + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE +MODULE_AUTHOR("Hans Berglund <hb@spacetec.no>"); +MODULE_DESCRIPTION("I2C-Bus adapter routines for PCF8584 ISA bus adapter"); + +MODULE_PARM(base, "i"); +MODULE_PARM(irq, "i"); +MODULE_PARM(clock, "i"); +MODULE_PARM(own, "i"); + +int init_module(void) +{ + return i2c_pcfisa_init(); +} + +void cleanup_module(void) +{ + i2c_pcf_del_bus(&pcf_isa_ops); + pcf_isa_exit(); +} + +#endif + + diff --git a/drivers/i2c/i2c-elv.c b/drivers/i2c/i2c-elv.c new file mode 100644 index 000000000..1eb17cacf --- /dev/null +++ b/drivers/i2c/i2c-elv.c @@ -0,0 +1,236 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-elv.c i2c-hw access for philips style parallel port adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-99 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. */ +/* ------------------------------------------------------------------------- + +/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even + Frodo Looijaard <frodol@dds.nl> */ + +/* $Id: i2c-elv.c,v 1.12 1999/12/21 23:45:58 frodo Exp $ */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/malloc.h> +#include <linux/version.h> +#if LINUX_VERSION_CODE >= 0x020135 +#include <linux/init.h> +#else +#define __init +#endif + +/* 2.0.0 kernel compatibility */ +#if LINUX_VERSION_CODE < 0x020100 +#define MODULE_AUTHOR(noone) +#define MODULE_DESCRIPTION(none) +#define MODULE_PARM(no,param) +#define MODULE_PARM_DESC(no,description) +#define EXPORT_SYMBOL(noexport) +#define EXPORT_NO_SYMBOLS +#endif + +#if LINUX_VERSION_CODE >= 0x020100 +# include <asm/uaccess.h> +#else +# include <asm/segment.h> +#endif + +#include <linux/ioport.h> +#include <asm/io.h> +#include <linux/errno.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> + +#define DEFAULT_BASE 0x378 +static int base=0; +static unsigned char PortData = 0; + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) /* should be reasonable open, close &c. */ +#define DEB2(x) /* low level debugging - very slow */ +#define DEBE(x) x /* error messages */ +#define DEBINIT(x) x /* detection status messages */ + +/* --- Convenience defines for the parallel port: */ +#define BASE (unsigned int)(data) +#define DATA BASE /* Centronics data port */ +#define STAT (BASE+1) /* Centronics status port */ +#define CTRL (BASE+2) /* Centronics control port */ + + +/* ----- local functions ---------------------------------------------- */ + + +static void bit_elv_setscl(void *data, int state) +{ + if (state) { + PortData &= 0xfe; + } else { + PortData |=1; + } + outb(PortData, DATA); +} + +static void bit_elv_setsda(void *data, int state) +{ + if (state) { + PortData &=0xfd; + } else { + PortData |=2; + } + outb(PortData, DATA); +} + +static int bit_elv_getscl(void *data) +{ + return ( 0 == ( (inb_p(STAT)) & 0x08 ) ); +} + +static int bit_elv_getsda(void *data) +{ + return ( 0 == ( (inb_p(STAT)) & 0x40 ) ); +} + +static int bit_elv_init(void) +{ + if (check_region(base,(base == 0x3bc)? 3 : 8) < 0 ) { + return -ENODEV; + } else { + /* test for ELV adap. */ + if (inb(base+1) & 0x80) { /* BUSY should be high */ + DEBINIT(printk("i2c-elv.o: Busy was low.\n")); + return -ENODEV; + } else { + outb(0x0c,base+2); /* SLCT auf low */ + udelay(400); + if ( !(inb(base+1) && 0x10) ) { + outb(0x04,base+2); + DEBINIT(printk("i2c-elv.o: Select was high.\n")); + return -ENODEV; + } + } + request_region(base,(base == 0x3bc)? 3 : 8, + "i2c (ELV adapter)"); + PortData = 0; + bit_elv_setsda((void*)base,1); + bit_elv_setscl((void*)base,1); + } + return 0; +} + +static void bit_elv_exit(void) +{ + release_region( base , (base == 0x3bc)? 3 : 8 ); +} + +static int bit_elv_reg(struct i2c_client *client) +{ + return 0; +} + +static int bit_elv_unreg(struct i2c_client *client) +{ + return 0; +} + +static void bit_elv_inc_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +static void bit_elv_dec_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ +static struct i2c_algo_bit_data bit_elv_data = { + NULL, + bit_elv_setsda, + bit_elv_setscl, + bit_elv_getsda, + bit_elv_getscl, + 80, 80, 100, /* waits, timeout */ +}; + +static struct i2c_adapter bit_elv_ops = { + "ELV Parallel port adaptor", + I2C_HW_B_ELV, + NULL, + &bit_elv_data, + bit_elv_inc_use, + bit_elv_dec_use, + bit_elv_reg, + bit_elv_unreg, +}; + +int __init i2c_bitelv_init(void) +{ + printk("i2c-elv.o: i2c ELV parallel port adapter module\n"); + if (base==0) { + /* probe some values */ + base=DEFAULT_BASE; + bit_elv_data.data=(void*)DEFAULT_BASE; + if (bit_elv_init()==0) { + if(i2c_bit_add_bus(&bit_elv_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + } else { + bit_elv_ops.data=(void*)base; + if (bit_elv_init()==0) { + if(i2c_bit_add_bus(&bit_elv_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + } + printk("i2c-elv.o: found device at %#x.\n",base); + return 0; +} + + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE +MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>"); +MODULE_DESCRIPTION("I2C-Bus adapter routines for ELV parallel port adapter") +; + +MODULE_PARM(base, "i"); + +int init_module(void) +{ + return i2c_bitelv_init(); +} + +void cleanup_module(void) +{ + i2c_bit_del_bus(&bit_elv_ops); + bit_elv_exit(); +} + +#endif diff --git a/drivers/i2c/i2c-pcf8584.h b/drivers/i2c/i2c-pcf8584.h new file mode 100644 index 000000000..7dabbd9a9 --- /dev/null +++ b/drivers/i2c/i2c-pcf8584.h @@ -0,0 +1,78 @@ +/* -------------------------------------------------------------------- */ +/* i2c-pcf8584.h: PCF 8584 global defines */ +/* -------------------------------------------------------------------- */ +/* Copyright (C) 1996 Simon G. Vogl + 1999 Hans Berglund + + 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. */ +/* -------------------------------------------------------------------- */ + +/* With some changes from Frodo Looijaard <frodol@dds.nl> */ + +/* $Id: i2c-pcf8584.h,v 1.2 1999/12/21 23:45:58 frodo Exp $ */ + +#ifndef I2C_PCF8584_H +#define I2C_PCF8584_H 1 + +/* ----- Control register bits ---------------------------------------- */ +#define I2C_PCF_PIN 0x80 +#define I2C_PCF_ESO 0x40 +#define I2C_PCF_ES1 0x20 +#define I2C_PCF_ES2 0x10 +#define I2C_PCF_ENI 0x08 +#define I2C_PCF_STA 0x04 +#define I2C_PCF_STO 0x02 +#define I2C_PCF_ACK 0x01 + +#define I2C_PCF_START (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_STA | I2C_PCF_ACK) +#define I2C_PCF_STOP (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_STO | I2C_PCF_ACK) +#define I2C_PCF_REPSTART ( I2C_PCF_ESO | I2C_PCF_STA | I2C_PCF_ACK) +#define I2C_PCF_IDLE (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_ACK) + +/* ----- Status register bits ----------------------------------------- */ +/*#define I2C_PCF_PIN 0x80 as above*/ + +#define I2C_PCF_INI 0x40 /* 1 if not initialized */ +#define I2C_PCF_STS 0x20 +#define I2C_PCF_BER 0x10 +#define I2C_PCF_AD0 0x08 +#define I2C_PCF_LRB 0x08 +#define I2C_PCF_AAS 0x04 +#define I2C_PCF_LAB 0x02 +#define I2C_PCF_BB 0x01 + +/* ----- Chip clock frequencies --------------------------------------- */ +#define I2C_PCF_CLK3 0x00 +#define I2C_PCF_CLK443 0x10 +#define I2C_PCF_CLK6 0x14 +#define I2C_PCF_CLK8 0x18 +#define I2C_PCF_CLK12 0x1c + +/* ----- transmission frequencies ------------------------------------- */ +#define I2C_PCF_TRNS90 0x00 /* 90 kHz */ +#define I2C_PCF_TRNS45 0x01 /* 45 kHz */ +#define I2C_PCF_TRNS11 0x02 /* 11 kHz */ +#define I2C_PCF_TRNS15 0x03 /* 1.5 kHz */ + + +/* ----- Access to internal registers according to ES1,ES2 ------------ */ +/* they are mapped to the data port ( a0 = 0 ) */ +/* available when ESO == 0 : */ + +#define I2C_PCF_OWNADR 0 +#define I2C_PCF_INTREG I2C_PCF_ES2 +#define I2C_PCF_CLKREG I2C_PCF_ES1 + +#endif I2C_PCF8584_H diff --git a/drivers/i2c/i2c-philips-par.c b/drivers/i2c/i2c-philips-par.c new file mode 100644 index 000000000..52079eb88 --- /dev/null +++ b/drivers/i2c/i2c-philips-par.c @@ -0,0 +1,232 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-philips-par.c i2c-hw access for philips style parallel port adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-99 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. */ +/* ------------------------------------------------------------------------- + +/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even + Frodo Looijaard <frodol@dds.nl> */ + +/* $Id: i2c-philips-par.c,v 1.12 1999/12/21 23:45:58 frodo Exp $ */ + +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <linux/module.h> +#if LINUX_VERSION_CODE >= 0x020135 +#include <linux/init.h> +#else +#define __init +#endif +#include <asm/io.h> +#include <linux/stddef.h> + +/* 2.0.0 kernel compatibility */ +#if LINUX_VERSION_CODE < 0x020100 +#define MODULE_AUTHOR(noone) +#define MODULE_DESCRIPTION(none) +#define MODULE_PARM(no,param) +#define MODULE_PARM_DESC(no,description) +#define EXPORT_SYMBOL(noexport) +#define EXPORT_NO_SYMBOLS +#endif + +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> + +#define DEFAULT_BASE 0x378 +static int base=0; + +/* Note: all we need to know is the base address of the parallel port, so + * instead of having a dedicated struct to store this value, we store this + * int in the pointer field (=bit_lp_ops.data) itself. + */ + +/* Note2: as the hw that implements the i2c bus on the parallel port is + * incompatible with other epp stuff etc., we access the port exclusively + * and don't cooperate with parport functions. + */ + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) /* should be reasonable open, close &c. */ +#define DEB2(x) /* low level debugging - very slow */ +#define DEBE(x) x /* error messages */ + +/* ----- printer port defines ------------------------------------------*/ + /* Pin Port Inverted name */ +#define I2C_ON 0x20 /* 12 status N paper */ + /* ... only for phil. not used */ +#define I2C_SDA 0x80 /* 9 data N data7 */ +#define I2C_SCL 0x08 /* 17 ctrl N dsel */ + +#define I2C_SDAIN 0x80 /* 11 stat Y busy */ +#define I2C_SCLIN 0x08 /* 15 stat Y enable */ + +#define I2C_DMASK 0x7f +#define I2C_CMASK 0xf7 + +/* --- Convenience defines for the parallel port: */ +#define BASE (unsigned int)(data) +#define DATA BASE /* Centronics data port */ +#define STAT (BASE+1) /* Centronics status port */ +#define CTRL (BASE+2) /* Centronics control port */ + +/* ----- local functions ---------------------------------------------- */ + +static void bit_lp_setscl(void *data, int state) +{ + /*be cautious about state of the control register - + touch only the one bit needed*/ + if (state) { + outb(inb(CTRL)|I2C_SCL, CTRL); + } else { + outb(inb(CTRL)&I2C_CMASK, CTRL); + } +} + +static void bit_lp_setsda(void *data, int state) +{ + if (state) { + outb(I2C_DMASK , DATA); + } else { + outb(I2C_SDA , DATA); + } +} + +static int bit_lp_getscl(void *data) +{ + return ( 0 != ( (inb(STAT)) & I2C_SCLIN ) ); +} + +static int bit_lp_getsda(void *data) +{ + return ( 0 != ( (inb(STAT)) & I2C_SDAIN ) ); +} + +static int bit_lp_init(void) +{ + if (check_region(base,(base == 0x3bc)? 3 : 8) < 0 ) { + return -ENODEV; + } else { + request_region(base,(base == 0x3bc)? 3 : 8, + "i2c (parallel port adapter)"); + /* reset hardware to sane state */ + bit_lp_setsda((void*)base,1); + bit_lp_setscl((void*)base,1); + } + return 0; +} + +static void bit_lp_exit(void) +{ + release_region( base , (base == 0x3bc)? 3 : 8 ); +} + +static int bit_lp_reg(struct i2c_client *client) +{ + return 0; +} + +static int bit_lp_unreg(struct i2c_client *client) +{ + return 0; +} + +static void bit_lp_inc_use(struct i2c_adapter *adap) +{ + MOD_INC_USE_COUNT; +} + +static void bit_lp_dec_use(struct i2c_adapter *adap) +{ + MOD_DEC_USE_COUNT; +} + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ + +static struct i2c_algo_bit_data bit_lp_data = { + NULL, + bit_lp_setsda, + bit_lp_setscl, + bit_lp_getsda, + bit_lp_getscl, + 80, 80, 100, /* waits, timeout */ +}; + +static struct i2c_adapter bit_lp_ops = { + "Philips Parallel port adapter", + I2C_HW_B_LP, + NULL, + &bit_lp_data, + bit_lp_inc_use, + bit_lp_dec_use, + bit_lp_reg, + bit_lp_unreg, +}; + + +int __init i2c_bitlp_init(void) +{ + printk("i2c-philips-par.o: i2c Philips parallel port adapter module\n"); + if (base==0) { + /* probe some values */ + base=DEFAULT_BASE; + bit_lp_data.data=(void*)DEFAULT_BASE; + if (bit_lp_init()==0) { + if (i2c_bit_add_bus(&bit_lp_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + } else { + bit_lp_data.data=(void*)base; + if (bit_lp_init()==0) { + if (i2c_bit_add_bus(&bit_lp_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + } + printk("i2c-philips-par.o: found device at %#x.\n",base); + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE +MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>"); +MODULE_DESCRIPTION("I2C-Bus adapter routines for Philips parallel port adapter"); + +MODULE_PARM(base, "i"); + +int init_module(void) +{ + return i2c_bitlp_init(); +} + +void cleanup_module(void) +{ + i2c_bit_del_bus(&bit_lp_ops); + bit_lp_exit(); +} + +#endif + + + diff --git a/drivers/i2c/i2c-velleman.c b/drivers/i2c/i2c-velleman.c new file mode 100644 index 000000000..7a3bc3522 --- /dev/null +++ b/drivers/i2c/i2c-velleman.c @@ -0,0 +1,219 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-velleman.c i2c-hw access for Velleman K9000 adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-96 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. */ +/* ------------------------------------------------------------------------- + +/* $Id: i2c-velleman.c,v 1.14 1999/12/21 23:45:58 frodo Exp $ */ + +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <linux/module.h> +#if LINUX_VERSION_CODE >= 0x020135 +#include <linux/init.h> +#else +#define __init +#endif +#include <linux/string.h> /* for 2.0 kernels to get NULL */ +#include <asm/errno.h> /* for 2.0 kernels to get ENODEV */ +#include <asm/io.h> + +/* 2.0.0 kernel compatibility */ +#if LINUX_VERSION_CODE < 0x020100 +#define MODULE_AUTHOR(noone) +#define MODULE_DESCRIPTION(none) +#define MODULE_PARM(no,param) +#define MODULE_PARM_DESC(no,description) +#define EXPORT_SYMBOL(noexport) +#define EXPORT_NO_SYMBOLS +#endif + +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) /* should be reasonable open, close &c. */ +#define DEB2(x) /* low level debugging - very slow */ +#define DEBE(x) x /* error messages */ + + /* Pin Port Inverted name */ +#define I2C_SDA 0x02 /* ctrl bit 1 (inv) */ +#define I2C_SCL 0x08 /* ctrl bit 3 (inv) */ + +#define I2C_SDAIN 0x10 /* stat bit 4 */ +#define I2C_SCLIN 0x08 /* ctrl bit 3 (inv) (reads own output) */ + +#define I2C_DMASK 0xfd +#define I2C_CMASK 0xf7 + + +/* --- Convenience defines for the parallel port: */ +#define BASE (unsigned int)(data) +#define DATA BASE /* Centronics data port */ +#define STAT (BASE+1) /* Centronics status port */ +#define CTRL (BASE+2) /* Centronics control port */ + +#define DEFAULT_BASE 0x378 +static int base=0; + +/* ----- local functions --------------------------------------------------- */ + +static void bit_velle_setscl(void *data, int state) +{ + if (state) { + outb(inb(CTRL) & I2C_CMASK, CTRL); + } else { + outb(inb(CTRL) | I2C_SCL, CTRL); + } + +} + +static void bit_velle_setsda(void *data, int state) +{ + if (state) { + outb(inb(CTRL) & I2C_DMASK , CTRL); + } else { + outb(inb(CTRL) | I2C_SDA, CTRL); + } + +} + +static int bit_velle_getscl(void *data) +{ + return ( 0 == ( (inb(CTRL)) & I2C_SCLIN ) ); +} + +static int bit_velle_getsda(void *data) +{ + return ( 0 != ( (inb(STAT)) & I2C_SDAIN ) ); +} + +static int bit_velle_init(void) +{ + if (check_region(base,(base == 0x3bc)? 3 : 8) < 0 ) { + DEBE(printk("i2c-velleman.o: Port %#x already in use.\n", base)); + return -ENODEV; + } else { + request_region(base, (base == 0x3bc)? 3 : 8, + "i2c (Vellemann adapter)"); + bit_velle_setsda((void*)base,1); + bit_velle_setscl((void*)base,1); + } + return 0; +} + +static void bit_velle_exit(void) +{ + release_region( base , (base == 0x3bc)? 3 : 8 ); +} + + +static int bit_velle_reg(struct i2c_client *client) +{ + return 0; +} + +static int bit_velle_unreg(struct i2c_client *client) +{ + return 0; +} + +static void bit_velle_inc_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +static void bit_velle_dec_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ + +static struct i2c_algo_bit_data bit_velle_data = { + NULL, + bit_velle_setsda, + bit_velle_setscl, + bit_velle_getsda, + bit_velle_getscl, + 10, 10, 100, /* waits, timeout */ +}; + +static struct i2c_adapter bit_velle_ops = { + "Velleman K8000", + I2C_HW_B_VELLE, + NULL, + &bit_velle_data, + bit_velle_inc_use, + bit_velle_dec_use, + bit_velle_reg, + bit_velle_unreg, +}; + +int __init i2c_bitvelle_init(void) +{ + printk("i2c-velleman.o: i2c Velleman K8000 adapter module\n"); + if (base==0) { + /* probe some values */ + base=DEFAULT_BASE; + bit_velle_data.data=(void*)DEFAULT_BASE; + if (bit_velle_init()==0) { + if(i2c_bit_add_bus(&bit_velle_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + } else { + bit_velle_data.data=(void*)base; + if (bit_velle_init()==0) { + if(i2c_bit_add_bus(&bit_velle_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + } + printk("i2c-velleman.o: found device at %#x.\n",base); + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE +MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>"); +MODULE_DESCRIPTION("I2C-Bus adapter routines for Velleman K8000 adapter"); + +MODULE_PARM(base, "i"); + +int init_module(void) +{ + return i2c_bitvelle_init(); +} + +void cleanup_module(void) +{ + i2c_bit_del_bus(&bit_velle_ops); + bit_velle_exit(); +} + +#endif |