⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 i2c.c

📁 linux内核源码
💻 C
字号:
/*!****************************************************************************!*! FILE NAME  : i2c.c*!*! DESCRIPTION: implements an interface for IIC/I2C, both directly from other*!              kernel modules (i2c_writereg/readreg) and from userspace using*!              ioctl()'s*!*! Nov 30 1998  Torbjorn Eliasson  Initial version.*!              Bjorn Wesen        Elinux kernel version.*! Jan 14 2000  Johan Adolfsson    Fixed PB shadow register stuff -*!                                 don't use PB_I2C if DS1302 uses same bits,*!                                 use PB.*| June 23 2003 Pieter Grimmerink  Added 'i2c_sendnack'. i2c_readreg now*|                                 generates nack on last received byte,*|                                 instead of ack.*|                                 i2c_getack changed data level while clock*|                                 was high, causing DS75 to see  a stop condition*!*! ---------------------------------------------------------------------------*!*! (C) Copyright 1999-2002 Axis Communications AB, LUND, SWEDEN*!*!***************************************************************************//* $Id: i2c.c,v 1.2 2005/05/09 15:29:49 starvik Exp $ *//****************** INCLUDE FILES SECTION ***********************************/#include <linux/module.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/errno.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/string.h>#include <linux/init.h>#include <asm/etraxi2c.h>#include <asm/system.h>#include <asm/io.h>#include <asm/delay.h>#include "i2c.h"/****************** I2C DEFINITION SECTION *************************/#define D(x)#define I2C_MAJOR 123  /* LOCAL/EXPERIMENTAL */static const char i2c_name[] = "i2c";#define CLOCK_LOW_TIME            8#define CLOCK_HIGH_TIME           8#define START_CONDITION_HOLD_TIME 8#define STOP_CONDITION_HOLD_TIME  8#define ENABLE_OUTPUT 0x01#define ENABLE_INPUT 0x00#define I2C_CLOCK_HIGH 1#define I2C_CLOCK_LOW 0#define I2C_DATA_HIGH 1#define I2C_DATA_LOW 0#define i2c_enable()#define i2c_disable()/* enable or disable output-enable, to select output or input on the i2c bus */#define i2c_dir_out() crisv32_io_set_dir(&cris_i2c_data, crisv32_io_dir_out)#define i2c_dir_in() crisv32_io_set_dir(&cris_i2c_data, crisv32_io_dir_in)/* control the i2c clock and data signals */#define i2c_clk(x) crisv32_io_set(&cris_i2c_clk, x)#define i2c_data(x) crisv32_io_set(&cris_i2c_data, x)/* read a bit from the i2c interface */#define i2c_getbit() crisv32_io_rd(&cris_i2c_data)#define i2c_delay(usecs) udelay(usecs)/****************** VARIABLE SECTION ************************************/static struct crisv32_iopin cris_i2c_clk;static struct crisv32_iopin cris_i2c_data;/****************** FUNCTION DEFINITION SECTION *************************//* generate i2c start condition */voidi2c_start(void){	/*	 * SCL=1 SDA=1	 */	i2c_dir_out();	i2c_delay(CLOCK_HIGH_TIME/6);	i2c_data(I2C_DATA_HIGH);	i2c_clk(I2C_CLOCK_HIGH);	i2c_delay(CLOCK_HIGH_TIME);	/*	 * SCL=1 SDA=0	 */	i2c_data(I2C_DATA_LOW);	i2c_delay(START_CONDITION_HOLD_TIME);	/*	 * SCL=0 SDA=0	 */	i2c_clk(I2C_CLOCK_LOW);	i2c_delay(CLOCK_LOW_TIME);}/* generate i2c stop condition */voidi2c_stop(void){	i2c_dir_out();	/*	 * SCL=0 SDA=0	 */	i2c_clk(I2C_CLOCK_LOW);	i2c_data(I2C_DATA_LOW);	i2c_delay(CLOCK_LOW_TIME*2);	/*	 * SCL=1 SDA=0	 */	i2c_clk(I2C_CLOCK_HIGH);	i2c_delay(CLOCK_HIGH_TIME*2);	/*	 * SCL=1 SDA=1	 */	i2c_data(I2C_DATA_HIGH);	i2c_delay(STOP_CONDITION_HOLD_TIME);	i2c_dir_in();}/* write a byte to the i2c interface */voidi2c_outbyte(unsigned char x){	int i;	i2c_dir_out();	for (i = 0; i < 8; i++) {		if (x & 0x80) {			i2c_data(I2C_DATA_HIGH);		} else {			i2c_data(I2C_DATA_LOW);		}		i2c_delay(CLOCK_LOW_TIME/2);		i2c_clk(I2C_CLOCK_HIGH);		i2c_delay(CLOCK_HIGH_TIME);		i2c_clk(I2C_CLOCK_LOW);		i2c_delay(CLOCK_LOW_TIME/2);		x <<= 1;	}	i2c_data(I2C_DATA_LOW);	i2c_delay(CLOCK_LOW_TIME/2);	/*	 * enable input	 */	i2c_dir_in();}/* read a byte from the i2c interface */unsigned chari2c_inbyte(void){	unsigned char aBitByte = 0;	int i;	/* Switch off I2C to get bit */	i2c_disable();	i2c_dir_in();	i2c_delay(CLOCK_HIGH_TIME/2);	/* Get bit */	aBitByte |= i2c_getbit();	/* Enable I2C */	i2c_enable();	i2c_delay(CLOCK_LOW_TIME/2);	for (i = 1; i < 8; i++) {		aBitByte <<= 1;		/* Clock pulse */		i2c_clk(I2C_CLOCK_HIGH);		i2c_delay(CLOCK_HIGH_TIME);		i2c_clk(I2C_CLOCK_LOW);		i2c_delay(CLOCK_LOW_TIME);		/* Switch off I2C to get bit */		i2c_disable();		i2c_dir_in();		i2c_delay(CLOCK_HIGH_TIME/2);		/* Get bit */		aBitByte |= i2c_getbit();		/* Enable I2C */		i2c_enable();		i2c_delay(CLOCK_LOW_TIME/2);	}	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;}/*#---------------------------------------------------------------------------*#*# FUNCTION NAME: i2c_getack*#*# DESCRIPTION  : checks if ack was received from ic2*#*#--------------------------------------------------------------------------*/inti2c_getack(void){	int ack = 1;	/*	 * enable output	 */	i2c_dir_out();	/*	 * Release data bus by setting	 * data high	 */	i2c_data(I2C_DATA_HIGH);	/*	 * enable input	 */	i2c_dir_in();	i2c_delay(CLOCK_HIGH_TIME/4);	/*	 * generate ACK clock pulse	 */	i2c_clk(I2C_CLOCK_HIGH);	/*	 * Use PORT PB instead of I2C	 * for input. (I2C not working)	 */	i2c_clk(1);	i2c_data(1);	/*	 * switch off I2C	 */	i2c_data(1);	i2c_disable();	i2c_dir_in();	/*	 * now wait for ack	 */	i2c_delay(CLOCK_HIGH_TIME/2);	/*	 * check for ack	 */	if(i2c_getbit())		ack = 0;	i2c_delay(CLOCK_HIGH_TIME/2);	if(!ack){		if(!i2c_getbit()) /* receiver pulled SDA low */			ack = 1;		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	 */	i2c_enable();	i2c_dir_out();	i2c_clk(I2C_CLOCK_LOW);	i2c_delay(CLOCK_HIGH_TIME/4);	/*	 * enable output	 */	i2c_dir_out();	/*	 * remove ACK clock pulse	 */	i2c_data(I2C_DATA_HIGH);	i2c_delay(CLOCK_LOW_TIME/2);	return ack;}/*#---------------------------------------------------------------------------*#*# FUNCTION NAME: I2C::sendAck*#*# DESCRIPTION  : Send ACK on received data*#*#--------------------------------------------------------------------------*/voidi2c_sendack(void){	/*	 * enable output	 */	i2c_delay(CLOCK_LOW_TIME);	i2c_dir_out();	/*	 * set ack pulse high	 */	i2c_data(I2C_DATA_LOW);	/*	 * 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/6);	/*	 * reset data out	 */	i2c_data(I2C_DATA_HIGH);	i2c_delay(CLOCK_LOW_TIME);	i2c_dir_in();}/*#---------------------------------------------------------------------------*#*# FUNCTION NAME: i2c_sendnack*#*# DESCRIPTION  : Sends NACK on received data*#*#--------------------------------------------------------------------------*/voidi2c_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();}/*#---------------------------------------------------------------------------*#*# FUNCTION NAME: i2c_writereg*#*# DESCRIPTION  : Writes a value to an I2C device*#*#--------------------------------------------------------------------------*/inti2c_writereg(unsigned char theSlave, unsigned char theReg,	     unsigned char theValue){	int error, cntr = 3;	unsigned long flags;	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;		/*		 * now select register		 */		i2c_dir_out();		i2c_outbyte(theReg);		/*		 * now it's time to wait for ack		 */		if(!i2c_getack())			error |= 2;		/*		 * send register register data		 */		i2c_outbyte(theValue);		/*		 * 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);	return -error;}/*#---------------------------------------------------------------------------*#*# FUNCTION NAME: i2c_readreg*#*# DESCRIPTION  : Reads a value from the decoder registers.*#*#--------------------------------------------------------------------------*/unsigned chari2c_readreg(unsigned char theSlave, unsigned char theReg){	unsigned char b = 0;	int error, cntr = 3;	unsigned long flags;	do {		error = 0;		/*		 * we don't like to be interrupted		 */                local_irq_save(flags);		/*		 * generate start condition		 */		i2c_start();		/*		 * send slave address		 */		i2c_outbyte((theSlave & 0xfe));		/*		 * wait for ack		 */		if(!i2c_getack())			error = 1;		/*		 * now select register		 */		i2c_dir_out();		i2c_outbyte(theReg);		/*		 * now it's time to wait for ack		 */		if(!i2c_getack())			error = 1;		/*		 * repeat start condition		 */		i2c_delay(CLOCK_LOW_TIME);		i2c_start();		/*		 * send slave address		 */		i2c_outbyte(theSlave | 0x01);		/*		 * wait for ack		 */		if(!i2c_getack())			error = 1;		/*		 * fetch register		 */		b = i2c_inbyte();		/*		 * 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--);	return b;}static inti2c_open(struct inode *inode, struct file *filp){	return 0;}static inti2c_release(struct inode *inode, struct file *filp){	return 0;}/* Main device API. ioctl's to write or read to/from i2c registers. */static inti2c_ioctl(struct inode *inode, struct file *file,	  unsigned int cmd, unsigned long arg){	if(_IOC_TYPE(cmd) != ETRAXI2C_IOCTYPE) {		return -EINVAL;	}	switch (_IOC_NR(cmd)) {		case I2C_WRITEREG:			/* write to an i2c slave */			D(printk("i2cw %d %d %d\n",				 I2C_ARGSLAVE(arg),				 I2C_ARGREG(arg),				 I2C_ARGVALUE(arg)));			return i2c_writereg(I2C_ARGSLAVE(arg),					    I2C_ARGREG(arg),					    I2C_ARGVALUE(arg));		case I2C_READREG:		{			unsigned char val;			/* read from an i2c slave */			D(printk("i2cr %d %d ",				I2C_ARGSLAVE(arg),				I2C_ARGREG(arg)));			val = i2c_readreg(I2C_ARGSLAVE(arg), I2C_ARGREG(arg));			D(printk("= %d\n", val));			return val;		}		default:			return -EINVAL;	}	return 0;}static const struct file_operations i2c_fops = {	.owner =    THIS_MODULE,	.ioctl =    i2c_ioctl,	.open =     i2c_open,	.release =  i2c_release,};int __initi2c_init(void){	int res;	/* Setup and enable the Port B I2C interface */        crisv32_io_get_name(&cris_i2c_data, CONFIG_ETRAX_I2C_DATA_PORT);        crisv32_io_get_name(&cris_i2c_clk, CONFIG_ETRAX_I2C_CLK_PORT);	/* register char device */	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(KERN_INFO "I2C driver v2.2, (c) 1999-2001 Axis Communications AB\n");	return 0;}/* this makes sure that i2c_init is called during boot */module_init(i2c_init);/****************** END OF FILE i2c.c ********************************/

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -