• 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
    ", 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 write
    122 
    123     /* 休眠-等待i2c读写状态改变 */
    124     timeout = wait_event_timeout(s3c2440_i2c_xfer_data.wait, (s3c2440_i2c_xfer_data.state == STATE_STOP), HZ * 5); //等待状态成立或5s
    125     if (0 == timeout)
    126     {
    127         printk("s3c2440_i2c_xfer time out
    ");
    128         return -ETIMEDOUT;
    129     }
    130     else
    131     {
    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_adapter
    149  */
    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
    
    "); }
    178 
    179     switch (s3c2440_i2c_xfer_data.state)
    180     {
    181         case STATE_START : /* 发出S和设备地址后,产生中断 */
    182         {
    183             PRINTK("Start
    ");
    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             else
    204             {
    205                 s3c2440_i2c_xfer_data.state = STATE_WRITE;
    206             }    
    207         }
    208 
    209         case STATE_WRITE:
    210         {
    211             PRINTK("STATE_WRITE
    ");
    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             else
    242             {
    243                 /* 是最后一个消息的最后一个数据 */
    244                 s3c2440_i2c_stop(0);
    245                 break;                
    246             }
    247 
    248             break;
    249         }
    250 
    251         case STATE_READ:
    252         {
    253             PRINTK("STATE_READ
    ");
    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传输,接收到下一数据时无ACK
    263                 }
    264                 else
    265                 {
    266                     s3c2440_i2c_regs->iiccon = 0xaf;   // 恢复I2C传输,接收到下一数据时发出ACK
    267                 }                
    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             else
    282             {
    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:IICSCL
    311         s3c_gpio_cfgpin(S3C2410_GPE(14), S3C2410_GPE14_IICSCL);
    312     s3c_gpio_cfgpin(S3C2410_GPE(15), S3C2410_GPE15_IICSDA);
    313 
    314     /* bit[7] = 1, 使能ACK
    315      * bit[6] = 0, IICCLK = PCLK/16
    316      * bit[5] = 1, 使能中断
    317      * bit[3:0] = 0xf, Tx clock = IICCLK/16
    318      * PCLK = 50MHz, IICCLK = 3.125MHz, Tx Clock = 0.195MHz
    319      */
    320     s3c2440_i2c_regs->iiccon = (1<<7) | (0<<6) | (1<<5) | (0xf);  // 0xaf
    321 
    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_irq
    334 
    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 addr
    11  * i2c_usr_test </dev/i2c-0> <dev_addr> w addr val
    12  */
    13 
    14 void print_usage(char *file)
    15 {
    16     printf("%s </dev/i2c-0> <dev_addr> r addr
    ", file);
    17     printf("%s </dev/i2c-0> <dev_addr> w addr val
    ", 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
    ", 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!
    ");
    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
    ", 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     else
    62     {
    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
  • 相关阅读:
    [原]将工程由VC6迁移到VS2005
    [原]DirectDraw视频播放要点
    [原]代码优化学习笔记
    [原]Linux文件交换
    [原]计划
    [原]写在2006年的最后一天
    [原]技术发展规划
    FindBugs的安装和使用
    VirtualBox常用命令
    eclipse中统计代码行数
  • 原文地址:https://www.cnblogs.com/blogs-of-lxl/p/5295149.html
Copyright © 2020-2023  润新知