linux i2c设备驱动

linux下i2c设备驱动框架

linux下i2c体系结构分为三部分:
1. i2c core 层:提供设备驱动,总线驱动的接口API,i2c 通讯方法,探测设备。
2. i2c 总线驱动:实现i2c硬件体系结构中适配器(解决通讯中怎么发数据)。
3. i2c 设备驱动: 寻找i2c适配器与CPU交换数据(解决通讯中向谁发数据与发什么数据)。

框架图:
linux i2c设备驱动_第1张图片
在linux源码 driver/i2c目录下:
i2c-core.c 实现i2c 核心层
i2c-dev.c 实现适配器设备功能
buses文件 实现主机控制器设备驱动
algos文件夹 实现总线适配器通讯方法。

## 几个重要的数据结构 ##

//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:

//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函数初始化适配器硬件,时钟,中断等资源,最终注册一个适配器。

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如何产生

//在注册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,
};

你可能感兴趣的:(linux驱动开发)