I2C(二) linux2.6


title: I2C(二) linux2.6
date: 2019/1/28 18:18:42
toc: true
---

I2C(二) linux2.6

总线驱动

官方例子是drivers\i2c\busses\i2c-s3c2410.c

关键结构

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

static struct s3c24xx_i2c s3c24xx_i2c = {
    .lock       = __SPIN_LOCK_UNLOCKED(s3c24xx_i2c.lock),
    .wait       = __WAIT_QUEUE_HEAD_INITIALIZER(s3c24xx_i2c.wait),
    .tx_setup   = 50,
    .adap       = {
        .name           = "s3c2410-i2c",
        .owner          = THIS_MODULE,
        .algo           = &s3c24xx_i2c_algorithm,
        .retries        = 2,
        .class          = I2C_CLASS_HWMON,
    },
};


struct i2c_adapter {  
 struct module *owner;              //所属模块  
 unsigned int id;                //algorithm的类型,定义于i2c-id.h,  
 unsigned int class;      
 const struct i2c_algorithm *algo;     //总线通信方法结构体指针  
 void *algo_data;               //algorithm数据  
 struct rt_mutex bus_lock;        //控制并发访问的自旋锁  
 int timeout;     
 int retries;                //重试次数  
 struct device dev;             //适配器设备   
 int nr;                          //存放在i2c_adapter_idr里的位置号
 char name[48];              //适配器名称  
 struct completion dev_released;    //用于同步  
 struct list_head userspace_clients;   //client链表头  

};

入口

这里是一个platform总线框架,第一个函数也就是probe

static struct platform_driver s3c2440_i2c_driver = {
    .probe      = s3c24xx_i2c_probe,
    .remove     = s3c24xx_i2c_remove,
    .resume     = s3c24xx_i2c_resume,
    .driver     = {
        .owner  = THIS_MODULE,
        .name   = "s3c2440-i2c",
    },
};
static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
    struct s3c24xx_i2c *i2c = &s3c24xx_i2c;
    struct resource *res;
    int ret;

    /* find the clock and enable it */
    i2c->dev = &pdev->dev;
    i2c->clk = clk_get(&pdev->dev, "i2c");
    clk_enable(i2c->clk);
    /* map the registers */
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    i2c->ioarea = request_mem_region(res->start, (res->end-res->start)+1,
                     pdev->name);
    i2c->regs = ioremap(res->start, (res->end-res->start)+1);

    // 设置了具体的适配器算法
    /* setup info block for the i2c core */
    i2c->adap.algo_data = i2c;
    i2c->adap.dev.parent = &pdev->dev;

    //寄存器 gpio 初始化
    /* initialise the i2c controller */
    ret = s3c24xx_i2c_init(i2c);
    
    //设置中断函数,中断里执行具体的读写函数
    /* find the IRQ for this unit (note, this relies on the init call to
     * ensure no current IRQs pending 
     */
    res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
    ret = request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED,
              pdev->name, i2c);
    i2c->irq = res;
        
        
    //添加adapt       
    ret = i2c_add_adapter(&i2c->adap);

    //pdev.dev.driver_data=i2c
    platform_set_drvdata(pdev, i2c);

    return 0;
}

i2c_add_adapter

  1. 设置这个具体的适配器相关的信息
  2. 添加新的适配器,这个情况下可能有新的client能够挂接到dev链表
  3. 调用这个i2c_driverattach_adapter
int i2c_add_adapter(struct i2c_adapter *adapter)
{
    res = idr_get_new_above(&i2c_adapter_idr, adapter,__i2c_first_dynamic_bus_num, &id);
    //调用idr_get_new_above()将结构插入i2c_adapter_idr中,并将插入的位置赋给id,以后可以通过id在i2c_adapter_idr中找到相应的i2c_adapter结构体
    i2c_register_adapter(adapter)
}

static int i2c_register_adapter(struct i2c_adapter *adap)
{
    int res = 0;
    struct list_head   *item;
    struct i2c_driver  *driver;

    INIT_LIST_HEAD(&adap->clients);
    list_add_tail(&adap->list, &adapters);

    
    // 这个parent 在上面的probe 指向了 资源文件指向的platform_device.dev
    /* Add the adapter to the driver core.
     * If the parent pointer is not set up,
     * we add this adapter to the host bus.
     */
    if (adap->dev.parent == NULL) {
        adap->dev.parent = &platform_bus;
        pr_debug("I2C adapter driver [%s] forgot to specify "
             "physical device\n", adap->name);
    }
    sprintf(adap->dev.bus_id, "i2c-%d", adap->nr);
    adap->dev.release = &i2c_adapter_dev_release;
    //这里设置了具体的class 
    adap->dev.class = &i2c_adapter_class;
    
    //注册device 在总线框架中,注册dev 会去寻找driver,执行probe
    /* for new style drivers, when registration returns the driver core
     * will have called probe() for all matching-but-unbound devices.
     */
    res = device_register(&adap->dev);



    // 扫描静态的单板外设信息,有个全局链表__i2c_board_list 保存着未添加到链表的
    // 想要加入到的client的相关信息,这个后续可以看 linux3.4
    // 这里就是根据这个新的适配器添加这个client到链表
    /* create pre-declared device nodes for new-style drivers */
    if (adap->nr < __i2c_first_dynamic_bus_num)
        i2c_scan_static_board_info(adap);

    
    // 遍历这个drivers ,去执行实际驱动i2c_driver 的attach_adapter
    /* let legacy drivers scan this bus for matching devices */
    list_for_each(item,&drivers) {
        driver = list_entry(item, struct i2c_driver, list);
        if (driver->attach_adapter)
            /* We ignore the return code; if it fails, too bad */
            driver->attach_adapter(adap);
    }
}

最后执行attach_adapter,这个在后面的设备驱动再分析

硬件操作

硬件操作最后归结到接口master_xfer,I2C的传输到最后可以整理为

  1. 开始信号
  2. 结束信号
  3. ACK/NAK
  4. 数据传输1字节,读写是根据起始信号决定

在官方的驱动中,使用中断来传输,传输中使用休眠唤醒

//休眠
timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);
//完成后唤醒
wake_up(&i2c->wait);
// probe 时候申请中断
request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED,pdev->name, i2c);

设备驱动

参考文件driver/i2c/chips/eeprom.c

入口

static int __init eeprom_init(void)
{
    return i2c_add_driver(&eeprom_driver);
}
static inline int i2c_add_driver(struct i2c_driver *driver)
{
    return i2c_register_driver(THIS_MODULE, driver);
}

注册

注册的时候会加入驱动到链表,最后执行驱动的attach_adapter

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;

    //注册driver 会去寻找dev,执行probe
    /* for new style drivers, when registration returns the driver core
     * will have called probe() for all matching-but-unbound devices.
     */
    res = driver_register(&driver->driver);

    //添加链表
    list_add_tail(&driver->list,&drivers);

    //遍历adapt,执行驱动的attach_adapter
    //这个和我们最后注册 adapt的时候,遍历 driver 时是差不多的
    /* legacy drivers scan i2c busses directly */
    if (driver->attach_adapter) {
        struct i2c_adapter *adapter;
        list_for_each_entry(adapter, &adapters, list) {
            driver->attach_adapter(adapter);
        }
    }
    return 0;
}

attach_adapter

这里看下eeprom的是怎样的

static struct i2c_driver eeprom_driver = {
    .driver = {
        .name   = "eeprom",
    },
    .id     = I2C_DRIVERID_EEPROM,
    .attach_adapter = eeprom_attach_adapter,
    .detach_client  = eeprom_detach_client,
};

i2c_probe总结起来就是 使用adapter先去检测地址addr_data,成功后调用eeprom_detect,挂接client,注册我们实际的字符设备驱动等操作

static int eeprom_attach_adapter(struct i2c_adapter *adapter)
{
    return i2c_probe(adapter, &addr_data, eeprom_detect);
}

我们这里的地址有以下几种,依次处理Force,probe,normal_i2c,ignore

struct i2c_client_address_data {
    unsigned short *normal_i2c;
    unsigned short *probe;
    unsigned short *ignore;
    unsigned short **forces;
};

具体流程如下

int i2c_probe(struct i2c_adapter *adapter,
          struct i2c_client_address_data *address_data,
          int (*found_proc) (struct i2c_adapter *, int, int))
{
    int i, err;
    int adap_id = i2c_adapter_id(adapter);

    /* Force entries are done first, and are not affected by ignore
       entries */
    if (address_data->forces) {
        unsigned short **forces = address_data->forces;
        int kind;

        for (kind = 0; forces[kind]; kind++) {
            for (i = 0; forces[kind][i] != I2C_CLIENT_END;
                 i += 2) {
                if (forces[kind][i] == adap_id
                 || forces[kind][i] == ANY_I2C_BUS) {
                    dev_dbg(&adapter->dev, "found force "
                        "parameter for adapter %d, "
                        "addr 0x%02x, kind %d\n",
                        adap_id, forces[kind][i + 1],
                        kind);
                    err = i2c_probe_address(adapter,
                        forces[kind][i + 1],
                        kind, found_proc);
                    if (err)
                        return err;
                }
            }
        }
    }

    /* Stop here if we can't use SMBUS_QUICK */
    if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK)) {
        if (address_data->probe[0] == I2C_CLIENT_END
         && address_data->normal_i2c[0] == I2C_CLIENT_END)
            return 0;

        dev_warn(&adapter->dev, "SMBus Quick command not supported, "
             "can't probe for chips\n");
        return -1;
    }

    /* Probe entries are done second, and are not affected by ignore
       entries either */
    for (i = 0; address_data->probe[i] != I2C_CLIENT_END; i += 2) {
        if (address_data->probe[i] == adap_id
         || address_data->probe[i] == ANY_I2C_BUS) {
            dev_dbg(&adapter->dev, "found probe parameter for "
                "adapter %d, addr 0x%02x\n", adap_id,
                address_data->probe[i + 1]);
            err = i2c_probe_address(adapter,
                        address_data->probe[i + 1],
                        -1, found_proc);
            if (err)
                return err;
        }
    }

    /* Normal entries are done last, unless shadowed by an ignore entry */
    for (i = 0; address_data->normal_i2c[i] != I2C_CLIENT_END; i += 1) {
        int j, ignore;

        ignore = 0;
        for (j = 0; address_data->ignore[j] != I2C_CLIENT_END;
             j += 2) {
            if ((address_data->ignore[j] == adap_id ||
                 address_data->ignore[j] == ANY_I2C_BUS)
             && address_data->ignore[j + 1]
                == address_data->normal_i2c[i]) {
                dev_dbg(&adapter->dev, "found ignore "
                    "parameter for adapter %d, "
                    "addr 0x%02x\n", adap_id,
                    address_data->ignore[j + 1]);
                ignore = 1;
                break;
            }
        }
        if (ignore)
            continue;

        dev_dbg(&adapter->dev, "found normal entry for adapter %d, "
            "addr 0x%02x\n", adap_id,
            address_data->normal_i2c[i]);
        err = i2c_probe_address(adapter, address_data->normal_i2c[i],
                    -1, found_proc);
        if (err)
            return err;
    }

    return 0;
}

eeprom_detect

我们在这个驱动中的found_proc实际上是eeprom_detect,这里设置client,设置具体的adapt,driver,执行依附

