ChangeSet 1.1419.1.4, 2003/06/11 11:43:02-07:00, peterm at remware.demon.co.uk [PATCH] I2C: add New bus driver: XSCALE iop3xx drivers/i2c/Kconfig | 4 drivers/i2c/Makefile | 1 drivers/i2c/i2c-iop3xx.c | 565 +++++++++++++++++++++++++++++++++++++++++++++++ drivers/i2c/i2c-iop3xx.h | 120 +++++++++ 4 files changed, 690 insertions(+) diff -Nru a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig --- a/drivers/i2c/Kconfig Wed Jun 11 13:25:23 2003 +++ b/drivers/i2c/Kconfig Wed Jun 11 13:25:23 2003 @@ -193,6 +193,10 @@ tristate "IBM on-chip I2C Adapter" depends on I2C_IBM_OCP_ALGO +config I2C_IOP3XX + tristate "Intel XScale IOP3xx on-chip I2C interface" + depends on ARCH_IOP3XX && I2C + config I2C_CHARDEV tristate "I2C device interface" depends on I2C diff -Nru a/drivers/i2c/Makefile b/drivers/i2c/Makefile --- a/drivers/i2c/Makefile Wed Jun 11 13:25:23 2003 +++ b/drivers/i2c/Makefile Wed Jun 11 13:25:23 2003 @@ -16,4 +16,5 @@ obj-$(CONFIG_SCx200_I2C) += scx200_i2c.o obj-$(CONFIG_SCx200_ACB) += scx200_acb.o obj-$(CONFIG_I2C_SENSOR) += i2c-sensor.o +obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o obj-y += busses/ chips/ diff -Nru a/drivers/i2c/i2c-iop3xx.c b/drivers/i2c/i2c-iop3xx.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/i2c/i2c-iop3xx.c Wed Jun 11 13:25:23 2003 @@ -0,0 +1,565 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-iop3xx.c i2c driver algorithms for Intel XScale IOP3xx */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 2003 Peter Milne, D-TACQ Solutions Ltd + * <Peter dot Milne at D hyphen TACQ dot com> + + 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, version 2. + + + 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 acknowledgements to i2c-algo-ibm_ocp.c by + Ian DaSilva, MontaVista Software, Inc. idasilva at mvista.com + + And 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 + + And which acknowledged Ky?sti M?lkki <kmalkki at cc.hut.fi>, + Frodo Looijaard <frodol at dds.nl>, Martin Bailey<mbailey at littlefeet-inc.com> + + ---------------------------------------------------------------------------*/ + + +#include <linux/interrupt.h> +#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 <asm/arch-iop3xx/iop321.h> +#include <asm/arch-iop3xx/iop321-irqs.h> +#include "i2c-iop3xx.h" + + +/* ----- global defines ----------------------------------------------- */ +#define PASSERT(x) do { if (!(x) ) \ + printk(KERN_CRIT "PASSERT %s in %s:%d\n", #x, __FILE__, __LINE__ );\ + } while (0) + + +/* ----- global variables --------------------------------------------- */ + + +static inline unsigned char iic_cook_addr(struct i2c_msg *msg) +{ + unsigned char addr; + + addr = ( msg->addr << 1 ); + + if (msg->flags & I2C_M_RD ) + addr |= 1; + +/* PGM: what is M_REV_DIR_ADDR - do we need it ?? */ + if (msg->flags & I2C_M_REV_DIR_ADDR ) + addr ^= 1; + + return addr; +} + + +static inline void iop3xx_adap_reset(struct i2c_algo_iop3xx_data *iop3xx_adap) +{ + // Follows devman 9.3 + *iop3xx_adap->biu->CR = IOP321_ICR_UNIT_RESET; + *iop3xx_adap->biu->SR = IOP321_ISR_CLEARBITS; + *iop3xx_adap->biu->CR = 0; +} + +static inline void iop3xx_adap_set_slave_addr( + struct i2c_algo_iop3xx_data *iop3xx_adap ) +{ + *iop3xx_adap->biu->SAR = MYSAR; +} + +static inline void iop3xx_adap_enable( + struct i2c_algo_iop3xx_data *iop3xx_adap ) +{ + u32 cr = IOP321_ICR_GCD|IOP321_ICR_SCLEN|IOP321_ICR_UE; + +/* NB SR bits not same position as CR IE bits :-( */ + iop3xx_adap->biu->SR_enabled = + IOP321_ISR_ALD|IOP321_ISR_BERRD| + IOP321_ISR_RXFULL|IOP321_ISR_TXEMPTY; + + cr |= IOP321_ICR_ALDIE|IOP321_ICR_BERRIE| + IOP321_ICR_RXFULLIE|IOP321_ICR_TXEMPTYIE; + + *iop3xx_adap->biu->CR = cr; +} + +static void iop3xx_adap_transaction_cleanup( + struct i2c_algo_iop3xx_data *iop3xx_adap ) +{ + unsigned cr = *iop3xx_adap->biu->CR; + + cr &= ~(IOP321_ICR_MSTART | IOP321_ICR_TBYTE | + IOP321_ICR_MSTOP | IOP321_ICR_SCLEN ); + *iop3xx_adap->biu->CR = cr; +} + +static void iop3xx_adap_final_cleanup( + struct i2c_algo_iop3xx_data *iop3xx_adap ) +{ + unsigned cr = *iop3xx_adap->biu->CR; + + cr &= ~(IOP321_ICR_ALDIE|IOP321_ICR_BERRIE| + IOP321_ICR_RXFULLIE|IOP321_ICR_TXEMPTYIE); + iop3xx_adap->biu->SR_enabled = 0; + *iop3xx_adap->biu->CR = cr; +} + + + +static void iop3xx_i2c_handler( int this_irq, + void *dev_id, + struct pt_regs *regs) +/* + * NB: the handler has to clear the source of the interrupt! + * Then it passes the SR flags of interest to BH via adap data + */ +{ + struct i2c_algo_iop3xx_data *iop3xx_adap = dev_id; + + u32 sr = *iop3xx_adap->biu->SR; + + if ( (sr &= iop3xx_adap->biu->SR_enabled) ){ + *iop3xx_adap->biu->SR = sr; + iop3xx_adap->biu->SR_received |= sr; + wake_up_interruptible(&iop3xx_adap->waitq); + } +} + + +static int iop3xx_adap_error( u32 sr ) +// check all error conditions, clear them , report most important +{ + int rc = 0; + + if ( (sr&IOP321_ISR_BERRD) ) { + if ( !rc ) rc = -I2C_ERR_BERR; + } + if ( (sr&IOP321_ISR_ALD) ){ + if ( !rc ) rc = -I2C_ERR_ALD; + } + return rc; +} + +static inline u32 get_srstat( struct i2c_algo_iop3xx_data *iop3xx_adap ) +{ + unsigned long flags; + u32 sr; + + spin_lock_irqsave( &iop3xx_adap->lock, flags ); + sr = iop3xx_adap->biu->SR_received; + iop3xx_adap->biu->SR_received = 0; + spin_unlock_irqrestore( &iop3xx_adap->lock, flags ); + + return sr; +} + +/* + * sleep until interrupted, then recover and analyse the SR + * saved by handler + */ +typedef int (* CompareFunc)( unsigned test, unsigned mask ); +/* returns 1 on correct comparison */ + +static int iop3xx_adap_wait_event( struct i2c_algo_iop3xx_data *iop3xx_adap, + unsigned flags, unsigned* status, + CompareFunc compare ) +{ + unsigned sr = 0; + int interrupted; + int done; + int rc; + + do { + interrupted = wait_event_interruptible_timeout ( + iop3xx_adap->waitq, + (done = compare( sr = get_srstat(iop3xx_adap),flags )), + iop3xx_adap->timeout + ); + if ( (rc = iop3xx_adap_error( sr )) < 0 ){ + *status = sr; + return rc; + }else if ( !interrupted ){ + *status = sr; + return rc = -ETIMEDOUT; + } + } while( !done ); + + *status = sr; + + return rc = 0; +} + + + + +/* + * Concrete CompareFuncs + */ +static int all_bits_clear( unsigned test, unsigned mask ) { + return (test&mask) == 0; +} +static int any_bits_set( unsigned test, unsigned mask ) { + return (test&mask) != 0; +} + + +static int iop3xx_adap_wait_tx_done( + struct i2c_algo_iop3xx_data *iop3xx_adap, int *status) +{ + return iop3xx_adap_wait_event( + iop3xx_adap, + IOP321_ISR_TXEMPTY|IOP321_ISR_ALD|IOP321_ISR_BERRD, + status, any_bits_set ); +} + +static int iop3xx_adap_wait_rx_done( + struct i2c_algo_iop3xx_data *iop3xx_adap, int *status) +{ + return iop3xx_adap_wait_event( + iop3xx_adap, + IOP321_ISR_RXFULL|IOP321_ISR_ALD|IOP321_ISR_BERRD, + status, any_bits_set ); +} + +static int iop3xx_adap_wait_idle( + struct i2c_algo_iop3xx_data *iop3xx_adap, int *status) +{ + return iop3xx_adap_wait_event( + iop3xx_adap, IOP321_ISR_UNITBUSY, status, all_bits_clear ); +} + +// +// Description: This performs the IOP3xx initialization sequence +// Valid for IOP321. Maybe valid for IOP310?. +// +static int iop3xx_adap_init (struct i2c_algo_iop3xx_data *iop3xx_adap) +{ + *IOP321_GPOD &= ~(iop3xx_adap->channel==0? + IOP321_GPOD_I2C0: + IOP321_GPOD_I2C1); + + iop3xx_adap_reset( iop3xx_adap ); + iop3xx_adap_set_slave_addr( iop3xx_adap ); + iop3xx_adap_enable( iop3xx_adap ); + + return 0; +} + +static int iop3xx_adap_send_target_slave_addr( + struct i2c_algo_iop3xx_data *iop3xx_adap, struct i2c_msg* msg ) +{ + unsigned cr = *iop3xx_adap->biu->CR; + int status; + int rc; + + *iop3xx_adap->biu->DBR = iic_cook_addr( msg ); + + cr &= ~(IOP321_ICR_MSTOP|IOP321_ICR_NACK); + cr |= IOP321_ICR_MSTART | IOP321_ICR_TBYTE; + + *iop3xx_adap->biu->CR = cr; + rc = iop3xx_adap_wait_tx_done( iop3xx_adap, &status ); +/* this assert fires every time, contrary to IOP manual + PASSERT( (status&IOP321_ISR_UNITBUSY)!=0 ); +*/ + PASSERT( (status&IOP321_ISR_RXREAD)==0 ); + + return rc; +} + +static int iop3xx_adap_write_byte( + struct i2c_algo_iop3xx_data *iop3xx_adap, char byte, int stop ) +{ + unsigned cr = *iop3xx_adap->biu->CR; + int status; + int rc; + + *iop3xx_adap->biu->DBR = byte; + cr &= ~IOP321_ICR_MSTART; + if ( stop ){ + cr |= IOP321_ICR_MSTOP; + }else{ + cr &= ~IOP321_ICR_MSTOP; + } + *iop3xx_adap->biu->CR = cr |= IOP321_ICR_TBYTE; + rc = iop3xx_adap_wait_tx_done( iop3xx_adap, &status ); + + return rc; +} + +static int iop3xx_adap_read_byte( + struct i2c_algo_iop3xx_data *iop3xx_adap, char* byte, int stop ) +{ + unsigned cr = *iop3xx_adap->biu->CR; + int status; + int rc; + + cr &= ~IOP321_ICR_MSTART; + + if ( stop ){ + cr |= IOP321_ICR_MSTOP|IOP321_ICR_NACK; + }else{ + cr &= ~(IOP321_ICR_MSTOP|IOP321_ICR_NACK); + } + *iop3xx_adap->biu->CR = cr |= IOP321_ICR_TBYTE; + + rc = iop3xx_adap_wait_rx_done( iop3xx_adap, &status ); + + *byte = *iop3xx_adap->biu->DBR; + + return rc; +} + + +static int iop3xx_i2c_writebytes( struct i2c_adapter *i2c_adap, + const char *buf, int count ) +{ + struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data; + int ii; + int rc = 0; + + for ( ii = 0; rc == 0 && ii != count; ++ii ){ + rc = iop3xx_adap_write_byte(iop3xx_adap, buf[ii], ii==count-1); + } + return rc; +} + + +static int iop3xx_i2c_readbytes( struct i2c_adapter *i2c_adap, + char *buf, int count ) +{ + struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data; + int ii; + int rc = 0; + + for ( ii = 0; rc == 0 && ii != count; ++ii ){ + rc = iop3xx_adap_read_byte(iop3xx_adap, &buf[ii], ii==count-1); + } + return rc; +} + + +/* + * Description: This function implements combined transactions. Combined + * transactions consist of combinations of reading and writing blocks of data. + * FROM THE SAME ADDRESS + * Each transfer (i.e. a read or a write) is separated by a repeated start + * condition. + */ + +static int iop3xx_handle_msg( + struct i2c_adapter *i2c_adap, struct i2c_msg* pmsg ) +{ + struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data; + int rc; + + rc = iop3xx_adap_send_target_slave_addr( iop3xx_adap, pmsg ); + if ( rc < 0 ){ + return rc; + } + + if ( (pmsg->flags&I2C_M_RD) ){ + return iop3xx_i2c_readbytes( i2c_adap, pmsg->buf, pmsg->len ); + }else{ + return iop3xx_i2c_writebytes( i2c_adap, pmsg->buf, pmsg->len ); + } +} + + + +/* + * master_xfer() - main read/write entry + */ +static int iop3xx_master_xfer( + struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num ) +{ + struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data; + int im = 0; + int ret = 0; + int status; + + iop3xx_adap_wait_idle( iop3xx_adap, &status ); + iop3xx_adap_reset( iop3xx_adap ); + iop3xx_adap_enable( iop3xx_adap ); + + for ( im = 0; ret == 0 && im != num; ++im ){ + ret = iop3xx_handle_msg( i2c_adap, &msgs[im] ); + } + + iop3xx_adap_transaction_cleanup( iop3xx_adap ); + + return ret; +} + + + +static int algo_control(struct i2c_adapter *adapter, + unsigned int cmd, unsigned long arg) +{ + return 0; +} + + +static u32 iic_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C|I2C_FUNC_SMBUS_EMUL; +} + + +/* -----exported algorithm data: ------------------------------------- */ + +static struct i2c_algorithm iic_algo = { + .name = "IOP3xx I2C algorithm", + .id = I2C_ALGO_OCP_IOP3XX, + .master_xfer = iop3xx_master_xfer, + .algo_control = algo_control, + .functionality = iic_func, +}; + +/* + * registering functions to load algorithms at runtime + */ + + +static int i2c_iop3xx_add_bus(struct i2c_adapter *iic_adap) +{ + struct i2c_algo_iop3xx_data *iop3xx_adap = iic_adap->algo_data; + + if ( !request_region( REGION_START(iop3xx_adap), + REGION_LENGTH(iop3xx_adap), + iic_adap->name ) ){ + return -ENODEV; + } + + init_waitqueue_head( &iop3xx_adap->waitq ); + spin_lock_init( &iop3xx_adap->lock ); + + if ( request_irq( + iop3xx_adap->biu->irq, + iop3xx_i2c_handler, + /* SA_SAMPLE_RANDOM */ 0, + iic_adap->name, + iop3xx_adap ) ){ + return -ENODEV; + } + +/* register new iic_adapter to i2c module... */ + + iic_adap->id |= iic_algo.id; + iic_adap->algo = &iic_algo; + + iic_adap->timeout = 100; /* default values, should */ + iic_adap->retries = 3; /* be replaced by defines */ + + iop3xx_adap_init( iic_adap->algo_data ); + i2c_add_adapter(iic_adap); + return 0; +} + + +static int i2c_iop3xx_del_bus(struct i2c_adapter *iic_adap) +{ + struct i2c_algo_iop3xx_data *iop3xx_adap = iic_adap->algo_data; + + iop3xx_adap_final_cleanup( iop3xx_adap ); + free_irq( iop3xx_adap->biu->irq, iop3xx_adap ); + + release_region( REGION_START(iop3xx_adap), REGION_LENGTH(iop3xx_adap)); + + return i2c_del_adapter( iic_adap ); +} + +#ifdef CONFIG_ARCH_IOP321 + +static struct iop3xx_biu biu0 = { + .CR = IOP321_ICR0, + .SR = IOP321_ISR0, + .SAR = IOP321_ISAR0, + .DBR = IOP321_IDBR0, + .BMR = IOP321_IBMR0, + .irq = IRQ_IOP321_I2C_0 +}; + +static struct iop3xx_biu biu1 = { + .CR = IOP321_ICR1, + .SR = IOP321_ISR1, + .SAR = IOP321_ISAR1, + .DBR = IOP321_IDBR1, + .BMR = IOP321_IBMR1, + .irq = IRQ_IOP321_I2C_1 +}; + +#define ADAPTER_NAME_ROOT "IOP321 i2c biu adapter " +#else +#error Please define the BIU struct iop3xx_biu for your processor arch +#endif + +static struct i2c_algo_iop3xx_data algo_iop3xx_data0 = { + .channel = 0, + .biu = &biu0, + .timeout = 1*HZ +}; +static struct i2c_algo_iop3xx_data algo_iop3xx_data1 = { + .channel = 1, + .biu = &biu1, + .timeout = 1*HZ +}; + +static struct i2c_adapter iop3xx_ops0 = { + .owner = THIS_MODULE, + .name = ADAPTER_NAME_ROOT "0", + .id = I2C_HW_IOP321, + .algo_data = &algo_iop3xx_data0 +}; +static struct i2c_adapter iop3xx_ops1 = { + .owner = THIS_MODULE, + .name = ADAPTER_NAME_ROOT "1", + .id = I2C_HW_IOP321, + .algo_data = &algo_iop3xx_data1 +}; + +static int __init i2c_iop3xx_init (void) +{ + return i2c_iop3xx_add_bus(&iop3xx_ops0) || + i2c_iop3xx_add_bus(&iop3xx_ops1); +} + +static void __exit i2c_iop3xx_exit (void) +{ + i2c_iop3xx_del_bus(&iop3xx_ops0); + i2c_iop3xx_del_bus(&iop3xx_ops1); +} + +module_init (i2c_iop3xx_init); +module_exit (i2c_iop3xx_exit); + +MODULE_AUTHOR("D-TACQ Solutions Ltd <www.d-tacq.com>"); +MODULE_DESCRIPTION("IOP3xx iic algorithm and driver"); +MODULE_LICENSE("GPL"); + +MODULE_PARM(i2c_debug,"i"); + +MODULE_PARM_DESC(i2c_debug, + "debug level - 0 off; 1 normal; 2,3 more verbose; 9 iic-protocol"); + diff -Nru a/drivers/i2c/i2c-iop3xx.h b/drivers/i2c/i2c-iop3xx.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/i2c/i2c-iop3xx.h Wed Jun 11 13:25:23 2003 @@ -0,0 +1,120 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-iop3xx.h algorithm driver definitions private to i2c-iop3xx.c */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 2003 Peter Milne, D-TACQ Solutions Ltd + * <Peter dot Milne at D hyphen TACQ dot com> + + 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, version 2. + + 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. */ +/* ------------------------------------------------------------------------- */ + + +#ifndef I2C_IOP3XX_H +#define I2C_IOP3XX_H 1 + +/* + * iop321 hardware bit definitions + */ +#define IOP321_ICR_FAST_MODE 0x8000 /* 1=400kBps, 0=100kBps */ +#define IOP321_ICR_UNIT_RESET 0x4000 /* 1=RESET */ +#define IOP321_ICR_SADIE 0x2000 /* 1=Slave Detect Interrupt Enable */ +#define IOP321_ICR_ALDIE 0x1000 /* 1=Arb Loss Detect Interrupt Enable */ +#define IOP321_ICR_SSDIE 0x0800 /* 1=Slave STOP Detect Interrupt Enable */ +#define IOP321_ICR_BERRIE 0x0400 /* 1=Bus Error Interrupt Enable */ +#define IOP321_ICR_RXFULLIE 0x0200 /* 1=Receive Full Interrupt Enable */ +#define IOP321_ICR_TXEMPTYIE 0x0100 /* 1=Transmit Empty Interrupt Enable */ +#define IOP321_ICR_GCD 0x0080 /* 1=General Call Disable */ +/* + * IOP321_ICR_GCD: 1 disables response as slave. "This bit must be set + * when sending a master mode general call message from the I2C unit" + */ +#define IOP321_ICR_UE 0x0040 /* 1=Unit Enable */ +/* + * "NOTE: To avoid I2C bus integrity problems, + * the user needs to ensure that the GPIO Output Data Register - + * GPOD bits associated with an I2C port are cleared prior to setting + * the enable bit for that I2C serial port. + * The user prepares to enable I2C port 0 and + * I2C port 1 by clearing GPOD bits 7:6 and GPOD bits 5:4, respectively. + */ +#define IOP321_ICR_SCLEN 0x0020 /* 1=SCL enable for master mode */ +#define IOP321_ICR_MABORT 0x0010 /* 1=Send a STOP with no data + * NB TBYTE must be clear */ +#define IOP321_ICR_TBYTE 0x0008 /* 1=Send/Receive a byte. i2c clears */ +#define IOP321_ICR_NACK 0x0004 /* 1=reply with NACK */ +#define IOP321_ICR_MSTOP 0x0002 /* 1=send a STOP after next data byte */ +#define IOP321_ICR_MSTART 0x0001 /* 1=initiate a START */ + + +#define IOP321_ISR_BERRD 0x0400 /* 1=BUS ERROR Detected */ +#define IOP321_ISR_SAD 0x0200 /* 1=Slave ADdress Detected */ +#define IOP321_ISR_GCAD 0x0100 /* 1=General Call Address Detected */ +#define IOP321_ISR_RXFULL 0x0080 /* 1=Receive Full */ +#define IOP321_ISR_TXEMPTY 0x0040 /* 1=Transmit Empty */ +#define IOP321_ISR_ALD 0x0020 /* 1=Arbitration Loss Detected */ +#define IOP321_ISR_SSD 0x0010 /* 1=Slave STOP Detected */ +#define IOP321_ISR_BBUSY 0x0008 /* 1=Bus BUSY */ +#define IOP321_ISR_UNITBUSY 0x0004 /* 1=Unit Busy */ +#define IOP321_ISR_NACK 0x0002 /* 1=Unit Rx or Tx a NACK */ +#define IOP321_ISR_RXREAD 0x0001 /* 1=READ 0=WRITE (R/W bit of slave addr */ + +#define IOP321_ISR_CLEARBITS 0x07f0 + +#define IOP321_ISAR_SAMASK 0x007f + +#define IOP321_IDBR_MASK 0x00ff + +#define IOP321_IBMR_SCL 0x0002 +#define IOP321_IBMR_SDA 0x0001 + +#define IOP321_GPOD_I2C0 0x00c0 /* clear these bits to enable ch0 */ +#define IOP321_GPOD_I2C1 0x0030 /* clear these bits to enable ch1 */ + +#define MYSAR 0x02 /* SWAG a suitable slave address */ + +#define I2C_ERR 321 +#define I2C_ERR_BERR (I2C_ERR+0) +#define I2C_ERR_ALD (I2C_ERR+1) + + +typedef volatile u32* r32; + +struct iop3xx_biu { /* Bus Interface Unit - the hardware */ +/* physical hardware defs - regs*/ + r32 CR; + r32 SR; + r32 SAR; + r32 DBR; + r32 BMR; +/* irq bit vector */ + u32 irq; +/* stored flags */ + u32 SR_enabled, SR_received; +}; + +struct i2c_algo_iop3xx_data { + int channel; + + wait_queue_head_t waitq; + spinlock_t lock; + int timeout; + struct iop3xx_biu* biu; +}; + +#define REGION_START( adap ) ((u32)((adap)->biu->CR)) +#define REGION_END( adap ) ((u32)((adap)->biu->BMR+1)) +#define REGION_LENGTH( adap ) (REGION_END(adap)-REGION_START(adap)) + +#define IRQ_STATUS_MASK( adap ) (1<<adap->biu->irq) + +#endif /* I2C_IOP3XX_H */