本文主要关注bsp中,关于smbus(系统管理总线,是i2c的子集)的配置过程,了解如如何配置i2c寄存器。
所有发送的数据都会写在FIFO中,使能之后就发送出去。接收数据就从接收寄存器中读取。
读取和发送的数据都保存在iproc_xact_info结构体中。
结合smbus数据帧的格式,分析下面代码。

struct iproc_xact_info {
     bool cmd_valid; /* true if command field below is valid. Otherwise, false */
     unsigned short command; /* Passed by caller to send SMBus command code */
     unsigned char *data; /* actual data pased by the caller */ 
     unsigned int size; /* Size of data buffer passed */
     unsigned short flags; /* Sent by caller specifying PEC, 10-bit addresses */
     unsigned char smb_proto; /* SMBus protocol to use to perform transaction */
 }; 
static struct i2c_algorithm iproc_smb_algorithm = {
    /*    .name           = "iproc-smb", */
    .smbus_xfer     = iproc_smb_xfer,
    .master_xfer    = NULL,
    .functionality  = iproc_smb_funcs,
};

static int iproc_smb_xfer(struct i2c_adapter *i2c_adap, u16 addr, 
                          unsigned short flags, char read_write,
                          u8 command, int size, union i2c_smbus_data *data)
{
    int rc;
    struct iproc_smb_drv_int_data *dev = i2c_get_adapdata(i2c_adap);
    struct iproc_xact_info info;
    unsigned int num_bytes_read = 0;

#ifdef IPROC_SMB_DBG
    printk(KERN_DEBUG "\n%s: dev=0x%08X\n", __func__, (unsigned int)dev);
#endif

    down(&dev->xfer_lock);

    addr <<= 1;
    //smbus发送的数据有多种形式,有些是写1字节,有些是多字节
    //switch中根据不同操作,将信息保存到info结构体中
    switch (size /* protocol */) {
        //一字节,有些不需要command
        case I2C_SMBUS_BYTE:
            info.cmd_valid = false;
            info.command = command; /* not used */
            if (read_write == I2C_SMBUS_WRITE) {     //写操作

              info.data = &command;

            }
            else {

            info.data = &data->byte; 

            }
            info.size = 1;
            info.flags = flags;
            //读操作
            if (read_write == I2C_SMBUS_READ) {

                addr |= 0x1; /* Read operation */

                info.smb_proto = SMBUS_PROT_RECV_BYTE;
                info.data = &data->byte; 

            }
            else {

                info.smb_proto = SMBUS_PROT_SEND_BYTE;

            }
        break;
     //发送字节和数据
        case I2C_SMBUS_BYTE_DATA:
            info.cmd_valid = true;
            info.command = command;
            info.data = &data->byte; 
            info.size = 1;
            info.flags = flags;

            if (read_write == I2C_SMBUS_READ) {

                info.smb_proto = SMBUS_PROT_RD_BYTE;

            }
            else {

                info.smb_proto = SMBUS_PROT_WR_BYTE;
                //info.smb_proto = SMBUS_PROT_WR_WORD; /* TEMP chg. remove later */

            }
        break;
     //发送字,也就是2字节
        case I2C_SMBUS_WORD_DATA:
            info.cmd_valid = true;
            info.command = command;
            info.data = (unsigned char *)(&data->word);
            info.size = 2;
            info.flags = flags;
            if (read_write == I2C_SMBUS_READ) {
                info.smb_proto = SMBUS_PROT_RD_WORD;
            }
            else {
                info.smb_proto = SMBUS_PROT_WR_WORD;
            }

        break;
    //块数据操作
        case I2C_SMBUS_BLOCK_DATA:
            info.cmd_valid = true;
            info.command = command;
            info.data = &data->block[1];
            info.flags = flags;

            if (read_write == I2C_SMBUS_READ) {

                info.smb_proto = SMBUS_PROT_BLK_RD;

                /* Protocol(hw) returns data byte count as part of response */
                info.size = 0; 

            }
            else {

                info.smb_proto = SMBUS_PROT_BLK_WR;

                info.size = data->block[0]; /* i2c-core passes the length in
                                               this field */

            }

        break;

        case I2C_SMBUS_BLOCK_PROC_CALL:
            info.cmd_valid = true;
            info.command = command;
            info.data = &data->block[1];
            info.flags = flags;
            info.smb_proto = SMBUS_PROT_BLK_WR_BLK_RD_PROC_CALL;
        break;

        default:
            printk(KERN_ERR "%s: Unsupported transaction %d\n", __func__, size);
            up(&dev->xfer_lock);
            return -EINVAL;

    }

    //接收操作
    if (read_write == I2C_SMBUS_READ) {
        /* Refer to i2c_smbus_read_byte for params passed. */
        rc = iproc_smb_data_recv(i2c_adap, addr, &info, &num_bytes_read);      //------------------------>>>      1

        /* if failed due to bus hang, but recovered, retry once */
        if (rc == -ECOMM) {
            rc = iproc_smb_data_recv(i2c_adap, addr, &info, &num_bytes_read);
        }

        /* For block read call, we pass the actual amount of data sent by
         * slave, as expected by std Linux API
         */
        if ((info.smb_proto == SMBUS_PROT_BLK_RD) ||
            (info.smb_proto == SMBUS_PROT_BLK_WR_BLK_RD_PROC_CALL)) {

            if (rc == 0) {

                data->block[0] = num_bytes_read;

#ifdef IPROC_SMB_DBG
                printk(KERN_ERR "%s: num bytes read=%u\n", 
                       __func__, data->block[0]);
#endif

            }
        }

    }  //接收
    else {

        /* Refer to i2c_smbus_write_byte params passed. */
        rc = iproc_smb_data_send(i2c_adap, addr, &info);        // ---------------------->>> 2

        /* if failed due to bus hang, but recovered, retry */
        if (rc == -ECOMM) {
          rc = iproc_smb_data_send(i2c_adap, addr, &info);
        }

    }   

    if (rc < 0) {

        printk(KERN_INFO "%s %s: %s error accessing device 0x%X rc=%d", __func__, dev->adapter.name, 
                (read_write == I2C_SMBUS_READ) ? "Read" : "Write", addr, rc);

        up(&dev->xfer_lock);

        return -EREMOTEIO;

    }

    up(&dev->xfer_lock);

    return (rc);
}


