最经在一个项目上用到hi3515芯片的i2c配置成从设备用于接收数据。采用中断阻塞方式。i2c硬件缓存区一次只能缓存8个字节,
超出部分会被硬件自动丢去。
代码如下:
hi_i2c.c文件:
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/fcntl.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/completion.h>
#include <linux/list.h>
#include <linux/seq_file.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/io.h>
#include <linux/kcom.h>
#include <asm-arm/arch-hi3515v100/gpio.h>
#include <asm-arm/arch-hi3515v100/hardware.h>
#include <asm-arm/arch-hi3515v100/io.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/workqueue.h>
#define SC_PERCTRL1 IO_ADDRESS(0x200F0000+0x0098)
#define SD_PERCTRL1 IO_ADDRESS(0x200F0000+0x0094)
#define HCLK_FREQ (100000000)
#define I2C_CLK (HCLK_FREQ)
#define I2C_RATE (50000)
#define CONTROL_VALUE 0x0023
#define I2C_BASE 0x200D0000
#define I2C_REG_READL(reg,result) \
(result=readl(reg))
#define I2C_REG_WRITEL(reg,data) \
(writel(data,reg))
#define I2C_REG_READW(reg,result) \
(result=readw(reg))
#define I2C_REG_WRITEW(reg,data) \
(writew(data,reg))
#define I2C_CON IO_ADDRESS(I2C_BASE+0x00) /*I2C control reg*/
#define I2C_TAR IO_ADDRESS(I2C_BASE+0x04) /*slave chip address reg*/
#define I2C_HCNT IO_ADDRESS(I2C_BASE+0x14)
#define I2C_LCNT IO_ADDRESS(I2C_BASE+0x18)
#define I2C_HS_HCNT IO_ADDRESS(I2C_BASE+0x1C)
#define I2C_HS_LCNT IO_ADDRESS(I2C_BASE+0x20)
#define I2C_INTR_MASK IO_ADDRESS(I2C_BASE+0x30) /*interrupt mask reg*/
#define I2C_SAR IO_ADDRESS(I2C_BASE+0x08)
#define I2C_INTR_STAT IO_ADDRESS(I2C_BASE+0x2C) /*interrupt status reg*/
#define I2C_ENABLE IO_ADDRESS(I2C_BASE+0x6C) /*IP enable*/
#define I2C_DATA_CMD IO_ADDRESS(I2C_BASE+0x10) /*I2C TX FIFO*/
#define I2C_TXFIFO_FLAG IO_ADDRESS(I2C_BASE+0x74) /*indicate TXFIFO level*/
#define I2C_RXFIFO_FLAG IO_ADDRESS(I2C_BASE+0x78) /*indicate RXFIFO level*/
#define I2C_STATUS IO_ADDRESS(I2C_BASE+0x70) /*IP status flag reg*/
#define I2C_COMP_PARAM_1 IO_ADDRESS(I2C_BASE+0xF4) /*IP status flag reg*/
#define I2C_TX_TL IO_ADDRESS(I2C_BASE+0x3c)
#define I2C_RX_TL IO_ADDRESS(I2C_BASE+0x38)
#define I2C_RAW_INTSTATUS IO_ADDRESS(I2C_BASE+0x34)
/*clear write interrupt reg */
#define I2C_CLR_INTR IO_ADDRESS(I2C_BASE+0x40) /*clear combined and individual interrupt*/
#define I2C_CLR_TX_ABRT IO_ADDRESS(I2C_BASE+0x54) /*clear RT abrt interrupt reg*/
#define I2C_CLR_STOPDET IO_ADDRESS(I2C_BASE+0x60) /*clear stop_det interrupt reg*/
#define I2C_CLR_ACTIVITY IO_ADDRESS(I2C_BASE+0x5C) /*clear activity*/
#define I2C_CLR_TX_OVER IO_ADDRESS(I2C_BASE+0x4C) /*clear tx fifo over interrupt reg*/
#define I2C_CLR_RX_ABRT IO_ADDRESS(I2C_BASE+0x80) /*clear RX abrt interrupt reg*/
#define TIMECOUNT 0x100
#define RW_RETRY_TIME 5
static DECLARE_WAIT_QUEUE_HEAD(waitq);
static volatile int ev_data = 0;
/*
* sends a character over I2C bus routine.
* @param sendvalue: character to send
* @return value:0--success; -1--error.
*
*/
static int i2csendbyte(unsigned char sendvalue)
{
unsigned short data = 0x00FF;
unsigned short status;
unsigned short count;
unsigned long i = 0;
/*set write CMD*/
data = sendvalue & data;
/*check TX fifo status*/ //缓存中数据的个数
I2C_REG_READW(I2C_TXFIFO_FLAG, status);
/*check TX fifo status*/
I2C_REG_READW(I2C_TX_TL, count);
count&=0xff;
/*TX FIFO full and wait for a while*/
while(status >= count)
{
udelay(20);
if(++i>TIMECOUNT)
{
/*printk("TIME OUT: i2csendbyte\n");*/
return -1;
}
I2C_REG_READW(I2C_TXFIFO_FLAG, status);
}
/*write one byte to TX fifo*/
I2C_REG_WRITEW(I2C_DATA_CMD,data);
return 0;
}
/*
* receives a character from I2C bus routine.
* @return value:character received
*/
static int i2creceivebyte(unsigned char* data)
{
unsigned char ucvalue;
unsigned short status;
unsigned long i = 0;
/*read Rx FIFO*/
//I2C_REG_WRITEW(I2C_DATA_CMD,0x0100);
I2C_REG_READW(I2C_RXFIFO_FLAG, status); //缓存中数据的个数
/*RX FIFO is empty and wait for a while*/
while(status == 0)
{
udelay(10);
if(++i>TIMECOUNT)
{
printk("TIME OUT: i2creceivebyte\n");
return -1;
}
I2C_REG_READW(I2C_RXFIFO_FLAG, status);
}
I2C_REG_READW(I2C_DATA_CMD, ucvalue);
*data = ucvalue;
return 0;
}
static int i2creceivebytes(unsigned char * buff, int buff_size)
{
unsigned char *pbuff;
unsigned short status;
int i;
pbuff = buff;
for(i = 0; i < 11; i++)
{
status &= 0;
I2C_REG_READW(I2C_RAW_INTSTATUS, status);
printk("I2C_RAW_INTSTATUS = %#x \n", status);
status &= 0;
I2C_REG_READW(I2C_INTR_STAT, status);
printk("I2C_INTR_STATUS = %#x \n", status);
i2creceivebyte(pbuff);
pbuff++;
}
return 0;
}
static int i2c_receive_bytes(unsigned char * buff, int buff_size)
{
unsigned char status;
unsigned char *pbuff = buff;
int i = 0;
int j;
I2C_REG_READW(I2C_RXFIFO_FLAG, status); //缓存中数据的个数
j = status;
while(j--)
{
I2C_REG_READW(I2C_DATA_CMD, *pbuff);
pbuff++;
// I2C_REG_READW(I2C_RXFIFO_FLAG, status);
}
return 0;
}
/*
* read continuing data from the I2C bus of a device rountine.
* @param devaddress: address of the device
* @param regaddress: address of register within device
* @param reg_addr_count : regaddress byte count
* @param data: data point from the device readed
* @param count: data count from the device readed
* @return value: 0--success; -1--error.
*/
int hi_i2c_muti_read(unsigned char* data)
{
unsigned long j = 0;
int ret = 0;
int count = 20;
for(j = 0; j < count; j++)
{
ret = i2creceivebyte(data);
if(-1 == ret)
{
return -1;
}
data ++;
}
return 0;
}
static irqreturn_t iic_interrupt(int irq, void * dev_id)
{
unsigned short status;
disable_irq(INTNR_I2C);
I2C_REG_READW(I2C_CLR_ACTIVITY, status);
I2C_REG_READW(I2C_RX_TL, status);
if(status)
{
ev_data = 1;
wake_up_interruptible(&waitq);
}
enable_irq(INTNR_I2C);
return IRQ_HANDLED;
}
static int i2c_read(struct file* file, char __user * userbuf, size_t count,loff_t * off)
{
unsigned char buff[100];
int i;
if(!ev_data)
{
if(file->f_flags & O_NONBLOCK)
return -EAGAIN;
else
{
wait_event_interruptible(waitq, ev_data);
}
}
ev_data = 0;
memset(buff, 0, sizeof(buff));
i2c_receive_bytes(buff, sizeof(buff)-2);
// hi_i2c_muti_read(buff);
// i2creceivebytes(buff, sizeof(buff)-2);
if (copy_to_user(userbuf, buff, strlen(buff))!= 0)
{
printk("read is error!!\n");
return EFAULT;
}
return strlen(buff);
}
static int i2c_open(struct inode* inode, struct file* filp)
{
unsigned int cnt, hcnt, lcnt;
unsigned short status;
int i = 0;
unsigned int reg;
/* 配置复用端口 */
I2C_REG_READL(SC_PERCTRL1,reg);
reg &= ~0x00000001;
I2C_REG_WRITEL(SC_PERCTRL1,reg);
I2C_REG_READL(SD_PERCTRL1,reg);
reg &= ~0x00000001;
I2C_REG_WRITEL(SD_PERCTRL1,reg);
I2C_REG_READW(I2C_STATUS, status);
status &= 0x01;
/*I2C BUS is not idle and wait for a while*/
while((status != 0) )
{
udelay(20);
if(++i>TIMECOUNT)
{
printk("i2c TimeCount \n");
break;
}
I2C_REG_READW(I2C_STATUS, status);
status &= 0x01;
}
I2C_REG_WRITEW(I2C_ENABLE, 0); //禁止i2c
/* standard speed.*/
cnt = I2C_CLK/I2C_RATE;
hcnt = (cnt/9)*4;
lcnt = (cnt/9)*5;
I2C_REG_WRITEW(I2C_CON, 0x0002);//关闭重新发送起始位,配置从端模式等
I2C_REG_WRITEW(I2C_SAR, 0x07F); //设置作为从机i2c地址
I2C_REG_WRITEW(I2C_RX_TL, 0x00ff);
I2C_REG_WRITEW(I2C_TX_TL, 0x00ff);
I2C_REG_WRITEW(I2C_INTR_MASK, 0x0100); //开启中断
I2C_REG_WRITEW(I2C_ENABLE, 1);
return 0;
}
static int i2c_release(struct inode* inode, struct file* filp)
{
return 0;
}
static int i2c_ioctl(struct inode* inode, struct file* filp,unsigned int cmd, unsigned long arg)
{
return 0;
}
static struct file_operations i2c_fops = {
owner:THIS_MODULE,
open:i2c_open,
read:i2c_read,
ioctl:i2c_ioctl,
release:i2c_release,
};
static struct miscdevice i2c_dev = {
MISC_DYNAMIC_MINOR,
"i2c",
&i2c_fops,
};
static int __init hi_i2c_init(void)
{
int ret;
ret = misc_register(&i2c_dev); //注册字符设备
if (ret)
{
printk(KERN_ERR "register misc dev for i2c fail!\n");
return ret;
}
ret = request_irq(INTNR_I2C, iic_interrupt, IRQF_SHARED, "i2c", iic_interrupt); //申请中断
if (ret < 0)
{
printk("the iic_interrupt is requestted fail\n");
return ENODEV;
} else {
printk("the iic_interrupt is requestted sccuss!! \n");
}
return 0;
}
static void __exit hi_i2c_exit(void)
{
misc_deregister(&i2c_dev);
disable_irq(INTNR_I2C);
free_irq(INTNR_I2C, iic_interrupt);
}
module_init(hi_i2c_init);
module_exit(hi_i2c_exit);
#ifdef MODULE
#include <linux/compile.h>
#endif
MODULE_INFO(build, UTS_VERSION);
MODULE_LICENSE("GPL");
MODULE_VERSION("HI_VERSION=" OSDRV_MODULE_VERSION_STRING);
仅供参考,如有理解错误,或不对的地方欢迎指出。。