/* This function is called by i2c_probe */
static int eeprom_detect(struct i2c_adapter *adapter, int address, int kind)
{
    struct i2c_client *new_client;
    struct eeprom_data *data;
    int err = 0;

    /* There are three ways we can read the EEPROM data:
       (1) I2C block reads (faster, but unsupported by most adapters)
       (2) Consecutive byte reads (100% overhead)
       (3) Regular byte data reads (200% overhead)
       The third method is not implemented by this driver because all
       known adapters support at least the second. */
    if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA
                        | I2C_FUNC_SMBUS_BYTE))
        goto exit;

    if (!(data = kzalloc(sizeof(struct eeprom_data), GFP_KERNEL))) {
        err = -ENOMEM;
        goto exit;
    }

    
    // 设置client
    new_client = &data->client;
    memset(data->data, 0xff, EEPROM_SIZE);
    i2c_set_clientdata(new_client, data);
    new_client->addr = address;
    new_client->adapter = adapter;
    new_client->driver = &eeprom_driver;
    new_client->flags = 0;

    /* Fill in the remaining client fields */
    strlcpy(new_client->name, "eeprom", I2C_NAME_SIZE);
    data->valid = 0;
    mutex_init(&data->update_lock);
    data->nature = UNKNOWN;

    //i2c_client与适配器进行连接
    /* Tell the I2C layer a new client has arrived */
    if ((err = i2c_attach_client(new_client)))
        goto exit_kfree;


    // 这个类似字符设备驱动
    /* create the sysfs eeprom file */
    err = sysfs_create_bin_file(&new_client->dev.kobj, &eeprom_attr);
}

i2c_attach_client

将i2c_client与适配器进行连接

int i2c_attach_client(struct i2c_client *client)
{
    struct i2c_adapter *adapter = client->adapter;
    int res = 0;

    list_add_tail(&client->list,&adapter->clients);

    client->usage_count = 0;
    client->dev.parent = &client->adapter->dev;
    client->dev.bus = &i2c_bus_type;
    if (client->driver)
        client->dev.driver = &client->driver->driver;
    if (client->driver && !is_newstyle_driver(client->driver)) {
        client->dev.release = i2c_client_release;
        client->dev.uevent_suppress = 1;
    } else
        client->dev.release = i2c_client_dev_release;

    snprintf(&client->dev.bus_id[0], sizeof(client->dev.bus_id),
        "%d-%04x", i2c_adapter_id(adapter), client->addr);
    dev_dbg(&adapter->dev, "client [%s] registered with bus id %s\n",
        client->name, client->dev.bus_id);
    res = device_register(&client->dev);
    if (adapter->client_register)  {
        if (adapter->client_register(client)) {
            dev_dbg(&adapter->dev, "client_register "
                "failed for client [%s] at 0x%02x\n",
                client->name, client->addr);
        }
    }
}

设备驱动参考代码

mark

图片来自 https://www.cnblogs.com/lifexy/p/7816324.html

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/fs.h>
#include <asm/uaccess.h>

static unsigned short ignore[]      = { I2C_CLIENT_END };
static unsigned short normal_addr[] = { 0x50, I2C_CLIENT_END }; /* 地址值是7位 */
                                        /* 改为0x60的话, 由于不存在设备地址为0x60的设备, 所以at24cxx_detect不被调用 */

static unsigned short force_addr[] = {ANY_I2C_BUS, 0x60, I2C_CLIENT_END};
static unsigned short * forces[] = {force_addr, NULL};
                                        
static struct i2c_client_address_data addr_data = {
    .normal_i2c = normal_addr,  /* 要发出S信号和设备地址并得到ACK信号,才能确定存在这个设备 */
    .probe      = ignore,
    .ignore     = ignore,
    //.forces     = forces, /* 强制认为存在这个设备 */
};

static struct i2c_driver at24cxx_driver;


static int major;
static struct class *cls;
struct i2c_client *at24cxx_client;