static int iproc_smb_data_send(struct i2c_adapter *adapter,      //   <<<----------------------- 2
                               unsigned short addr,
                               struct iproc_xact_info *info)
{
    int rc;
    unsigned int regval;
    struct iproc_smb_drv_int_data *dev = i2c_get_adapdata(adapter);
    unsigned long time_left;

    //检查总线是否空闲
    /* Make sure the previous transaction completed */
    rc = iproc_smb_startbusy_wait(dev);                // --------------------------->>> 3

    if (rc < 0) {

#ifdef IPROC_SMB_DBG
        printk(KERN_ERR "%s: Send: %s bus is busy, attempt recovery \n", 
               __func__, dev->adapter.name);
#endif
        //恢复总线
        /* attempt to recover the bus */
        if (iproc_smb_startbusy_recovery(dev) != 0) {

          return rc;

        }
    }

    if (dev->enable_evts == ENABLE_INTR) {

        /* Enable start_busy interrupt */
        regval = iproc_smb_reg_read((unsigned long)dev->block_base_addr + 
                                       CCB_SMB_EVTEN_REG);

        regval |= CCB_SMB_MSTRSTARTBUSYEN_MASK;

        iproc_smb_reg_write((unsigned long)dev->block_base_addr + 
                            CCB_SMB_EVTEN_REG, regval);

        /* Mark as incomplete before sending the data */
        INIT_COMPLETION(dev->ses_done);

    }
    //把所有的数据都写道FIFO中
    /* Write transaction bytes to Tx FIFO */
    iproc_smb_write_trans_data((unsigned long)dev->block_base_addr, addr, info);      // ----------------->>> 4
    //是能寄存器,初始化写操作
    /* Program master command register (0x30) with protocol type and set
     * start_busy_command bit to initiate the write transaction
     */
    regval = (info->smb_proto << CCB_SMB_MSTRSMBUSPROTO_SHIFT) |
              CCB_SMB_MSTRSTARTBUSYCMD_MASK;

