2014-04-12 i2c总线驱动程序__

实验描述:

i2c总线驱动程序

i2c设备驱动程序读写EEPROM


注意事项:

如何确定i2c总线编号:cat /sys/class/i2c-dev/i2c-0/name


内核版本:

Linux 2.6.38


开发板: 

 Mini 6410




i2c_my_bus.c

#include <linux/init.h>  
#include <linux/module.h>  
#include <linux/device.h>
#include <linux/cdev.h> 
#include <linux/i2c.h>
#include <linux/fs.h> 
#include <asm/uaccess.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <asm/irq.h>
#include <linux/time.h>
#include <linux/interrupt.h>
#include <linux/io.h>

#define PRINTK printk
//#define PRINTK(...) 
enum s3c6410_i2c_state {
	STATE_IDLE,
	STATE_START,
	STATE_READ,
	STATE_WRITE,
	STATE_STOP
};

struct s3c6410_i2c_regs {
	unsigned int iic_con;
	unsigned int iic_stat;
	unsigned int iic_add;
	unsigned int iic_ds;
	unsigned int iic_lc;
};

struct s3c6410_i2c{
	struct i2c_adapter adapter;
	spinlock_t		lock;
	wait_queue_head_t	wait;
	struct clk		*clk;
	enum s3c6410_i2c_state s3c6410_i2c_state;
	struct s3c6410_i2c_regs *s3c6410_i2c_regs;
	unsigned long *gpbcon;

	struct i2c_msg *msgs;
	int msn_num;
	int cur_msg;
	int cur_ptr;
	int state;
	int err;
};

struct  s3c6410_i2c my_s3c6410_i2c;

static void s3c6410_i2c_start(void)
{
	my_s3c6410_i2c.state = STATE_START;
	
	if (my_s3c6410_i2c.msgs->flags & I2C_M_RD) /* read */
	{
		my_s3c6410_i2c.s3c6410_i2c_regs->iic_ds		 = my_s3c6410_i2c.msgs->addr << 1;	//iic data store
		my_s3c6410_i2c.s3c6410_i2c_regs->iic_stat 	 = 0b10110000;	 // master receive, enable Tx/Rx, send start signal
	}
	else /* write */
	{
		my_s3c6410_i2c.s3c6410_i2c_regs->iic_ds		 = my_s3c6410_i2c.msgs->addr << 1;
		my_s3c6410_i2c.s3c6410_i2c_regs->iic_stat    = 0b11110000; 	// master transmit, enable Tx/Rx, send start signal
	}
}

static void s3c6410_i2c_stop(int err)
{
	my_s3c6410_i2c.state = STATE_STOP;
	my_s3c6410_i2c.err   = err;

	PRINTK("iic STATE_STOP, err = %d\n", err);

	if (my_s3c6410_i2c.msgs->flags & I2C_M_RD) /* read */
	{
		// master receive, enable Tx/Rx, send P signal
		my_s3c6410_i2c.s3c6410_i2c_regs->iic_stat = 0b10010000;
		my_s3c6410_i2c.s3c6410_i2c_regs->iic_con  = 0b10101111;
		ndelay(50);  	//wait
	}
	else 
	{
		// master transmit, enable Tx/Rx, send P signal?
		my_s3c6410_i2c.s3c6410_i2c_regs->iic_stat = 0b11010000;
		my_s3c6410_i2c.s3c6410_i2c_regs->iic_con  = 0b10101111;
		ndelay(50);  
	}

	/* wait the queue*/
	wake_up(&my_s3c6410_i2c.wait);
	
}

static int s3c6410_master_xfer(struct i2c_adapter * adapter, struct i2c_msg * msg, int num)
{	
	printk(KERN_ALERT"s3c6410_i2c_xfer time i2c_msg addr:%d, buf first:%d\n", msg->addr, msg->buf[0]);
	//we can get the messages;
	unsigned long timeout;
	
	/* send num messages*/
	my_s3c6410_i2c.msgs    = msg;
	my_s3c6410_i2c.msn_num = num;
	my_s3c6410_i2c.cur_msg = 0;
	my_s3c6410_i2c.cur_ptr = 0;
	my_s3c6410_i2c.err     = -ENODEV;

	s3c6410_i2c_start();

	timeout = wait_event_timeout(my_s3c6410_i2c.wait, (my_s3c6410_i2c.state == STATE_STOP), HZ * 5);
	if (0 == timeout)
	{
		printk("s3c6410_i2c_xfer time out\n");
		return -ETIMEDOUT;
	}
	else
	{
		return my_s3c6410_i2c.err;
	}
}

