X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fcris%2Farch-v10%2Fdrivers%2Fi2c.c;h=092c724a645f8096b32c07c60de600dd665da932;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=7731f7d22f91855e175d074eff2df919c299c65b;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/arch/cris/arch-v10/drivers/i2c.c b/arch/cris/arch-v10/drivers/i2c.c index 7731f7d22..092c724a6 100644 --- a/arch/cris/arch-v10/drivers/i2c.c +++ b/arch/cris/arch-v10/drivers/i2c.c @@ -12,6 +12,27 @@ *! don't use PB_I2C if DS1302 uses same bits, *! use PB. *! $Log: i2c.c,v $ +*! Revision 1.13 2005/03/07 13:13:07 starvik +*! Added spinlocks to protect states etc +*! +*! Revision 1.12 2005/01/05 06:11:22 starvik +*! No need to do local_irq_disable after local_irq_save. +*! +*! Revision 1.11 2004/12/13 12:21:52 starvik +*! Added I/O and DMA allocators from Linux 2.4 +*! +*! Revision 1.9 2004/08/24 06:49:14 starvik +*! Whitespace cleanup +*! +*! Revision 1.8 2004/06/08 08:48:26 starvik +*! Removed unused code +*! +*! Revision 1.7 2004/05/28 09:26:59 starvik +*! Modified I2C initialization to work in 2.6. +*! +*! Revision 1.6 2004/05/14 07:58:03 starvik +*! Merge of changes from 2.4 +*! *! Revision 1.4 2002/12/11 13:13:57 starvik *! Added arch/ to v10 specific includes *! Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer) @@ -26,7 +47,7 @@ *! Update Port B register and shadow even when running with hardware support *! to avoid glitches when reading bits *! Never set direction to out in i2c_inbyte -*! Removed incorrect clock togling at end of i2c_inbyte +*! Removed incorrect clock toggling at end of i2c_inbyte *! *! Revision 1.8 2002/08/13 06:31:53 starvik *! Made SDA and SCL line configurable @@ -63,7 +84,7 @@ *! (C) Copyright 1999-2002 Axis Communications AB, LUND, SWEDEN *! *!***************************************************************************/ -/* $Id: i2c.c,v 1.4 2002/12/11 13:13:57 starvik Exp $ */ +/* $Id: i2c.c,v 1.13 2005/03/07 13:13:07 starvik Exp $ */ /****************** INCLUDE FILES SECTION ***********************************/ @@ -75,7 +96,6 @@ #include #include #include -#include #include @@ -83,6 +103,7 @@ #include #include #include +#include #include "i2c.h" @@ -104,14 +125,6 @@ static const char i2c_name[] = "i2c"; #define I2C_DATA_HIGH 1 #define I2C_DATA_LOW 0 -#if 0 -/* TODO: fix this so the CONFIG_ETRAX_I2C_USES... is set in Config.in instead */ -#if defined(CONFIG_DS1302) && (CONFIG_DS1302_SDABIT==0) && \ - (CONFIG_DS1302_SCLBIT == 1) -#define CONFIG_ETRAX_I2C_USES_PB_NOT_PB_I2C -#endif -#endif - #ifdef CONFIG_ETRAX_I2C_USES_PB_NOT_PB_I2C /* Use PB and not PB_I2C */ #ifndef CONFIG_ETRAX_I2C_DATA_PORT @@ -180,6 +193,7 @@ static const char i2c_name[] = "i2c"; #define i2c_delay(usecs) udelay(usecs) +static DEFINE_SPINLOCK(i2c_lock); /* Protect directions etc */ /****************** FUNCTION DEFINITION SECTION *************************/ @@ -310,6 +324,12 @@ i2c_inbyte(void) } i2c_clk(I2C_CLOCK_HIGH); i2c_delay(CLOCK_HIGH_TIME); + + /* + * we leave the clock low, getbyte is usually followed + * by sendack/nack, they assume the clock to be low + */ + i2c_clk(I2C_CLOCK_LOW); return aBitByte; } @@ -371,6 +391,13 @@ i2c_getack(void) i2c_delay(CLOCK_HIGH_TIME/2); } + /* + * our clock is high now, make sure data is low + * before we enable our output. If we keep data high + * and enable output, we would generate a stop condition. + */ + i2c_data(I2C_DATA_LOW); + /* * end clock pulse */ @@ -422,7 +449,38 @@ i2c_sendack(void) */ i2c_data(I2C_DATA_HIGH); i2c_delay(CLOCK_LOW_TIME); - + + i2c_dir_in(); +} + +/*#--------------------------------------------------------------------------- +*# +*# FUNCTION NAME: i2c_sendnack +*# +*# DESCRIPTION : Sends NACK on received data +*# +*#--------------------------------------------------------------------------*/ +void +i2c_sendnack(void) +{ + /* + * enable output + */ + i2c_delay(CLOCK_LOW_TIME); + i2c_dir_out(); + /* + * set data high + */ + i2c_data(I2C_DATA_HIGH); + /* + * generate clock pulse + */ + i2c_delay(CLOCK_HIGH_TIME/6); + i2c_clk(I2C_CLOCK_HIGH); + i2c_delay(CLOCK_HIGH_TIME); + i2c_clk(I2C_CLOCK_LOW); + i2c_delay(CLOCK_LOW_TIME); + i2c_dir_in(); } @@ -440,13 +498,14 @@ i2c_writereg(unsigned char theSlave, unsigned char theReg, int error, cntr = 3; unsigned long flags; + spin_lock(&i2c_lock); + do { error = 0; /* * we don't like to be interrupted */ local_irq_save(flags); - local_irq_disable(); i2c_start(); /* @@ -489,7 +548,9 @@ i2c_writereg(unsigned char theSlave, unsigned char theReg, } while(error && cntr--); i2c_delay(CLOCK_LOW_TIME); - + + spin_unlock(&i2c_lock); + return -error; } @@ -507,13 +568,14 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg) int error, cntr = 3; unsigned long flags; + spin_lock(&i2c_lock); + do { error = 0; /* * we don't like to be interrupted */ local_irq_save(flags); - local_irq_disable(); /* * generate start condition */ @@ -557,7 +619,8 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg) */ b = i2c_inbyte(); /* - * send Ack + * last received byte needs to be nacked + * instead of acked */ i2c_sendack(); /* @@ -571,6 +634,8 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg) } while(error && cntr--); + spin_unlock(&i2c_lock); + return b; } @@ -634,20 +699,29 @@ static struct file_operations i2c_fops = { .release = i2c_release, }; -static int __init +int __init i2c_init(void) { - int res; + static int res = 0; + static int first = 1; + + if (!first) { + return res; + } /* Setup and enable the Port B I2C interface */ #ifndef CONFIG_ETRAX_I2C_USES_PB_NOT_PB_I2C + if ((res = cris_request_io_interface(if_i2c, "I2C"))) { + printk(KERN_CRIT "i2c_init: Failed to get IO interface\n"); + return res; + } + *R_PORT_PB_I2C = port_pb_i2c_shadow |= IO_STATE(R_PORT_PB_I2C, i2c_en, on) | IO_FIELD(R_PORT_PB_I2C, i2c_d, 1) | IO_FIELD(R_PORT_PB_I2C, i2c_clk, 1) | IO_STATE(R_PORT_PB_I2C, i2c_oe_, enable); -#endif port_pb_dir_shadow &= ~IO_MASK(R_PORT_PB_DIR, dir0); port_pb_dir_shadow &= ~IO_MASK(R_PORT_PB_DIR, dir1); @@ -655,22 +729,49 @@ i2c_init(void) *R_PORT_PB_DIR = (port_pb_dir_shadow |= IO_STATE(R_PORT_PB_DIR, dir0, input) | IO_STATE(R_PORT_PB_DIR, dir1, output)); +#else + if ((res = cris_io_interface_allocate_pins(if_i2c, + 'b', + CONFIG_ETRAX_I2C_DATA_PORT, + CONFIG_ETRAX_I2C_DATA_PORT))) { + printk(KERN_WARNING "i2c_init: Failed to get IO pin for I2C data port\n"); + return res; + } else if ((res = cris_io_interface_allocate_pins(if_i2c, + 'b', + CONFIG_ETRAX_I2C_CLK_PORT, + CONFIG_ETRAX_I2C_CLK_PORT))) { + cris_io_interface_free_pins(if_i2c, + 'b', + CONFIG_ETRAX_I2C_DATA_PORT, + CONFIG_ETRAX_I2C_DATA_PORT); + printk(KERN_WARNING "i2c_init: Failed to get IO pin for I2C clk port\n"); + } +#endif + + return res; +} - /* register char device */ +static int __init +i2c_register(void) +{ + int res; - res = register_chrdev(I2C_MAJOR, i2c_name, &i2c_fops); + res = i2c_init(); + if (res < 0) + return res; + res = register_chrdev(I2C_MAJOR, i2c_name, &i2c_fops); if(res < 0) { printk(KERN_ERR "i2c: couldn't get a major number.\n"); return res; } - printk("I2C driver v2.2, (c) 1999-2001 Axis Communications AB\n"); + printk(KERN_INFO "I2C driver v2.2, (c) 1999-2004 Axis Communications AB\n"); return 0; } -/* this makes sure that i2c_init is called during boot */ +/* this makes sure that i2c_register is called during boot */ -module_init(i2c_init); +module_init(i2c_register); /****************** END OF FILE i2c.c ********************************/