Chinaunix首页 | 论坛 | 博客
  • 博客访问: 157213
  • 博文数量: 80
  • 博客积分: 1597
  • 博客等级: 上尉
  • 技术积分: 597
  • 用 户 组: 普通用户
  • 注册时间: 2008-10-30 13:49
  • 认证徽章:
文章分类

全部博文(80)

文章存档

2017年(1)

2015年(1)

2014年(1)

2013年(10)

2012年(10)

2011年(27)

2010年(30)

分类: LINUX

2013-09-04 23:22:50

------------------------------------------
本文系本站原创,欢迎转载!
转载请注明出处: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的驱动架构.
阅读(1455) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
评论热议
请登录后评论。

登录 注册