    iproc_smb_reg_write((unsigned long)dev->block_base_addr + 
                        CCB_SMB_MSTRCMD_REG, regval);

    if (dev->enable_evts == ENABLE_INTR) {

        /*
         * Block waiting for the transaction to finish. When it's finished,
         * we'll be signaled by an interrupt
         */
        time_left = wait_for_completion_timeout(&dev->ses_done, XACT_TIMEOUT);

        /* Disable start_busy interrupt */
        regval = iproc_smb_reg_read((unsigned long)dev->block_base_addr + 
                                       CCB_SMB_EVTEN_REG);

        regval &= ~CCB_SMB_MSTRSTARTBUSYEN_MASK;

        iproc_smb_reg_write((unsigned long)dev->block_base_addr + 
                               CCB_SMB_EVTEN_REG, regval);

        if (time_left == 0) {

           printk (KERN_INFO "%s: Send: %s timeout accessing device x%02x\n", 
                   __func__, dev->adapter.name, addr);

           /* attempt to recover the bus */
           rc = iproc_smb_timeout_recovery(dev);
           if ( rc != 0 ) {

                return -ETIMEDOUT;

            }
           else {
                return -ECOMM;
            }

        }

    }
    //读取寄存器,检查发送完之后的寄存器标志位,判断是否发送成功    
    regval = iproc_smb_reg_read((unsigned long)dev->block_base_addr + 
                                CCB_SMB_MSTRCMD_REG);

    /* If start_busy bit cleared, check if there are any errors */
    if (!(regval & CCB_SMB_MSTRSTARTBUSYCMD_MASK)) {

        /* start_busy bit cleared, check master_status field now */
        regval &= CCB_SMB_MSTRSTS_MASK;
        regval >>= CCB_SMB_MSTRSTS_SHIFT;
        //检查是否发送成功
        if (regval != MSTR_STS_XACT_SUCCESS) {

            /* We can flush Tx FIFO here */

            printk(KERN_INFO "\n\n%s:Send: %s Error in transaction %d to device x%02x, exiting\n",
                     __func__, dev->adapter.name, regval, addr);

           return -EREMOTEIO;

        }
    }

    return(0);
}

static int iproc_smb_startbusy_wait(struct iproc_smb_drv_int_data *dev)    // <<<-----------------------   3
{
    unsigned int regval;

    regval = iproc_smb_reg_read((unsigned long)dev->block_base_addr + 
                                    CCB_SMB_MSTRCMD_REG);

    /* Check if an operation is in progress. During probe it won't be.
     * But when shutdown/remove was called we want to make sure that
     * the transaction in progress completed
     */
    //检查总线是否空闲,如果非零表示busy,那么就等待
    if (regval & CCB_SMB_MSTRSTARTBUSYCMD_MASK) {
        unsigned int i = 0;

        do {

               msleep(1); /* Wait for 1 msec */

               i++;
                //再次读取,判断是否空闲
               regval = iproc_smb_reg_read(
                     (unsigned long)dev->block_base_addr + CCB_SMB_MSTRCMD_REG);
        
          /* If start-busy bit cleared, exit the loop */
        } while ((regval & CCB_SMB_MSTRSTARTBUSYCMD_MASK) &&
                 (i < IPROC_SMB_MAX_RETRIES));

        if (i >= IPROC_SMB_MAX_RETRIES) {
#ifdef IPROC_SMB_DBG
            printk(KERN_ERR "%s: %s START_BUSY bit didn't clear, exiting\n",
                   __func__, dev->adapter.name);
#endif
            return -ETIMEDOUT;

        }

    }

   return 0;
}


static int iproc_smb_data_recv(struct i2c_adapter *adapter,        // <<<-----------------------  1
                               unsigned short addr,
                               struct iproc_xact_info *info,
                               unsigned int *num_bytes_read)
{
    int rc;
    unsigned int regval;
    struct iproc_smb_drv_int_data *dev = i2c_get_adapdata(adapter);
    unsigned long time_left;
    //等待总线空闲
    /* Make sure the previous transaction completed */
    rc = iproc_smb_startbusy_wait(dev);

    if (rc < 0) {
#ifdef IPROC_SMB_DBG
        printk(KERN_ERR "%s: Receive: %s bus is busy, attempt recovery \n", __func__, dev->adapter.name);
#endif
        /* attempt to recover the bus */
        if (iproc_smb_startbusy_recovery(dev) != 0) {
        return rc;
        }
    }

    if (dev->enable_evts == ENABLE_INTR) {
        //启用开始信号终端
        /* Enable start_busy interrupt */
        regval = iproc_smb_reg_read((unsigned long)dev->block_base_addr + 
                                    CCB_SMB_EVTEN_REG);
        //设置标志位为1,通知接收事件
        /* Set Rx_event_en bit for notification of reception event */
        regval |= (CCB_SMB_MSTRSTARTBUSYEN_MASK);

        iproc_smb_reg_write((unsigned long)dev->block_base_addr + 
                               CCB_SMB_EVTEN_REG, regval);

        /* Mark as incomplete before sending the data */
        INIT_COMPLETION(dev->ses_done);

    }
    //把所有的数据防盗FIFO中发送
    /* Program all transaction bytes into master Tx FIFO */
    iproc_smb_write_trans_data((unsigned long)dev->block_base_addr, addr, info);          //----------------------->>> 4
    //初始化发送操作,设置发送的数据大小,激活发送操作,发送数据,设置其中的字节数
    /* Program master command register (0x30) with protocol type and set
     * start_busy_command bit to initiate the write transaction
     */
    regval = (info->smb_proto << CCB_SMB_MSTRSMBUSPROTO_SHIFT) |
              CCB_SMB_MSTRSTARTBUSYCMD_MASK | info->size;

    iproc_smb_reg_write((unsigned long)dev->block_base_addr + 
                        CCB_SMB_MSTRCMD_REG, regval);

    if (dev->enable_evts == ENABLE_INTR) {

        /*
         * Block waiting for the transaction to finish. When it's finished,
         * we'll be signaled by an interrupt
         */
        time_left = wait_for_completion_timeout(&dev->ses_done, XACT_TIMEOUT);

        /* Disable start_busy and rx_event interrupts. Above call has handled
         * the interrupt
         */
        regval = iproc_smb_reg_read((unsigned long)dev->block_base_addr + 
                                    CCB_SMB_EVTEN_REG);

        regval &= ~(CCB_SMB_MSTRSTARTBUSYEN_MASK);

        iproc_smb_reg_write((unsigned long)dev->block_base_addr + 
                               CCB_SMB_EVTEN_REG, regval);

        if (time_left == 0) {

           printk (KERN_INFO "\n%s: Receive: %s timeout accessing device 0x%02x\n", 
                   __func__, dev->adapter.name, addr);

           /* attempt to recover the bus */
           rc = iproc_smb_timeout_recovery(dev);          // ------------------------------>>> 5
           if ( rc != 0 ) {
           return -ETIMEDOUT;
           }
           else {
             return -ECOMM;
           }
        }

    }
    //读取状态位
    regval = iproc_smb_reg_read((unsigned long)dev->block_base_addr + 
                                CCB_SMB_MSTRCMD_REG);
    //start_busy位清0的时候,检查是否有错
    /* If start_busy bit cleared, check if there are any errors */
    if (!(regval & CCB_SMB_MSTRSTARTBUSYCMD_MASK)) {

        /* start_busy bit cleared, check master_status field now */
        regval &= CCB_SMB_MSTRSTS_MASK;
        regval >>= CCB_SMB_MSTRSTS_SHIFT;
        //检查发送是否成功
        if (regval != MSTR_STS_XACT_SUCCESS) {

            /* We can flush Tx FIFO here */
            printk(KERN_INFO "\n%s: %s Error in transaction %d to device x%02x, exiting\n",
            __func__, dev->adapter.name, regval, addr);

           return -EREMOTEIO;

        }

    }

    /* In the isr we will read the received byte, and also deal with
     * rx fifo full event. The above check is for timeout error. If needed
     * we may move it to rx isr
     */
    //开始接受数据,接受第一字节
    /* Read received byte(s) */
    regval = iproc_smb_reg_read((unsigned long)dev->block_base_addr + 
                                   CCB_SMB_MSTRDATARD_REG);
    //如果是读取块数据
    /* For block read, protocol (hw) returns byte count, as the first byte */
    if ((info->smb_proto == SMBUS_PROT_BLK_RD) || 
        (info->smb_proto == SMBUS_PROT_BLK_WR_BLK_RD_PROC_CALL)) {

        int i;
        //根据协议定义,再发送了读取信号之后,接收的第一个字节是接受数据的大小
        *num_bytes_read = regval & CCB_SMB_MSTRRDDATA_MASK;

        /* Limit to reading a max of 32 bytes only; just a safeguard. If
         * # bytes read is a number > 32, check transaction set up, and contact
         * hw engg. Assumption: PEC is disabled 
         */
        for (i = 0; (i < *num_bytes_read) && (i < I2C_SMBUS_BLOCK_MAX); i++) {

            /* Read Rx FIFO for data bytes */
            regval = iproc_smb_reg_read((unsigned long)dev->block_base_addr + 
                                        CCB_SMB_MSTRDATARD_REG);

            info->data[i] = regval & CCB_SMB_MSTRRDDATA_MASK;

        }

    }
    else {

        *info->data = regval & CCB_SMB_MSTRRDDATA_MASK;

        *num_bytes_read = 1;

        if (info->smb_proto == SMBUS_PROT_RD_WORD) {
          /* Read Rx FIFO for data bytes */
          regval = iproc_smb_reg_read((unsigned long)dev->block_base_addr + 
                                      CCB_SMB_MSTRDATARD_REG);

          info->data[1] = regval & CCB_SMB_MSTRRDDATA_MASK;

          *num_bytes_read = 2;
        }
    }

    return(0);
}

static void iproc_smb_write_trans_data(unsigned long base_addr,       // <<<----------------------- 4
                                       unsigned short dev_addr,
                                       struct iproc_xact_info *info)
{
    unsigned int regval;
    unsigned int i;
    unsigned int num_data_bytes = 0;

#ifdef IPROC_SMB_DBG
    printk(KERN_DEBUG "\n%s: dev_addr=0x%X, offset=%u, cmd_valid=%u, size=%u\n", __func__, dev_addr, info->command, info->cmd_valid, info->size);
#endif /* IPROC_SMB_DBG */

    /* Write SMBus device address first */
    /* Note, we are assuming 7-bit addresses for now. For 10-bit addresses,
     * we may have one more write to send the upper 3 bits of 10-bit addr
     */
    //将地址写入到主机数据写寄存器中,发送出去
    iproc_smb_reg_write(base_addr + CCB_SMB_MSTRDATAWR_REG, dev_addr);
    //发送command 
    /* If the protocol needs command code, copy it */
    if (info->cmd_valid == true) {

        iproc_smb_reg_write(base_addr + CCB_SMB_MSTRDATAWR_REG, info->command);
 
    }

    /* Depending on the SMBus protocol, we need to write additional transaction
     * data in to Tx FIFO. Refer to section 5.5 of SMBus spec for sequence for a
     * transaction
     */
    //寄存器偏移0x30, 判断要发送数据的大小
    //判断操作的类型,添加额外的数据给到FIFO中
    switch (info->smb_proto) {
        //接受字节
        case SMBUS_PROT_RECV_BYTE:
            /* No additional data to be written */
            num_data_bytes = 0;
        break;
        //发送字节
        case SMBUS_PROT_SEND_BYTE:
            num_data_bytes = info->size;
        break;

        case SMBUS_PROT_RD_BYTE:
        case SMBUS_PROT_RD_WORD:
        case SMBUS_PROT_BLK_RD:
            /* Write slave address with R/W~ set (bit #0) */
            //块数据,多个字节的读写
            //根据协议格式,读操作需要再次发送开始信号
            iproc_smb_reg_write(base_addr + CCB_SMB_MSTRDATAWR_REG,
                                dev_addr | 0x1);
            num_data_bytes = 0;
        break;

        case SMBUS_PROT_WR_BYTE:
        case SMBUS_PROT_WR_WORD:
            /* No additional bytes to be written. Data portion is written in the
             * 'for' loop below
             */
            num_data_bytes = info->size;

            /* Note for hx4 eeprom (at24c64). the low addr bytes can be passed
             * in to 1st byte of info->data 
             */
        break;

        case SMBUS_PROT_BLK_WR:
            /* 3rd byte is byte count */
            //块数据写,需要发送要些的字节个数
            iproc_smb_reg_write(base_addr + CCB_SMB_MSTRDATAWR_REG, info->size);
            num_data_bytes = info->size;
        break;

        case SMBUS_PROT_BLK_WR_BLK_RD_PROC_CALL:
            /* Write byte count */
            //块数据读写调用,需要发送字节的个数
            iproc_smb_reg_write(base_addr + CCB_SMB_MSTRDATAWR_REG, info->size);
            num_data_bytes = info->size;
        break;

        default:
        break;

    }
    //发送实际的数据 
    /* Copy actual data from caller, next. In general, for reads, no data is
     * copied
     */
    for (i = 0; num_data_bytes; --num_data_bytes, i++) {

        /* For the last byte, set MASTER_WR_STATUS bit. For block rd/wr process
         * call, we need to program slave addr after copying data byte(s), so 
         * master status bit is set later, after the loop
         */
        //最后一个字节,需要设置master写状态为1
        if ((num_data_bytes == 1) && 
            (info->smb_proto != SMBUS_PROT_BLK_WR_BLK_RD_PROC_CALL)) { 
            regval = info->data[i] | CCB_SMB_MSTRWRSTS_MASK;
        }
        else {
            regval =  info->data[i];
        }
        //发送数据
        iproc_smb_reg_write(base_addr + CCB_SMB_MSTRDATAWR_REG, regval);

    }
    //如果是读写调用操作,数据写完之后,还要再次读回来,
    //所以下面这条命令就是再次发送开始信号,并指定为读操作 
    if (info->smb_proto == SMBUS_PROT_BLK_WR_BLK_RD_PROC_CALL) {
        /* Write device address needed during repeat start condition */
        //重复的开始信号,还有地址,有些操作需要这样
        iproc_smb_reg_write(base_addr + CCB_SMB_MSTRDATAWR_REG,
                            CCB_SMB_MSTRWRSTS_MASK | dev_addr | 0x1);
    }

    return;
}

static int iproc_smb_startbusy_recovery(struct iproc_smb_drv_int_data *dev)      // <<<------------- 5
{
  int rc = -1;
  unsigned int recoveryCnt;

  if (dev->adapter.nr == 0) {
    recoveryCnt = ++smbus0_startBusyCnt;
  }
  else {
    recoveryCnt = ++smbus1_startBusyCnt;
  }

  printk(KERN_INFO "%s: %s START_BUSY recovery #%d \n", __func__, dev->adapter.name, recoveryCnt);
    //复位i2c总线到默认值
  /* reset the SMBus block, wait a minimum of 50 uSecs and then re-initialize */
  iproc_smb_reg_write((unsigned long)dev->block_base_addr + CCB_SMB_CFG_REG, CCB_SMB_CFG_RST_MASK);
  udelay(60);
    //从新初始化
  if ( iproc_smbus_block_init(dev) == 0 ) {
    rc = 0;
  }
  
  return rc;
}