你的位置:首页 > 操作系统

[操作系统]Linux I2C总线控制器驱动(S3C2440)


 s3c2440的i2c控制器驱动(精简DIY),直接上代码,注释很详细:

 1 #include <linux/kernel.h> 2 #include <linux/module.h> 3  4 #include <linux/i2c.h> 5 #include <linux/init.h> 6 #include <linux/time.h> 7 #include <linux/interrupt.h> 8 #include <linux/delay.h> 9 #include <linux/errno.h> 10 #include <linux/err.h> 11 #include <linux/platform_device.h> 12 #include <linux/pm_runtime.h> 13 #include <linux/clk.h> 14 #include <linux/cpufreq.h> 15 #include <linux/slab.h> 16 #include <linux/io.h> 17 #include <linux/of_i2c.h> 18 #include <linux/of_gpio.h> 19 #include <plat/gpio-cfg.h> 20 #include <mach/regs-gpio.h> 21  22 #include <asm/irq.h> 23  24 #include <plat/regs-iic.h> 25 #include <plat/iic.h> 26  27 //#define PRINTK printk 28 #define PRINTK(...)  29  30 enum s3c24xx_i2c_state { 31   STATE_IDLE, 32   STATE_START, 33   STATE_READ, 34   STATE_WRITE, 35   STATE_STOP 36 }; 37  38 //i2c控制器寄存器 39 struct s3c2440_i2c_regs { 40   unsigned int iiccon; 41   unsigned int iicstat; 42   unsigned int iicadd; 43   unsigned int iicds; 44   unsigned int iiclc; 45 }; 46  47 //i2c数据传输载体 48 struct s3c2440_i2c_xfer_data { 49   struct i2c_msg *msgs; 50   int msn_num; 51   int cur_msg; 52   int cur_ptr; 53   int state; 54   int err; 55   wait_queue_head_t wait; 56 }; 57  58 static struct s3c2440_i2c_xfer_data s3c2440_i2c_xfer_data; 59  60  61 static struct s3c2440_i2c_regs *s3c2440_i2c_regs; 62  63  64 static void s3c2440_i2c_start(void) 65 { 66   s3c2440_i2c_xfer_data.state = STATE_START; 67    68   if (s3c2440_i2c_xfer_data.msgs->flags & I2C_M_RD) /* 读 */ 69   { 70     s3c2440_i2c_regs->iicds     = s3c2440_i2c_xfer_data.msgs->addr << 1;  71     s3c2440_i2c_regs->iicstat   = 0xb0;  // 主机接收,启动 72   } 73   else /* 写 */ 74   { 75     s3c2440_i2c_regs->iicds     = s3c2440_i2c_xfer_data.msgs->addr << 1; 76     s3c2440_i2c_regs->iicstat  = 0xf0;     // 主机发送,启动 77   } 78 } 79  80 static void s3c2440_i2c_stop(int err) 81 { 82   s3c2440_i2c_xfer_data.state = STATE_STOP; 83   s3c2440_i2c_xfer_data.err  = err; 84  85   PRINTK("STATE_STOP, err = %d\n", err); 86  87  88   if (s3c2440_i2c_xfer_data.msgs->flags & I2C_M_RD) /* 读 */ 89   { 90     // 下面两行恢复I2C操作,发出P信号 91     s3c2440_i2c_regs->iicstat = 0x90; 92     s3c2440_i2c_regs->iiccon = 0xaf; 93     ndelay(50); // 等待一段时间以便P信号已经发出 94   } 95   else /* 写 */ 96   { 97     // 下面两行用来恢复I2C操作,发出P信号 98     s3c2440_i2c_regs->iicstat = 0xd0; 99     s3c2440_i2c_regs->iiccon = 0xaf;100     ndelay(50); // 等待一段时间以便P信号已经发出101   }102 103   /* 唤醒 */104   wake_up(&s3c2440_i2c_xfer_data.wait);105   106 }107 108 //i2c总线数据传输处理函数109 static int s3c2440_i2c_xfer(struct i2c_adapter *adap,110       struct i2c_msg *msgs, int num)111 {112   unsigned long timeout;113   114   /* 把num个msg的I2C数据发送出去/读进来 */115   s3c2440_i2c_xfer_data.msgs  = msgs;116   s3c2440_i2c_xfer_data.msn_num = num;117   s3c2440_i2c_xfer_data.cur_msg = 0;118   s3c2440_i2c_xfer_data.cur_ptr = 0;119   s3c2440_i2c_xfer_data.err   = -ENODEV; //确认是否有ack应答120 121   s3c2440_i2c_start(); //发出start信号,判断read or write122 123   /* 休眠-等待i2c读写状态改变 */124   timeout = wait_event_timeout(s3c2440_i2c_xfer_data.wait, (s3c2440_i2c_xfer_data.state == STATE_STOP), HZ * 5); //等待状态成立或5s125   if (0 == timeout)126   {127     printk("s3c2440_i2c_xfer time out\n");128     return -ETIMEDOUT;129   }130   else131   {132     return s3c2440_i2c_xfer_data.err;133   }134 }135 136 static u32 s3c2440_i2c_func(struct i2c_adapter *adap)137 {138   return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;139 }140 141 142 static const struct i2c_algorithm s3c2440_i2c_algo = {143 //  .smbus_xfer   = ,  //smbus是i2c传输的一个子集,支持的话可以在这里指定处理函数144   .master_xfer  = s3c2440_i2c_xfer, //传输函数145   .functionality  = s3c2440_i2c_func,146 };147 148 /* 1. 分配/设置i2c_adapter149 */150 static struct i2c_adapter s3c2440_i2c_adapter = {151 .name       = "s3c2440_sheldon",152 .algo       = &s3c2440_i2c_algo, //算法函数153 .owner     = THIS_MODULE,154 };155 156 static int isLastMsg(void)157 {158   return (s3c2440_i2c_xfer_data.cur_msg == s3c2440_i2c_xfer_data.msn_num - 1);159 }160 161 static int isEndData(void)162 {163   return (s3c2440_i2c_xfer_data.cur_ptr >= s3c2440_i2c_xfer_data.msgs->len);164 }165 166 static int isLastData(void)167 {168   return (s3c2440_i2c_xfer_data.cur_ptr == s3c2440_i2c_xfer_data.msgs->len - 1);169 }170 171 static irqreturn_t s3c2440_i2c_xfer_irq(int irq, void *dev_id)172 {173   unsigned int iicSt;174     175   iicSt = s3c2440_i2c_regs->iicstat; //读取i2c控制器的状态寄存器,判断是否读写成功176 177   if(iicSt & 0x8){ printk("Bus arbitration failed\n\r"); }178 179   switch (s3c2440_i2c_xfer_data.state)180   {181     case STATE_START : /* 发出S和设备地址后,产生中断 */182     {183       PRINTK("Start\n");184       /* 如果没有ACK, 返回错误 */185       if (iicSt & S3C2410_IICSTAT_LASTBIT)186       {187         s3c2440_i2c_stop(-ENODEV);188         break;189       }190 191       if (isLastMsg() && isEndData())192       {193         s3c2440_i2c_stop(0);194         break;195       }196 197       /* 进入下一个状态 */198       if (s3c2440_i2c_xfer_data.msgs->flags & I2C_M_RD) /* 读 */199       {200         s3c2440_i2c_xfer_data.state = STATE_READ;201         goto next_read;202       }203       else204       {205         s3c2440_i2c_xfer_data.state = STATE_WRITE;206       }  207     }208 209     case STATE_WRITE:210     {211       PRINTK("STATE_WRITE\n");212       /* 如果没有ACK, 返回错误 */213       if (iicSt & S3C2410_IICSTAT_LASTBIT)214       {215         s3c2440_i2c_stop(-ENODEV);216         break;217       }218 219       if (!isEndData()) /* 如果当前msg还有数据要发送 */220       {221         s3c2440_i2c_regs->iicds = s3c2440_i2c_xfer_data.msgs->buf[s3c2440_i2c_xfer_data.cur_ptr];222         s3c2440_i2c_xfer_data.cur_ptr++;223         224         // 将数据写入IICDS后,需要一段时间才能出现在SDA线上225         ndelay(50);  226         227         s3c2440_i2c_regs->iiccon = 0xaf;    // 恢复I2C传输228         break;        229       }230       else if (!isLastMsg())231       {232         /* 开始处理下一个消息 */233         s3c2440_i2c_xfer_data.msgs++;234         s3c2440_i2c_xfer_data.cur_msg++;235         s3c2440_i2c_xfer_data.cur_ptr = 0;236         s3c2440_i2c_xfer_data.state = STATE_START;237         /* 发出START信号和发出设备地址 */238         s3c2440_i2c_start();239         break;240       }241       else242       {243         /* 是最后一个消息的最后一个数据 */244         s3c2440_i2c_stop(0);245         break;        246       }247 248       break;249     }250 251     case STATE_READ:252     {253       PRINTK("STATE_READ\n");254       /* 读出数据 */255       s3c2440_i2c_xfer_data.msgs->buf[s3c2440_i2c_xfer_data.cur_ptr] = s3c2440_i2c_regs->iicds;      256       s3c2440_i2c_xfer_data.cur_ptr++;257 next_read:258       if (!isEndData()) /* 如果数据没读写, 继续发起读操作 */259       {260         if (isLastData()) /* 如果即将读的数据是最后一个, 不发ack */261         {262           s3c2440_i2c_regs->iiccon = 0x2f;  // 恢复I2C传输,接收到下一数据时无ACK263         }264         else265         {266           s3c2440_i2c_regs->iiccon = 0xaf;  // 恢复I2C传输,接收到下一数据时发出ACK267         }        268         break;269       }270       else if (!isLastMsg())271       {272         /* 开始处理下一个消息 */273         s3c2440_i2c_xfer_data.msgs++;274         s3c2440_i2c_xfer_data.cur_msg++;275         s3c2440_i2c_xfer_data.cur_ptr = 0;276         s3c2440_i2c_xfer_data.state = STATE_START;277         /* 发出START信号和发出设备地址 */278         s3c2440_i2c_start();279         break;280       }281       else282       {283         /* 是最后一个消息的最后一个数据 */284         s3c2440_i2c_stop(0);285         break;                286       }287       break;288     }289 290     default: break;291   }292 293   /* 清中断 */294   s3c2440_i2c_regs->iiccon &= ~(S3C2410_IICCON_IRQPEND);295 296   return IRQ_HANDLED;  297 }298 299 300 /*301  * I2C初始化302 */303 static void s3c2440_i2c_init(void)304 {305   struct clk *clk;306 307   clk = clk_get(NULL, "i2c");308   clk_enable(clk);309   310   // 选择引脚功能:GPE15:IICSDA, GPE14:IICSCL311       s3c_gpio_cfgpin(S3C2410_GPE(14), S3C2410_GPE14_IICSCL);312   s3c_gpio_cfgpin(S3C2410_GPE(15), S3C2410_GPE15_IICSDA);313 314   /* bit[7] = 1, 使能ACK315    * bit[6] = 0, IICCLK = PCLK/16316    * bit[5] = 1, 使能中断317    * bit[3:0] = 0xf, Tx clock = IICCLK/16318    * PCLK = 50MHz, IICCLK = 3.125MHz, Tx Clock = 0.195MHz319   */320   s3c2440_i2c_regs->iiccon = (1<<7) | (0<<6) | (1<<5) | (0xf); // 0xaf321 322   s3c2440_i2c_regs->iicadd = 0x10;   // S3C24xx slave address = [7:1]323   s3c2440_i2c_regs->iicstat = 0x10;   // I2C串行输出使能(Rx/Tx)324 }325 326 static int i2c_bus_s3c2440_init(void)327 {328   /* 2. 硬件相关的设置 */329   s3c2440_i2c_regs = ioremap(0x54000000, sizeof(struct s3c2440_i2c_regs));//映射功能寄存器330   331   s3c2440_i2c_init(); //初始化i2c控制器332 333   request_irq(IRQ_IIC, s3c2440_i2c_xfer_irq, 0, "s3c2440-i2c", NULL); //申请中断源,加载中断处理函数-s3c2440_i2c_xfer_irq334 335   init_waitqueue_head(&s3c2440_i2c_xfer_data.wait); //初始化一个等待队列头336   337   /* 3. 注册i2c_adapter */338   i2c_add_adapter(&s3c2440_i2c_adapter);339   340   return 0;341 }342 343 static void i2c_bus_s3c2440_exit(void)344 {345   i2c_del_adapter(&s3c2440_i2c_adapter);  346   free_irq(IRQ_IIC, NULL);347   iounmap(s3c2440_i2c_regs);348 }349 350 module_init(i2c_bus_s3c2440_init);351 module_exit(i2c_bus_s3c2440_exit);352 MODULE_LICENSE("GPL");

