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

来源:互联网 发布:电脑设置闹钟软件 编辑:程序博客网 时间:2024/06/02 02:14

实验描述:

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_tlock;wait_queue_head_twait;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 storemy_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 signalmy_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 stateif (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 bufPRINTK("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 SDAndelay(50);my_s3c6410_i2c.s3c6410_i2c_regs->iic_con = 0b10101111;// recover iic transmitbreak;}else if (!isLastMsg()){//we will deal next messagemy_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 addresss3c6410_i2c_start();//we will do a new transmitbreak;}else{//last message and last datas3c6410_i2c_stop(0);break;}break;}case STATE_READ:{//read datamy_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 signalPRINTK("last data no ack\n");}else{my_s3c6410_i2c.s3c6410_i2c_regs->iic_con = 0b10101111;   // recover iic transmit, next data we will receive ackPRINTK("last data have ack\n");}break;}else if (!isLastMsg()){//we will deal next messagemy_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 addresss3c6410_i2c_start();break;}else{//last message and last datas3c6410_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 paramtermy_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/Txinit_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>");



0 0