您的位置:首页 > 其它

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

2014-04-12 14:58 260 查看
实验描述:

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>");
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: