From 201ca54aa039eb1e5143a98311e7ea25afc57ebb Mon Sep 17 00:00:00 2001 From: Jesper Nilsson Date: Fri, 30 Nov 2007 15:54:01 +0100 Subject: 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. --- arch/cris/arch-v32/drivers/i2c.c | 199 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 185 insertions(+), 14 deletions(-) (limited to 'arch/cris/arch-v32/drivers/i2c.c') diff --git a/arch/cris/arch-v32/drivers/i2c.c b/arch/cris/arch-v32/drivers/i2c.c index f1edd2e359b2..4eda3236792a 100644 --- a/arch/cris/arch-v32/drivers/i2c.c +++ b/arch/cris/arch-v32/drivers/i2c.c @@ -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 @@ -79,6 +79,8 @@ static const char i2c_name[] = "i2c"; #define i2c_delay(usecs) udelay(usecs) +static DEFINE_SPINLOCK(i2c_lock); /* Protect directions etc */ + /****************** VARIABLE SECTION ************************************/ static struct crisv32_iopin cris_i2c_clk; @@ -252,6 +254,7 @@ i2c_getack(void) * generate ACK clock pulse */ i2c_clk(I2C_CLOCK_HIGH); +#if 0 /* * Use PORT PB instead of I2C * for input. (I2C not working) @@ -264,6 +267,8 @@ i2c_getack(void) i2c_data(1); i2c_disable(); i2c_dir_in(); +#endif + /* * now wait for ack */ @@ -271,11 +276,11 @@ i2c_getack(void) /* * check for ack */ - if(i2c_getbit()) + if (i2c_getbit()) ack = 0; i2c_delay(CLOCK_HIGH_TIME/2); - if(!ack){ - if(!i2c_getbit()) /* receiver pulled SDA low */ + if (!ack) { + if (!i2c_getbit()) /* receiver pulld SDA low */ ack = 1; i2c_delay(CLOCK_HIGH_TIME/2); } @@ -285,6 +290,7 @@ i2c_getack(void) * before we enable our output. If we keep data high * and enable output, we would generate a stop condition. */ +#if 0 i2c_data(I2C_DATA_LOW); /* @@ -292,6 +298,7 @@ i2c_getack(void) */ i2c_enable(); i2c_dir_out(); +#endif i2c_clk(I2C_CLOCK_LOW); i2c_delay(CLOCK_HIGH_TIME/4); /* @@ -373,6 +380,137 @@ i2c_sendnack(void) 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 @@ -387,6 +525,8 @@ i2c_writereg(unsigned char theSlave, unsigned char theReg, int error, cntr = 3; unsigned long flags; + spin_lock(&i2c_lock); + do { error = 0; /* @@ -431,11 +571,12 @@ i2c_writereg(unsigned char theSlave, unsigned char theReg, * enable interrupt again */ local_irq_restore(flags); - } while(error && cntr--); i2c_delay(CLOCK_LOW_TIME); + spin_unlock(&i2c_lock); + return -error; } @@ -453,6 +594,8 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg) int error, cntr = 3; unsigned long flags; + spin_lock(&i2c_lock); + do { error = 0; /* @@ -482,7 +625,7 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg) * now it's time to wait for ack */ if(!i2c_getack()) - error = 1; + error |= 2; /* * repeat start condition */ @@ -496,7 +639,7 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg) * wait for ack */ if(!i2c_getack()) - error = 1; + error |= 4; /* * fetch register */ @@ -517,6 +660,8 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg) } while(error && cntr--); + spin_unlock(&i2c_lock); + return b; } @@ -583,12 +728,37 @@ static const struct file_operations i2c_fops = { int __init 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 */ @@ -598,13 +768,14 @@ i2c_init(void) 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; } /* this makes sure that i2c_init is called during boot */ -module_init(i2c_init); +module_init(i2c_register); /****************** END OF FILE i2c.c ********************************/ -- cgit v1.2.3 From 69b06c15e7e24d2b17ec8f89a7f3ae9fa55f5667 Mon Sep 17 00:00:00 2001 From: Jesper Nilsson Date: Fri, 8 Feb 2008 17:00:25 +0100 Subject: CRIS v32: Change drivers/i2c.c locking. - Change spin_lock + local_irq_save into spin_lock_irqsave - Change spin_unlock + local_irq_restore into spin_unlock_irqrestore - Return ENOTTY if ioctl is not recognized as a cris ioctl. - Make init functions static. --- arch/cris/arch-v32/drivers/i2c.c | 61 ++++++++-------------------------------- 1 file changed, 12 insertions(+), 49 deletions(-) (limited to 'arch/cris/arch-v32/drivers/i2c.c') diff --git a/arch/cris/arch-v32/drivers/i2c.c b/arch/cris/arch-v32/drivers/i2c.c index 4eda3236792a..c2fb7a5c1396 100644 --- a/arch/cris/arch-v32/drivers/i2c.c +++ b/arch/cris/arch-v32/drivers/i2c.c @@ -395,14 +395,10 @@ i2c_write(unsigned char theSlave, void *data, size_t nbytes) unsigned char value; unsigned long flags; - spin_lock(&i2c_lock); + spin_lock_irqsave(&i2c_lock, flags); do { error = 0; - /* - * we don't like to be interrupted - */ - local_irq_save(flags); i2c_start(); /* @@ -430,16 +426,12 @@ i2c_write(unsigned char theSlave, void *data, size_t nbytes) * end byte stream */ i2c_stop(); - /* - * enable interrupt again - */ - local_irq_restore(flags); } while (error && cntr--); i2c_delay(CLOCK_LOW_TIME); - spin_unlock(&i2c_lock); + spin_unlock_irqrestore(&i2c_lock, flags); return -error; } @@ -459,15 +451,11 @@ i2c_read(unsigned char theSlave, void *data, size_t nbytes) int error, cntr = 3; unsigned long flags; - spin_lock(&i2c_lock); + spin_lock_irqsave(&i2c_lock, flags); do { error = 0; memset(data, 0, nbytes); - /* - * we don't like to be interrupted - */ - local_irq_save(flags); /* * generate start condition */ @@ -500,13 +488,9 @@ i2c_read(unsigned char theSlave, void *data, size_t nbytes) * end sequence */ i2c_stop(); - /* - * enable interrupt again - */ - local_irq_restore(flags); } while (error && cntr--); - spin_unlock(&i2c_lock); + spin_unlock_irqrestore(&i2c_lock, flags); return -error; } @@ -525,14 +509,10 @@ i2c_writereg(unsigned char theSlave, unsigned char theReg, int error, cntr = 3; unsigned long flags; - spin_lock(&i2c_lock); + spin_lock_irqsave(&i2c_lock, flags); do { error = 0; - /* - * we don't like to be interrupted - */ - local_irq_save(flags); i2c_start(); /* @@ -567,15 +547,11 @@ i2c_writereg(unsigned char theSlave, unsigned char theReg, * end byte stream */ i2c_stop(); - /* - * enable interrupt again - */ - local_irq_restore(flags); } while(error && cntr--); i2c_delay(CLOCK_LOW_TIME); - spin_unlock(&i2c_lock); + spin_unlock_irqrestore(&i2c_lock, flags); return -error; } @@ -594,14 +570,10 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg) int error, cntr = 3; unsigned long flags; - spin_lock(&i2c_lock); + spin_lock_irqsave(&i2c_lock, flags); do { error = 0; - /* - * we don't like to be interrupted - */ - local_irq_save(flags); /* * generate start condition */ @@ -653,14 +625,10 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg) * end sequence */ i2c_stop(); - /* - * enable interrupt again - */ - local_irq_restore(flags); } while(error && cntr--); - spin_unlock(&i2c_lock); + spin_unlock_irqrestore(&i2c_lock, flags); return b; } @@ -685,7 +653,7 @@ i2c_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { if(_IOC_TYPE(cmd) != ETRAXI2C_IOCTYPE) { - return -EINVAL; + return -ENOTTY; } switch (_IOC_NR(cmd)) { @@ -725,8 +693,7 @@ static const struct file_operations i2c_fops = { .release = i2c_release, }; -int __init -i2c_init(void) +static int __init i2c_init(void) { static int res; static int first = 1; @@ -750,10 +717,8 @@ i2c_init(void) } -int __init -i2c_register(void) +static int __init i2c_register(void) { - int res; res = i2c_init(); @@ -763,7 +728,7 @@ i2c_register(void) /* register char device */ res = register_chrdev(I2C_MAJOR, i2c_name, &i2c_fops); - if(res < 0) { + if (res < 0) { printk(KERN_ERR "i2c: couldn't get a major number.\n"); return res; } @@ -773,9 +738,7 @@ i2c_register(void) return 0; } - /* this makes sure that i2c_init is called during boot */ - module_init(i2c_register); /****************** END OF FILE i2c.c ********************************/ -- cgit v1.2.3