Linux设备驱动之I2C总线适配器驱动分析

Linux设备驱动之I2C总线适配器驱动分析

—————————————— 本文系本站原创,欢迎转载!转载请注明出处:http://ericxiao.cublog.cn/——————————————一:前言在前面已经分析了I2C架构,今天以ICH5中的I2C总线适配器驱动做为I2C总线适配器驱动的例子进行分析.相关的datasheet可以在Intel的网页上找到.因此,在这里不再介绍芯片功能.以下代码分析是基于linux kernel2.6.26.相关的代码位于linux-2.6.26.3/drivers/i2c/busses/i2c-i801.c二:驱动入口分析首先,ICH5 I2C适配器是一个PCI设备.首先它的驱动符合我们之前分析过的PCI架构.代码如下:static int __init i2c_i801_init(void){ return pci_register_driver(&i801_driver);}module_init(i2c_i801_init);i801_driver定义如下:static struct pci_driver i801_driver = { .name = "i801_smbus", .id_table = i801_ids, .probe = i801_probe, .remove = __devexit_p(i801_remove), .suspend = i801_suspend, .resume = i801_resume,};所有符合这个驱动的设备列表为:static struct pci_device_id i801_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_3) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_3) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_2) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_3) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_3) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_3) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_4) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_16) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_17) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_17) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_5) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_6) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TOLAPAI_1) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH10_4) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH10_5) }, { 0, }};我们在<< Intel® 82801EB I/O Controller Hub 5 (ICH5) / Intel® 82801ER I/O Controller Hub 5 R (ICH5R) >>上可以查得它的vendor_id和device_id分别为0x8086和0x24D3.在linux kernel中分别定义为了PCI_VENDOR_ID_INTEL和PCI_DEVICE_ID_INTEL_82801EB_3.它在上面的列表中,因此会匹配这个驱动.该驱动的probe函数如下所示:static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id *id){ unsigned char temp; int err; I801_dev = dev; i801_features = 0; switch (dev->device) { case PCI_DEVICE_ID_INTEL_82801EB_3: case PCI_DEVICE_ID_INTEL_ESB_4: case PCI_DEVICE_ID_INTEL_ICH6_16: case PCI_DEVICE_ID_INTEL_ICH7_17: case PCI_DEVICE_ID_INTEL_ESB2_17: case PCI_DEVICE_ID_INTEL_ICH8_5: case PCI_DEVICE_ID_INTEL_ICH9_6: case PCI_DEVICE_ID_INTEL_TOLAPAI_1: case PCI_DEVICE_ID_INTEL_ICH10_4: case PCI_DEVICE_ID_INTEL_ICH10_5: i801_features |= FEATURE_I2C_BLOCK_READ; /* fall through */ case PCI_DEVICE_ID_INTEL_82801DB_3: i801_features |= FEATURE_SMBUS_PEC; i801_features |= FEATURE_BLOCK_BUFFER; break; } //启用pci 设备 err = pci_enable_device(dev); if (err) { dev_err(&dev->dev, "Failed to enable SMBus PCI device (%d)/n", err); goto exit; } /* Determine the address of the SMBus area */ //IO区域的起始地址 i801_smba = pci_resource_start(dev, SMBBAR); if (!i801_smba) { dev_err(&dev->dev, "SMBus base address uninitialized, " "upgrade BIOS/n"); err = -ENODEV; goto exit; } //请求I/O资源 err = pci_request_region(dev, SMBBAR, i801_driver.name); if (err) { dev_err(&dev->dev, "Failed to request SMBus region " "0x%lx-0x%Lx/n", i801_smba, (unsigned long long)pci_resource_end(dev, SMBBAR)); goto exit; } //读HOSTC寄存器 pci_read_config_byte(I801_dev, SMBHSTCFG, &temp); i801_original_hstcfg = temp; //去掉I2C_EN位 temp &= ~SMBHSTCFG_I2C_EN; /* SMBus timing */ //如果HST_EN位为0.表示i2c 控制器被禁用 if (!(temp & SMBHSTCFG_HST_EN)) { dev_info(&dev->dev, "Enabling SMBus device/n"); //启用i2c host temp |= SMBHSTCFG_HST_EN; } //清除了SMBHSTCFG_I2C_EN设置了SMBHSTCFG_HST_EN pci_write_config_byte(I801_dev, SMBHSTCFG, temp); //SMI_EN被置,表示使SMI中断 if (temp & SMBHSTCFG_SMB_SMI_EN) dev_dbg(&dev->dev, "SMBus using interrupt SMI#/n"); else dev_dbg(&dev->dev, "SMBus using PCI Interrupt/n"); /* Clear special mode bits */ //清除AUX_CTL寄存器的CRC和E32B位 if (i801_features & (FEATURE_SMBUS_PEC | FEATURE_BLOCK_BUFFER)) outb_p(inb_p(SMBAUXCTL) & ~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), SMBAUXCTL); /* set up the sysfs linkage to our parent device */ i801_adapter.dev.parent = &dev->dev; snprintf(i801_adapter.name, sizeof(i801_adapter.name), "SMBus I801 adapter at %04lx", i801_smba); //注册adapter驱动 err = i2c_add_adapter(&i801_adapter); if (err) { dev_err(&dev->dev, "Failed to add SMBus adapter/n"); goto exit_release; } return 0;exit_release: pci_release_region(dev, SMBBAR);exit: return err;}首先,因为device id是PCI_DEVICE_ID_INTEL_82801EB_3,所以i801_features含有所有的标志.之后就是一般PCI设备驱动的流程,启用PCI设备.以及取得设备的I/O配置区间.之后,就对设备做一些初始化设定.下面来了解一下这里初始化过程中所涉及到的寄存器:HOSTC寄存器,在datasheet中被定义如下:Bit0就是启用禁用位.bit1表示中断的类型,被置时,表示使用SMI interrupt,为0的时候表示是Pci interrupt.Bit2的含义有点难懂,这里就涉及到了smbus和i2c.实际上smbus和i2c都很相同,仅仅是传输的格式不同罢了.当bit2为0的话,表示使用smbus,如果为1的话,表示使用i2c.在使用I2c Read的时候,必须要将bit2置1.另外,Quick, Send/Receive Byte, Write byte/word, Read byte/word 这些指令必须要将bit2设为0.另外,关于数据格式的差别,datasheet上已经有详细的描述.AUX_CTL:按字面意思,它是指辅助传输功能,包括PEC和块传输等.它在datasheet中的定义如下:如上所示,bit0表示自动CRC检验,也就是我们之前分析的PEC.bit1是否使用块传输功能.进行一系列初始化之后,会注册adapter.这个adapter定义如下:static const struct i2c_algorithm smbus_algorithm = { .smbus_xfer = i801_access, .functionality = i801_func,};static struct i2c_adapter i801_adapter = { .owner = THIS_MODULE, .id = I2C_HW_SMBUS_I801, .class = I2C_CLASS_HWMON, .algo = &smbus_algorithm,};三:I2C总线上的数据传输结合我们之前的分析,所有的数据传输都会调用adapter的master_xfer和smbus_xfer操作.在这个驱动中, master_xfer操作为空,所以,所有的传输操作都会到smbus_xfer中.在这里对应的就是i801_access().它的代码如下:static s32 i801_access(struct i2c_adapter * adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data * data){ int hwpec; int block = 0; int ret, xact = 0; //如果需要PEC.且i2c host 支持PEC 其中,Quick和I2c Block Data不支持PEC hwpec = (i801_features & FEATURE_SMBUS_PEC) && (flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK && size != I2C_SMBUS_I2C_BLOCK_DATA; switch (size) { case I2C_SMBUS_QUICK: //对于Quick,只需要将地址和传输标志写入XMIT_SLAVE outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD); xact = I801_QUICK; break; case I2C_SMBUS_BYTE: //对于BYTE传输,将地址写入XMIT_SLAVE.如果是写操作,还要将command写入HST_CMD outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD); if (read_write == I2C_SMBUS_WRITE) outb_p(command, SMBHSTCMD); xact = I801_BYTE; break; case I2C_SMBUS_BYTE_DATA: //对于BYTE_DATA,写地址,写comand,写操作还要写DATA0 outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD); outb_p(command, SMBHSTCMD); if (read_write == I2C_SMBUS_WRITE) outb_p(data->byte, SMBHSTDAT0); xact = I801_BYTE_DATA; break; case I2C_SMBUS_WORD_DATA: //对于Word_Data:写地址,写command.如果是写操作还要写DaTa0 Data1 outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD); outb_p(command, SMBHSTCMD); if (read_write == I2C_SMBUS_WRITE) { outb_p(data->word & 0xff, SMBHSTDAT0); outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); } xact = I801_WORD_DATA; break; case I2C_SMBUS_BLOCK_DATA: //Blocal_Data:写地址,写command outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD); outb_p(command, SMBHSTCMD); block = 1; break; case I2C_SMBUS_I2C_BLOCK_DATA: //i2c Block Data /* NB: page 240 of ICH5 datasheet shows that the R/#W * bit should be cleared here, even when reading */ outb_p((addr & 0x7f) << 1, SMBHSTADD); if (read_write == I2C_SMBUS_READ) { /* NB: page 240 of ICH5 datasheet also shows * that DATA1 is the cmd field when reading */ outb_p(command, SMBHSTDAT1); } else outb_p(command, SMBHSTCMD); block = 1; break; case I2C_SMBUS_PROC_CALL: default: dev_err(&I801_dev->dev, "Unsupported transaction %d/n", size); return -1; } //如果启用/禁用PEC.设置/清除 AUX_CTL的CRC if (hwpec) /* enable/disable hardware PEC */ outb_p(inb_p(SMBAUXCTL) | SMBAUXCTL_CRC, SMBAUXCTL); else outb_p(inb_p(SMBAUXCTL) & (~SMBAUXCTL_CRC), SMBAUXCTL); //等待传输完成 if(block) ret = i801_block_transaction(data, read_write, size, hwpec); else ret = i801_transaction(xact | ENABLE_INT9); /* Some BIOSes don’t like it when PEC is enabled at reboot or resume time, so we forcibly disable it after every transaction. Turn off E32B for the same reason. */ //复位AUXCTL寄存器 if (hwpec || block) outb_p(inb_p(SMBAUXCTL) & ~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), SMBAUXCTL); if(block) return ret; if(ret) return -1; //如果是写操作,或者是Quick,不需要取read结果,直接返回即可 if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK)) return 0; //如果不是带block的读操作 switch (xact & 0x7f) { case I801_BYTE: /* Result put in SMBHSTDAT0 */ case I801_BYTE_DATA: data->byte = inb_p(SMBHSTDAT0); break; case I801_WORD_DATA: data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); break; } return 0;}这个过程的逻辑比较简单,就是将数据写入寄存器,如果需要PEC.则启用AUX_CTL中的CRC.然后,就启动传输,并等待这个传输完成.如果是Read操作,将传输数据将寄存器取出.在这里涉及到了两个比较重要的子函数,分别分析如下:i801_transaction()代码如下:static int i801_transaction(int xact){ int temp; int result = 0; int timeout = 0; dev_dbg(&I801_dev->dev, "Transaction (pre): CNT=%02x, CMD=%02x, " "ADD=%02x, DAT0=%02x, DAT1=%02x/n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); /* Make sure the SMBus host is ready to start transmitting */ /* 0x1f = Failed, Bus_Err, Dev_Err, Intr, Host_Busy */ //在传输之前,HOST_STS有错误位,清除它 if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) { dev_dbg(&I801_dev->dev, "SMBus busy (%02x). Resetting…/n", temp); outb_p(temp, SMBHSTSTS); if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) { dev_dbg(&I801_dev->dev, "Failed! (%02x)/n", temp); return -1; } else { dev_dbg(&I801_dev->dev, "Successful!/n"); } } /* the current contents of SMBHSTCNT can be overwritten, since PEC, * INTREN, SMBSCMD are passed in xact */ //写入命令代码和开始传输位,开始传输 outb_p(xact | I801_START, SMBHSTCNT); /* We will always wait for a fraction of a second! */ //等待这次操作完成 do { msleep(1); temp = inb_p(SMBHSTSTS); } while ((temp & SMBHSTSTS_HOST_BUSY) && (timeout++ < MAX_TIMEOUT)); /* If the SMBus is still busy, we give up */ //如果超时了,取消这次传输.置HST_CNT的Kill位 if (timeout >= MAX_TIMEOUT) { dev_dbg(&I801_dev->dev, "SMBus Timeout!/n"); result = -1; /* try to stop the current command */ dev_dbg(&I801_dev->dev, "Terminating the current operation/n"); outb_p(inb_p(SMBHSTCNT) | SMBHSTCNT_KILL, SMBHSTCNT); msleep(1); outb_p(inb_p(SMBHSTCNT) & (~SMBHSTCNT_KILL), SMBHSTCNT); } //传输失败 if (temp & SMBHSTSTS_FAILED) { result = -1; dev_dbg(&I801_dev->dev, "Error: Failed bus transaction/n"); } //总线错误 if (temp & SMBHSTSTS_BUS_ERR) { result = -1; dev_err(&I801_dev->dev, "Bus collision! SMBus may be locked " "until next hard reset. (sorry!)/n"); /* Clock stops and slave is stuck in mid-transmission */ } //设备有错误 if (temp & SMBHSTSTS_DEV_ERR) { result = -1; dev_dbg(&I801_dev->dev, "Error: no response!/n"); } //传输有错误,清除HOST_STS中的错误位 if ((inb_p(SMBHSTSTS) & 0x1f) != 0x00) outb_p(inb(SMBHSTSTS), SMBHSTSTS); //HOST_STS复位错误 if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) { dev_dbg(&I801_dev->dev, "Failed reset at end of transaction " "(%02x)/n", temp); } dev_dbg(&I801_dev->dev, "Transaction (post): CNT=%02x, CMD=%02x, " "ADD=%02x, DAT0=%02x, DAT1=%02x/n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); return result;}代码中的注释已经很详细了,这里不再赘述.另一个重要的子函数是i801_block_transaction().代码如下:static int i801_block_transaction(union i2c_smbus_data *data, char read_write, int command, int hwpec){ int result = 0; unsigned char hostc; //如果是I2C 的写操作必须要启用 I2C 功能 if (command == I2C_SMBUS_I2C_BLOCK_DATA) { if (read_write == I2C_SMBUS_WRITE) { /* set I2C_EN bit in configuration register */ pci_read_config_byte(I801_dev, SMBHSTCFG, &hostc); pci_write_config_byte(I801_dev, SMBHSTCFG, hostc | SMBHSTCFG_I2C_EN); } else if (!(i801_features & FEATURE_I2C_BLOCK_READ)) { dev_err(&I801_dev->dev, "I2C block read is unsupported!/n"); return -1; } } //修正有效数据长度. 存放在data->block[0] if (read_write == I2C_SMBUS_WRITE || command == I2C_SMBUS_I2C_BLOCK_DATA) { if (data->block[0] < 1) data->block[0] = 1; if (data->block[0] > I2C_SMBUS_BLOCK_MAX) data->block[0] = I2C_SMBUS_BLOCK_MAX; } //如果是读操作,将block有效长度置为最大值 else { data->block[0] = 32; /* max for SMBus block reads */ } //i2c host 支持block data传输且不为i2C的Read操作 if ((i801_features & FEATURE_BLOCK_BUFFER) && !(command == I2C_SMBUS_I2C_BLOCK_DATA && read_write == I2C_SMBUS_READ) && i801_set_block_buffer_mode() == 0) result = i801_block_transaction_by_block(data, read_write, hwpec); else result = i801_block_transaction_byte_by_byte(data, read_write, command, hwpec); //等待中断位被设 if (result == 0 && hwpec) i801_wait_hwpec(); //如果是I2C的Write操作,要回复HOSTCr 的值 if (command == I2C_SMBUS_I2C_BLOCK_DATA && read_write == I2C_SMBUS_WRITE) { /* restore saved configuration register value */ pci_write_config_byte(I801_dev, SMBHSTCFG, hostc); } return result;}实际的传输是调用i801_block_transaction_by_block()或者i801_block_transaction_byte_by_byte()来完成的.先来看i801_block_transaction_by_block()的代码.如下示:static int i801_block_transaction_by_block(union i2c_smbus_data *data, char read_write, int hwpec){ int i, len; inb_p(SMBHSTCNT); /* reset the data buffer index */ /* Use 32-byte buffer to process this transaction */ if (read_write == I2C_SMBUS_WRITE) { len = data->block[0]; outb_p(len, SMBHSTDAT0); for (i = 0; i < len; i++) outb_p(data->block[i+1], SMBBLKDAT); } if (i801_transaction(I801_BLOCK_DATA | ENABLE_INT9 | I801_PEC_EN * hwpec)) return -1; if (read_write == I2C_SMBUS_READ) { len = inb_p(SMBHSTDAT0); if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) return -1; data->block[0] = len; for (i = 0; i < len; i++) data->block[i + 1] = inb_p(SMBBLKDAT); } return 0;}它先将传输的数据总数写到SMBHSTDAT0.将要传输的数据写入到Host_BLOCK_DB寄存器,然后调用i801_transaction()启用操作并等待传输完成.如果是读操作,将结果读出.i801_block_transaction_byte_by_byte()函数与i801_block_transaction_by_block()类似,这里就不再详细分析了.四:小结在本节里,大概分析了一下ICH5中I2C Bus Adapter的驱动代码.了解了一般adapter的驱动架构.寂寞的人总是记住生命中出现的每一个人,

Linux设备驱动之I2C总线适配器驱动分析

相关文章:

你感兴趣的文章:

标签云: