linux I2C驱动

1. samsung平台I2C控制器

    三星平台的I2C控制器支持四种工作模式:master tansmit、master receive、slave transmit、slave receive。由于嵌入式系统中,I2C接口常用来接camera、CTP等,所以一般只用到master transmit和master receive两种模式。

                   

    master transmit方式:先发送start信号,然后发送1个字节的地址数据(最低位为0),等待从设备返回ACK信号,然后连续写N个字节的数据,每个字节发送完毕后都要等待从设备的ACK信号,最终发送stop信号结束。

    master receive方式:先发送start信号,然后发送1字节的地址数据(最低位为1),等待从设备返回ACK信号,然后连续读N个字节的数据,每个字节读取完毕后,要发送ACK信号给从设备,最终发送stop信号结束。

    注意:不能在写模式的中间实现读操作,例如要读取从设备的0xA5寄存器的值,流程应该时:start + slave addr + 0xA5 + stop + start + slave addr + readbyte + stop,其中readbyte是读取出来的寄存器数据。

    中断发生的时刻:发送完每个字节、接收完每个字节。(start信号和stop信号发送完毕后不会收到中断,另外,ACK信号是通过IICSTAT寄存器来检测的)

    三星平台的I2C控制器主要有IICCON、IICSTAT、IICDS这三个寄存器。

         

    IICCON[7] 用来控制是否发送ACK信号,如果enable,那么每次接收1个字节后就会发送ACK信号

    IICCON[5] 用来控制是否允许中断,IICCON[4] 为中断标志,此位清零后才可以继续I2C的读写。

    IICSTAT[7:6] 用于控制当前的工作模式:11-master transmit、10-master receive

    IICSTAT[5] 用于发送start/stop信号,以及判断当前I2C总线是否处于忙状态。写1 - 发送start信号,写0 - 发送stop信号,读出1 - 当前I2C总线忙

    IICSTAT[4] 使能I2C接口的数据收发

    IICSTAT[0] 用于判断是否收到ACK信号,0 - 收到ACK信号,1 - 没收到ACK信号

    IICDS[7:0] 写 - 要发送的数据,读 -  接收到的数据。

    因此,I2C接口的使用流程如下

    1)初始化I2C接口,即配置SDA、SCL对应的GPIO,配置clk频率

    2)在读写操作时,打开i2c的时钟,判断总线是否忙状态(可以重试多次判断,比如每毫秒检测一次,最多400次)

    3)使能I2C中断,允许ACK,配置工作模式,发送start信号,发送从设备地址

    4)等待中断,在中断处理里判断是否收到ACK,然后可以连续发送或接收数据(每发完或接收到一个字节时就会产生一次中断)注意这里要么全部是发送,要么全部是接收

    5)最后发送stop信号,并关闭中断及时钟

2. linux I2C 总线驱动

    I2C总线驱动的主要目的是编写i2c_algorithm,并注册i2c_adapter。代码路径:drivers/i2c/busses/i2c-s3c2410.c,这个总线驱动代码会被很多三星平台使用,例如s5pv210

    此总线驱动使用了平台设备驱动模型,先来看一下它的平台设备:

arch/arm/plat-samsung/devs.c   平台设备platform_device

static struct resource s3c_i2c0_resource[] = {
    [0] = DEFINE_RES_MEM(S3C_PA_IIC, SZ_4K),    //控制寄存器地址
    [1] = DEFINE_RES_IRQ(IRQ_IIC),              //I2C0中断
};

struct platform_device s3c_device_i2c0 = {
    .name       = "s3c2410-i2c",
    .id     = 0, 
    .num_resources  = ARRAY_SIZE(s3c_i2c0_resource),
    .resource   = s3c_i2c0_resource,
};

struct s3c2410_platform_i2c default_i2c_data __initdata = {
    .flags      = 0, 
    .slave_addr = 0x10,
    .frequency  = 100*1000,
    .sda_delay  = 100,                  //初始化s3c2410_platfrom_i2c结构的数据
};

