📄 i2c.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.*! $Log: i2c.c,v $*! Revision 1.13 2005/03/07 13:13:07 starvik*! Added spinlocks to protect states etc*!*! Revision 1.12 2005/01/05 06:11:22 starvik*! No need to do local_irq_disable after local_irq_save.*!*! Revision 1.11 2004/12/13 12:21:52 starvik*! Added I/O and DMA allocators from Linux 2.4*!*! Revision 1.9 2004/08/24 06:49:14 starvik*! Whitespace cleanup*!*! Revision 1.8 2004/06/08 08:48:26 starvik*! Removed unused code*!*! Revision 1.7 2004/05/28 09:26:59 starvik*! Modified I2C initialization to work in 2.6.*!*! Revision 1.6 2004/05/14 07:58:03 starvik*! Merge of changes from 2.4*!*! Revision 1.4 2002/12/11 13:13:57 starvik*! Added arch/ to v10 specific includes*! Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer)*!*! Revision 1.3 2002/11/20 11:56:11 starvik*! Merge of Linux 2.5.48*!*! Revision 1.2 2002/11/18 13:16:06 starvik*! Linux 2.5 port of latest 2.4 drivers*!*! 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 toggling 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.13 2005/03/07 13:13:07 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/arch/svinto.h>#include <asm/io.h>#include <asm/delay.h>#include <asm/arch/io_interface_mux.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#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/* 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/* use the kernels delay routine */#define i2c_delay(usecs) udelay(usecs)static DEFINE_SPINLOCK(i2c_lock); /* Protect directions etc *//****************** 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 pulld SDA low */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -