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.
This commit is contained in:
@@ -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";
|
|||||||
|
|
||||||
#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)
|
|||||||
* 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)
|
|||||||
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)
|
|||||||
/*
|
/*
|
||||||
* 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)
|
|||||||
* 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)
|
|||||||
*/
|
*/
|
||||||
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)
|
|||||||
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,
|
|||||||
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,
|
|||||||
* 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)
|
|||||||
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)
|
|||||||
* 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)
|
|||||||
* 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)
|
|||||||
|
|
||||||
} 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 = {
|
|||||||
int __init
|
int __init
|
||||||
i2c_init(void)
|
i2c_init(void)
|
||||||
{
|
{
|
||||||
|
static int res;
|
||||||
|
static int first = 1;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int __init
|
||||||
|
i2c_register(void)
|
||||||
|
{
|
||||||
|
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
/* Setup and enable the Port B I2C interface */
|
res = i2c_init();
|
||||||
|
if (res < 0)
|
||||||
crisv32_io_get_name(&cris_i2c_data, CONFIG_ETRAX_I2C_DATA_PORT);
|
return res;
|
||||||
crisv32_io_get_name(&cris_i2c_clk, CONFIG_ETRAX_I2C_CLK_PORT);
|
|
||||||
|
|
||||||
/* register char device */
|
/* register char device */
|
||||||
|
|
||||||
@@ -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 @@
|
|||||||
|
|
||||||
/* 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);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user