void __init s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *pd) 
{
    struct s3c2410_platform_i2c *npd;

    if (!pd) {
        pd = &default_i2c_data;
        pd->bus_num = 0;        //后面写I2C设备驱动时要指定它挂在哪个bus_num上
    }    

    npd = s3c_set_platdata(pd, sizeof(struct s3c2410_platform_i2c),
                   &s3c_device_i2c0);   /*将s3c2410_platfrom_i2c数据存储到
                                          pdev->dev.platform_data中*/
    if (!npd->cfg_gpio)
        npd->cfg_gpio = s3c_i2c0_cfg_gpio; //配置I2C对应的GPIO
}

//============================================================================

struct s3c2410_platform_i2c {
    int     bus_num;
    unsigned int    flags;
    unsigned int    slave_addr;  //注意这个并不是我们外接的I2C设备地址
    unsigned long   frequency;     而是将本控制器做为从设备时的地址
    unsigned int    sda_delay;

    void    (*cfg_gpio)(struct platform_device *dev);
};

/*每个I2C平台设备都会在其platfrom_data中存储一个s3c2410_platfrom_i2c数据,
其中bus_num就是指定给这个I2C总线的编号,在编写I2C设备驱动时会用到它,
使用i2c_register_board_info注册I2C设备时,就要指定它挂在哪个I2C总线上*/
    通过 platform_device->name 与 platform_driver->id_table->name 匹配,从而调到platform_driver->probe,在这个函数里从平台设备中获取i2c接口的寄存器地址、中断号、以及 s3c2410_platform_i2c数据,然后初始化I2C接口,注册中断服务,构造i2c_adapter数据并注册。请注意代码里的注释

struct s3c24xx_i2c {
    
    struct i2c_msg      *msg;
    unsigned int        msg_num;
    unsigned int        msg_idx;
    unsigned int        msg_ptr;

    unsigned int        irq;
    enum s3c24xx_i2c_state  state;
    void __iomem        *regs;
    struct clk          *clk;
    struct device       *dev;

    struct i2c_adapter  adap;

    struct s3c2410_platform_i2c *pdata;
};


static u32 s3c24xx_i2c_func(struct i2c_adapter *adap)
{
    return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
}

static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
    .master_xfer        = s3c24xx_i2c_xfer,
    .functionality      = s3c24xx_i2c_func,
};

static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
    struct s3c24xx_i2c *i2c;
    struct s3c2410_platform_i2c *pdata = NULL
    
    pdata = pdev->dev.platform_data;
    i2c = devm_kzalloc(&pdev->dev, sizeof(struct s3c24xx_i2c), GFP_KERNEL);
    i2c->pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
    memcpy(i2c->pdata, pdata, sizeof(*pdata));
    strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
    i2c->adap.owner   = THIS_MODULE;
    i2c->adap.algo    = &s3c24xx_i2c_algorithm;
    i2c->adap.retries = 2;
    i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;
    i2c->dev = &pdev->dev;
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    i2c->regs = ioremap(res->start, resource_size(res));
    i2c->adap.algo_data = i2c;
    s3c24xx_i2c_init(i2c);  //初始化I2C控制器
    i2c->irq = ret = platform_get_irq(pdev, 0);
    request_irq(i2c->irq, s3c24xx_i2c_irq, 0, dev_name(&pdev->dev), i2c);
    i2c->adap.nr = i2c->pdata->bus_num;     //获得总线号
    i2c_add_numbered_adapter(&i2c->adap);   //注册i2c_adapter
}

//我们以后写I2C设备驱动时会用到i2c_transfer来实现读写操作
struct i2c_msg {
    __u16 addr; /* slave address            */
    __u16 flags;
    __u16 len;      /* msg length               */
    __u8 *buf;      /* pointer to msg data          */
};

/*例如,向I2C设备的寄存器0x5A写入数据0x46,I2C设备的地址是0x35*/
u8 writebuf[2] = {0x5A,0x46};
struct i2c_msg msgs[] = {
    {
        .addr = 0x35,
        .flags = 0,
        .len = 2,
        .buf = writebuf,
    },
};
i2c_transfer(i2c_adapter,msgs,1);

/*例如,从I2C设备的寄存器0x5A读取数据*/
u8 reg = 0x5A,value;
struct i2c_msg msgs[] = {
    {
        .addr = 0x35,
        .flags = 0,
        .len = 1,
        .buf = ®,
    },
    {
        .addr = 0x35,
        .flags = I2C_M_RD,
        .len = 1,
        .buf = &value,
    },
};
i2c_transfer(i2c_adapter,msgs,2);

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
    int ret,try;
    for (ret = 0, try = 0; try <= adap->retries; try++) {
        ret = adap->algo->master_xfer(adap, msgs, num);
        if (ret != -EAGAIN)
            break;
    }            
}
//可以看到只需通过i2c_adapter->i2c_algorithm->master_xfer就可以实现数据读写。

/*在总线驱动里,我们将master_xfer指向了s3c24xx_i2c_xfer,另外还注册了I2C的中断
  服务函数s3c24xx_i2c_irq*/

static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
            struct i2c_msg *msgs, int num)
{
    struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;
    for (retry = 0; retry < adap->retries; retry++) {
        ret = s3c24xx_i2c_doxfer(i2c, msgs, num);
    }
}

static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,
                  struct i2c_msg *msgs, int num)
{
    i2c->msg     = msgs;
    i2c->msg_num = num;
    i2c->msg_ptr = 0;   //用来索引i2c_msg->buf里的数据
    i2c->msg_idx = 0;
    i2c->state   = STATE_START;     //状态机处于START状态

    s3c24xx_i2c_enable_irq(i2c);    //允许I2C中断
    s3c24xx_i2c_message_start(i2c, msgs);   //发送start信号以及设备地址
    
    timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);
    //此处等待msgs全部发送完毕,在中断里会wake_up(&i2c->wait);
    
    ret = i2c->msg_idx; //成功送出的msg个数
}

static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id)
{
    struct s3c24xx_i2c *i2c = dev_id;
    status = readl(i2c->regs + S3C2410_IICSTAT);
    i2c_s3c_irq_nextbyte(i2c, status);
    return IRQ_HANDLED;
}

static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
{
    switch (i2c->state) {
    case STATE_START:
        if (iicstat & S3C2410_IICSTAT_LASTBIT &&
            !(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
            /* ack was not received... */

            dev_dbg(i2c->dev, "ack was not received\n");
            s3c24xx_i2c_stop(i2c, -ENXIO);  //在这个函数里发送stop信号,关中断
            goto out_ack;                   //并wake_up(&i2c->wait)
        }            
        if (i2c->msg->flags & I2C_M_RD)
            i2c->state = STATE_READ;
        else
            i2c->state = STATE_WRITE;
        if (i2c->state == STATE_READ)
            goto prepare_read;
    case STATE_WRITE:
        if(i2c->msg_ptr < i2c->msg->len){
            u8 byte = i2c->msg->buf[i2c->msg_ptr++];
            writeb(byte, i2c->regs + S3C2410_IICDS);
        }else if(i2c->msg_idx < i2c->msg_num -1){
            dev_dbg(i2c->dev, "WRITE: Next Message\n");
            i2c->msg_ptr = 0;
            i2c->msg_idx++;
            i2c->msg++;
            s3c24xx_i2c_message_start(i2c, i2c->msg);
            i2c->state = STATE_START;
        }else{
            s3c24xx_i2c_stop(i2c, 0);   //发送stop信号,并wake_up(i2c->wait);
        }
        break;
    case STATE_READ:
        byte = readb(i2c->regs + S3C2410_IICDS);
        i2c->msg->buf[i2c->msg_ptr++] = byte;
 prepare_read:
        if(i2c->msg_ptr >= i2c->msg->len){
            if(i2c->msg_idx >= i2c->msg_num -1){
                dev_dbg(i2c->dev, "READ: Send Stop\n");
                s3c24xx_i2c_stop(i2c, 0);
            }else{
                dev_dbg(i2c->dev, "READ: Next Transfer\n");
                i2c->msg_ptr = 0;
                i2c->msg_idx++;
                i2c->msg++;
            }
        }
        break;
    }
 out_ack:
    tmp = readl(i2c->regs + S3C2410_IICCON);
    tmp &= ~S3C2410_IICCON_IRQPEND;
    writel(tmp, i2c->regs + S3C2410_IICCON);
}

static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,
                      struct i2c_msg *msg)
{
    unsigned int addr = (msg->addr & 0x7f) << 1;
    unsigned long stat;
    unsigned long iiccon;
    
    stat = S3C2410_IICSTAT_TXRXEN;
    if (msg->flags & I2C_M_RD) {
        stat |= S3C2410_IICSTAT_MASTER_RX;
        addr |= 1;
    } else
        stat |= S3C2410_IICSTAT_MASTER_TX;
    s3c24xx_i2c_enable_ack(i2c);
    iiccon = readl(i2c->regs + S3C2410_IICCON);
    writel(stat, i2c->regs + S3C2410_IICSTAT);
    writeb(addr, i2c->regs + S3C2410_IICDS);
    writel(iiccon, i2c->regs + S3C2410_IICCON);
    
    stat |= S3C2410_IICSTAT_START;
    writel(stat, i2c->regs + S3C2410_IICSTAT); 
}                      

static inline void s3c24xx_i2c_stop(struct s3c24xx_i2c *i2c, int ret)
{
    iicstat &= ~S3C2410_IICSTAT_START;
    writel(iicstat, i2c->regs + S3C2410_IICSTAT);
    i2c->state = STATE_STOP;
    wake_up(&i2c->wait);
    s3c24xx_i2c_disable_irq(i2c);
}

从代码中可以看到i2c_adapter->i2c_algorithm->master_xfer的具体实现,并清楚地看到I2C数据是如何通过i2c_msg来实现读写的。

可以知道,我们定义了多少个I2C平台设备platform_device->name能够和此驱动的platform_driver->id_table->name匹配上,

就会注册多少个i2c_adapter,每个i2c_adapter->nr是不一样的,它就是我们在平台设备里所指定的bus_num

3. i2c_adapter、i2c_algorithm、i2c_client、i2c_devinfo、i2c_board_info

    三星平台上一般带有3、4个I2C接口,那么就会注册3、4个i2c_adapter,每个i2c_adapter的nr即bus_num是不一样的,不过它们可能会共用一套i2c_algorithm算法。i2c_client其实就是绑定了i2c_adapter的从设备实体,例如电容触摸屏、摄像头芯片、EEPROM等,每个器件都会有一个i2c_client与之对应。如果这些I2C接口的芯片都挂在了同一条I2C总线上,那么这些i2c_client就使用的是同一个i2c_adapter。

struct i2c_client {
    unsigned short flags;       
    unsigned short addr;        //从设备地址,例如wm8994芯片的地址为0x1A
    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;                    //设备使用的中断,例如电容触摸屏的中断
    struct list_head detected;
};

struct i2c_devinfo {
    struct list_head    list;           //所有的I2C设备都会放在一条链表上
    int         busnum;                 //I2C设备挂在哪条总线上
    struct i2c_board_info   board_info; //I2C设备的信息
};

struct i2c_board_info {
    char        type[I2C_NAME_SIZE];    //I2C设备的名字
    unsigned short  flags;
    unsigned short  addr;               //I2C设备地址
    void        *platform_data;         //自定义的一些数据
    struct dev_archdata *archdata;
    struct device_node *of_node;
    int     irq;                        //设备使用的中断
};

#define I2C_BOARD_INFO(dev_type, dev_addr) \
    .type = dev_type, .addr = (dev_addr)
    
/*我们为某个I2C设备定义一个i2c_board_info结构,然后调用i2c_register_board_info
  来注册它,最终我们会得到一个i2c_devinfo对象,并且挂到链表上
  在注册i2c_adapter的时候就会用到它*/

LIST_HEAD(__i2c_board_list);

int __init i2c_register_board_info(int busnum, struct i2c_board_info const *info,
    unsigned len)
{
    int status;

    for (status = 0; len; len--, info++) {
        struct i2c_devinfo  *devinfo;

        devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
        devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
        if (!devinfo) {
            pr_debug("i2c-core: can't register boardinfo!\n");
            status = -ENOMEM;
            break;
        }        

        devinfo->busnum = busnum;
        devinfo->board_info = *info;
        list_add_tail(&devinfo->list, &__i2c_board_list);
    }   

    return status;
}

/*编写I2C设备驱动时,我们会先在平台的init_machine时,注册I2C设备*/

static struct ft5x06_ts_platform_data ft5x06_platformdata = { 
    .x_max      = 320,
    .y_max      = 480,
    .reset_gpio = FT5X06_RESET_GPIO,
    .irq_gpio   = FT5X06_IRQ_GPIO,
    .irqflags   = IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
};

static struct i2c_board_info ft5x06_device_info[] __initdata = { 
    {   
        I2C_BOARD_INFO("ft5x06_ts", 0x38),
        .platform_data = &ft5x06_platformdata,
        .irq = IRQ_EINT7,
    },  
};

static void __init smdkv210_machine_init(void)
{
    .....
    i2c_register_board_info(2,ft5x06_device_info,ARRAY_SIZE(ft5x06_device_info));
    .....            
}
    有了这些信息后,我们来研究下之前注册I2C总线驱动时调用i2c_add_numbered_adapter来注册i2c_adapter,那么我们看下它到底做了什么

int i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
    ....
    i2c_register_adapter(adap);
}

static int i2c_register_adapter(struct i2c_adapter *adap)
{
    ....
    if (adap->timeout == 0)
        adap->timeout = HZ;

    dev_set_name(&adap->dev, "i2c-%d", adap->nr);
    adap->dev.bus = &i2c_bus_type;
    adap->dev.type = &i2c_adapter_type;
    res = device_register(&adap->dev);
    dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
    
    i2c_scan_static_board_info(adap);   //扫描i2c_devinfo链表,创建i2c_client
}

static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
    struct i2c_devinfo  *devinfo;
    
    list_for_each_entry(devinfo, &__i2c_board_list, list) {
        if (devinfo->busnum == adapter->nr
                && !i2c_new_device(adapter,
                        &devinfo->board_info))
            dev_err(&adapter->dev,
                "Can't create device at 0x%02x\n",
                devinfo->board_info.addr);
    }
}

/*可以看到只要i2c_devinfo->busnum和i2c_adapter->nr匹配,就会为这个I2C设备
  创建i2c_client,详见i2c_new_device函数*/
  
struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
    struct i2c_client   *client;

    client = kzalloc(sizeof *client, GFP_KERNEL);
    client->adapter = adap;     //将这个i2c_adapter绑定到这个i2c_client上
    client->dev.platform_data = info->platform_data;
    client->flags = info->flags;
    client->addr = info->addr;
    client->irq = info->irq;
    strlcpy(client->name, info->type, sizeof(client->name));
    
    client->dev.parent = &client->adapter->dev;
    client->dev.bus = &i2c_bus_type;
    client->dev.type = &i2c_client_type;
    client->dev.of_node = info->of_node;

    device_register(&client->dev);
    
    dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
        client->name, dev_name(&client->dev));
}  

/*可以看到在i2c_scan_static_board_info里,会为每个匹配成功的i2c_devinfo创建
  i2c_client,其数据大都是从i2c_board_info里拿过来的*/
4. I2C设备驱动

    编写I2C设备驱动时,主要就是定义一个i2c_driver,然后i2c_add_driver注册就可以了,当这个i2c_driver和某个I2C设备匹配时,就会调用到i2c_driver->probe,在这里可以拿到这个I2C设备的i2c_client,然后就可以从i2c_client拿到设备相关的信息以及绑定的i2c_adapter,要实现I2C读写的话,只需要调用i2c_transfer就可以了。


struct i2c_driver {
    unsigned int class;

    /* Standard driver model interfaces */
    int (*probe)(struct i2c_client *, const struct i2c_device_id *);
    int (*remove)(struct i2c_client *);

    struct device_driver driver;
    const struct i2c_device_id *id_table;

};

struct i2c_device_id {
    char name[I2C_NAME_SIZE];
    kernel_ulong_t driver_data  /* Data private to the driver */
            __attribute__((aligned(sizeof(kernel_ulong_t))));
};

#define i2c_add_driver(driver) \
    i2c_register_driver(THIS_MODULE, driver)
    
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
    int res; 

    /* add the driver to the list of i2c drivers in the driver core */
    driver->driver.owner = owner;
    driver->driver.bus = &i2c_bus_type; //注意到它和i2c_client->dev.bus是一样的

    /* When registration returns, the driver core
     * will have called probe() for all matching-but-unbound devices.
     */
    res = driver_register(&driver->driver);
    if (res)
        return res; 

    pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);

    return 0;
}    

struct bus_type i2c_bus_type = {
    .name   = "i2c",
    .match  = i2c_device_match,  //i2c_client->name与i2c_driver->id_table->name
    .probe  = i2c_device_probe,     //在这个probe里会调用到i2c_driver->probe
    .remove = i2c_device_remove,
    .shutdown   = i2c_device_shutdown,
    .pm     = &i2c_device_pm_ops,
};

