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
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>");
相关文章推荐
- I2C总线驱动程序的实现
- I2C总线驱动程序
- I2C总线驱动程序
- Linux I2C总线驱动程序
- I2C总线 驱动程序设计 --- EEPROM 驱动设计
- I2C总线及EEPROM的Linux驱动程序的设计
- SPI、I2C、UART三种串行总线的原理、区别及应用
- Linux&nbsp;I2C核心、总线与设备驱动
- SPI、I2C、UART三种串行总线协议的区别和SPI接口介绍(转)
- arduino读取I2C总线上连接设备的地址
- I2C总线驱动
- linux-2.6.22.6 i2c总线驱动分析
- I2C总线协议
- I2C之知(四)--I2C总线的7bit从机地址
- I2C总线
- COM载板设计之三:I2C总线及其他信号
- 半年之殇:困扰半年的MSP430的I2C总线问题在今天解决,发文总结
- 7-i2C总线_mpu6050驱动编程
- I2C总线简介
- 基于MCP2515的Linux CAN总线驱动程序设计(三)