歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux技術 >> linux i2c設備驅動

linux i2c設備驅動

日期:2017/3/3 13:41:00   编辑:Linux技術

linux下i2c設備驅動框架

linux下i2c體系結構分為三部分: 1. i2c core 層:提供設備驅動,總線驅動的接口API,i2c 通訊方法,探測設備。

2. i2c 總線驅動:實現i2c硬件體系結構中適配器(解決通訊中怎麼發數據)。

3. i2c 設備驅動: 尋找i2c適配器與CPU交換數據(解決通訊中向誰發數據與發什麼數據)。

框架圖:

在linux源碼 driver/i2c目錄下:

i2c-core.c 實現i2c 核心層

i2c-dev.c 實現適配器設備功能

buses文件 實現主機控制器設備驅動

algos文件夾 實現總線適配器通訊方法。

## 幾個重要的數據結構 ##

[code]//i2c_adapter對應一個物理上的適配器
struct i2c_adapter {
    struct module *owner;
    unsigned int id;
    unsigned int class;       /* classes to allow probing for */
    const struct i2c_algorithm *algo; /* the algorithm to access the bus */
    void *algo_data;
    /* data fields that are valid for all devices   */
    struct rt_mutex bus_lock;
    int timeout;            /* in jiffies */
    int retries;
    struct device dev;      /* the adapter device */
    int nr;
    char name[48];
    struct completion dev_released;

    struct list_head userspace_clients;
};

//對應一套i2c總線通訊方法
struct i2c_algorithm {
    /* If an adapter algorithm can't do I2C-level access, set master_xfer
       to NULL. If an adapter algorithm can do SMBus access, set
       smbus_xfer. If set to NULL, the SMBus protocol is simulated
       using common I2C messages */
    /* master_xfer should return the number of messages successfully
       processed, or a negative value on error */
    int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
               int num);
    //一個i2c通訊周期的信號
    int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
               unsigned short flags, char read_write,
               u8 command, int size, union i2c_smbus_data *data);

    /* To determine what the adapter supports */
    u32 (*functionality) (struct i2c_adapter *);
};

//對應一套驅動方法
struct i2c_driver {
    unsigned int class;

    /* Notifies the driver that a new bus has appeared or is about to be
     * removed. You should avoid using this if you can, it will probably
     * be removed in a near future.
     */
    int (*attach_adapter)(struct i2c_adapter *);
    int (*detach_adapter)(struct i2c_adapter *);

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

    /* driver model interfaces that don't relate to enumeration  */
    void (*shutdown)(struct i2c_client *);
    int (*suspend)(struct i2c_client *, pm_message_t mesg);
    int (*resume)(struct i2c_client *);

    /* Alert callback, for example for the SMBus alert protocol.
     * The format and meaning of the data value depends on the protocol.
     * For the SMBus alert protocol, there is a single bit of data passed
     * as the alert response's low bit ("event flag").
     */
    void (*alert)(struct i2c_client *, unsigned int data);

    /* a ioctl like command that can be used to perform specific functions
     * with the device.
     */
    int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);

    struct device_driver driver;
    const struct i2c_device_id *id_table; //驅動支持i2c設備的id表

    /* Device detection callback for automatic device creation */
    int (*detect)(struct i2c_client *, struct i2c_board_info *);
    const unsigned short *address_list;
    struct list_head clients;
};

//對應一個真實的i2c物理設備
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消息結構體
struct i2c_msg {
    __u16 addr; /* slave address            */
    __u16 flags;
#define I2C_M_TEN       0x0010  /* this is a ten bit chip address */
#define I2C_M_RD        0x0001  /* read data, from slave to master */
#define I2C_M_NOSTART       0x4000  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR  0x2000  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK    0x1000  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK     0x0800  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN      0x0400  /* length will be first received byte */
    __u16 len;      /* msg length               */
    __u8 *buf;      /* pointer to msg data          */
};

i2c總線驅動

i2c總線驅動是一個單獨的驅動,使用platform總線,最終目的完成注冊一個adapter

platform_device:

[code]//plat-s2410.c
struct platform_device s3c_device_i2c2 = {
    .name         = "s3c2410-i2c",
    .id       = 2,
    .num_resources    = ARRAY_SIZE(s3c_i2c_resource), 
    .resource     = s3c_i2c_resource, //i2c寄存器與i2c中斷號
};

//傳給plat_driver的私有數據
static struct s3c2410_platform_i2c default_i2c_data2 __initdata = {
    .flags      = 0,
    .bus_num    = 2,  //對應i2c-2
    .slave_addr = 0x10,
    .frequency  = 100*1000,
    .sda_delay  = 100,
};

platform_driver:
static struct platform_driver s3c24xx_i2c_driver = {
    .probe      = s3c24xx_i2c_probe, //匹配後執行
    .remove     = s3c24xx_i2c_remove,
    .id_table   = s3c24xx_driver_ids,
    .driver     = {
        .owner  = THIS_MODULE,
        .name   = "s3c-i2c",
        .pm = S3C24XX_DEV_PM_OPS,
    },
};
//match-id
static struct platform_device_id s3c24xx_driver_ids[] = { //id_table
    {
        .name       = "s3c2410-i2c",
        .driver_data    = TYPE_S3C2410,
    }, {
        .name       = "s3c2440-i2c",
        .driver_data    = TYPE_S3C2440,
    }, { },
};
MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);
probe函數初始化適配器硬件,時鐘,中斷等資源,最終注冊一個適配器。

[code]static int s3c24xx_i2c_probe(struct platform_device *pdev)
    pdata = pdev->dev.platform_data; //獲取私有數據
    i2c->adap.algo    = &s3c24xx_i2c_algorithm; //綁定信號傳輸函數  
    init_waitqueue_head(&i2c->wait); //申請等待隊列
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0); //獲取i2c控制寄存器
    i2c->regs = ioremap(res->start, resource_size(res)); 
    ret = s3c24xx_i2c_init(i2c); //初始化配置寄存器
    i2c->irq = ret = platform_get_irq(pdev, 0); //獲取中斷號
    ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,dev_name(&pdev->dev), i2c); //申請中斷
    i2c->adap.nr = pdata->bus_num; //設置適配器的bus_name
    ret = i2c_add_numbered_adapter(&i2c->adap);//add_adapter到總線,注冊adapter
        status = i2c_register_adapter(adap);
            i2c_scan_static_board_info(adap); //掃描總線,適配產生i2c_client
                list_for_each_entry(devinfo, &__i2c_board_list, list) {
                    if (devinfo->busnum == adapter->nr
                        && !i2c_new_device(adapter,&devinfo->board_info))
//信號傳輸函數                    
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
    .master_xfer        = s3c24xx_i2c_xfer,
    .functionality      = s3c24xx_i2c_func,
};
//發送iic信號幀
static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msgs, int num)
    ret = s3c24xx_i2c_doxfer(i2c, msgs, num);
        ret = s3c24xx_i2c_set_master(i2c);
        i2c->msg     = msgs;
        i2c->msg_num = num;
        s3c24xx_i2c_enable_irq(i2c);
        s3c24xx_i2c_message_start(i2c, msgs); //發送start信號
            unsigned int addr = (msg->addr & 0x7f) << 1; //注意msg的iic地址

        timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5); //等待中斷完成    
//中斷到來且i2c數據幀傳輸完成,就是喚醒i2c-wait的時候
static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id)
    i2s_s3c_irq_nextbyte(i2c, status); //繼續發送
        .......//繼續發送讀寫byte,繼續中斷
            s3c24xx_i2c_stop(i2c, 0); //發送stop信號
               s3c24xx_i2c_master_complete(i2c, ret);
                    wake_up(&i2c->wait); //喚醒i2c->wait
            s3c24xx_i2c_disable_irq(i2c);

i2c設備驅動

i2c_diver需要匹配i2c_client,那麼i2c_client如何產生

[code]//在注冊i2c_adapter會掃描__i2c_board_list,根據i2c_devinfo信息 創建帶adapter的適配器,供i2c_driver匹配。
//方法1.在bsp文件中注冊i2c_register_board_info()
//方法2.如果內核支持設備樹,在dts文件中添加一個i2c的節點
struct i2c_board_info {
    char        type[I2C_NAME_SIZE]; //名字如 lm75
    unsigned short  flags; 
    unsigned short  addr; //7位地址
    void        *platform_data;
    struct dev_archdata *archdata;
#ifdef CONFIG_OF
    struct device_node *of_node;
#endif
    int     irq;
};

i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
    devinfo->busnum = busnum;
    devinfo->board_info = *info;
    list_add_tail(&devinfo->list, &__i2c_board_list); //將i2c_board_info加入__i2c_board_list

在注冊i2c_adapter時:
static int i2c_register_adapter(struct i2c_adapter *adap)
    i2c_scan_static_board_info(adap);
        list_for_each_entry(devinfo, &__i2c_board_list, list)
            i2c_new_device(adapter,&devinfo->board_info)//產生依附adaper的client

struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
    client->adapter = adap; //放adapter到client
    client->dev.platform_data = info->platform_data;
    client->dev.bus = &i2c_bus_type;
    client->dev.type = &i2c_client_type;
    status = device_register(&client->dev); //將生成的i2c_client放到i2c_bus上

//在寫i2c_driver時,提供匹配的id_table
static struct i2c_device_id id_tables[] = {
        {"lm75", 0x1111},
        {"lm75a", 0x1112},
};
static struct i2c_driver lm75_drv = {
    .probe = lm75_drv_probe, //在probe中注冊一個fops,來對設備進行讀寫
    .remove = lm75_drv_remove,
    //必須要有
    .driver = {
        .name   = "lm75",
    },
    .id_table = id_tables,
};

Copyright © Linux教程網 All Rights Reserved