static ssize_t at24cxx_read(struct file *file, char __user *buf, size_t size, loff_t * offset)
{
    unsigned char address;
    unsigned char data;
    struct i2c_msg msg[2];
    int ret;
    
    /* address = buf[0] 
     * data    = buf[1]
     */
    if (size != 1)
        return -EINVAL;
    
    copy_from_user(&address, buf, 1);

    /* 数据传输三要素: 源,目的,长度 */

    /* 读AT24CXX时,要先把要读的存储空间的地址发给它 */
    msg[0].addr  = at24cxx_client->addr;  /* 目的 */
    msg[0].buf   = &address;              /* 源 */
    msg[0].len   = 1;                     /* 地址=1 byte */
    msg[0].flags = 0;                     /* 表示写 */

    /* 然后启动读操作 */
    msg[1].addr  = at24cxx_client->addr;  /* 源 */
    msg[1].buf   = &data;                 /* 目的 */
    msg[1].len   = 1;                     /* 数据=1 byte */
    msg[1].flags = I2C_M_RD;                     /* 表示读 */


    ret = i2c_transfer(at24cxx_client->adapter, msg, 2);
    if (ret == 2)
    {
        copy_to_user(buf, &data, 1);
        return 1;
    }
    else
        return -EIO;
}

static ssize_t at24cxx_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
    unsigned char val[2];
    struct i2c_msg msg[1];
    int ret;
    
    /* address = buf[0] 
     * data    = buf[1]
     */
    if (size != 2)
        return -EINVAL;
    
    copy_from_user(val, buf, 2);

    /* 数据传输三要素: 源,目的,长度 */
    msg[0].addr  = at24cxx_client->addr;  /* 目的 */
    msg[0].buf   = val;                   /* 源 */
    msg[0].len   = 2;                     /* 地址+数据=2 byte */
    msg[0].flags = 0;                     /* 表示写 */

    ret = i2c_transfer(at24cxx_client->adapter, msg, 1);
    if (ret == 1)
        return 2;
    else
        return -EIO;
}


static struct file_operations at24cxx_fops = {
    .owner = THIS_MODULE,
    .read  = at24cxx_read,
    .write = at24cxx_write,
};

static int at24cxx_detect(struct i2c_adapter *adapter, int address, int kind)
{   
    printk("at24cxx_detect\n");

    /* 构构一个i2c_client结构体: 以后收改数据时会用到它 */
    at24cxx_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
    at24cxx_client->addr    = address;
    at24cxx_client->adapter = adapter;
    at24cxx_client->driver  = &at24cxx_driver;
    strcpy(at24cxx_client->name, "at24cxx");
    i2c_attach_client(at24cxx_client);
    
    major = register_chrdev(0, "at24cxx", &at24cxx_fops);

    cls = class_create(THIS_MODULE, "at24cxx");
    class_device_create(cls, NULL, MKDEV(major, 0), NULL, "at24cxx"); /* /dev/at24cxx */
    
    return 0;
}

static int at24cxx_attach(struct i2c_adapter *adapter)
{
    return i2c_probe(adapter, &addr_data, at24cxx_detect);
}

static int at24cxx_detach(struct i2c_client *client)
{
    printk("at24cxx_detach\n");
    class_device_destroy(cls, MKDEV(major, 0));
    class_destroy(cls);
    unregister_chrdev(major, "at24cxx");

    i2c_detach_client(client);
    kfree(i2c_get_clientdata(client));

    return 0;
}


/* 1. 分配一个i2c_driver结构体 */
/* 2. 设置i2c_driver结构体 */
static struct i2c_driver at24cxx_driver = {
    .driver = {
        .name   = "at24cxx",
    },
    .attach_adapter = at24cxx_attach,
    .detach_client  = at24cxx_detach,
};

static int at24cxx_init(void)
{
    i2c_add_driver(&at24cxx_driver);
    return 0;
}

static void at24cxx_exit(void)
{
    i2c_del_driver(&at24cxx_driver);
}

module_init(at24cxx_init);
module_exit(at24cxx_exit);

MODULE_LICENSE("GPL");

转载于:https://www.cnblogs.com/zongzi10010/p/10334607.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值