diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/i2c/algos |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/i2c/algos')
-rw-r--r-- | drivers/i2c/algos/Kconfig | 70 | ||||
-rw-r--r-- | drivers/i2c/algos/Makefile | 14 | ||||
-rw-r--r-- | drivers/i2c/algos/i2c-algo-bit.c | 573 | ||||
-rw-r--r-- | drivers/i2c/algos/i2c-algo-ite.c | 812 | ||||
-rw-r--r-- | drivers/i2c/algos/i2c-algo-ite.h | 117 | ||||
-rw-r--r-- | drivers/i2c/algos/i2c-algo-pca.c | 399 | ||||
-rw-r--r-- | drivers/i2c/algos/i2c-algo-pca.h | 26 | ||||
-rw-r--r-- | drivers/i2c/algos/i2c-algo-pcf.c | 507 | ||||
-rw-r--r-- | drivers/i2c/algos/i2c-algo-pcf.h | 76 | ||||
-rw-r--r-- | drivers/i2c/algos/i2c-algo-sgi.c | 189 | ||||
-rw-r--r-- | drivers/i2c/algos/i2c-algo-sibyte.c | 222 |
11 files changed, 3005 insertions, 0 deletions
diff --git a/drivers/i2c/algos/Kconfig b/drivers/i2c/algos/Kconfig new file mode 100644 index 000000000000..30408015d231 --- /dev/null +++ b/drivers/i2c/algos/Kconfig @@ -0,0 +1,70 @@ +# +# Character device configuration +# + +menu "I2C Algorithms" + depends on I2C + +config I2C_ALGOBIT + tristate "I2C bit-banging interfaces" + depends on I2C + help + This allows you to use a range of I2C adapters called bit-banging + adapters. Say Y if you own an I2C adapter belonging to this class + and then say Y to the specific driver for you adapter below. + + This support is also available as a module. If so, the module + will be called i2c-algo-bit. + +config I2C_ALGOPCF + tristate "I2C PCF 8584 interfaces" + depends on I2C + help + This allows you to use a range of I2C adapters called PCF adapters. + Say Y if you own an I2C adapter belonging to this class and then say + Y to the specific driver for you adapter below. + + This support is also available as a module. If so, the module + will be called i2c-algo-pcf. + +config I2C_ALGOPCA + tristate "I2C PCA 9564 interfaces" + depends on I2C + help + This allows you to use a range of I2C adapters called PCA adapters. + Say Y if you own an I2C adapter belonging to this class and then say + Y to the specific driver for you adapter below. + + This support is also available as a module. If so, the module + will be called i2c-algo-pca. + +config I2C_ALGOITE + tristate "ITE I2C Algorithm" + depends on MIPS_ITE8172 && I2C + help + This supports the use of the ITE8172 I2C interface found on some MIPS + systems. Say Y if you have one of these. You should also say Y for + the ITE I2C peripheral driver support below. + + This support is also available as a module. If so, the module + will be called i2c-algo-ite. + +config I2C_ALGO8XX + tristate "MPC8xx CPM I2C interface" + depends on 8xx && I2C + +config I2C_ALGO_SIBYTE + tristate "SiByte SMBus interface" + depends on SIBYTE_SB1xxx_SOC && I2C + help + Supports the SiByte SOC on-chip I2C interfaces (2 channels). + +config I2C_ALGO_SGI + tristate "I2C SGI interfaces" + depends on I2C && (SGI_IP22 || SGI_IP32 || X86_VISWS) + help + Supports the SGI interfaces like the ones found on SGI Indy VINO + or SGI O2 MACE. + +endmenu + diff --git a/drivers/i2c/algos/Makefile b/drivers/i2c/algos/Makefile new file mode 100644 index 000000000000..867fe1f67401 --- /dev/null +++ b/drivers/i2c/algos/Makefile @@ -0,0 +1,14 @@ +# +# Makefile for the i2c algorithms +# + +obj-$(CONFIG_I2C_ALGOBIT) += i2c-algo-bit.o +obj-$(CONFIG_I2C_ALGOPCF) += i2c-algo-pcf.o +obj-$(CONFIG_I2C_ALGOPCA) += i2c-algo-pca.o +obj-$(CONFIG_I2C_ALGOITE) += i2c-algo-ite.o +obj-$(CONFIG_I2C_ALGO_SIBYTE) += i2c-algo-sibyte.o +obj-$(CONFIG_I2C_ALGO_SGI) += i2c-algo-sgi.o + +ifeq ($(CONFIG_I2C_DEBUG_ALGO),y) +EXTRA_CFLAGS += -DDEBUG +endif diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c new file mode 100644 index 000000000000..fb5b732238ed --- /dev/null +++ b/drivers/i2c/algos/i2c-algo-bit.c @@ -0,0 +1,573 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-algo-bit.c i2c driver algorithms for bit-shift adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-2000 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 Frodo Looijaard <frodol@dds.nl>, Kyösti Mälkki + <kmalkki@cc.hut.fi> and Jean Delvare <khali@linux-fr.org> */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/init.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 */ + + +/* ----- global variables --------------------------------------------- */ + +/* module parameters: + */ +static int i2c_debug; +static int bit_test; /* see if the line-setting functions work */ + +/* --- 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); +} + +/* + * Raise scl line, and do checking for delays. This is necessary for slower + * devices. + */ +static inline int sclhi(struct i2c_algo_bit_data *adap) +{ + unsigned long start; + + setscl(adap,1); + + /* Not all adapters have scl sense line... */ + if (adap->getscl == NULL ) { + udelay(adap->udelay); + return 0; + } + + start=jiffies; + 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. + */ + if (time_after_eq(jiffies, start+adap->timeout)) { + return -ETIMEDOUT; + } + cond_resched(); + } + DEBSTAT(printk(KERN_DEBUG "needed %ld jiffies\n", jiffies-start)); + udelay(adap->udelay); + 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); + sclhi(adap); + 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 occurred (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 */ + for ( i=7 ; i>=0 ; i-- ) { + sb = c & ( 1 << i ); + setsda(adap,sb); + udelay(adap->udelay); + DEBPROTO(printk(KERN_DEBUG "%d",sb!=0)); + if (sclhi(adap)<0) { /* timed out */ + sdahi(adap); /* we don't want to block the net */ + DEB2(printk(KERN_DEBUG " i2c_outb: 0x%02x, timeout at bit #%d\n", c&0xff, i)); + 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 */ + DEB2(printk(KERN_DEBUG " i2c_outb: 0x%02x, timeout at ack\n", c&0xff)); + return -ETIMEDOUT; + }; + /* read ack: SDA should be pulled down by slave */ + ack=getsda(adap); /* ack: sda is pulled low ->success. */ + DEB2(printk(KERN_DEBUG " i2c_outb: 0x%02x , getsda() = %d\n", c & 0xff, ack)); + + DEBPROTO( printk(KERN_DEBUG "[%2.2x]",c&0xff) ); + DEBPROTO(if (0==ack){ printk(KERN_DEBUG " A ");} else printk(KERN_DEBUG " 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 */ + sdahi(adap); + for (i=0;i<8;i++) { + if (sclhi(adap)<0) { /* timeout */ + DEB2(printk(KERN_DEBUG " i2c_inb: timeout at bit #%d\n", 7-i)); + return -ETIMEDOUT; + }; + indata *= 2; + if ( getsda(adap) ) + indata |= 0x01; + scllo(adap); + } + /* assert: scl is low */ + DEB2(printk(KERN_DEBUG "i2c_inb: 0x%02x\n", indata & 0xff)); + + DEBPROTO(printk(KERN_DEBUG " 0x%02x", 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; + + if (adap->getscl==NULL) + printk(KERN_INFO "i2c-algo-bit.o: Testing SDA only, " + "SCL is not readable.\n"); + + sda=getsda(adap); + scl=(adap->getscl==NULL?1:getscl(adap)); + printk(KERN_DEBUG "i2c-algo-bit.o: (0) scl=%d, sda=%d\n",scl,sda); + if (!scl || !sda ) { + printk(KERN_WARNING "i2c-algo-bit.o: %s seems to be busy.\n", name); + goto bailout; + } + + sdalo(adap); + sda=getsda(adap); + scl=(adap->getscl==NULL?1:getscl(adap)); + printk(KERN_DEBUG "i2c-algo-bit.o: (1) scl=%d, sda=%d\n",scl,sda); + if ( 0 != sda ) { + printk(KERN_WARNING "i2c-algo-bit.o: SDA stuck high!\n"); + goto bailout; + } + if ( 0 == scl ) { + printk(KERN_WARNING "i2c-algo-bit.o: SCL unexpected low " + "while pulling SDA low!\n"); + goto bailout; + } + + sdahi(adap); + sda=getsda(adap); + scl=(adap->getscl==NULL?1:getscl(adap)); + printk(KERN_DEBUG "i2c-algo-bit.o: (2) scl=%d, sda=%d\n",scl,sda); + if ( 0 == sda ) { + printk(KERN_WARNING "i2c-algo-bit.o: SDA stuck low!\n"); + goto bailout; + } + if ( 0 == scl ) { + printk(KERN_WARNING "i2c-algo-bit.o: SCL unexpected low " + "while pulling SDA high!\n"); + goto bailout; + } + + scllo(adap); + sda=getsda(adap); + scl=(adap->getscl==NULL?0:getscl(adap)); + printk(KERN_DEBUG "i2c-algo-bit.o: (3) scl=%d, sda=%d\n",scl,sda); + if ( 0 != scl ) { + printk(KERN_WARNING "i2c-algo-bit.o: SCL stuck high!\n"); + goto bailout; + } + if ( 0 == sda ) { + printk(KERN_WARNING "i2c-algo-bit.o: SDA unexpected low " + "while pulling SCL low!\n"); + goto bailout; + } + + sclhi(adap); + sda=getsda(adap); + scl=(adap->getscl==NULL?1:getscl(adap)); + printk(KERN_DEBUG "i2c-algo-bit.o: (4) scl=%d, sda=%d\n",scl,sda); + if ( 0 == scl ) { + printk(KERN_WARNING "i2c-algo-bit.o: SCL stuck low!\n"); + goto bailout; + } + if ( 0 == sda ) { + printk(KERN_WARNING "i2c-algo-bit.o: SDA unexpected low " + "while pulling SCL high!\n"); + goto bailout; + } + printk(KERN_INFO "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(KERN_DEBUG "i2c-algo-bit.o: Used %d tries to %s client at 0x%02x : %s\n", + i+1, addr & 1 ? "read" : "write", addr>>1, + ret==1 ? "success" : ret==0 ? "no ack" : "failed, timeout?" ) + ); + return ret; +} + +static int sendbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) +{ + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + char c; + const char *temp = msg->buf; + int count = msg->len; + unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK; + int retval; + int wrcount=0; + + while (count > 0) { + c = *temp; + DEB2(dev_dbg(&i2c_adap->dev, "sendbytes: writing %2.2X\n", c&0xff)); + retval = i2c_outb(i2c_adap,c); + if ((retval>0) || (nak_ok && (retval==0))) { /* ok or ignored NAK */ + count--; + temp++; + wrcount++; + } else { /* arbitration or no acknowledge */ + dev_err(&i2c_adap->dev, "sendbytes: error - bailout.\n"); + 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, struct i2c_msg *msg) +{ + int inval; + int rdcount=0; /* counts bytes read */ + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + char *temp = msg->buf; + int count = msg->len; + + 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(KERN_ERR "i2c-algo-bit.o: readbytes: i2c_inb timed out.\n"); + break; + } + + temp++; + count--; + + if (msg->flags & I2C_M_NO_RD_ACK) + continue; + + if ( count > 0 ) { /* 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(KERN_ERR "i2c-algo-bit.o: readbytes: Timeout at ack\n"); + return -ETIMEDOUT; + }; + scllo(adap); + sdahi(adap); + } + 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, or IGNORE_NAK flag was set + * -x an error occurred (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) +{ + unsigned short flags = msg->flags; + unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK; + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + unsigned char addr; + int ret, retries; + + retries = nak_ok ? 0 : i2c_adap->retries; + + if ( (flags & I2C_M_TEN) ) { + /* a ten bit address */ + addr = 0xf0 | (( msg->addr >> 7) & 0x03); + DEB2(printk(KERN_DEBUG "addr0: %d\n",addr)); + /* try extended address code...*/ + ret = try_address(i2c_adap, addr, retries); + if ((ret != 1) && !nak_ok) { + printk(KERN_ERR "died at extended address code.\n"); + return -EREMOTEIO; + } + /* the remaining 8 bit address */ + ret = i2c_outb(i2c_adap,msg->addr & 0x7f); + if ((ret != 1) && !nak_ok) { + /* the chip did not ack / xmission error occurred */ + printk(KERN_ERR "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) && !nak_ok) { + printk(KERN_ERR "died at extended address code.\n"); + return -EREMOTEIO; + } + } + } else { /* normal 7bit address */ + addr = ( msg->addr << 1 ); + if (flags & I2C_M_RD ) + addr |= 1; + if (flags & I2C_M_REV_DIR_ADDR ) + addr ^= 1; + ret = try_address(i2c_adap, addr, retries); + if ((ret!=1) && !nak_ok) + 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; + unsigned short nak_ok; + + i2c_start(adap); + for (i=0;i<num;i++) { + pmsg = &msgs[i]; + nak_ok = pmsg->flags & I2C_M_IGNORE_NAK; + if (!(pmsg->flags & I2C_M_NOSTART)) { + if (i) { + i2c_repstart(adap); + } + ret = bit_doAddress(i2c_adap, pmsg); + if ((ret != 0) && !nak_ok) { + DEB2(printk(KERN_DEBUG "i2c-algo-bit.o: NAK from device addr %2.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); + DEB2(printk(KERN_DEBUG "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); + DEB2(printk(KERN_DEBUG "i2c-algo-bit.o: wrote %d bytes.\n",ret)); + if (ret < pmsg->len ) { + return (ret<0) ? ret : -EREMOTEIO; + } + } + } + i2c_stop(adap); + return num; +} + +static u32 bit_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | + I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING; +} + + +/* -----exported algorithm data: ------------------------------------- */ + +static struct i2c_algorithm i2c_bit_algo = { + .name = "Bit-shift algorithm", + .id = I2C_ALGO_BIT, + .master_xfer = bit_xfer, + .functionality = bit_func, +}; + +/* + * registering functions to load algorithms at runtime + */ +int i2c_bit_add_bus(struct i2c_adapter *adap) +{ + 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(dev_dbg(&adap->dev, "hw routines registered.\n")); + + /* 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 */ + + i2c_add_adapter(adap); + return 0; +} + + +int i2c_bit_del_bus(struct i2c_adapter *adap) +{ + return i2c_del_adapter(adap); +} + +EXPORT_SYMBOL(i2c_bit_add_bus); +EXPORT_SYMBOL(i2c_bit_del_bus); + +MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>"); +MODULE_DESCRIPTION("I2C-Bus bit-banging algorithm"); +MODULE_LICENSE("GPL"); + +module_param(bit_test, bool, 0); +module_param(i2c_debug, int, S_IRUGO | S_IWUSR); + +MODULE_PARM_DESC(bit_test, "Test the lines of the bus to see if it is stuck"); +MODULE_PARM_DESC(i2c_debug, + "debug level - 0 off; 1 normal; 2,3 more verbose; 9 bit-protocol"); diff --git a/drivers/i2c/algos/i2c-algo-ite.c b/drivers/i2c/algos/i2c-algo-ite.c new file mode 100644 index 000000000000..68e9e6832ca0 --- /dev/null +++ b/drivers/i2c/algos/i2c-algo-ite.c @@ -0,0 +1,812 @@ +/* + ------------------------------------------------------------------------- + i2c-algo-ite.c i2c driver algorithms for ITE adapters + + Hai-Pao Fan, MontaVista Software, Inc. + hpfan@mvista.com or source@mvista.com + + Copyright 2000 MontaVista Software Inc. + + --------------------------------------------------------------------------- + This file was highly leveraged from i2c-algo-pcf.c, which was created + by Simon G. Vogl and Hans Berglund: + + + Copyright (C) 1995-1997 Simon G. Vogl + 1998-2000 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 + Frodo Looijaard <frodol@dds.nl> ,and also from Martin Bailey + <mbailey@littlefeet-inc.com> */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <asm/uaccess.h> +#include <linux/ioport.h> +#include <linux/errno.h> +#include <linux/sched.h> + +#include <linux/i2c.h> +#include <linux/i2c-algo-ite.h> +#include "i2c-algo-ite.h" + +#define PM_DSR IT8172_PCI_IO_BASE + IT_PM_DSR +#define PM_IBSR IT8172_PCI_IO_BASE + IT_PM_DSR + 0x04 +#define GPIO_CCR IT8172_PCI_IO_BASE + IT_GPCCR + +#define DEB2(x) if (i2c_debug>=2) x +#define DEB3(x) if (i2c_debug>=3) x /* print several statistical values*/ +#define DEF_TIMEOUT 16 + + +/* module parameters: + */ +static int i2c_debug; +static int iic_test; /* see if the line-setting functions work */ + +/* --- setting states on the bus with the right timing: --------------- */ + +#define get_clock(adap) adap->getclock(adap->data) +#define iic_outw(adap, reg, val) adap->setiic(adap->data, reg, val) +#define iic_inw(adap, reg) adap->getiic(adap->data, reg) + + +/* --- other auxiliary functions -------------------------------------- */ + +static void iic_start(struct i2c_algo_iic_data *adap) +{ + iic_outw(adap,ITE_I2CHCR,ITE_CMD); +} + +static void iic_stop(struct i2c_algo_iic_data *adap) +{ + iic_outw(adap,ITE_I2CHCR,0); + iic_outw(adap,ITE_I2CHSR,ITE_I2CHSR_TDI); +} + +static void iic_reset(struct i2c_algo_iic_data *adap) +{ + iic_outw(adap, PM_IBSR, iic_inw(adap, PM_IBSR) | 0x80); +} + + +static int wait_for_bb(struct i2c_algo_iic_data *adap) +{ + int timeout = DEF_TIMEOUT; + short status; + + status = iic_inw(adap, ITE_I2CHSR); +#ifndef STUB_I2C + while (timeout-- && (status & ITE_I2CHSR_HB)) { + udelay(1000); /* How much is this? */ + status = iic_inw(adap, ITE_I2CHSR); + } +#endif + if (timeout<=0) { + printk(KERN_ERR "Timeout, host is busy\n"); + iic_reset(adap); + } + return(timeout<=0); +} + +/* After we issue a transaction on the IIC bus, this function + * is called. It puts this process to sleep until we get an interrupt from + * from the controller telling us that the transaction we requested in complete. + */ +static int wait_for_pin(struct i2c_algo_iic_data *adap, short *status) { + + int timeout = DEF_TIMEOUT; + + timeout = wait_for_bb(adap); + if (timeout) { + DEB2(printk("Timeout waiting for host not busy\n");) + return -EIO; + } + timeout = DEF_TIMEOUT; + + *status = iic_inw(adap, ITE_I2CHSR); +#ifndef STUB_I2C + while (timeout-- && !(*status & ITE_I2CHSR_TDI)) { + adap->waitforpin(); + *status = iic_inw(adap, ITE_I2CHSR); + } +#endif + if (timeout <= 0) + return(-1); + else + return(0); +} + +static int wait_for_fe(struct i2c_algo_iic_data *adap, short *status) +{ + int timeout = DEF_TIMEOUT; + + *status = iic_inw(adap, ITE_I2CFSR); +#ifndef STUB_I2C + while (timeout-- && (*status & ITE_I2CFSR_FE)) { + udelay(1000); + iic_inw(adap, ITE_I2CFSR); + } +#endif + if (timeout <= 0) + return(-1); + else + return(0); +} + +static int iic_init (struct i2c_algo_iic_data *adap) +{ + short i; + + /* Clear bit 7 to set I2C to normal operation mode */ + i=iic_inw(adap, PM_DSR)& 0xff7f; + iic_outw(adap, PM_DSR, i); + + /* set IT_GPCCR port C bit 2&3 as function 2 */ + i = iic_inw(adap, GPIO_CCR) & 0xfc0f; + iic_outw(adap,GPIO_CCR,i); + + /* Clear slave address/sub-address */ + iic_outw(adap,ITE_I2CSAR, 0); + iic_outw(adap,ITE_I2CSSAR, 0); + + /* Set clock counter register */ + iic_outw(adap,ITE_I2CCKCNT, get_clock(adap)); + + /* Set START/reSTART/STOP time registers */ + iic_outw(adap,ITE_I2CSHDR, 0x0a); + iic_outw(adap,ITE_I2CRSUR, 0x0a); + iic_outw(adap,ITE_I2CPSUR, 0x0a); + + /* Enable interrupts on completing the current transaction */ + iic_outw(adap,ITE_I2CHCR, ITE_I2CHCR_IE | ITE_I2CHCR_HCE); + + /* Clear transfer count */ + iic_outw(adap,ITE_I2CFBCR, 0x0); + + DEB2(printk("iic_init: Initialized IIC on ITE 0x%x\n", + iic_inw(adap, ITE_I2CHSR))); + 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_iic_data *adap, char *name) { +#if 0 + int scl,sda; + sda=getsda(adap); + if (adap->getscl==NULL) { + printk("test_bus: Warning: Adapter can't read from clock line - skipping test.\n"); + return 0; + } + scl=getscl(adap); + printk("test_bus: Adapter: %s scl: %d sda: %d -- testing...\n", + name,getscl(adap),getsda(adap)); + if (!scl || !sda ) { + printk("test_bus: %s seems to be busy.\n",adap->name); + goto bailout; + } + sdalo(adap); + printk("test_bus:1 scl: %d sda: %d \n",getscl(adap), + getsda(adap)); + if ( 0 != getsda(adap) ) { + printk("test_bus: %s SDA stuck high!\n",name); + sdahi(adap); + goto bailout; + } + if ( 0 == getscl(adap) ) { + printk("test_bus: %s SCL unexpected low while pulling SDA low!\n", + name); + goto bailout; + } + sdahi(adap); + printk("test_bus:2 scl: %d sda: %d \n",getscl(adap), + getsda(adap)); + if ( 0 == getsda(adap) ) { + printk("test_bus: %s SDA stuck low!\n",name); + sdahi(adap); + goto bailout; + } + if ( 0 == getscl(adap) ) { + printk("test_bus: %s SCL unexpected low while SDA high!\n", + adap->name); + goto bailout; + } + scllo(adap); + printk("test_bus:3 scl: %d sda: %d \n",getscl(adap), + getsda(adap)); + if ( 0 != getscl(adap) ) { + + sclhi(adap); + goto bailout; + } + if ( 0 == getsda(adap) ) { + printk("test_bus: %s SDA unexpected low while pulling SCL low!\n", + name); + goto bailout; + } + sclhi(adap); + printk("test_bus:4 scl: %d sda: %d \n",getscl(adap), + getsda(adap)); + if ( 0 == getscl(adap) ) { + printk("test_bus: %s SCL stuck low!\n",name); + sclhi(adap); + goto bailout; + } + if ( 0 == getsda(adap) ) { + printk("test_bus: %s SDA unexpected low while SCL high!\n", + name); + goto bailout; + } + printk("test_bus: %s passed test.\n",name); + return 0; +bailout: + sdahi(adap); + sclhi(adap); + return -ENODEV; +#endif + return (0); +} + +/* ----- Utility functions + */ + + +/* Verify the device we want to talk to on the IIC bus really exists. */ +static inline int try_address(struct i2c_algo_iic_data *adap, + unsigned int addr, int retries) +{ + int i, ret = -1; + short status; + + for (i=0;i<retries;i++) { + iic_outw(adap, ITE_I2CSAR, addr); + iic_start(adap); + if (wait_for_pin(adap, &status) == 0) { + if ((status & ITE_I2CHSR_DNE) == 0) { + iic_stop(adap); + iic_outw(adap, ITE_I2CFCR, ITE_I2CFCR_FLUSH); + ret=1; + break; /* success! */ + } + } + iic_stop(adap); + udelay(adap->udelay); + } + DEB2(if (i) printk("try_address: needed %d retries for 0x%x\n",i, + addr)); + return ret; +} + + +static int iic_sendbytes(struct i2c_adapter *i2c_adap,const char *buf, + int count) +{ + struct i2c_algo_iic_data *adap = i2c_adap->algo_data; + int wrcount=0, timeout; + short status; + int loops, remainder, i, j; + union { + char byte[2]; + unsigned short word; + } tmp; + + iic_outw(adap, ITE_I2CSSAR, (unsigned short)buf[wrcount++]); + count--; + if (count == 0) + return -EIO; + + loops = count / 32; /* 32-byte FIFO */ + remainder = count % 32; + + if(loops) { + for(i=0; i<loops; i++) { + + iic_outw(adap, ITE_I2CFBCR, 32); + for(j=0; j<32/2; j++) { + tmp.byte[1] = buf[wrcount++]; + tmp.byte[0] = buf[wrcount++]; + iic_outw(adap, ITE_I2CFDR, tmp.word); + } + + /* status FIFO overrun */ + iic_inw(adap, ITE_I2CFSR); + iic_inw(adap, ITE_I2CFBCR); + + iic_outw(adap, ITE_I2CHCR, ITE_WRITE); /* Issue WRITE command */ + + /* Wait for transmission to complete */ + timeout = wait_for_pin(adap, &status); + if(timeout) { + iic_stop(adap); + printk("iic_sendbytes: %s write timeout.\n", i2c_adap->name); + return -EREMOTEIO; /* got a better one ?? */ + } + if (status & ITE_I2CHSR_DB) { + iic_stop(adap); + printk("iic_sendbytes: %s write error - no ack.\n", i2c_adap->name); + return -EREMOTEIO; /* got a better one ?? */ + } + } + } + if(remainder) { + iic_outw(adap, ITE_I2CFBCR, remainder); + for(i=0; i<remainder/2; i++) { + tmp.byte[1] = buf[wrcount++]; + tmp.byte[0] = buf[wrcount++]; + iic_outw(adap, ITE_I2CFDR, tmp.word); + } + + /* status FIFO overrun */ + iic_inw(adap, ITE_I2CFSR); + iic_inw(adap, ITE_I2CFBCR); + + iic_outw(adap, ITE_I2CHCR, ITE_WRITE); /* Issue WRITE command */ + + timeout = wait_for_pin(adap, &status); + if(timeout) { + iic_stop(adap); + printk("iic_sendbytes: %s write timeout.\n", i2c_adap->name); + return -EREMOTEIO; /* got a better one ?? */ + } +#ifndef STUB_I2C + if (status & ITE_I2CHSR_DB) { + iic_stop(adap); + printk("iic_sendbytes: %s write error - no ack.\n", i2c_adap->name); + return -EREMOTEIO; /* got a better one ?? */ + } +#endif + } + iic_stop(adap); + return wrcount; +} + + +static int iic_readbytes(struct i2c_adapter *i2c_adap, char *buf, int count, + int sread) +{ + int rdcount=0, i, timeout; + short status; + struct i2c_algo_iic_data *adap = i2c_adap->algo_data; + int loops, remainder, j; + union { + char byte[2]; + unsigned short word; + } tmp; + + loops = count / 32; /* 32-byte FIFO */ + remainder = count % 32; + + if(loops) { + for(i=0; i<loops; i++) { + iic_outw(adap, ITE_I2CFBCR, 32); + if (sread) + iic_outw(adap, ITE_I2CHCR, ITE_SREAD); + else + iic_outw(adap, ITE_I2CHCR, ITE_READ); /* Issue READ command */ + + timeout = wait_for_pin(adap, &status); + if(timeout) { + iic_stop(adap); + printk("iic_readbytes: %s read timeout.\n", i2c_adap->name); + return (-1); + } +#ifndef STUB_I2C + if (status & ITE_I2CHSR_DB) { + iic_stop(adap); + printk("iic_readbytes: %s read error - no ack.\n", i2c_adap->name); + return (-1); + } +#endif + + timeout = wait_for_fe(adap, &status); + if(timeout) { + iic_stop(adap); + printk("iic_readbytes: %s FIFO is empty\n", i2c_adap->name); + return (-1); + } + + for(j=0; j<32/2; j++) { + tmp.word = iic_inw(adap, ITE_I2CFDR); + buf[rdcount++] = tmp.byte[1]; + buf[rdcount++] = tmp.byte[0]; + } + + /* status FIFO underrun */ + iic_inw(adap, ITE_I2CFSR); + + } + } + + + if(remainder) { + remainder=(remainder+1)/2 * 2; + iic_outw(adap, ITE_I2CFBCR, remainder); + if (sread) + iic_outw(adap, ITE_I2CHCR, ITE_SREAD); + else + iic_outw(adap, ITE_I2CHCR, ITE_READ); /* Issue READ command */ + + timeout = wait_for_pin(adap, &status); + if(timeout) { + iic_stop(adap); + printk("iic_readbytes: %s read timeout.\n", i2c_adap->name); + return (-1); + } +#ifndef STUB_I2C + if (status & ITE_I2CHSR_DB) { + iic_stop(adap); + printk("iic_readbytes: %s read error - no ack.\n", i2c_adap->name); + return (-1); + } +#endif + timeout = wait_for_fe(adap, &status); + if(timeout) { + iic_stop(adap); + printk("iic_readbytes: %s FIFO is empty\n", i2c_adap->name); + return (-1); + } + + for(i=0; i<(remainder+1)/2; i++) { + tmp.word = iic_inw(adap, ITE_I2CFDR); + buf[rdcount++] = tmp.byte[1]; + buf[rdcount++] = tmp.byte[0]; + } + + /* status FIFO underrun */ + iic_inw(adap, ITE_I2CFSR); + + } + + iic_stop(adap); + return rdcount; +} + + +/* This function implements combined transactions. Combined + * transactions consist of combinations of reading and writing blocks of data. + * Each transfer (i.e. a read or a write) is separated by a repeated start + * condition. + */ +#if 0 +static int iic_combined_transaction(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) +{ + int i; + struct i2c_msg *pmsg; + int ret; + + DEB2(printk("Beginning combined transaction\n")); + + for(i=0; i<(num-1); i++) { + pmsg = &msgs[i]; + if(pmsg->flags & I2C_M_RD) { + DEB2(printk(" This one is a read\n")); + ret = iic_readbytes(i2c_adap, pmsg->buf, pmsg->len, IIC_COMBINED_XFER); + } + else if(!(pmsg->flags & I2C_M_RD)) { + DEB2(printk("This one is a write\n")); + ret = iic_sendbytes(i2c_adap, pmsg->buf, pmsg->len, IIC_COMBINED_XFER); + } + } + /* Last read or write segment needs to be terminated with a stop */ + pmsg = &msgs[i]; + + if(pmsg->flags & I2C_M_RD) { + DEB2(printk("Doing the last read\n")); + ret = iic_readbytes(i2c_adap, pmsg->buf, pmsg->len, IIC_SINGLE_XFER); + } + else if(!(pmsg->flags & I2C_M_RD)) { + DEB2(printk("Doing the last write\n")); + ret = iic_sendbytes(i2c_adap, pmsg->buf, pmsg->len, IIC_SINGLE_XFER); + } + + return ret; +} +#endif + + +/* Whenever we initiate a transaction, the first byte clocked + * onto the bus after the start condition is the address (7 bit) of the + * device we want to talk to. This function manipulates the address specified + * so that it makes sense to the hardware when written to the IIC peripheral. + * + * Note: 10 bit addresses are not supported in this driver, although they are + * supported by the hardware. This functionality needs to be implemented. + */ +static inline int iic_doAddress(struct i2c_algo_iic_data *adap, + struct i2c_msg *msg, int retries) +{ + unsigned short flags = msg->flags; + unsigned int addr; + int ret; + +/* Ten bit addresses not supported right now */ + if ( (flags & I2C_M_TEN) ) { +#if 0 + addr = 0xf0 | (( msg->addr >> 7) & 0x03); + DEB2(printk("addr0: %d\n",addr)); + ret = try_address(adap, addr, retries); + if (ret!=1) { + printk("iic_doAddress: died at extended address code.\n"); + return -EREMOTEIO; + } + iic_outw(adap,msg->addr & 0x7f); + if (ret != 1) { + printk("iic_doAddress: died at 2nd address code.\n"); + return -EREMOTEIO; + } + if ( flags & I2C_M_RD ) { + i2c_repstart(adap); + addr |= 0x01; + ret = try_address(adap, addr, retries); + if (ret!=1) { + printk("iic_doAddress: died at extended address code.\n"); + return -EREMOTEIO; + } + } +#endif + } else { + + addr = ( msg->addr << 1 ); + +#if 0 + if (flags & I2C_M_RD ) + addr |= 1; + if (flags & I2C_M_REV_DIR_ADDR ) + addr ^= 1; +#endif + + if (iic_inw(adap, ITE_I2CSAR) != addr) { + iic_outw(adap, ITE_I2CSAR, addr); + ret = try_address(adap, addr, retries); + if (ret!=1) { + printk("iic_doAddress: died at address code.\n"); + return -EREMOTEIO; + } + } + + } + + return 0; +} + + +/* Description: Prepares the controller for a transaction (clearing status + * registers, data buffers, etc), and then calls either iic_readbytes or + * iic_sendbytes to do the actual transaction. + * + * still to be done: Before we issue a transaction, we should + * verify that the bus is not busy or in some unknown state. + */ +static int iic_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg *msgs, + int num) +{ + struct i2c_algo_iic_data *adap = i2c_adap->algo_data; + struct i2c_msg *pmsg; + int i = 0; + int ret, timeout; + + pmsg = &msgs[i]; + + if(!pmsg->len) { + DEB2(printk("iic_xfer: read/write length is 0\n");) + return -EIO; + } + if(!(pmsg->flags & I2C_M_RD) && (!(pmsg->len)%2) ) { + DEB2(printk("iic_xfer: write buffer length is not odd\n");) + return -EIO; + } + + /* Wait for any pending transfers to complete */ + timeout = wait_for_bb(adap); + if (timeout) { + DEB2(printk("iic_xfer: Timeout waiting for host not busy\n");) + return -EIO; + } + + /* Flush FIFO */ + iic_outw(adap, ITE_I2CFCR, ITE_I2CFCR_FLUSH); + + /* Load address */ + ret = iic_doAddress(adap, pmsg, i2c_adap->retries); + if (ret) + return -EIO; + +#if 0 + /* Combined transaction (read and write) */ + if(num > 1) { + DEB2(printk("iic_xfer: Call combined transaction\n")); + ret = iic_combined_transaction(i2c_adap, msgs, num); + } +#endif + + DEB3(printk("iic_xfer: Msg %d, addr=0x%x, flags=0x%x, len=%d\n", + i, msgs[i].addr, msgs[i].flags, msgs[i].len);) + + if(pmsg->flags & I2C_M_RD) /* Read */ + ret = iic_readbytes(i2c_adap, pmsg->buf, pmsg->len, 0); + else { /* Write */ + udelay(1000); + ret = iic_sendbytes(i2c_adap, pmsg->buf, pmsg->len); + } + + if (ret != pmsg->len) + DEB3(printk("iic_xfer: error or fail on read/write %d bytes.\n",ret)); + else + DEB3(printk("iic_xfer: read/write %d bytes.\n",ret)); + + return ret; +} + + +/* Implements device specific ioctls. Higher level ioctls can + * be found in i2c-core.c and are typical of any i2c controller (specifying + * slave address, timeouts, etc). These ioctls take advantage of any hardware + * features built into the controller for which this algorithm-adapter set + * was written. These ioctls allow you to take control of the data and clock + * lines and set the either high or low, + * similar to a GPIO pin. + */ +static int algo_control(struct i2c_adapter *adapter, + unsigned int cmd, unsigned long arg) +{ + + struct i2c_algo_iic_data *adap = adapter->algo_data; + struct i2c_iic_msg s_msg; + char *buf; + int ret; + + if (cmd == I2C_SREAD) { + if(copy_from_user(&s_msg, (struct i2c_iic_msg *)arg, + sizeof(struct i2c_iic_msg))) + return -EFAULT; + buf = kmalloc(s_msg.len, GFP_KERNEL); + if (buf== NULL) + return -ENOMEM; + + /* Flush FIFO */ + iic_outw(adap, ITE_I2CFCR, ITE_I2CFCR_FLUSH); + + /* Load address */ + iic_outw(adap, ITE_I2CSAR,s_msg.addr<<1); + iic_outw(adap, ITE_I2CSSAR,s_msg.waddr & 0xff); + + ret = iic_readbytes(adapter, buf, s_msg.len, 1); + if (ret>=0) { + if(copy_to_user( s_msg.buf, buf, s_msg.len) ) + ret = -EFAULT; + } + kfree(buf); + } + return 0; +} + + +static u32 iic_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR | + I2C_FUNC_PROTOCOL_MANGLING; +} + +/* -----exported algorithm data: ------------------------------------- */ + +static struct i2c_algorithm iic_algo = { + .name = "ITE IIC algorithm", + .id = I2C_ALGO_IIC, + .master_xfer = iic_xfer, + .algo_control = algo_control, /* ioctl */ + .functionality = iic_func, +}; + + +/* + * registering functions to load algorithms at runtime + */ +int i2c_iic_add_bus(struct i2c_adapter *adap) +{ + struct i2c_algo_iic_data *iic_adap = adap->algo_data; + + if (iic_test) { + int ret = test_bus(iic_adap, adap->name); + if (ret<0) + return -ENODEV; + } + + DEB2(printk("i2c-algo-ite: hw routines for %s registered.\n", + adap->name)); + + /* register new adapter to i2c module... */ + + adap->id |= iic_algo.id; + adap->algo = &iic_algo; + + adap->timeout = 100; /* default values, should */ + adap->retries = 3; /* be replaced by defines */ + adap->flags = 0; + + i2c_add_adapter(adap); + iic_init(iic_adap); + + return 0; +} + + +int i2c_iic_del_bus(struct i2c_adapter *adap) +{ + int res; + if ((res = i2c_del_adapter(adap)) < 0) + return res; + DEB2(printk("i2c-algo-ite: adapter unregistered: %s\n",adap->name)); + + return 0; +} + + +int __init i2c_algo_iic_init (void) +{ + printk(KERN_INFO "ITE iic (i2c) algorithm module\n"); + return 0; +} + + +void i2c_algo_iic_exit(void) +{ + return; +} + + +EXPORT_SYMBOL(i2c_iic_add_bus); +EXPORT_SYMBOL(i2c_iic_del_bus); + +/* The MODULE_* macros resolve to nothing if MODULES is not defined + * when this file is compiled. + */ +MODULE_AUTHOR("MontaVista Software <www.mvista.com>"); +MODULE_DESCRIPTION("ITE iic algorithm"); +MODULE_LICENSE("GPL"); + +module_param(iic_test, bool, 0); +module_param(i2c_debug, int, S_IRUGO | S_IWUSR); + +MODULE_PARM_DESC(iic_test, "Test if the I2C bus is available"); +MODULE_PARM_DESC(i2c_debug, + "debug level - 0 off; 1 normal; 2,3 more verbose; 9 iic-protocol"); + + +/* This function resolves to init_module (the function invoked when a module + * is loaded via insmod) when this file is compiled with MODULES defined. + * Otherwise (i.e. if you want this driver statically linked to the kernel), + * a pointer to this function is stored in a table and called + * during the initialization of the kernel (in do_basic_setup in /init/main.c) + * + * All this functionality is complements of the macros defined in linux/init.h + */ +module_init(i2c_algo_iic_init); + + +/* If MODULES is defined when this file is compiled, then this function will + * resolved to cleanup_module. + */ +module_exit(i2c_algo_iic_exit); diff --git a/drivers/i2c/algos/i2c-algo-ite.h b/drivers/i2c/algos/i2c-algo-ite.h new file mode 100644 index 000000000000..a8ca3c9b546a --- /dev/null +++ b/drivers/i2c/algos/i2c-algo-ite.h @@ -0,0 +1,117 @@ +/* + -------------------------------------------------------------------- + i2c-ite.h: Global defines for the I2C controller on board the + ITE MIPS processor. + -------------------------------------------------------------------- + Hai-Pao Fan, MontaVista Software, Inc. + hpfan@mvista.com or source@mvista.com + + Copyright 2001 MontaVista Software Inc. + + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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. + + */ + +#ifndef I2C_ITE_H +#define I2C_ITE_H 1 + +#include <asm/it8172/it8172.h> + +/* I2C Registers */ +#define ITE_I2CHCR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x30 +#define ITE_I2CHSR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x34 +#define ITE_I2CSAR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x38 +#define ITE_I2CSSAR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x3c +#define ITE_I2CCKCNT IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x48 +#define ITE_I2CSHDR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x4c +#define ITE_I2CRSUR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x50 +#define ITE_I2CPSUR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x54 + +#define ITE_I2CFDR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x70 +#define ITE_I2CFBCR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x74 +#define ITE_I2CFCR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x78 +#define ITE_I2CFSR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x7c + + +/* Host Control Register ITE_I2CHCR */ +#define ITE_I2CHCR_HCE 0x01 /* Enable I2C Host Controller */ +#define ITE_I2CHCR_IE 0x02 /* Enable the interrupt after completing + the current transaction */ +#define ITE_I2CHCR_CP_W 0x00 /* bit2-4 000 - Write */ +#define ITE_I2CHCR_CP_R 0x08 /* 010 - Current address read */ +#define ITE_I2CHCR_CP_S 0x10 /* 100 - Sequential read */ +#define ITE_I2CHCR_ST 0x20 /* Initiates the I2C host controller to execute + the command and send the data programmed in + all required registers to I2C bus */ +#define ITE_CMD ITE_I2CHCR_HCE | ITE_I2CHCR_IE | ITE_I2CHCR_ST +#define ITE_WRITE ITE_CMD | ITE_I2CHCR_CP_W +#define ITE_READ ITE_CMD | ITE_I2CHCR_CP_R +#define ITE_SREAD ITE_CMD | ITE_I2CHCR_CP_S + +/* Host Status Register ITE_I2CHSR */ +#define ITE_I2CHSR_DB 0x01 /* Device is busy, receives NACK response except + in the first and last bytes */ +#define ITE_I2CHSR_DNE 0x02 /* Target address on I2C bus does not exist */ +#define ITE_I2CHSR_TDI 0x04 /* R/W Transaction on I2C bus was completed */ +#define ITE_I2CHSR_HB 0x08 /* Host controller is processing transactions */ +#define ITE_I2CHSR_FER 0x10 /* Error occurs in the FIFO */ + +/* Slave Address Register ITE_I2CSAR */ +#define ITE_I2CSAR_SA_MASK 0xfe /* Target I2C device address */ +#define ITE_I2CSAR_ASO 0x0100 /* Output 1/0 to I2CAS port when the + next slave address is addressed */ + +/* Slave Sub-address Register ITE_I2CSSAR */ +#define ITE_I2CSSAR_SUBA_MASK 0xff /* Target I2C device sub-address */ + +/* Clock Counter Register ITE_I2CCKCNT */ +#define ITE_I2CCKCNT_STOP 0x00 /* stop I2C clock */ +#define ITE_I2CCKCNT_HPCC_MASK 0x7f /* SCL high period counter */ +#define ITE_I2CCKCNT_LPCC_MASK 0x7f00 /* SCL low period counter */ + +/* START Hold Time Register ITE_I2CSHDR */ +/* value is counted based on 16 MHz internal clock */ +#define ITE_I2CSHDR_FM 0x0a /* START condition at fast mode */ +#define ITE_I2CSHDR_SM 0x47 /* START contition at standard mode */ + +/* (Repeated) START Setup Time Register ITE_I2CRSUR */ +/* value is counted based on 16 MHz internal clock */ +#define ITE_I2CRSUR_FM 0x0a /* repeated START condition at fast mode */ +#define ITE_I2CRSUR_SM 0x50 /* repeated START condition at standard mode */ + +/* STOP setup Time Register ITE_I2CPSUR */ + +/* FIFO Data Register ITE_I2CFDR */ +#define ITE_I2CFDR_MASK 0xff + +/* FIFO Byte Count Register ITE_I2CFBCR */ +#define ITE_I2CFBCR_MASK 0x3f + +/* FIFO Control Register ITE_I2CFCR */ +#define ITE_I2CFCR_FLUSH 0x01 /* Flush FIFO and reset the FIFO point + and I2CFSR */ +/* FIFO Status Register ITE_I2CFSR */ +#define ITE_I2CFSR_FO 0x01 /* FIFO is overrun when write */ +#define ITE_I2CFSR_FU 0x02 /* FIFO is underrun when read */ +#define ITE_I2CFSR_FF 0x04 /* FIFO is full when write */ +#define ITE_I2CFSR_FE 0x08 /* FIFO is empty when read */ + +#endif /* I2C_ITE_H */ diff --git a/drivers/i2c/algos/i2c-algo-pca.c b/drivers/i2c/algos/i2c-algo-pca.c new file mode 100644 index 000000000000..c3d912cbbbc3 --- /dev/null +++ b/drivers/i2c/algos/i2c-algo-pca.c @@ -0,0 +1,399 @@ +/* + * i2c-algo-pca.c i2c driver algorithms for PCA9564 adapters + * Copyright (C) 2004 Arcom Control Systems + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-pca.h> +#include "i2c-algo-pca.h" + +#define DRIVER "i2c-algo-pca" + +#define DEB1(fmt, args...) do { if (i2c_debug>=1) printk(fmt, ## args); } while(0) +#define DEB2(fmt, args...) do { if (i2c_debug>=2) printk(fmt, ## args); } while(0) +#define DEB3(fmt, args...) do { if (i2c_debug>=3) printk(fmt, ## args); } while(0) + +static int i2c_debug=0; + +#define pca_outw(adap, reg, val) adap->write_byte(adap, reg, val) +#define pca_inw(adap, reg) adap->read_byte(adap, reg) + +#define pca_status(adap) pca_inw(adap, I2C_PCA_STA) +#define pca_clock(adap) adap->get_clock(adap) +#define pca_own(adap) adap->get_own(adap) +#define pca_set_con(adap, val) pca_outw(adap, I2C_PCA_CON, val) +#define pca_get_con(adap) pca_inw(adap, I2C_PCA_CON) +#define pca_wait(adap) adap->wait_for_interrupt(adap) + +/* + * Generate a start condition on the i2c bus. + * + * returns after the start condition has occured + */ +static void pca_start(struct i2c_algo_pca_data *adap) +{ + int sta = pca_get_con(adap); + DEB2("=== START\n"); + sta |= I2C_PCA_CON_STA; + sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_SI); + pca_set_con(adap, sta); + pca_wait(adap); +} + +/* + * Generate a repeated start condition on the i2c bus + * + * return after the repeated start condition has occured + */ +static void pca_repeated_start(struct i2c_algo_pca_data *adap) +{ + int sta = pca_get_con(adap); + DEB2("=== REPEATED START\n"); + sta |= I2C_PCA_CON_STA; + sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_SI); + pca_set_con(adap, sta); + pca_wait(adap); +} + +/* + * Generate a stop condition on the i2c bus + * + * returns after the stop condition has been generated + * + * STOPs do not generate an interrupt or set the SI flag, since the + * part returns the the idle state (0xf8). Hence we don't need to + * pca_wait here. + */ +static void pca_stop(struct i2c_algo_pca_data *adap) +{ + int sta = pca_get_con(adap); + DEB2("=== STOP\n"); + sta |= I2C_PCA_CON_STO; + sta &= ~(I2C_PCA_CON_STA|I2C_PCA_CON_SI); + pca_set_con(adap, sta); +} + +/* + * Send the slave address and R/W bit + * + * returns after the address has been sent + */ +static void pca_address(struct i2c_algo_pca_data *adap, + struct i2c_msg *msg) +{ + int sta = pca_get_con(adap); + int addr; + + addr = ( (0x7f & msg->addr) << 1 ); + if (msg->flags & I2C_M_RD ) + addr |= 1; + DEB2("=== SLAVE ADDRESS %#04x+%c=%#04x\n", + msg->addr, msg->flags & I2C_M_RD ? 'R' : 'W', addr); + + pca_outw(adap, I2C_PCA_DAT, addr); + + sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_STA|I2C_PCA_CON_SI); + pca_set_con(adap, sta); + + pca_wait(adap); +} + +/* + * Transmit a byte. + * + * Returns after the byte has been transmitted + */ +static void pca_tx_byte(struct i2c_algo_pca_data *adap, + __u8 b) +{ + int sta = pca_get_con(adap); + DEB2("=== WRITE %#04x\n", b); + pca_outw(adap, I2C_PCA_DAT, b); + + sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_STA|I2C_PCA_CON_SI); + pca_set_con(adap, sta); + + pca_wait(adap); +} + +/* + * Receive a byte + * + * returns immediately. + */ +static void pca_rx_byte(struct i2c_algo_pca_data *adap, + __u8 *b, int ack) +{ + *b = pca_inw(adap, I2C_PCA_DAT); + DEB2("=== READ %#04x %s\n", *b, ack ? "ACK" : "NACK"); +} + +/* + * Setup ACK or NACK for next received byte and wait for it to arrive. + * + * Returns after next byte has arrived. + */ +static void pca_rx_ack(struct i2c_algo_pca_data *adap, + int ack) +{ + int sta = pca_get_con(adap); + + sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_STA|I2C_PCA_CON_SI|I2C_PCA_CON_AA); + + if ( ack ) + sta |= I2C_PCA_CON_AA; + + pca_set_con(adap, sta); + pca_wait(adap); +} + +/* + * Reset the i2c bus / SIO + */ +static void pca_reset(struct i2c_algo_pca_data *adap) +{ + /* apparently only an external reset will do it. not a lot can be done */ + printk(KERN_ERR DRIVER ": Haven't figured out how to do a reset yet\n"); +} + +static int pca_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg *msgs, + int num) +{ + struct i2c_algo_pca_data *adap = i2c_adap->algo_data; + struct i2c_msg *msg = NULL; + int curmsg; + int numbytes = 0; + int state; + int ret; + + state = pca_status(adap); + if ( state != 0xF8 ) { + dev_dbg(&i2c_adap->dev, "bus is not idle. status is %#04x\n", state ); + /* FIXME: what to do. Force stop ? */ + return -EREMOTEIO; + } + + DEB1("{{{ XFER %d messages\n", num); + + if (i2c_debug>=2) { + for (curmsg = 0; curmsg < num; curmsg++) { + int addr, i; + msg = &msgs[curmsg]; + + addr = (0x7f & msg->addr) ; + + if (msg->flags & I2C_M_RD ) + printk(KERN_INFO " [%02d] RD %d bytes from %#02x [%#02x, ...]\n", + curmsg, msg->len, addr, (addr<<1) | 1); + else { + printk(KERN_INFO " [%02d] WR %d bytes to %#02x [%#02x%s", + curmsg, msg->len, addr, addr<<1, + msg->len == 0 ? "" : ", "); + for(i=0; i < msg->len; i++) + printk("%#04x%s", msg->buf[i], i == msg->len - 1 ? "" : ", "); + printk("]\n"); + } + } + } + + curmsg = 0; + ret = -EREMOTEIO; + while (curmsg < num) { + state = pca_status(adap); + + DEB3("STATE is 0x%02x\n", state); + msg = &msgs[curmsg]; + + switch (state) { + case 0xf8: /* On reset or stop the bus is idle */ + pca_start(adap); + break; + + case 0x08: /* A START condition has been transmitted */ + case 0x10: /* A repeated start condition has been transmitted */ + pca_address(adap, msg); + break; + + case 0x18: /* SLA+W has been transmitted; ACK has been received */ + case 0x28: /* Data byte in I2CDAT has been transmitted; ACK has been received */ + if (numbytes < msg->len) { + pca_tx_byte(adap, msg->buf[numbytes]); + numbytes++; + break; + } + curmsg++; numbytes = 0; + if (curmsg == num) + pca_stop(adap); + else + pca_repeated_start(adap); + break; + + case 0x20: /* SLA+W has been transmitted; NOT ACK has been received */ + DEB2("NOT ACK received after SLA+W\n"); + pca_stop(adap); + goto out; + + case 0x40: /* SLA+R has been transmitted; ACK has been received */ + pca_rx_ack(adap, msg->len > 1); + break; + + case 0x50: /* Data bytes has been received; ACK has been returned */ + if (numbytes < msg->len) { + pca_rx_byte(adap, &msg->buf[numbytes], 1); + numbytes++; + pca_rx_ack(adap, numbytes < msg->len - 1); + break; + } + curmsg++; numbytes = 0; + if (curmsg == num) + pca_stop(adap); + else + pca_repeated_start(adap); + break; + + case 0x48: /* SLA+R has been transmitted; NOT ACK has been received */ + DEB2("NOT ACK received after SLA+R\n"); + pca_stop(adap); + goto out; + + case 0x30: /* Data byte in I2CDAT has been transmitted; NOT ACK has been received */ + DEB2("NOT ACK received after data byte\n"); + goto out; + + case 0x38: /* Arbitration lost during SLA+W, SLA+R or data bytes */ + DEB2("Arbitration lost\n"); + goto out; + + case 0x58: /* Data byte has been received; NOT ACK has been returned */ + if ( numbytes == msg->len - 1 ) { + pca_rx_byte(adap, &msg->buf[numbytes], 0); + curmsg++; numbytes = 0; + if (curmsg == num) + pca_stop(adap); + else + pca_repeated_start(adap); + } else { + DEB2("NOT ACK sent after data byte received. " + "Not final byte. numbytes %d. len %d\n", + numbytes, msg->len); + pca_stop(adap); + goto out; + } + break; + case 0x70: /* Bus error - SDA stuck low */ + DEB2("BUS ERROR - SDA Stuck low\n"); + pca_reset(adap); + goto out; + case 0x90: /* Bus error - SCL stuck low */ + DEB2("BUS ERROR - SCL Stuck low\n"); + pca_reset(adap); + goto out; + case 0x00: /* Bus error during master or slave mode due to illegal START or STOP condition */ + DEB2("BUS ERROR - Illegal START or STOP\n"); + pca_reset(adap); + goto out; + default: + printk(KERN_ERR DRIVER ": unhandled SIO state 0x%02x\n", state); + break; + } + + } + + ret = curmsg; + out: + DEB1(KERN_CRIT "}}} transfered %d/%d messages. " + "status is %#04x. control is %#04x\n", + curmsg, num, pca_status(adap), + pca_get_con(adap)); + return ret; +} + +static u32 pca_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static int pca_init(struct i2c_algo_pca_data *adap) +{ + static int freqs[] = {330,288,217,146,88,59,44,36}; + int own, clock; + + own = pca_own(adap); + clock = pca_clock(adap); + DEB1(KERN_INFO DRIVER ": own address is %#04x\n", own); + DEB1(KERN_INFO DRIVER ": clock freqeuncy is %dkHz\n", freqs[clock]); + + pca_outw(adap, I2C_PCA_ADR, own << 1); + + pca_set_con(adap, I2C_PCA_CON_ENSIO | clock); + udelay(500); /* 500 µs for oscilator to stabilise */ + + return 0; +} + +static struct i2c_algorithm pca_algo = { + .name = "PCA9564 algorithm", + .id = I2C_ALGO_PCA, + .master_xfer = pca_xfer, + .functionality = pca_func, +}; + +/* + * registering functions to load algorithms at runtime + */ +int i2c_pca_add_bus(struct i2c_adapter *adap) +{ + struct i2c_algo_pca_data *pca_adap = adap->algo_data; + int rval; + + /* register new adapter to i2c module... */ + + adap->id |= pca_algo.id; + adap->algo = &pca_algo; + + adap->timeout = 100; /* default values, should */ + adap->retries = 3; /* be replaced by defines */ + + rval = pca_init(pca_adap); + + if (!rval) + i2c_add_adapter(adap); + + return rval; +} + +int i2c_pca_del_bus(struct i2c_adapter *adap) +{ + return i2c_del_adapter(adap); +} + +EXPORT_SYMBOL(i2c_pca_add_bus); +EXPORT_SYMBOL(i2c_pca_del_bus); + +MODULE_AUTHOR("Ian Campbell <icampbell@arcom.com>"); +MODULE_DESCRIPTION("I2C-Bus PCA9564 algorithm"); +MODULE_LICENSE("GPL"); + +module_param(i2c_debug, int, 0); diff --git a/drivers/i2c/algos/i2c-algo-pca.h b/drivers/i2c/algos/i2c-algo-pca.h new file mode 100644 index 000000000000..2fee07e05211 --- /dev/null +++ b/drivers/i2c/algos/i2c-algo-pca.h @@ -0,0 +1,26 @@ +#ifndef I2C_PCA9564_H +#define I2C_PCA9564_H 1 + +#define I2C_PCA_STA 0x00 /* STATUS Read Only */ +#define I2C_PCA_TO 0x00 /* TIMEOUT Write Only */ +#define I2C_PCA_DAT 0x01 /* DATA Read/Write */ +#define I2C_PCA_ADR 0x02 /* OWN ADR Read/Write */ +#define I2C_PCA_CON 0x03 /* CONTROL Read/Write */ + +#define I2C_PCA_CON_AA 0x80 /* Assert Acknowledge */ +#define I2C_PCA_CON_ENSIO 0x40 /* Enable */ +#define I2C_PCA_CON_STA 0x20 /* Start */ +#define I2C_PCA_CON_STO 0x10 /* Stop */ +#define I2C_PCA_CON_SI 0x08 /* Serial Interrupt */ +#define I2C_PCA_CON_CR 0x07 /* Clock Rate (MASK) */ + +#define I2C_PCA_CON_330kHz 0x00 +#define I2C_PCA_CON_288kHz 0x01 +#define I2C_PCA_CON_217kHz 0x02 +#define I2C_PCA_CON_146kHz 0x03 +#define I2C_PCA_CON_88kHz 0x04 +#define I2C_PCA_CON_59kHz 0x05 +#define I2C_PCA_CON_44kHz 0x06 +#define I2C_PCA_CON_36kHz 0x07 + +#endif /* I2C_PCA9564_H */ diff --git a/drivers/i2c/algos/i2c-algo-pcf.c b/drivers/i2c/algos/i2c-algo-pcf.c new file mode 100644 index 000000000000..8d087dac32af --- /dev/null +++ b/drivers/i2c/algos/i2c-algo-pcf.c @@ -0,0 +1,507 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-algo-pcf.c i2c driver algorithms for PCF8584 adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-1997 Simon G. Vogl + 1998-2000 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 + Frodo Looijaard <frodol@dds.nl> ,and also from Martin Bailey + <mbailey@littlefeet-inc.com> */ + +/* Partially rewriten by Oleg I. Vdovikin <vdovikin@jscc.ru> to handle multiple + messages, proper stop/repstart signaling during receive, + added detect code */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-pcf.h> +#include "i2c-algo-pcf.h" + + +#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 + +/* module parameters: + */ +static int i2c_debug; + +/* --- 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 -------------------------------------- */ + +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); +#ifndef STUB_I2C + while (timeout-- && !(status & I2C_PCF_BB)) { + udelay(100); /* wait for 100 us */ + status = get_pcf(adap, 1); + } +#endif + if (timeout <= 0) { + printk(KERN_ERR "Timeout waiting for Bus Busy\n"); + } + + return (timeout<=0); +} + + +static int wait_for_pin(struct i2c_algo_pcf_data *adap, int *status) { + + int timeout = DEF_TIMEOUT; + + *status = get_pcf(adap, 1); +#ifndef STUB_I2C + while (timeout-- && (*status & I2C_PCF_PIN)) { + adap->waitforpin(); + *status = get_pcf(adap, 1); + } + if (*status & I2C_PCF_LAB) { + DEB2(printk(KERN_INFO + "i2c-algo-pcf.o: lost arbitration (CSR 0x%02x)\n", + *status)); + /* Cleanup from LAB-- reset and enable ESO. + * This resets the PCF8584; since we've lost the bus, no + * further attempts should be made by callers to clean up + * (no i2c_stop() etc.) + */ + set_pcf(adap, 1, I2C_PCF_PIN); + set_pcf(adap, 1, I2C_PCF_ESO); + /* TODO: we should pause for a time period sufficient for any + * running I2C transaction to complete-- the arbitration + * logic won't work properly until the next START is seen. + */ + DEB2(printk(KERN_INFO + "i2c-algo-pcf.o: reset LAB condition (CSR 0x%02x)\n", + get_pcf(adap,1))); + return(-EINTR); + } +#endif + 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. + * + * vdovikin: added detect code for PCF8584 + */ +static int pcf_init_8584 (struct i2c_algo_pcf_data *adap) +{ + unsigned char temp; + + DEB3(printk(KERN_DEBUG "i2c-algo-pcf.o: PCF state 0x%02x\n", get_pcf(adap, 1))); + + /* S1=0x80: S0 selected, serial interface off */ + set_pcf(adap, 1, I2C_PCF_PIN); + /* check to see S1 now used as R/W ctrl - + PCF8584 does that when ESO is zero */ + if (((temp = get_pcf(adap, 1)) & 0x7f) != (0)) { + DEB2(printk(KERN_ERR "i2c-algo-pcf.o: PCF detection failed -- can't select S0 (0x%02x).\n", temp)); + return -ENXIO; /* definetly not PCF8584 */ + } + + /* load own address in S0, effective address is (own << 1) */ + i2c_outb(adap, get_own(adap)); + /* check it's really written */ + if ((temp = i2c_inb(adap)) != get_own(adap)) { + DEB2(printk(KERN_ERR "i2c-algo-pcf.o: PCF detection failed -- can't set S0 (0x%02x).\n", temp)); + return -ENXIO; + } + + /* S1=0xA0, next byte in S2 */ + set_pcf(adap, 1, I2C_PCF_PIN | I2C_PCF_ES1); + /* check to see S2 now selected */ + if (((temp = get_pcf(adap, 1)) & 0x7f) != I2C_PCF_ES1) { + DEB2(printk(KERN_ERR "i2c-algo-pcf.o: PCF detection failed -- can't select S2 (0x%02x).\n", temp)); + return -ENXIO; + } + + /* load clock register S2 */ + i2c_outb(adap, get_clock(adap)); + /* check it's really written, the only 5 lowest bits does matter */ + if (((temp = i2c_inb(adap)) & 0x1f) != get_clock(adap)) { + DEB2(printk(KERN_ERR "i2c-algo-pcf.o: PCF detection failed -- can't set S2 (0x%02x).\n", temp)); + return -ENXIO; + } + + /* Enable serial interface, idle, S0 selected */ + set_pcf(adap, 1, I2C_PCF_IDLE); + + /* check to see PCF is really idled and we can access status register */ + if ((temp = get_pcf(adap, 1)) != (I2C_PCF_PIN | I2C_PCF_BB)) { + DEB2(printk(KERN_ERR "i2c-algo-pcf.o: PCF detection failed -- can't select S1` (0x%02x).\n", temp)); + return -ENXIO; + } + + printk(KERN_DEBUG "i2c-algo-pcf.o: deteted and initialized PCF8584.\n"); + + 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; + int wfp; + for (i=0;i<retries;i++) { + i2c_outb(adap, addr); + i2c_start(adap); + status = get_pcf(adap, 1); + if ((wfp = wait_for_pin(adap, &status)) >= 0) { + if ((status & I2C_PCF_LRB) == 0) { + i2c_stop(adap); + break; /* success! */ + } + } + if (wfp == -EINTR) { + /* arbitration lost */ + udelay(adap->udelay); + return -EINTR; + } + i2c_stop(adap); + udelay(adap->udelay); + } + DEB2(if (i) printk(KERN_DEBUG "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, int last) +{ + struct i2c_algo_pcf_data *adap = i2c_adap->algo_data; + int wrcount, status, timeout; + + for (wrcount=0; wrcount<count; ++wrcount) { + DEB2(dev_dbg(&i2c_adap->dev, "i2c_write: writing %2.2X\n", + buf[wrcount]&0xff)); + i2c_outb(adap, buf[wrcount]); + timeout = wait_for_pin(adap, &status); + if (timeout) { + if (timeout == -EINTR) { + /* arbitration lost */ + return -EINTR; + } + i2c_stop(adap); + dev_err(&i2c_adap->dev, "i2c_write: error - timeout.\n"); + return -EREMOTEIO; /* got a better one ?? */ + } +#ifndef STUB_I2C + if (status & I2C_PCF_LRB) { + i2c_stop(adap); + dev_err(&i2c_adap->dev, "i2c_write: error - no ack.\n"); + return -EREMOTEIO; /* got a better one ?? */ + } +#endif + } + if (last) { + i2c_stop(adap); + } + else { + i2c_repstart(adap); + } + + return (wrcount); +} + + +static int pcf_readbytes(struct i2c_adapter *i2c_adap, char *buf, + int count, int last) +{ + int i, status; + struct i2c_algo_pcf_data *adap = i2c_adap->algo_data; + int wfp; + + /* increment number of bytes to read by one -- read dummy byte */ + for (i = 0; i <= count; i++) { + + if ((wfp = wait_for_pin(adap, &status))) { + if (wfp == -EINTR) { + /* arbitration lost */ + return -EINTR; + } + i2c_stop(adap); + dev_err(&i2c_adap->dev, "pcf_readbytes timed out.\n"); + return (-1); + } + +#ifndef STUB_I2C + if ((status & I2C_PCF_LRB) && (i != count)) { + i2c_stop(adap); + dev_err(&i2c_adap->dev, "i2c_read: i2c_inb, No ack.\n"); + return (-1); + } +#endif + + if (i == count - 1) { + set_pcf(adap, 1, I2C_PCF_ESO); + } else + if (i == count) { + if (last) { + i2c_stop(adap); + } else { + i2c_repstart(adap); + } + }; + + if (i) { + buf[i - 1] = i2c_inb(adap); + } else { + i2c_inb(adap); /* dummy read */ + } + } + + return (i - 1); +} + + +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(KERN_DEBUG "addr0: %d\n",addr)); + /* try extended address code...*/ + ret = try_address(adap, addr, retries); + if (ret!=1) { + printk(KERN_ERR "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(KERN_ERR "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(KERN_ERR "died at extended address code.\n"); + return -EREMOTEIO; + } + } + } else { /* normal 7bit address */ + addr = ( msg->addr << 1 ); + if (flags & I2C_M_RD ) + addr |= 1; + if (flags & I2C_M_REV_DIR_ADDR ) + 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; + int ret=0, timeout, status; + + + /* Check for bus busy */ + timeout = wait_for_bb(adap); + if (timeout) { + DEB2(printk(KERN_ERR "i2c-algo-pcf.o: " + "Timeout waiting for BB in pcf_xfer\n");) + return -EIO; + } + + for (i = 0;ret >= 0 && i < num; i++) { + pmsg = &msgs[i]; + + DEB2(printk(KERN_DEBUG "i2c-algo-pcf.o: Doing %s %d bytes to 0x%02x - %d of %d messages\n", + pmsg->flags & I2C_M_RD ? "read" : "write", + pmsg->len, pmsg->addr, i + 1, num);) + + ret = pcf_doAddress(adap, pmsg, i2c_adap->retries); + + /* Send START */ + if (i == 0) { + i2c_start(adap); + } + + /* Wait for PIN (pending interrupt NOT) */ + timeout = wait_for_pin(adap, &status); + if (timeout) { + if (timeout == -EINTR) { + /* arbitration lost */ + return (-EINTR); + } + i2c_stop(adap); + DEB2(printk(KERN_ERR "i2c-algo-pcf.o: Timeout waiting " + "for PIN(1) in pcf_xfer\n");) + return (-EREMOTEIO); + } + +#ifndef STUB_I2C + /* Check LRB (last rcvd bit - slave ack) */ + if (status & I2C_PCF_LRB) { + i2c_stop(adap); + DEB2(printk(KERN_ERR "i2c-algo-pcf.o: No LRB(1) in pcf_xfer\n");) + return (-EREMOTEIO); + } +#endif + + DEB3(printk(KERN_DEBUG "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);) + + /* Read */ + if (pmsg->flags & I2C_M_RD) { + /* read bytes into buffer*/ + ret = pcf_readbytes(i2c_adap, pmsg->buf, pmsg->len, + (i + 1 == num)); + + if (ret != pmsg->len) { + DEB2(printk(KERN_DEBUG "i2c-algo-pcf.o: fail: " + "only read %d bytes.\n",ret)); + } else { + DEB2(printk(KERN_DEBUG "i2c-algo-pcf.o: read %d bytes.\n",ret)); + } + } else { /* Write */ + ret = pcf_sendbytes(i2c_adap, pmsg->buf, pmsg->len, + (i + 1 == num)); + + if (ret != pmsg->len) { + DEB2(printk(KERN_DEBUG "i2c-algo-pcf.o: fail: " + "only wrote %d bytes.\n",ret)); + } else { + DEB2(printk(KERN_DEBUG "i2c-algo-pcf.o: wrote %d bytes.\n",ret)); + } + } + } + + return (i); +} + +static u32 pcf_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | + I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING; +} + +/* -----exported algorithm data: ------------------------------------- */ + +static struct i2c_algorithm pcf_algo = { + .name = "PCF8584 algorithm", + .id = I2C_ALGO_PCF, + .master_xfer = pcf_xfer, + .functionality = pcf_func, +}; + +/* + * registering functions to load algorithms at runtime + */ +int i2c_pcf_add_bus(struct i2c_adapter *adap) +{ + struct i2c_algo_pcf_data *pcf_adap = adap->algo_data; + int rval; + + DEB2(dev_dbg(&adap->dev, "hw routines registered.\n")); + + /* 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 */ + + rval = pcf_init_8584(pcf_adap); + if (!rval) + i2c_add_adapter(adap); + return rval; +} + + +int i2c_pcf_del_bus(struct i2c_adapter *adap) +{ + return i2c_del_adapter(adap); +} + +EXPORT_SYMBOL(i2c_pcf_add_bus); +EXPORT_SYMBOL(i2c_pcf_del_bus); + +MODULE_AUTHOR("Hans Berglund <hb@spacetec.no>"); +MODULE_DESCRIPTION("I2C-Bus PCF8584 algorithm"); +MODULE_LICENSE("GPL"); + +module_param(i2c_debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(i2c_debug, + "debug level - 0 off; 1 normal; 2,3 more verbose; 9 pcf-protocol"); diff --git a/drivers/i2c/algos/i2c-algo-pcf.h b/drivers/i2c/algos/i2c-algo-pcf.h new file mode 100644 index 000000000000..5263a9eeb8d7 --- /dev/null +++ b/drivers/i2c/algos/i2c-algo-pcf.h @@ -0,0 +1,76 @@ +/* -------------------------------------------------------------------- */ +/* 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> */ + +#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_CLK 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/algos/i2c-algo-sgi.c b/drivers/i2c/algos/i2c-algo-sgi.c new file mode 100644 index 000000000000..422721b241e5 --- /dev/null +++ b/drivers/i2c/algos/i2c-algo-sgi.c @@ -0,0 +1,189 @@ +/* + * i2c-algo-sgi.c: i2c driver algorithms for SGI adapters. + * + * This file is subject to the terms and conditions of the GNU General Public + * License version 2 as published by the Free Software Foundation. + * + * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/delay.h> + +#include <linux/i2c.h> +#include <linux/i2c-algo-sgi.h> + + +#define SGI_I2C_FORCE_IDLE (0 << 0) +#define SGI_I2C_NOT_IDLE (1 << 0) +#define SGI_I2C_WRITE (0 << 1) +#define SGI_I2C_READ (1 << 1) +#define SGI_I2C_RELEASE_BUS (0 << 2) +#define SGI_I2C_HOLD_BUS (1 << 2) +#define SGI_I2C_XFER_DONE (0 << 4) +#define SGI_I2C_XFER_BUSY (1 << 4) +#define SGI_I2C_ACK (0 << 5) +#define SGI_I2C_NACK (1 << 5) +#define SGI_I2C_BUS_OK (0 << 7) +#define SGI_I2C_BUS_ERR (1 << 7) + +#define get_control() adap->getctrl(adap->data) +#define set_control(val) adap->setctrl(adap->data, val) +#define read_data() adap->rdata(adap->data) +#define write_data(val) adap->wdata(adap->data, val) + + +static int wait_xfer_done(struct i2c_algo_sgi_data *adap) +{ + int i; + + for (i = 0; i < adap->xfer_timeout; i++) { + if ((get_control() & SGI_I2C_XFER_BUSY) == 0) + return 0; + udelay(1); + } + + return -ETIMEDOUT; +} + +static int wait_ack(struct i2c_algo_sgi_data *adap) +{ + int i; + + if (wait_xfer_done(adap)) + return -ETIMEDOUT; + for (i = 0; i < adap->ack_timeout; i++) { + if ((get_control() & SGI_I2C_NACK) == 0) + return 0; + udelay(1); + } + + return -ETIMEDOUT; +} + +static int force_idle(struct i2c_algo_sgi_data *adap) +{ + int i; + + set_control(SGI_I2C_FORCE_IDLE); + for (i = 0; i < adap->xfer_timeout; i++) { + if ((get_control() & SGI_I2C_NOT_IDLE) == 0) + goto out; + udelay(1); + } + return -ETIMEDOUT; +out: + if (get_control() & SGI_I2C_BUS_ERR) + return -EIO; + return 0; +} + +static int do_address(struct i2c_algo_sgi_data *adap, unsigned int addr, + int rd) +{ + if (rd) + set_control(SGI_I2C_NOT_IDLE); + /* Check if bus is idle, eventually force it to do so */ + if (get_control() & SGI_I2C_NOT_IDLE) + if (force_idle(adap)) + return -EIO; + /* Write out the i2c chip address and specify operation */ + set_control(SGI_I2C_HOLD_BUS | SGI_I2C_WRITE | SGI_I2C_NOT_IDLE); + if (rd) + addr |= 1; + write_data(addr); + if (wait_ack(adap)) + return -EIO; + return 0; +} + +static int i2c_read(struct i2c_algo_sgi_data *adap, unsigned char *buf, + unsigned int len) +{ + int i; + + set_control(SGI_I2C_HOLD_BUS | SGI_I2C_READ | SGI_I2C_NOT_IDLE); + for (i = 0; i < len; i++) { + if (wait_xfer_done(adap)) + return -EIO; + buf[i] = read_data(); + } + set_control(SGI_I2C_RELEASE_BUS | SGI_I2C_FORCE_IDLE); + + return 0; + +} + +static int i2c_write(struct i2c_algo_sgi_data *adap, unsigned char *buf, + unsigned int len) +{ + int i; + + /* We are already in write state */ + for (i = 0; i < len; i++) { + write_data(buf[i]); + if (wait_ack(adap)) + return -EIO; + } + return 0; +} + +static int sgi_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, + int num) +{ + struct i2c_algo_sgi_data *adap = i2c_adap->algo_data; + struct i2c_msg *p; + int i, err = 0; + + for (i = 0; !err && i < num; i++) { + p = &msgs[i]; + err = do_address(adap, p->addr, p->flags & I2C_M_RD); + if (err || !p->len) + continue; + if (p->flags & I2C_M_RD) + err = i2c_read(adap, p->buf, p->len); + else + err = i2c_write(adap, p->buf, p->len); + } + + return err; +} + +static u32 sgi_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL; +} + +static struct i2c_algorithm sgi_algo = { + .name = "SGI algorithm", + .id = I2C_ALGO_SGI, + .master_xfer = sgi_xfer, + .functionality = sgi_func, +}; + +/* + * registering functions to load algorithms at runtime + */ +int i2c_sgi_add_bus(struct i2c_adapter *adap) +{ + adap->id |= sgi_algo.id; + adap->algo = &sgi_algo; + + return i2c_add_adapter(adap); +} + + +int i2c_sgi_del_bus(struct i2c_adapter *adap) +{ + return i2c_del_adapter(adap); +} + +EXPORT_SYMBOL(i2c_sgi_add_bus); +EXPORT_SYMBOL(i2c_sgi_del_bus); + +MODULE_AUTHOR("Ladislav Michl <ladis@linux-mips.org>"); +MODULE_DESCRIPTION("I2C-Bus SGI algorithm"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/algos/i2c-algo-sibyte.c b/drivers/i2c/algos/i2c-algo-sibyte.c new file mode 100644 index 000000000000..35789bb7126a --- /dev/null +++ b/drivers/i2c/algos/i2c-algo-sibyte.c @@ -0,0 +1,222 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-algo-sibyte.c i2c driver algorithms for bit-shift adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 2001,2002,2003 Broadcom Corporation + Copyright (C) 1995-2000 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>. */ + +/* Ported for SiByte SOCs by Broadcom Corporation. */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> + +#include <asm/io.h> +#include <asm/sibyte/sb1250_regs.h> +#include <asm/sibyte/sb1250_smbus.h> + +#include <linux/i2c.h> +#include <linux/i2c-algo-sibyte.h> + +/* ----- global defines ----------------------------------------------- */ +#define SMB_CSR(a,r) ((long)(a->reg_base + r)) + +/* ----- global variables --------------------------------------------- */ + +/* module parameters: + */ +static int bit_scan=0; /* have a look at what's hanging 'round */ + + +static int smbus_xfer(struct i2c_adapter *i2c_adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data * data) +{ + struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data; + int data_bytes = 0; + int error; + + while (csr_in32(SMB_CSR(adap, R_SMB_STATUS)) & M_SMB_BUSY) + ; + + switch (size) { + case I2C_SMBUS_QUICK: + csr_out32((V_SMB_ADDR(addr) | (read_write == I2C_SMBUS_READ ? M_SMB_QDATA : 0) | + V_SMB_TT_QUICKCMD), SMB_CSR(adap, R_SMB_START)); + break; + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_READ) { + csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_RD1BYTE), + SMB_CSR(adap, R_SMB_START)); + data_bytes = 1; + } else { + csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD)); + csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR1BYTE), + SMB_CSR(adap, R_SMB_START)); + } + break; + case I2C_SMBUS_BYTE_DATA: + csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD)); + if (read_write == I2C_SMBUS_READ) { + csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD1BYTE), + SMB_CSR(adap, R_SMB_START)); + data_bytes = 1; + } else { + csr_out32(V_SMB_LB(data->byte), SMB_CSR(adap, R_SMB_DATA)); + csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE), + SMB_CSR(adap, R_SMB_START)); + } + break; + case I2C_SMBUS_WORD_DATA: + csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD)); + if (read_write == I2C_SMBUS_READ) { + csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD2BYTE), + SMB_CSR(adap, R_SMB_START)); + data_bytes = 2; + } else { + csr_out32(V_SMB_LB(data->word & 0xff), SMB_CSR(adap, R_SMB_DATA)); + csr_out32(V_SMB_MB(data->word >> 8), SMB_CSR(adap, R_SMB_DATA)); + csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE), + SMB_CSR(adap, R_SMB_START)); + } + break; + default: + return -1; /* XXXKW better error code? */ + } + + while (csr_in32(SMB_CSR(adap, R_SMB_STATUS)) & M_SMB_BUSY) + ; + + error = csr_in32(SMB_CSR(adap, R_SMB_STATUS)); + if (error & M_SMB_ERROR) { + /* Clear error bit by writing a 1 */ + csr_out32(M_SMB_ERROR, SMB_CSR(adap, R_SMB_STATUS)); + return -1; /* XXXKW better error code? */ + } + + if (data_bytes == 1) + data->byte = csr_in32(SMB_CSR(adap, R_SMB_DATA)) & 0xff; + if (data_bytes == 2) + data->word = csr_in32(SMB_CSR(adap, R_SMB_DATA)) & 0xffff; + + return 0; +} + +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_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA); +} + + +/* -----exported algorithm data: ------------------------------------- */ + +static struct i2c_algorithm i2c_sibyte_algo = { + .name = "SiByte algorithm", + .id = I2C_ALGO_SIBYTE, + .smbus_xfer = smbus_xfer, + .algo_control = algo_control, /* ioctl */ + .functionality = bit_func, +}; + +/* + * registering functions to load algorithms at runtime + */ +int i2c_sibyte_add_bus(struct i2c_adapter *i2c_adap, int speed) +{ + int i; + struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data; + + /* register new adapter to i2c module... */ + + i2c_adap->id |= i2c_sibyte_algo.id; + i2c_adap->algo = &i2c_sibyte_algo; + + /* Set the frequency to 100 kHz */ + csr_out32(speed, SMB_CSR(adap,R_SMB_FREQ)); + csr_out32(0, SMB_CSR(adap,R_SMB_CONTROL)); + + /* scan bus */ + if (bit_scan) { + union i2c_smbus_data data; + int rc; + printk(KERN_INFO " i2c-algo-sibyte.o: scanning bus %s.\n", + i2c_adap->name); + for (i = 0x00; i < 0x7f; i++) { + /* XXXKW is this a realistic probe? */ + rc = smbus_xfer(i2c_adap, i, 0, I2C_SMBUS_READ, 0, + I2C_SMBUS_BYTE_DATA, &data); + if (!rc) { + printk("(%02x)",i); + } else + printk("."); + } + printk("\n"); + } + + i2c_add_adapter(i2c_adap); + + return 0; +} + + +int i2c_sibyte_del_bus(struct i2c_adapter *adap) +{ + int res; + + if ((res = i2c_del_adapter(adap)) < 0) + return res; + + return 0; +} + +int __init i2c_algo_sibyte_init (void) +{ + printk("i2c-algo-sibyte.o: i2c SiByte algorithm module\n"); + return 0; +} + + +EXPORT_SYMBOL(i2c_sibyte_add_bus); +EXPORT_SYMBOL(i2c_sibyte_del_bus); + +#ifdef MODULE +MODULE_AUTHOR("Kip Walker, Broadcom Corp."); +MODULE_DESCRIPTION("SiByte I2C-Bus algorithm"); +MODULE_PARM(bit_scan, "i"); +MODULE_PARM_DESC(bit_scan, "Scan for active chips on the bus"); +MODULE_LICENSE("GPL"); + +int init_module(void) +{ + return i2c_algo_sibyte_init(); +} + +void cleanup_module(void) +{ +} +#endif |