Commit 201ca54a authored by Jesper Nilsson's avatar Jesper Nilsson

CRIS v32: New version of I2C driver.

- Add i2c_write and i2c_read as functions.
- Use spinlocks for critical regions.
- Add config item to set I2C data and clock port.
- Put unneeded testcode inside #if 0.
- Remove CVS id tag.
parent 0f229504
...@@ -19,10 +19,10 @@ ...@@ -19,10 +19,10 @@
*! *!
*! --------------------------------------------------------------------------- *! ---------------------------------------------------------------------------
*! *!
*! (C) Copyright 1999-2002 Axis Communications AB, LUND, SWEDEN *! (C) Copyright 1999-2007 Axis Communications AB, LUND, SWEDEN
*! *!
*!***************************************************************************/ *!***************************************************************************/
/* $Id: i2c.c,v 1.2 2005/05/09 15:29:49 starvik Exp $ */
/****************** INCLUDE FILES SECTION ***********************************/ /****************** INCLUDE FILES SECTION ***********************************/
#include <linux/module.h> #include <linux/module.h>
...@@ -79,6 +79,8 @@ static const char i2c_name[] = "i2c"; ...@@ -79,6 +79,8 @@ static const char i2c_name[] = "i2c";
#define i2c_delay(usecs) udelay(usecs) #define i2c_delay(usecs) udelay(usecs)
static DEFINE_SPINLOCK(i2c_lock); /* Protect directions etc */
/****************** VARIABLE SECTION ************************************/ /****************** VARIABLE SECTION ************************************/
static struct crisv32_iopin cris_i2c_clk; static struct crisv32_iopin cris_i2c_clk;
...@@ -252,6 +254,7 @@ i2c_getack(void) ...@@ -252,6 +254,7 @@ i2c_getack(void)
* generate ACK clock pulse * generate ACK clock pulse
*/ */
i2c_clk(I2C_CLOCK_HIGH); i2c_clk(I2C_CLOCK_HIGH);
#if 0
/* /*
* Use PORT PB instead of I2C * Use PORT PB instead of I2C
* for input. (I2C not working) * for input. (I2C not working)
...@@ -264,6 +267,8 @@ i2c_getack(void) ...@@ -264,6 +267,8 @@ i2c_getack(void)
i2c_data(1); i2c_data(1);
i2c_disable(); i2c_disable();
i2c_dir_in(); i2c_dir_in();
#endif
/* /*
* now wait for ack * now wait for ack
*/ */
...@@ -271,11 +276,11 @@ i2c_getack(void) ...@@ -271,11 +276,11 @@ i2c_getack(void)
/* /*
* check for ack * check for ack
*/ */
if(i2c_getbit()) if (i2c_getbit())
ack = 0; ack = 0;
i2c_delay(CLOCK_HIGH_TIME/2); i2c_delay(CLOCK_HIGH_TIME/2);
if(!ack){ if (!ack) {
if(!i2c_getbit()) /* receiver pulled SDA low */ if (!i2c_getbit()) /* receiver pulld SDA low */
ack = 1; ack = 1;
i2c_delay(CLOCK_HIGH_TIME/2); i2c_delay(CLOCK_HIGH_TIME/2);
} }
...@@ -285,6 +290,7 @@ i2c_getack(void) ...@@ -285,6 +290,7 @@ i2c_getack(void)
* before we enable our output. If we keep data high * before we enable our output. If we keep data high
* and enable output, we would generate a stop condition. * and enable output, we would generate a stop condition.
*/ */
#if 0
i2c_data(I2C_DATA_LOW); i2c_data(I2C_DATA_LOW);
/* /*
...@@ -292,6 +298,7 @@ i2c_getack(void) ...@@ -292,6 +298,7 @@ i2c_getack(void)
*/ */
i2c_enable(); i2c_enable();
i2c_dir_out(); i2c_dir_out();
#endif
i2c_clk(I2C_CLOCK_LOW); i2c_clk(I2C_CLOCK_LOW);
i2c_delay(CLOCK_HIGH_TIME/4); i2c_delay(CLOCK_HIGH_TIME/4);
/* /*
...@@ -373,6 +380,137 @@ i2c_sendnack(void) ...@@ -373,6 +380,137 @@ i2c_sendnack(void)
i2c_dir_in(); i2c_dir_in();
} }
/*#---------------------------------------------------------------------------
*#
*# FUNCTION NAME: i2c_write
*#
*# DESCRIPTION : Writes a value to an I2C device
*#
*#--------------------------------------------------------------------------*/
int
i2c_write(unsigned char theSlave, void *data, size_t nbytes)
{
int error, cntr = 3;
unsigned char bytes_wrote = 0;
unsigned char value;
unsigned long flags;
spin_lock(&i2c_lock);
do {
error = 0;
/*
* we don't like to be interrupted
*/
local_irq_save(flags);
i2c_start();
/*
* send slave address
*/
i2c_outbyte((theSlave & 0xfe));
/*
* wait for ack
*/
if (!i2c_getack())
error = 1;
/*
* send data
*/
for (bytes_wrote = 0; bytes_wrote < nbytes; bytes_wrote++) {
memcpy(&value, data + bytes_wrote, sizeof value);
i2c_outbyte(value);
/*
* now it's time to wait for ack
*/
if (!i2c_getack())
error |= 4;
}
/*
* end byte stream
*/
i2c_stop();
/*
* enable interrupt again
*/
local_irq_restore(flags);
} while (error && cntr--);
i2c_delay(CLOCK_LOW_TIME);
spin_unlock(&i2c_lock);
return -error;
}
/*#---------------------------------------------------------------------------
*#
*# FUNCTION NAME: i2c_read
*#
*# DESCRIPTION : Reads a value from an I2C device
*#
*#--------------------------------------------------------------------------*/
int
i2c_read(unsigned char theSlave, void *data, size_t nbytes)
{
unsigned char b = 0;
unsigned char bytes_read = 0;
int error, cntr = 3;
unsigned long flags;
spin_lock(&i2c_lock);
do {
error = 0;
memset(data, 0, nbytes);
/*
* we don't like to be interrupted
*/
local_irq_save(flags);
/*
* generate start condition
*/
i2c_start();
/*
* send slave address
*/
i2c_outbyte((theSlave | 0x01));
/*
* wait for ack
*/
if (!i2c_getack())
error = 1;
/*
* fetch data
*/
for (bytes_read = 0; bytes_read < nbytes; bytes_read++) {
b = i2c_inbyte();
memcpy(data + bytes_read, &b, sizeof b);
if (bytes_read < (nbytes - 1))
i2c_sendack();
}
/*
* last received byte needs to be nacked
* instead of acked
*/
i2c_sendnack();
/*
* end sequence
*/
i2c_stop();
/*
* enable interrupt again
*/
local_irq_restore(flags);
} while (error && cntr--);
spin_unlock(&i2c_lock);
return -error;
}
/*#--------------------------------------------------------------------------- /*#---------------------------------------------------------------------------
*# *#
*# FUNCTION NAME: i2c_writereg *# FUNCTION NAME: i2c_writereg
...@@ -387,6 +525,8 @@ i2c_writereg(unsigned char theSlave, unsigned char theReg, ...@@ -387,6 +525,8 @@ i2c_writereg(unsigned char theSlave, unsigned char theReg,
int error, cntr = 3; int error, cntr = 3;
unsigned long flags; unsigned long flags;
spin_lock(&i2c_lock);
do { do {
error = 0; error = 0;
/* /*
...@@ -431,11 +571,12 @@ i2c_writereg(unsigned char theSlave, unsigned char theReg, ...@@ -431,11 +571,12 @@ i2c_writereg(unsigned char theSlave, unsigned char theReg,
* enable interrupt again * enable interrupt again
*/ */
local_irq_restore(flags); local_irq_restore(flags);
} while(error && cntr--); } while(error && cntr--);
i2c_delay(CLOCK_LOW_TIME); i2c_delay(CLOCK_LOW_TIME);
spin_unlock(&i2c_lock);
return -error; return -error;
} }
...@@ -453,6 +594,8 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg) ...@@ -453,6 +594,8 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg)
int error, cntr = 3; int error, cntr = 3;
unsigned long flags; unsigned long flags;
spin_lock(&i2c_lock);
do { do {
error = 0; error = 0;
/* /*
...@@ -482,7 +625,7 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg) ...@@ -482,7 +625,7 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg)
* now it's time to wait for ack * now it's time to wait for ack
*/ */
if(!i2c_getack()) if(!i2c_getack())
error = 1; error |= 2;
/* /*
* repeat start condition * repeat start condition
*/ */
...@@ -496,7 +639,7 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg) ...@@ -496,7 +639,7 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg)
* wait for ack * wait for ack
*/ */
if(!i2c_getack()) if(!i2c_getack())
error = 1; error |= 4;
/* /*
* fetch register * fetch register
*/ */
...@@ -517,6 +660,8 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg) ...@@ -517,6 +660,8 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg)
} while(error && cntr--); } while(error && cntr--);
spin_unlock(&i2c_lock);
return b; return b;
} }
...@@ -583,12 +728,37 @@ static const struct file_operations i2c_fops = { ...@@ -583,12 +728,37 @@ static const struct file_operations i2c_fops = {
int __init int __init
i2c_init(void) i2c_init(void)
{ {
int res; static int res;
static int first = 1;
/* Setup and enable the Port B I2C interface */ if (!first)
return res;
first = 0;
/* Setup and enable the DATA and CLK pins */
res = crisv32_io_get_name(&cris_i2c_data,
CONFIG_ETRAX_V32_I2C_DATA_PORT);
if (res < 0)
return res;
res = crisv32_io_get_name(&cris_i2c_clk, CONFIG_ETRAX_V32_I2C_CLK_PORT);
crisv32_io_set_dir(&cris_i2c_clk, crisv32_io_dir_out);
return res;
}
crisv32_io_get_name(&cris_i2c_data, CONFIG_ETRAX_I2C_DATA_PORT);
crisv32_io_get_name(&cris_i2c_clk, CONFIG_ETRAX_I2C_CLK_PORT); int __init
i2c_register(void)
{
int res;
res = i2c_init();
if (res < 0)
return res;
/* register char device */ /* register char device */
...@@ -598,13 +768,14 @@ i2c_init(void) ...@@ -598,13 +768,14 @@ i2c_init(void)
return res; return res;
} }
printk(KERN_INFO "I2C driver v2.2, (c) 1999-2001 Axis Communications AB\n"); printk(KERN_INFO
"I2C driver v2.2, (c) 1999-2007 Axis Communications AB\n");
return 0; return 0;
} }
/* this makes sure that i2c_init is called during boot */ /* this makes sure that i2c_init is called during boot */
module_init(i2c_init); module_init(i2c_register);
/****************** END OF FILE i2c.c ********************************/ /****************** END OF FILE i2c.c ********************************/
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
/* High level I2C actions */ /* High level I2C actions */
int __init i2c_init(void); int __init i2c_init(void);
int i2c_write(unsigned char theSlave, void *data, size_t nbytes);
int i2c_read(unsigned char theSlave, void *data, size_t nbytes);
int i2c_writereg(unsigned char theSlave, unsigned char theReg, unsigned char theValue); int i2c_writereg(unsigned char theSlave, unsigned char theReg, unsigned char theValue);
unsigned char i2c_readreg(unsigned char theSlave, unsigned char theReg); unsigned char i2c_readreg(unsigned char theSlave, unsigned char theReg);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment