Linux设备驱动之3.4.2内核下的I2C驱动

来源:互联网 发布:虚荣虚拟摇杆软件 编辑:程序博客网 时间:2024/06/01 20:07
  1. 框架
    1.1 硬件协议简介
    1.2 驱动框架
    1.3 bus-drv-dev模型及写程序
    a. 设备的4种构建方法

详情参照:linux-3.4.2\Documentation\i2c:instantiating-devices:
以下摘取部分

Method 1: Declare the I2C devices by bus numberMethod 2: Instantiate the devices explicitlystatic struct i2c_board_info sfe4001_hwmon_info = {    I2C_BOARD_INFO("max6647", 0x4e),};int sfe4001_init(struct efx_nic *efx){    (...)    efx->board_info.hwmon_client =        i2c_new_device(&efx->i2c_adap, &sfe4001_hwmon_info);    (...)}static const unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };static int __devinit usb_hcd_nxp_probe(struct platform_device *pdev){    (...)    struct i2c_adapter *i2c_adap;    struct i2c_board_info i2c_info;    (...)    i2c_adap = i2c_get_adapter(2);    memset(&i2c_info, 0, sizeof(struct i2c_board_info));    strlcpy(i2c_info.type, "isp1301_nxp", I2C_NAME_SIZE);    isp1301_i2c_client = i2c_new_probed_device(i2c_adap, &i2c_info,                           normal_i2c, NULL);    i2c_put_adapter(i2c_adap);    (...)}Method 3: Probe an I2C bus for certain devicesMethod 4: Instantiate from user-spaceExample:# echo eeprom 0x50 > /sys/bus/i2c/devices/i2c-3/new_device

对上面该Document文档进行解析:

a.1 定义一个i2c_board_info, 里面有:名字, 设备地址
然后i2c_register_board_info(busnum, …) (把它们放入__i2c_board_list链表)
list_add_tail(&devinfo->list, &__i2c_board_list);

链表何时使用:
i2c_register_adapter > i2c_scan_static_board_info > i2c_new_device

使用限制:必须在 i2c_register_adapter 之前 i2c_register_board_info
所以:不适合我们动态加载insmod

a.2 直接i2c_new_device, i2c_new_probed_device : probed:创建已经被枚举被识别的设备!
a.2.1 i2c_new_device : 强制认为设备肯定存在
a.2.2
i**2c_new_probed_device :对于”已经识别出来的设备”(probed_device),才会创建(“new”)**
i2c_new_probed_device :对addr_list里面的每一项调用probe函数,如果存在的话,才进行new_device
probe(adap, addr_list[i]) /* 确定设备是否真实存在 */
info->addr = addr_list[i];/遍历链表/
i2c_new_device(adap, info);

a.4 从用户空间创建设备
创建设备
echo at24c08 0x50 > /sys/class/i2c-adapter/i2c-0/new_device
导致i2c_new_device被调用

删除设备
echo 0x50 > /sys/class/i2c-adapter/i2c-0/delete_device
导致i2c_unregister_device

a.3
前面的3种方法都要事先确定适配器(I2C总线,I2C控制器)
问1:如果我事先并不知道这个I2C设备在哪个适配器上,怎么办?
答1:去class表示的所有的适配器上查找
问2:有上一些I2C设备的地址是一样,怎么继续分配它是哪一款?
答2:用detect函数

static struct i2c_driver at24cxx_driver = {    .class  = I2C_CLASS_HWMON,      /* 表示去哪些适配器上找设备 */    .driver    = {        .name    = "100ask",        .owner    = THIS_MODULE,    },    .probe        = at24cxx_probe,    .remove        = __devexit_p(at24cxx_remove),    .id_table    = at24cxx_id_table,    .detect     = at24cxx_detect,  /* 用这个函数来检测设备确实存在 */    .address_list    = addr_list,   /* 这些设备的地址 */};

例如at24xx的addr_list
static const unsigned short addr_list[] = { 0x60, 0x50, I2C_CLIENT_END };

解析:
去”class表示的这一类”I2C适配器,用”detect函数”来确定能否找到”address_list里的设备”,
如果能找到就调用i2c_new_device来注册i2c_client, 这会和i2c_driver的id_table比较,
如果匹配,调用probe

分析函数流程!
i2c_add_driver
i2c_register_driver
a. 将at24cxx_driver放入i2c_bus_type的drv链表
并且从dev链表里取出能匹配的i2c_client并调用probe
/* When registration returns, the driver core
* will have called probe() for all matching-but-unbound devices.
*/
driver_register

