天天看点

[rockchip]I2C学习与分析

从kernel/drivers/i2c/busses/i2c-rockchip.c开始分析

根据I2C总线模型,首先分配一个  rockchip_i2c_driver  结构体

static const struct of_device_id rockchip_i2c_of_match[] = {
        { .compatible = "rockchip,rk30-i2c", .data = NULL, },
        {},
};
MODULE_DEVICE_TABLE(of, rockchip_i2c_of_match);

static struct platform_driver rockchip_i2c_driver = {
        .probe          = rockchip_i2c_probe,          
        .remove         = rockchip_i2c_remove,         
        .driver         = {
                .owner  = THIS_MODULE,         
                .name   = "rockchip_i2c",      
                .pm     = ROCKCHIP_I2C_PM_OPS, 
                .of_match_table = of_match_ptr(rockchip_i2c_of_match),
        },
};

           

与设备树匹配成功后调用probe函数

subsys_initcall(rockchip_i2c_init_driver);     

subsys_initcall(rockchip_i2c_init_driver);     
    platform_driver_register(&rockchip_i2c_driver);
        static int rockchip_i2c_probe(struct platform_device *pdev)   
            i2c = devm_kzalloc(&pdev->dev, sizeof(struct rockchip_i2c), GFP_KERNEL);//分配内存
            res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
            i2c->regs = devm_ioremap_resource(&pdev->dev, res); //获取资源
===================================================================================            
            ret = i2c_add_adapter(&i2c->adap);  //这个比较重要
               i2c_register_adapter(adapter);
                   if (adap->nr < __i2c_first_dynamic_bus_num)
                   i2c_scan_static_board_info(adap); 
                        list_for_each_entry(devinfo, &__i2c_board_list, list)    //遍历链表
                        //遍历一个一个从机准备的信息,匹配busnum ,成功后调用i2c_new_device来创建 i2c_client            
                        //i2c->adap.nr = i2c->pdata->bus_num
                        if (devinfo->busnum == adapter->nr
                                && !i2c_new_device(adapter,
                                                &devinfo->board_info))
                            client = kzalloc(sizeof *client, GFP_KERNEL); //分配内存
                            
===================================================================================
            
           

struct i2c_client 

struct i2c_client {                                                     
        unsigned short flags;           /* div., see below              
        unsigned short addr;            /* chip address - NOTE: 7bit    
                                        /* addresses are stored in the  
                                        /* _LOWER_ 7 bits               
        char name[I2C_NAME_SIZE];                                       
        struct i2c_adapter *adapter;    /* the adapter we sit on        
        struct i2c_driver *driver;      /* and our access routines      
        struct device dev;              /* the device structure         
        int irq;                        /* irq issued by device         
        struct list_head detected;                                      
}; 
           

在简单分析i2c_new_device

struct i2c_client *                                                                            
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)                    
{                                                                                              
        struct i2c_client       *client;                                                       
        int                     status;                                                        
                                                                                               
        client = kzalloc(sizeof *client, GFP_KERNEL);                                          
        if (!client)                                                                           
                return NULL;                                                                   
                                                                                               
        client->adapter = adap;                                                                
                                    //info  从机真正的信息                                                            
        client->dev.platform_data = info->platform_data;                                       
                                                                                               
        if (info->archdata)                                                                    
                client->dev.archdata = *info->archdata;                                        
                                                                                               
        client->flags = info->flags;                                                           
        client->addr = info->addr;   //从机地址                                                          
        client->irq = info->irq;     //从机对应的外部中断号或者外部中断对应的GPIO
                                                          
                                                                                               
        strlcpy(client->name, info->type, sizeof(client->name)); //与匹配相关的名字                              
                                                                                               
        /* Check for address validity */                                                       
        status = i2c_check_client_addr_validity(client);                                       
        if (status) {                                                                          
                dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",                    
                        client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);                
                goto out_err_silent;                                                           
        }                                                                                      
                                                                                               
        /* Check for address business */                                                       
        #if 0                                                                                  
        status = i2c_check_addr_busy(adap, client->addr);                                      
        if (status)                                                                            
                goto out_err;                                                                  
        #else                                                                                  
        /* [email protected] : Devices which have some i2c addr can work in same i2c bus,     
           if devices havn't work at the same time.*/                                          
        status = i2c_check_addr_ex(adap, client->addr);                                        
        if (status != 0)                                                                       
                dev_err(&adap->dev, "%d i2c clients have been registered at 0x%02x",           
                        status, client->addr);                                                 
        #endif                                                                                 
                                                                                               
        client->dev.parent = &client->adapter->dev;                                            
        client->dev.bus = &i2c_bus_type;     //匹配成功后调用i2c_device_probe  
											  //查看匹配规则   后面列出  
			static int i2c_device_probe(struct device *dev)                                       
			{                                                                                             
					struct i2c_client       *client = i2c_verify_client(dev);                             
					struct i2c_driver       *driver;                                                      
					int status;                                                                           
																										  
					if (!client)                                                                          
							return 0;                                                                     
																										  
					driver = to_i2c_driver(dev->driver);                                                  
					if (!driver->probe || !driver->id_table)                                              
							return -ENODEV;                                                               
					client->driver = driver;                                                              
					if (!device_can_wakeup(&client->dev))                                                 
							device_init_wakeup(&client->dev,                                              
													client->flags & I2C_CLIENT_WAKE);                     
					dev_dbg(dev, "probe\n");                                                              
																										  
					status = driver->probe(client, i2c_match_id(driver->id_table, client));     //匹配的最后一个有个哨兵          
					if (status) {                                                                         
							client->driver = NULL;                                                        
							i2c_set_clientdata(client, NULL);                                             
					}                                                                                     
					return status;                                                                        
			}  		

        client->dev.type = &i2c_client_type;                                                   
        client->dev.of_node = info->of_node;                                                   
        ACPI_HANDLE_SET(&client->dev, info->acpi_node.handle);                                 
                                                                                               
        /* For 10-bit clients, add an arbitrary offset to avoid collisions */                  
                                                                                               
    /* [email protected] : Devices which have some i2c addr can work in same i2c bus,         
      if devices havn't work at the same time.*/                                               
        #if 0                                                                                  
        dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),                            
                     client->addr | ((client->flags & I2C_CLIENT_TEN)                          
                                     ? 0xa000 : 0));                                           
        #else                                                                                  
         status = device_register(&client->dev);         //注册                                    
        if (status)                                                                          
                goto out_err;                                                                
                                                                                             
        dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",                       
                client->name, dev_name(&client->dev));                                       
                                                                                             
        return client;                                                                       
                                                               
}                                                                                            
EXPORT_SYMBOL_GPL(i2c_new_device);  
           

匹配规则:i2c_device_match

struct rockchip_i2c {    
         spinlock_t              lock;  
         wait_queue_head_t       wait;  
         unsigned int            suspended:1;
   
         struct i2c_msg          *msg;           //IIC要传输的数据,
         unsigned int            is_busy;
         int                     error; 
         unsigned int            msg_ptr;
   
         unsigned int            irq;            //中断号
   
         enum rockchip_i2c_state state; 
         unsigned int            complete_what;
         unsigned long           clkrate;
   
         void __iomem            *regs;         //通过platform_get_resource拿到物理基地址,映射完后赋值
         struct clk              *clk;  
         struct device           *dev;  
         struct resource         *ioarea;
         struct i2c_adapter      adap;          //读写数据的算法
         struct mutex            suspend_lock;
   
         unsigned long           scl_rate;
         unsigned long           i2c_rate;
         unsigned int            addr;  
         unsigned char           addr_1st, addr_2nd;            
         unsigned int            mode;  
         unsigned int            count;
   
         unsigned int            check_idle;                    
         int                     sda_gpio, scl_gpio;
         struct pinctrl_state    *gpio_state;
 };
           

  struct i2c_msg

struct i2c_msg {
   __u16 addr;
   __u16 flags;
  #define I2C_M_TEN 0x10
  #define I2C_M_RD 0x01
  #define I2C_M_NOSTART 0x4000
  #define I2C_M_REV_DIR_ADDR 0x2000
  #define I2C_M_IGNORE_NAK 0x1000
  #define I2C_M_NO_RD_ACK 0x0800
   __u16 len;
   __u8 *buf;
  };
           

总结:系统维护了一个__i2c_board_list为头结点的双向循环链表。

继续阅读