i2c.c

来自「linux-2.4.29操作系统的源码」· C语言 代码 · 共 731 行 · 第 1/2 页

C
731
字号
/*!****************************************************************************!*! 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*|*! $Log: i2c.c,v $*! Revision 1.12  2003/06/23 14:43:47  oskarp*! * i2c_sendnack is added.*!   - 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.*!*! Revision 1.11  2003/05/23 09:37:23  oskarp*! * Made i2c_init() visible.*!   - e.g. pcf856.c uses i2c_init().*!*! Revision 1.10  2003/04/01 14:12:06  starvik*! Added loglevel for lots of printks*!*! Revision 1.9  2002/10/31 15:32:26  starvik*! 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*!*! Revision 1.8  2002/08/13 06:31:53  starvik*! Made SDA and SCL line configurable*! Modified i2c_inbyte to work with PCF8563*!*! Revision 1.7  2001/04/04 13:11:36  markusl*! Updated according to review remarks*!*! Revision 1.6  2001/03/19 12:43:00  markusl*! Made some symbols unstatic (used by the eeprom driver)*!*! Revision 1.5  2001/02/27 13:52:48  bjornw*! malloc.h -> slab.h*!*! Revision 1.4  2001/02/15 07:17:40  starvik*! Corrected usage if port_pb_i2c_shadow*!*! Revision 1.3  2001/01/26 17:55:13  bjornw*! * Made I2C_USES_PB_NOT_PB_I2C a CONFIG option instead of assigning it*!   magically. Config.in needs to set it for the options that need it, like*!   Dallas 1302 support. Actually, it should be default since it screws up*!   the PB bits even if you don't use I2C..*! * Include linux/config.h to get the above*!*! Revision 1.2  2001/01/18 15:49:30  bjornw*! 2.4 port of I2C including some cleanups (untested of course)*!*! Revision 1.1  2001/01/18 15:35:25  bjornw*! Verbatim copy of the Etrax i2c driver, 2.0 elinux version*!*!*! ---------------------------------------------------------------------------*!*! (C) Copyright 1999-2002 Axis Communications AB, LUND, SWEDEN*!*!***************************************************************************//* $Id: i2c.c,v 1.12 2003/06/23 14:43:47 oskarp 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 <linux/config.h>#include <asm/etraxi2c.h>#include <asm/system.h>#include <asm/svinto.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#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#define CONFIG_ETRAX_I2C_DATA_PORT 0#endif#ifndef CONFIG_ETRAX_I2C_CLK_PORT#define CONFIG_ETRAX_I2C_CLK_PORT 1#endif#define SDABIT CONFIG_ETRAX_I2C_DATA_PORT#define SCLBIT CONFIG_ETRAX_I2C_CLK_PORT#define i2c_enable() #define i2c_disable() /* enable or disable output-enable, to select output or input on the i2c bus */#define i2c_dir_out() \  REG_SHADOW_SET(R_PORT_PB_DIR, port_pb_dir_shadow, SDABIT, 1)#define i2c_dir_in()  \  REG_SHADOW_SET(R_PORT_PB_DIR, port_pb_dir_shadow, SDABIT, 0)/* control the i2c clock and data signals */#define i2c_clk(x) \  REG_SHADOW_SET(R_PORT_PB_DATA, port_pb_data_shadow, SCLBIT, x)#define i2c_data(x) \  REG_SHADOW_SET(R_PORT_PB_DATA, port_pb_data_shadow, SDABIT, x)/* read a bit from the i2c interface */#define i2c_getbit() (((*R_PORT_PB_READ & (1 << SDABIT))) >> SDABIT)#else /* !CONFIG_ETRAX_I2C_USES_PB_NOT_PB_I2C *//* enable or disable the i2c interface */#define i2c_enable() *R_PORT_PB_I2C = (port_pb_i2c_shadow |= IO_MASK(R_PORT_PB_I2C, i2c_en))#define i2c_disable() *R_PORT_PB_I2C = (port_pb_i2c_shadow &= ~IO_MASK(R_PORT_PB_I2C, i2c_en))/* enable or disable output-enable, to select output or input on the i2c bus */#define i2c_dir_out() \	*R_PORT_PB_I2C = (port_pb_i2c_shadow &= ~IO_MASK(R_PORT_PB_I2C, i2c_oe_)); \	REG_SHADOW_SET(R_PORT_PB_DIR, port_pb_dir_shadow, 0, 1); #define i2c_dir_in() \	*R_PORT_PB_I2C = (port_pb_i2c_shadow |= IO_MASK(R_PORT_PB_I2C, i2c_oe_)); \	REG_SHADOW_SET(R_PORT_PB_DIR, port_pb_dir_shadow, 0, 0);/* control the i2c clock and data signals */#define i2c_clk(x) \	*R_PORT_PB_I2C = (port_pb_i2c_shadow = (port_pb_i2c_shadow & \       ~IO_MASK(R_PORT_PB_I2C, i2c_clk)) | IO_FIELD(R_PORT_PB_I2C, i2c_clk, (x))); \       REG_SHADOW_SET(R_PORT_PB_DATA, port_pb_data_shadow, 1, x);#define i2c_data(x) \	*R_PORT_PB_I2C = (port_pb_i2c_shadow = (port_pb_i2c_shadow & \	   ~IO_MASK(R_PORT_PB_I2C, i2c_d)) | IO_FIELD(R_PORT_PB_I2C, i2c_d, (x))); \	REG_SHADOW_SET(R_PORT_PB_DATA, port_pb_data_shadow, 0, x);/* read a bit from the i2c interface */#define i2c_getbit() (*R_PORT_PB_READ & 0x1)#endif /* CONFIG_ETRAX_I2C_USES_PB_NOT_PB_I2C *//* use the kernels delay routine */#define i2c_delay(usecs) udelay(usecs)/****************** 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);

⌨️ 快捷键说明

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