static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
                        const struct i2c_client *client)
{
    while (id->name[0]) {
        if (strcmp(client->name, id->name) == 0)
            return id;
        id++;
    }
    return NULL;
}

static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
    struct i2c_client   *client = i2c_verify_client(dev);
    struct i2c_driver   *driver;

    if (!client)
        return 0;

    driver = to_i2c_driver(drv);

    if (driver->id_table)
        return i2c_match_id(driver->id_table, client) != NULL;

    return 0;
}
    接下来看一个I2C设备驱动实例:

struct ft5x06_ts_data {
    struct i2c_client *client;
    struct input_dev *input_dev;
    const struct ft5x06_ts_platform_data *pdata;    //自定义的数据
};

static int ft5x06_ts_probe(struct i2c_client *client,
               const struct i2c_device_id *id)
{
    struct ft5x06_ts_data *ts;
    u8 reg_addr,reg_value;
    int ret,err;
    
    ts = devm_kzalloc(&client->dev,
            sizeof(struct ft5x06_ts_data), GFP_KERNEL);
    
    ts->client = client;
    ts->pdata = client->dev.platform_data;
    /*
        reg_addr = 0xA3;    //#define FT_REG_ID     0xA3
        struct i2c_msg msgs[] = {
            {
                 .addr = client->addr,
                 .flags = 0,
                 .len = 1,
                 .buf = reg_addr,
             },
            {
                 .addr = client->addr,
                 .flags = I2C_M_RD,
                 .len = 1,
                 .buf = reg_value,
             },
        };
        ret = i2c_transfer(client->adapter, msgs, 2);

    dev_info(&client->dev, "Device ID = 0x%x\n", reg_value);
    */
    err = request_threaded_irq(client->irq, NULL,
                ft5x06_ts_interrupt,
                ts->pdata->irqflags | IRQF_ONESHOT,
                client->dev.driver->name, ts);    
    ....                
}    

static const struct i2c_device_id ft5x06_ts_id[] = {
    {"ft5x06_ts", 0},       //和i2c_register_board_info时使用的名字匹配就会
    {},                     //调用到i2c_driver->probe,并把i2c_client传进来
};

static struct i2c_driver ft5x06_ts_driver = {
    .probe = ft5x06_ts_probe,
    .remove = __devexit_p(ft5x06_ts_remove),
    .driver = {
           .name = "ft5x06_ts",
           .owner = THIS_MODULE,
    .id_table = ft5x06_ts_id,
};

static int __init ft5x06_ts_init(void)
{
    return i2c_add_driver(&ft5x06_ts_driver);
}
module_init(ft5x06_ts_init);

static void __exit ft5x06_ts_exit(void)
{
    i2c_del_driver(&ft5x06_ts_driver);
}
module_exit(ft5x06_ts_exit);
    再回想一下整个过程:

    1. 通过i2c_register_board_info会得到一个i2c_devinfo,在注册的时候就已经提供了I2C设备的一些信息以及要挂在哪个busnum上。

    2. 注册i2c总线驱动时,即i2c_register_adapter时,会扫描i2c_devinfo链表,根据i2c_adapter->nr来和i2c_devinfo->busnum来匹配,匹配成功后就会为这个I2C设备创建一个i2c_client,并把i2c_board_info都存到i2c_client上(i2c_client->name就是来自i2c_board_info->type),并将这个i2c_adapter绑定到设备的i2c_client上。

    3. 注册i2c设备驱动时,通过i2c_driver->id_table->name与i2c_client->name匹配,匹配成功后就可以调用到i2c_driver->probe,并传来配对的i2c_client,然后就可以使用i2c_client->adapter来实现I2C总线读写了。

    关于i2c_adapter->nr与哪个I2C接口对应,以及I2C总线读写操作,请回顾第2节 linux I2C 总线驱动

    

   一般平台厂商已经做好了I2C总线驱动,i2c_adapter不需要去修改。当我们需要为某个I2C设备编写驱动时,只需要在machine_init里注册一个i2c_board_info并指定busnum(根据实际硬件连接,从注册i2c_adapter时所使用的平台设备去找),然后定义一个i2c_driver并通过i2c_add_driver注册之后就可以用了。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值