附一份测试程序:

 1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <sys/types.h> 5 #include <sys/stat.h> 6 #include <fcntl.h> 7 #include "i2c-dev.h" 8  9 10 /* i2c_usr_test </dev/i2c-0> <dev_addr> r addr11  * i2c_usr_test </dev/i2c-0> <dev_addr> w addr val12 */13 14 void print_usage(char *file)15 {16   printf("%s </dev/i2c-0> <dev_addr> r addr\n", file);17   printf("%s </dev/i2c-0> <dev_addr> w addr val\n", file);18 }19 20 int main(int argc, char **argv)21 {22   int fd;23   unsigned char addr, data;24   int dev_addr;25   26   if ((argc != 5) && (argc != 6))27   {28     print_usage(argv[0]);29     return -1;30   }31 32   fd = open(argv[1], O_RDWR);33   if (fd < 0)34   {35     printf("can't open %s\n", argv[1]);36     return -1;37   }38 39   dev_addr = strtoul(argv[2], NULL, 0);40   if (ioctl(fd, I2C_SLAVE, dev_addr) < 0)41   {  42     /* ERROR HANDLING; you can check errno to see what went wrong */  43     printf("set addr error!\n");44     return -1;45   }46 47   if (strcmp(argv[3], "r") == 0)48   {49     addr = strtoul(argv[4], NULL, 0);50     51     data = i2c_smbus_read_word_data(fd, addr);52       53     printf("data: %c, %d, 0x%2x\n", data, data, data);54   }55   else if ((strcmp(argv[3], "w") == 0) && (argc == 6))56   {57     addr = strtoul(argv[4], NULL, 0);58     data = strtoul(argv[5], NULL, 0);59     i2c_smbus_write_byte_data(fd, addr, data);    60   }61   else62   {63     print_usage(argv[0]);64     return -1;65   }66   67   return 0;68 }

Make File:

KERN_DIR = /work/system/linux-3.4.2

all:
    make -C $(KERN_DIR) M=`pwd` modules

clean:
    make -C $(KERN_DIR) M=`pwd` modules clean
    rm -rf modules.order

obj-m    += i2c_bus_s3c2440.o