static u32 s3c6410_functionality (struct i2c_adapter * adapter)
{
	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
}

struct i2c_algorithm s3c6410_algorithm = {
	.master_xfer = s3c6410_master_xfer,
	.functionality = s3c6410_functionality,
};

static int isLastMsg(void)
{
	return (my_s3c6410_i2c.cur_msg == my_s3c6410_i2c.msn_num - 1);
}

static int isEndData(void)
{
	return (my_s3c6410_i2c.cur_ptr >= my_s3c6410_i2c.msgs->len);
}

static int isLastData(void)
{
	return (my_s3c6410_i2c.cur_ptr == (my_s3c6410_i2c.msgs->len - 1));
}

static irqreturn_t s3c6410_iic_xfer(int irq, void *dev)
{
	unsigned int iicSt;
	iicSt  = my_s3c6410_i2c.s3c6410_i2c_regs->iic_stat; 
	if(iicSt & 0b1000)
		printk("sorry Bus arbitration failed\n\r"); 

	switch (my_s3c6410_i2c.state)
	{
		case STATE_START : /* send S signal and device address, will produce interrupt and we will deal*/
		{
			PRINTK("Start\n");
			if (iicSt & 0b0001)	//check is ack
			{
				s3c6410_i2c_stop(-ENODEV);
				break;
			}
			if (isLastMsg() && isEndData())
			{
				s3c6410_i2c_stop(0);
				break;
			}

			//go to next state
			if (my_s3c6410_i2c.msgs->flags & I2C_M_RD) /* read */
			{
				my_s3c6410_i2c.state = STATE_READ;
				goto next_read;
			}
			else
			{
				my_s3c6410_i2c.state = STATE_WRITE;
			}	
		}

		case STATE_WRITE:
		{
			PRINTK("STATE_WRITE\n");
			if (iicSt & 0b0001)//if the last is ack, we will stop
			{
				s3c6410_i2c_stop(-ENODEV);
				break;
			}

			if (!isEndData())  //we still have messages to transmit
			{
				my_s3c6410_i2c.s3c6410_i2c_regs->iic_ds = my_s3c6410_i2c.msgs->buf[my_s3c6410_i2c.cur_ptr];//pointer the data buf
				PRINTK("cur:%d, len: %d, writeBuffer:%d\n", my_s3c6410_i2c.cur_ptr, my_s3c6410_i2c.msgs->len, my_s3c6410_i2c.msgs->buf[my_s3c6410_i2c.cur_ptr]);
				my_s3c6410_i2c.cur_ptr++;
				
				// we should wait a little time, which can enable data to be occureed int SDA
				ndelay(50);	
				my_s3c6410_i2c.s3c6410_i2c_regs->iic_con = 0b10101111;		// recover iic transmit
				break;				
			}
			else if (!isLastMsg())
			{
				//we will deal next message
				my_s3c6410_i2c.msgs++;
				my_s3c6410_i2c.cur_msg++;
				my_s3c6410_i2c.cur_ptr = 0;
				my_s3c6410_i2c.state = STATE_START;
				//send S signal and device address
				s3c6410_i2c_start();//we will do a new transmit
				break;
			}
			else
			{
				//last message and last data
				s3c6410_i2c_stop(0);
				break;				
			}
			break;
		}

		case STATE_READ:
		{

		//read data
			my_s3c6410_i2c.msgs->buf[my_s3c6410_i2c.cur_ptr] = my_s3c6410_i2c.s3c6410_i2c_regs->iic_ds;			
			PRINTK("cur:%d, len: %d, Read Buffer: %d\n", my_s3c6410_i2c.cur_ptr, my_s3c6410_i2c.msgs->len, my_s3c6410_i2c.msgs->buf[my_s3c6410_i2c.cur_ptr]);
			my_s3c6410_i2c.cur_ptr++;
next_read:	
			if (!isEndData()) //if we still have data to read, we should continue
			{
				if (isLastData())  //if this data is the last, we will have no ack
				{
					my_s3c6410_i2c.s3c6410_i2c_regs->iic_con = 0x00101111;   // diable ack signal
					PRINTK("last data no ack\n");
				}
				else
				{
					my_s3c6410_i2c.s3c6410_i2c_regs->iic_con = 0b10101111;   // recover iic transmit, next data we will receive ack
					PRINTK("last data have ack\n");
				}				
				break;
			}
			else if (!isLastMsg())
			{
				//we will deal next message
				my_s3c6410_i2c.msgs++;
				my_s3c6410_i2c.cur_msg++;
				my_s3c6410_i2c.cur_ptr = 0;
				my_s3c6410_i2c.state = STATE_START;
				//send S signal and device address
				s3c6410_i2c_start();
				break;
			}
			else
			{
				//last message and last data
				s3c6410_i2c_stop(0);
				break;							
			}
			break;
		}
		
		default: break;
	}

	//clear interrupt, so we can restore deal 
	my_s3c6410_i2c.s3c6410_i2c_regs->iic_con &= ~(1<<4);

	return IRQ_HANDLED;	
}