b. 对于每一个适配器,调用__process_new_driver
对于每一个适配器,调用它的函数确定address_list里的设备是否存在
如果存在,再调用detect进一步确定、设置,然后i2c_new_device

      /* Walk the adapters that are already present */        i2c_for_each_dev(driver, __process_new_driver);            __process_new_driver                i2c_do_add_adapter                    /* Detect supported devices on that bus, and instantiate them */                    i2c_detect(adap, driver);                        for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) {                            err = i2c_detect_address(temp_client, driver);                                         /* Make sure there is something at this address */                                        /* 判断这个设备是否存在:简单的发出S信号确定有ACK */                                        if (!i2c_default_probe(adapter, addr))                                            return 0;                                        memset(&info, 0, sizeof(struct i2c_board_info));                                        info.addr = addr;                                           // 设置info.type                                        err = driver->detect(temp_client, &info);                                        i2c_new_device

附上方法三(即是a3中对应的)代码:

at24cxx_drv.c:

#include <linux/kernel.h>#include <linux/module.h>#include <linux/platform_device.h>#include <linux/i2c.h>#include <linux/err.h>#include <linux/regmap.h>#include <linux/slab.h>static int __devinit at24cxx_probe(struct i2c_client *client,                  const struct i2c_device_id *id){    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);    return 0;}static int __devexit at24cxx_remove(struct i2c_client *client){    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);    return 0;}static const struct i2c_device_id at24cxx_id_table[] = {    { "at24c08", 0 },    {}};static int at24cxx_detect(struct i2c_client *client,               struct i2c_board_info *info){    /* 能运行到这里, 表示该addr的设备是存在的     * 但是有些设备单凭地址无法分辨(A芯片的地址是0x50, B芯片的地址也是0x50)     * 还需要进一步读写I2C设备来分辨是哪款芯片     * detect就是用来进一步分辨这个芯片是哪一款,并且设置info->type     */    printk("at24cxx_detect : addr = 0x%x\n", client->addr);    /* 进一步判断是哪一款 */    strlcpy(info->type, "at24c08", I2C_NAME_SIZE);    return 0;}static const unsigned short addr_list[] = { 0x60, 0x50, I2C_CLIENT_END };/* 1. 分配/设置i2c_driver */static struct i2c_driver at24cxx_driver = {    .class  = I2C_CLASS_HWMON, /* 表示去哪些适配器上找设备 */    .driver    = {        .name    = "100ask",        .owner    = THIS_MODULE,    },    .probe        = at24cxx_probe,    .remove        = __devexit_p(at24cxx_remove),    .id_table    = at24cxx_id_table,    .detect     = at24cxx_detect,  /* 用这个函数来检测设备确实存在 */    .address_list    = addr_list,   /* 这些设备的地址 */};static int at24cxx_drv_init(void){    /* 2. 注册i2c_driver */    i2c_add_driver(&at24cxx_driver);    return 0;}static void at24cxx_drv_exit(void){    i2c_del_driver(&at24cxx_driver);}module_init(at24cxx_drv_init);module_exit(at24cxx_drv_exit);MODULE_LICENSE("GPL");

总线:
1、SMBUS:system manager bus
2、 i2c_trasfer

 系统推荐我们用SMBUS,看下官方提供的Doucument

SMBus Protocol Summary======================The following is a summary of the SMBus protocol. It applies toall revisions of the protocol (1.0, 1.1, and 2.0).Certain protocol features which are not supported bythis package are briefly described at the end of this document.Some adapters understand only the SMBus (System Management Bus) protocol,which is a subset from the I2C protocol. Fortunately, many devices useonly the same subset, which makes it possible to put them on an SMBus.If you write a driver for some I2C device, please try to use the SMBuscommands if at all possible (if the device uses only that subset of theI2C protocol). This makes it possible to use the device driver on bothSMBus adapters and I2C adapters (the SMBus command set is automaticallytranslated to I2C on I2C adapters, but plain I2C commands can not behandled at all on most pure SMBus adapters).Below is a list of SMBus protocol operations, and the functions executingthem.  Note that the names used in the SMBus protocol specifications usuallydon't match these function names.  For some of the operations which pass asingle data byte, the functions using SMBus protocol operation names executea different protocol operation entirely.Key to symbols==============S     (1 bit) : Start bitP     (1 bit) : Stop bitRd/Wr (1 bit) : Read/Write bit. Rd equals 1, Wr equals 0.A, NA (1 bit) : Accept and reverse accept bit. Addr  (7 bits): I2C 7 bit address. Note that this can be expanded as usual to                 get a 10 bit I2C address.Comm  (8 bits): Command byte, a data byte which often selects a register on                the device.Data  (8 bits): A plain data byte. Sometimes, I write DataLow, DataHigh                for 16 bit data.Count (8 bits): A data byte containing the length of a block operation.[..]: Data sent by I2C device, as opposed to data sent by the host adapter.SMBus Quick Command===================This sends a single bit to the device, at the place of the Rd/Wr bit.A Addr Rd/Wr [A] PSMBus Receive Byte:  i2c_smbus_read_byte()==========================================This reads a single byte from a device, without specifying a deviceregister. Some devices are so simple that this interface is enough; forothers, it is a shorthand if you want to read the same register as inthe previous SMBus command.S Addr Rd [A] [Data] NA PSMBus Send Byte:  i2c_smbus_write_byte()========================================This operation is the reverse of Receive Byte: it sends a single byteto a device.  See Receive Byte for more information.S Addr Wr [A] Data [A] PSMBus Read Byte:  i2c_smbus_read_byte_data()============================================This reads a single byte from a device, from a designated register.The register is specified through the Comm byte.S Addr Wr [A] Comm [A] S Addr Rd [A] [Data] NA PSMBus Read Word:  i2c_smbus_read_word_data()============================================This operation is very like Read Byte; again, data is read from adevice, from a designated register that is specified through the Commbyte. But this time, the data is a complete word (16 bits).S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA PNote the convenience function i2c_smbus_read_word_swapped isavailable for reads where the two data bytes are the other wayaround (not SMBus compliant, but very popular.)SMBus Write Byte:  i2c_smbus_write_byte_data()==============================================This writes a single byte to a device, to a designated register. Theregister is specified through the Comm byte. This is the opposite ofthe Read Byte operation.S Addr Wr [A] Comm [A] Data [A] PSMBus Write Word:  i2c_smbus_write_word_data()==============================================This is the opposite of the Read Word operation. 16 bitsof data is written to a device, to the designated register that isspecified through the Comm byte. S Addr Wr [A] Comm [A] DataLow [A] DataHigh [A] PNote the convenience function i2c_smbus_write_word_swapped isavailable for writes where the two data bytes are the other wayaround (not SMBus compliant, but very popular.)SMBus Process Call:  i2c_smbus_process_call()=============================================This command selects a device register (through the Comm byte), sends16 bits of data to it, and reads 16 bits of data in return.S Addr Wr [A] Comm [A] DataLow [A] DataHigh [A]                              S Addr Rd [A] [DataLow] A [DataHigh] NA PSMBus Block Read:  i2c_smbus_read_block_data()==============================================This command reads a block of up to 32 bytes from a device, from a designated register that is specified through the Comm byte. The amountof data is specified by the device in the Count byte.S Addr Wr [A] Comm [A]            S Addr Rd [A] [Count] A [Data] A [Data] A ... A [Data] NA PSMBus Block Write:  i2c_smbus_write_block_data()================================================The opposite of the Block Read command, this writes up to 32 bytes to a device, to a designated register that is specified through theComm byte. The amount of data is specified in the Count byte.S Addr Wr [A] Comm [A] Count [A] Data [A] Data [A] ... [A] Data [A] PSMBus Block Write - Block Read Process Call===========================================SMBus Block Write - Block Read Process Call was introduced inRevision 2.0 of the specification.This command selects a device register (through the Comm byte), sends1 to 31 bytes of data to it, and reads 1 to 31 bytes of data in return.S Addr Wr [A] Comm [A] Count [A] Data [A] ...                             S Addr Rd [A] [Count] A [Data] ... A PSMBus Host Notify=================This command is sent from a SMBus device acting as a master to theSMBus host acting as a slave.It is the same form as Write Word, with the command code replaced by thealerting device's address.[S] [HostAddr] [Wr] A [DevAddr] A [DataLow] A [DataHigh] A [P]Packet Error Checking (PEC)===========================Packet Error Checking was introduced in Revision 1.1 of the specification.PEC adds a CRC-8 error-checking byte to transfers using it, immediatelybefore the terminating STOP.Address Resolution Protocol (ARP)=================================The Address Resolution Protocol was introduced in Revision 2.0 ofthe specification. It is a higher-layer protocol which uses themessages above.ARP adds device enumeration and dynamic address assignment tothe protocol. All ARP communications use slave address 0x61 andrequire PEC checksums.SMBus Alert===========SMBus Alert was introduced in Revision 1.0 of the specification.The SMBus alert protocol allows several SMBus slave devices to share asingle interrupt pin on the SMBus master, while still allowing the masterto know which slave triggered the interrupt.This is implemented the following way in the Linux kernel:* I2C bus drivers which support SMBus alert should call  i2c_setup_smbus_alert() to setup SMBus alert support.* I2C drivers for devices which can trigger SMBus alerts should implement  the optional alert() callback.I2C Block Transactions======================The following I2C block transactions are supported by theSMBus layer and are described here for completeness.They are *NOT* defined by the SMBus specification.I2C block transactions do not limit the number of bytes transferredbut the SMBus layer places a limit of 32 bytes.I2C Block Read:  i2c_smbus_read_i2c_block_data()================================================This command reads a block of bytes from a device, from a designated register that is specified through the Comm byte.S Addr Wr [A] Comm [A]            S Addr Rd [A] [Data] A [Data] A ... A [Data] NA PI2C Block Read (2 Comm bytes)=============================This command reads a block of bytes from a device, from a designated register that is specified through the two Comm bytes.S Addr Wr [A] Comm1 [A] Comm2 [A]            S Addr Rd [A] [Data] A [Data] A ... A [Data] NA PI2C Block Write:  i2c_smbus_write_i2c_block_data()==================================================The opposite of the Block Read command, this writes bytes to a device, to a designated register that is specified through theComm byte. Note that command lengths of 0, 2, or more bytes aresupported as they are indistinguishable from data.S Addr Wr [A] Comm [A] Data [A] Data [A] ... [A] Data [A] P

b. 驱动的写法

2、 完善设备驱动程序

at24cxx_dev.c:

#include <linux/kernel.h>#include <linux/module.h>#include <linux/platform_device.h>#include <linux/i2c.h>#include <linux/err.h>#include <linux/regmap.h>#include <linux/slab.h>static struct i2c_board_info at24cxx_info = {       I2C_BOARD_INFO("at24c08", 0x50),};static struct i2c_client *at24cxx_client;static int at24cxx_dev_init(void){    struct i2c_adapter *i2c_adap;    i2c_adap = i2c_get_adapter(0);    at24cxx_client = i2c_new_device(i2c_adap, &at24cxx_info);    i2c_put_adapter(i2c_adap);    return 0;}static void at24cxx_dev_exit(void){    i2c_unregister_device(at24cxx_client);}module_init(at24cxx_dev_init);module_exit(at24cxx_dev_exit);MODULE_LICENSE("GPL");

at24cxx_drv.c:

#include <linux/kernel.h>#include <linux/module.h>#include <linux/platform_device.h>#include <linux/i2c.h>#include <linux/err.h>#include <linux/regmap.h>#include <linux/slab.h>#include <linux/fs.h>#include <asm/uaccess.h>static int major;static struct class *class;static struct i2c_client *at24cxx_client;/* 传入: buf[0] : addr * 输出: buf[0] : data */static ssize_t at24cxx_read(struct file * file, char __user *buf, size_t count, loff_t *off){    unsigned char addr, data;    copy_from_user(&addr, buf, 1);    data = i2c_smbus_read_byte_data(at24cxx_client, addr);    copy_to_user(buf, &data, 1);    return 1;}/* buf[0] : addr * buf[1] : data */static ssize_t at24cxx_write(struct file *file, const char __user *buf, size_t count, loff_t *off){    unsigned char ker_buf[2];    unsigned char addr, data;    copy_from_user(ker_buf, buf, 2);    addr = ker_buf[0];    data = ker_buf[1];    printk("addr = 0x%02x, data = 0x%02x\n", addr, data);    if (!i2c_smbus_write_byte_data(at24cxx_client, addr, data))        return 2;    else        return -EIO;   }static struct file_operations at24cxx_fops = {    .owner = THIS_MODULE,    .read  = at24cxx_read,    .write = at24cxx_write,};static int __devinit at24cxx_probe(struct i2c_client *client,                  const struct i2c_device_id *id){    at24cxx_client = client;    //printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);    major = register_chrdev(0, "at24cxx", &at24cxx_fops);    class = class_create(THIS_MODULE, "at24cxx");    device_create(class, NULL, MKDEV(major, 0), NULL, "at24cxx"); /* /dev/at24cxx */    return 0;}static int __devexit at24cxx_remove(struct i2c_client *client){    //printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);    device_destroy(class, MKDEV(major, 0));    class_destroy(class);    unregister_chrdev(major, "at24cxx");    return 0;}static const struct i2c_device_id at24cxx_id_table[] = {    { "at24c08", 0 },    {}};/* 1. 分配/设置i2c_driver */static struct i2c_driver at24cxx_driver = {    .driver    = {        .name    = "100ask",        .owner    = THIS_MODULE,    },    .probe        = at24cxx_probe,    .remove        = __devexit_p(at24cxx_remove),    .id_table    = at24cxx_id_table,};static int at24cxx_drv_init(void){    /* 2. 注册i2c_driver */    i2c_add_driver(&at24cxx_driver);    return 0;}static void at24cxx_drv_exit(void){    i2c_del_driver(&at24cxx_driver);}module_init(at24cxx_drv_init);module_exit(at24cxx_drv_exit);MODULE_LICENSE("GPL");

i2c_test.c:

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>/* i2c_test r addr * i2c_test w addr val */void print_usage(char *file){    printf("%s r addr\n", file);    printf("%s w addr val\n", file);}int main(int argc, char **argv){    int fd;    unsigned char buf[2];    if ((argc != 3) && (argc != 4))    {        print_usage(argv[0]);        return -1;    }    fd = open("/dev/at24cxx", O_RDWR);    if (fd < 0)    {        printf("can't open /dev/at24cxx\n");        return -1;    }    if (strcmp(argv[1], "r") == 0)    {        buf[0] = strtoul(argv[2], NULL, 0);        read(fd, buf, 1);        printf("data: %c, %d, 0x%2x\n", buf[0], buf[0], buf[0]);    }    else if ((strcmp(argv[1], "w") == 0) && (argc == 4))    {        buf[0] = strtoul(argv[2], NULL, 0);        buf[1] = strtoul(argv[3], NULL, 0);        if (write(fd, buf, 2) != 2)            printf("write err, addr = 0x%02x, data = 0x%02x\n", buf[0], buf[1]);    }    else    {        print_usage(argv[0]);        return -1;    }    return 0;}


三、不自己写驱动,直接访问
参考文档:dev-interface

Usually, i2c devices are controlled by a kernel driver. But it is alsopossible to access all devices on an adapter from userspace, throughthe /dev interface. You need to load module i2c-dev for this.Each registered i2c adapter gets a number, counting from 0. You canexamine /sys/class/i2c-dev/ to see what number corresponds to which adapter.Alternatively, you can run "i2cdetect -l" to obtain a formated list of alli2c adapters present on your system at a given time. i2cdetect is part ofthe i2c-tools package.I2C device files are character device files with major device number 89and a minor device number corresponding to the number assigned as explained above. They should be called "i2c-%d" (i2c-0, i2c-1, ..., i2c-10, ...). All 256 minor device numbers are reserved for i2c.C example=========So let's say you want to access an i2c adapter from a C program. Thefirst thing to do is "#include <linux/i2c-dev.h>". Please note thatthere are two files named "i2c-dev.h" out there, one is distributedwith the Linux kernel and is meant to be included from kerneldriver code, the other one is distributed with i2c-tools and ismeant to be included from user-space programs. You obviously wantthe second one here.Now, you have to decide which adapter you want to access. You shouldinspect /sys/class/i2c-dev/ or run "i2cdetect -l" to decide this.Adapter numbers are assigned somewhat dynamically, so you can notassume much about them. They can even change from one boot to the next.Next thing, open the device file, as follows:  int file;  int adapter_nr = 2; /* probably dynamically determined */  char filename[20];  snprintf(filename, 19, "/dev/i2c-%d", adapter_nr);  file = open(filename, O_RDWR);  if (file < 0) {    /* ERROR HANDLING; you can check errno to see what went wrong */    exit(1);  }When you have opened the device, you must specify with what deviceaddress you want to communicate:  int addr = 0x40; /* The I2C address */  if (ioctl(file, I2C_SLAVE, addr) < 0) {    /* ERROR HANDLING; you can check errno to see what went wrong */    exit(1);  }Well, you are all set up now. You can now use SMBus commands or plainI2C to communicate with your device. SMBus commands are preferred ifthe device supports them. Both are illustrated below.  __u8 register = 0x10; /* Device register to access */  __s32 res;  char buf[10];  /* Using SMBus commands */  res = i2c_smbus_read_word_data(file, register);  if (res < 0) {    /* ERROR HANDLING: i2c transaction failed */  } else {    /* res contains the read word */  }  /* Using I2C Write, equivalent of      i2c_smbus_write_word_data(file, register, 0x6543) */  buf[0] = register;  buf[1] = 0x43;  buf[2] = 0x65;  if (write(file, buf, 3) ! =3) {    /* ERROR HANDLING: i2c transaction failed */  }  /* Using I2C Read, equivalent of i2c_smbus_read_byte(file) */  if (read(file, buf, 1) != 1) {    /* ERROR HANDLING: i2c transaction failed */  } else {    /* buf[0] contains the read byte */  }Note that only a subset of the I2C and SMBus protocols can be achieved bythe means of read() and write() calls. In particular, so-called combinedtransactions (mixing read and write messages in the same transaction)aren't supported. For this reason, this interface is almost never used byuser-space programs.IMPORTANT: because of the use of inline functions, you *have* to use'-O' or some variation when you compile your program!Full interface description==========================The following IOCTLs are defined:ioctl(file, I2C_SLAVE, long addr)  Change slave address. The address is passed in the 7 lower bits of the  argument (except for 10 bit addresses, passed in the 10 lower bits in this  case).ioctl(file, I2C_TENBIT, long select)  Selects ten bit addresses if select not equals 0, selects normal 7 bit  addresses if select equals 0. Default 0.  This request is only valid  if the adapter has I2C_FUNC_10BIT_ADDR.ioctl(file, I2C_PEC, long select)  Selects SMBus PEC (packet error checking) generation and verification  if select not equals 0, disables if select equals 0. Default 0.  Used only for SMBus transactions.  This request only has an effect if the  the adapter has I2C_FUNC_SMBUS_PEC; it is still safe if not, it just  doesn't have any effect.ioctl(file, I2C_FUNCS, unsigned long *funcs)  Gets the adapter functionality and puts it in *funcs.ioctl(file, I2C_RDWR, struct i2c_rdwr_ioctl_data *msgset)  Do combined read/write transaction without stop in between.  Only valid if the adapter has I2C_FUNC_I2C.  The argument is  a pointer to a  struct i2c_rdwr_ioctl_data {      struct i2c_msg *msgs;  /* ptr to array of simple messages */      int nmsgs;             /* number of messages to exchange */  }  The msgs[] themselves contain further pointers into data buffers.  The function will write or read data to or from that buffers depending  on whether the I2C_M_RD flag is set in a particular message or not.  The slave address and whether to use ten bit address mode has to be  set in each message, overriding the values set with the above ioctl's.ioctl(file, I2C_SMBUS, struct i2c_smbus_ioctl_data *args)  Not meant to be called  directly; instead, use the access functions  below.You can do plain i2c transactions by using read(2) and write(2) calls.You do not need to pass the address byte; instead, set it throughioctl I2C_SLAVE before you try to access the device.You can do SMBus level transactions (see documentation file smbus-protocol for details) through the following functions:  __s32 i2c_smbus_write_quick(int file, __u8 value);  __s32 i2c_smbus_read_byte(int file);  __s32 i2c_smbus_write_byte(int file, __u8 value);  __s32 i2c_smbus_read_byte_data(int file, __u8 command);  __s32 i2c_smbus_write_byte_data(int file, __u8 command, __u8 value);  __s32 i2c_smbus_read_word_data(int file, __u8 command);  __s32 i2c_smbus_write_word_data(int file, __u8 command, __u16 value);  __s32 i2c_smbus_process_call(int file, __u8 command, __u16 value);  __s32 i2c_smbus_read_block_data(int file, __u8 command, __u8 *values);  __s32 i2c_smbus_write_block_data(int file, __u8 command, __u8 length,                                    __u8 *values);All these transactions return -1 on failure; you can read errno to seewhat happened. The 'write' transactions return 0 on success; the'read' transactions return the read value, except for read_block, whichreturns the number of values read. The block buffers need not be longerthan 32 bytes.The above functions are all inline functions, that resolve to calls tothe i2c_smbus_access function, that on its turn calls a specific ioctlwith the data in a specific format. Read the source code if youwant to know what happens behind the screens.Implementation details======================For the interested, here's the code flow which happens inside the kernelwhen you use the /dev interface to I2C:1* Your program opens /dev/i2c-N and calls ioctl() on it, as described insection "C example" above.2* These open() and ioctl() calls are handled by the i2c-dev kerneldriver: see i2c-dev.c:i2cdev_open() and i2c-dev.c:i2cdev_ioctl(),respectively. You can think of i2c-dev as a generic I2C chip driverthat can be programmed from user-space.3* Some ioctl() calls are for administrative tasks and are handled byi2c-dev directly. Examples include I2C_SLAVE (set the address of thedevice you want to access) and I2C_PEC (enable or disable SMBus errorchecking on future transactions.)4* Other ioctl() calls are converted to in-kernel function calls byi2c-dev. Examples include I2C_FUNCS, which queries the I2C adapterfunctionality using i2c.h:i2c_get_functionality(), and I2C_SMBUS, whichperforms an SMBus transaction using i2c-core.c:i2c_smbus_xfer().The i2c-dev driver is responsible for checking all the parameters thatcome from user-space for validity. After this point, there is nodifference between these calls that came from user-space through i2c-devand calls that would have been performed by kernel I2C chip driversdirectly. This means that I2C bus drivers don't need to implementanything special to support access from user-space.5* These i2c-core.c/i2c.h functions are wrappers to the actualimplementation of your I2C bus driver. Each adapter must declarecallback functions implementing these standard calls.i2c.h:i2c_get_functionality() calls i2c_adapter.algo->functionality(),while i2c-core.c:i2c_smbus_xfer() calls eitheradapter.algo->smbus_xfer() if it is implemented, or if not,i2c-core.c:i2c_smbus_xfer_emulated() which in turn callsi2c_adapter.algo->master_xfer().After your I2C bus driver has processed these requests, execution runsup the call chain, with almost no processing done, except by i2c-dev topackage the returned data, if any, in suitable format for the ioctl.
Device Drivers     I2C support        <*>   I2C device interface
  1. 编写”总线(适配器adapter)”驱动
Device Drivers     I2C support         I2C Hardware Bus support             < > S3C2410 I2C Drivernfs 30000000 192.168.1.123:/work/nfs_root/uImage_noi2cbus; bootm 30000000

1、调用s3c2440_i2c_start时候:
设置状态: s3c2440_i2c_xfer_data.state = STATE_START;
发出Start信号: s3c2440_i2c_regs->iicstat = 0xb0; // 主机接收,启动
s3c2440_i2c_regs->iicstat = 0xf0; // 主机发送,启动
同时将设备地址发送出去:
s3c2440_i2c_regs->iicds = s3c2440_i2c_xfer_data.msgs->addr << 1;
2、上面start 发送完之后产生一个中断,进入中断处理函数 ;
①:如果没有ACK,返回错误
②:如果是最后一个数据最后一个消息时候,stop。枚举设备时,只发出Start信号再发出信号地址,得到信号地址之后就返回了
③:进入下一个状态,读与写
写:
1、如果没有ACK, 返回错误
2、如果不是最后一个数据的话,将数据发送出去
3、如果是最后一个数据,但不是最后一个消息的话,开始处理下一个消息,发出start信号
4、如果是最后一个数据最后一个消息的话成功
读:
1、一开始直接跳到goto next_read;是因为现在只是发出设备地址,而没有数据
2、具体操作看代码了

3、什么时候产生中断???
答:发出START产生中断,发出DEVICE ADDRESS还有R/W之后得到一个ACK也会尝试一个中断。
发出WORD ADDRESS(要读写的地址)得到一个ACK再产生一个中断
发出START又产生一个中断,DEVICE ADDRESS READ又产生一个中断
DATAn之后又产生一个中断

代码:

#include <linux/kernel.h>#include <linux/module.h>#include <linux/i2c.h>#include <linux/init.h>#include <linux/time.h>#include <linux/interrupt.h>#include <linux/delay.h>#include <linux/errno.h>#include <linux/err.h>#include <linux/platform_device.h>#include <linux/pm_runtime.h>#include <linux/clk.h>#include <linux/cpufreq.h>#include <linux/slab.h>#include <linux/io.h>#include <linux/of_i2c.h>#include <linux/of_gpio.h>#include <plat/gpio-cfg.h>#include <mach/regs-gpio.h>#include <asm/irq.h>#include <plat/regs-iic.h>#include <plat/iic.h>//#define PRINTK printk#define PRINTK(...)enum s3c24xx_i2c_state {    STATE_IDLE,    STATE_START,    STATE_READ,    STATE_WRITE,    STATE_STOP};struct s3c2440_i2c_regs {    unsigned int iiccon;    unsigned int iicstat;    unsigned int iicadd;    unsigned int iicds;    unsigned int iiclc;};struct s3c2440_i2c_xfer_data {    struct i2c_msg *msgs;    int msn_num;    int cur_msg;    int cur_ptr;    int state;    int err;    wait_queue_head_t wait;};static struct s3c2440_i2c_xfer_data s3c2440_i2c_xfer_data;static struct s3c2440_i2c_regs *s3c2440_i2c_regs;static void s3c2440_i2c_start(void){    s3c2440_i2c_xfer_data.state = STATE_START;    if (s3c2440_i2c_xfer_data.msgs->flags & I2C_M_RD) /* 读 */    {        s3c2440_i2c_regs->iicds         = s3c2440_i2c_xfer_data.msgs->addr << 1;        s3c2440_i2c_regs->iicstat      = 0xb0;    // 主机接收,启动    }    else /* 写 */    {        s3c2440_i2c_regs->iicds         = s3c2440_i2c_xfer_data.msgs->addr << 1;        s3c2440_i2c_regs->iicstat    = 0xf0;         // 主机发送,启动    }}static void s3c2440_i2c_stop(int err){    s3c2440_i2c_xfer_data.state = STATE_STOP;    s3c2440_i2c_xfer_data.err   = err;    PRINTK("STATE_STOP, err = %d\n", err);    if (s3c2440_i2c_xfer_data.msgs->flags & I2C_M_RD) /* 读 */    {        // 下面两行恢复I2C操作,发出P信号        s3c2440_i2c_regs->iicstat = 0x90;        s3c2440_i2c_regs->iiccon  = 0xaf;        ndelay(50);  // 等待一段时间以便P信号已经发出    }    else /* 写 */    {        // 下面两行用来恢复I2C操作,发出P信号        s3c2440_i2c_regs->iicstat = 0xd0;        s3c2440_i2c_regs->iiccon  = 0xaf;        ndelay(50);  // 等待一段时间以便P信号已经发出    }    /* 唤醒 */    wake_up(&s3c2440_i2c_xfer_data.wait);}static int s3c2440_i2c_xfer(struct i2c_adapter *adap,            struct i2c_msg *msgs, int num){    unsigned long timeout;    /* 把num个msg的I2C数据发送出去/读进来 */    s3c2440_i2c_xfer_data.msgs    = msgs;    s3c2440_i2c_xfer_data.msn_num = num;    s3c2440_i2c_xfer_data.cur_msg = 0;    s3c2440_i2c_xfer_data.cur_ptr = 0;    s3c2440_i2c_xfer_data.err     = -ENODEV;    s3c2440_i2c_start();    /* 休眠 */    /*Garmen:等待直到(s3c2440_i2c_xfer_data.state == STATE_STOP)这个条件成立*/    timeout = wait_event_timeout(s3c2440_i2c_xfer_data.wait, (s3c2440_i2c_xfer_data.state == STATE_STOP), HZ * 5);    if (0 == timeout)    {        /*超时退出*/        printk("s3c2440_i2c_xfer time out\n");        return -ETIMEDOUT;    }    else    {        return s3c2440_i2c_xfer_data.err;    }}static u32 s3c2440_i2c_func(struct i2c_adapter *adap){    return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;}static const struct i2c_algorithm s3c2440_i2c_algo = {//    .smbus_xfer     = ,    .master_xfer    = s3c2440_i2c_xfer,    .functionality    = s3c2440_i2c_func,};/* 1. 分配/设置i2c_adapter */static struct i2c_adapter s3c2440_i2c_adapter = { .name             = "s3c2440_100ask", .algo             = &s3c2440_i2c_algo, .owner          = THIS_MODULE,};static int isLastMsg(void){    return (s3c2440_i2c_xfer_data.cur_msg == s3c2440_i2c_xfer_data.msn_num - 1);}static int isEndData(void){    return (s3c2440_i2c_xfer_data.cur_ptr >= s3c2440_i2c_xfer_data.msgs->len);}static int isLastData(void){    return (s3c2440_i2c_xfer_data.cur_ptr == s3c2440_i2c_xfer_data.msgs->len - 1);}static irqreturn_t s3c2440_i2c_xfer_irq(int irq, void *dev_id){    unsigned int iicSt;    iicSt  = s3c2440_i2c_regs->iicstat;    if(iicSt & 0x8){ printk("Bus arbitration failed\n\r"); }    switch (s3c2440_i2c_xfer_data.state)    {        case STATE_START : /* 发出S和设备地址后,产生中断 */        {            PRINTK("Start\n");            /* 如果没有ACK, 返回错误 */            if (iicSt & S3C2410_IICSTAT_LASTBIT)            {                s3c2440_i2c_stop(-ENODEV);                break;            }            /*Garmen:枚举设备时,只发出Start信号再发出信号地址,得到信号地址之后就返回了。Msg长度是0*/            if (isLastMsg() && isEndData())            {                s3c2440_i2c_stop(0);                break;            }            /* 进入下一个状态 */            if (s3c2440_i2c_xfer_data.msgs->flags & I2C_M_RD) /* 读 */            {                s3c2440_i2c_xfer_data.state = STATE_READ;               /*Garmen:这个是由STATE_START信号进来的,所以*/                goto next_read;            }            else            {                s3c2440_i2c_xfer_data.state = STATE_WRITE;            }           }        case STATE_WRITE:        {            PRINTK("STATE_WRITE\n");            /* 如果没有ACK, 返回错误 */            if (iicSt & S3C2410_IICSTAT_LASTBIT)            {                s3c2440_i2c_stop(-ENODEV);                break;            }            /*Garmen:如果不是最后一个数据的话*/            if (!isEndData())  /* 如果当前msg还有数据要发送 */            {                s3c2440_i2c_regs->iicds = s3c2440_i2c_xfer_data.msgs->buf[s3c2440_i2c_xfer_data.cur_ptr];                s3c2440_i2c_xfer_data.cur_ptr++;                // 将数据写入IICDS后,需要一段时间才能出现在SDA线上                ndelay(50);                   s3c2440_i2c_regs->iiccon = 0xaf;        // 恢复I2C传输                break;                           }            /*如果是最后一个数据,但不是最后一个消息的话*/            else if (!isLastMsg())            {                /* 开始处理下一个消息 */                s3c2440_i2c_xfer_data.msgs++;                s3c2440_i2c_xfer_data.cur_msg++;                s3c2440_i2c_xfer_data.cur_ptr = 0;                s3c2440_i2c_xfer_data.state = STATE_START;                /* 发出START信号和发出设备地址 */                s3c2440_i2c_start();                break;            }            else            {                /* 是最后一个消息的最后一个数据 */                s3c2440_i2c_stop(0);                break;                           }            break;        }        case STATE_READ:        {            PRINTK("STATE_READ\n");            /* Garmen:一旦产生了中断,直接进入STATE_READ,表明已经读到了数据,于是读出数据 */            s3c2440_i2c_xfer_data.msgs->buf[s3c2440_i2c_xfer_data.cur_ptr] = s3c2440_i2c_regs->iicds;                       s3c2440_i2c_xfer_data.cur_ptr++;/*Garmen:这个是从STATE_START信号过来的。这里还无数据*/next_read:            if (!isEndData()) /* 如果数据没读写, 继续发起读操作 */            {                if (isLastData())  /* 如果即将读的数据是最后一个, 不发ack */                {                    s3c2440_i2c_regs->iiccon = 0x2f;   // 恢复I2C传输,接收到下一数据时无ACK                }                else                {                    s3c2440_i2c_regs->iiccon = 0xaf;   // 恢复I2C传输,接收到下一数据时发出ACK                }                               break;            }            /*Garmen:如果不是最后一个消息*/            else if (!isLastMsg())            {                /* 开始处理下一个消息 */                s3c2440_i2c_xfer_data.msgs++;                s3c2440_i2c_xfer_data.cur_msg++;                s3c2440_i2c_xfer_data.cur_ptr = 0;                s3c2440_i2c_xfer_data.state = STATE_START;                /* 发出START信号和发出设备地址 */                s3c2440_i2c_start();                break;            }            else            {                /* 是最后一个消息的最后一个数据 */                s3c2440_i2c_stop(0);                break;                                           }            break;        }        default: break;    }    /* 清中断 */    s3c2440_i2c_regs->iiccon &= ~(S3C2410_IICCON_IRQPEND);    return IRQ_HANDLED;   }/* * I2C初始化 */static void s3c2440_i2c_init(void){    struct clk *clk;    clk = clk_get(NULL, "i2c");    clk_enable(clk);    // 选择引脚功能:GPE15:IICSDA, GPE14:IICSCL    s3c_gpio_cfgpin(S3C2410_GPE(14), S3C2410_GPE14_IICSCL);    s3c_gpio_cfgpin(S3C2410_GPE(15), S3C2410_GPE15_IICSDA);    /* bit[7] = 1, 使能ACK     * bit[6] = 0, IICCLK = PCLK/16     * bit[5] = 1, 使能中断     * bit[3:0] = 0xf, Tx clock = IICCLK/16     * PCLK = 50MHz, IICCLK = 3.125MHz, Tx Clock = 0.195MHz     */    s3c2440_i2c_regs->iiccon = (1<<7) | (0<<6) | (1<<5) | (0xf);  // 0xaf    s3c2440_i2c_regs->iicadd  = 0x10;     // S3C24xx slave address = [7:1]    s3c2440_i2c_regs->iicstat = 0x10;     // I2C串行输出使能(Rx/Tx)}static int i2c_bus_s3c2440_init(void){    /* 2. 硬件相关的设置 */    s3c2440_i2c_regs = ioremap(0x54000000, sizeof(struct s3c2440_i2c_regs));    s3c2440_i2c_init();    request_irq(IRQ_IIC, s3c2440_i2c_xfer_irq, 0, "s3c2440-i2c", NULL);    init_waitqueue_head(&s3c2440_i2c_xfer_data.wait);    /* 3. 注册i2c_adapter */    i2c_add_adapter(&s3c2440_i2c_adapter);    return 0;}static void i2c_bus_s3c2440_exit(void){    i2c_del_adapter(&s3c2440_i2c_adapter);       free_irq(IRQ_IIC, NULL);    iounmap(s3c2440_i2c_regs);}module_init(i2c_bus_s3c2440_init);module_exit(i2c_bus_s3c2440_exit);MODULE_LICENSE("GPL");
原创粉丝点击