static int i2c_bus_init(void)  
{   
	strlcpy(my_s3c6410_i2c.adapter.name, "s3c6410-i2c", sizeof(my_s3c6410_i2c.adapter.name));
	my_s3c6410_i2c.adapter.algo = &s3c6410_algorithm;
	my_s3c6410_i2c.adapter.owner = THIS_MODULE;
	my_s3c6410_i2c.clk = clk_get(NULL, "i2c");
	clk_enable(my_s3c6410_i2c.clk);

	// set the hadware paramter
	my_s3c6410_i2c.gpbcon = (volatile unsigned long *)ioremap(0x7F008020, sizeof(unsigned int));  
	my_s3c6410_i2c.s3c6410_i2c_regs = ioremap(0x7F004000, sizeof(struct s3c6410_i2c_regs));//very impotant

	*my_s3c6410_i2c.gpbcon &= ~(0b1111 << 20 | 0b1111<<24);	//enable the i2cscl and i2csda
	*my_s3c6410_i2c.gpbcon |= (0b0010 << 20 | 0b0010<<24);
	my_s3c6410_i2c.s3c6410_i2c_regs->iic_con = (1<<7 ) | (0 << 6 ) | (1<<5 )  | (0xf<<3);
	my_s3c6410_i2c.s3c6410_i2c_regs->iic_add = 0x10;	
	my_s3c6410_i2c.s3c6410_i2c_regs->iic_stat = 0x10;	//enable Rx/Tx
	init_waitqueue_head(&my_s3c6410_i2c.wait);

	int err;
	err = request_irq(IRQ_IIC, s3c6410_iic_xfer, 0,  "s3c6410_iic_bus", 0);  
     	if(err){  
       		PRINTK(KERN_ALERT"can not obtain irq!\n");
		goto err_irq;
      	}  

	i2c_add_adapter(&my_s3c6410_i2c.adapter);
	PRINTK(KERN_ALERT"init!\n");
	return 0;  

err_irq:
	iounmap(my_s3c6410_i2c.s3c6410_i2c_regs);
	iounmap(my_s3c6410_i2c.gpbcon);
	clk_disable(my_s3c6410_i2c.clk);
	clk_put(my_s3c6410_i2c.clk);
	return err;
}  

static int i2c_bus_exit(void)  
{   
	iounmap(my_s3c6410_i2c.s3c6410_i2c_regs);
	iounmap(my_s3c6410_i2c.gpbcon);
	free_irq(IRQ_IIC, NULL);
	clk_disable(my_s3c6410_i2c.clk);
	clk_put(my_s3c6410_i2c.clk);
	i2c_del_adapter(&my_s3c6410_i2c.adapter);
	PRINTK(KERN_ALERT"exit!\n");
	return 0;  
}  
 
module_init(i2c_bus_init);  
module_exit(i2c_bus_exit);
MODULE_LICENSE("GPL");  
MODULE_DESCRIPTION("S3C6410 I2C Bus driver");
MODULE_AUTHOR("Books, <uppour@sina.cn>");



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值