zoukankan      html  css  js  c++  java
  • bcm56150_i2c驱动分析

    本文主要关注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 " %s: dev=0x%08X ", __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 ", __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 ", __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 ", __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 ", __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 " %s:Send: %s Error in transaction %d to device x%02x, exiting ", __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 ", __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 ", __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 " %s: Receive: %s timeout accessing device 0x%02x ", __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 " %s: %s Error in transaction %d to device x%02x, exiting ", __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 " %s: dev_addr=0x%X, offset=%u, cmd_valid=%u, size=%u ", __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 ", __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; }
  • 相关阅读:
    iOS开发——Autolayout下动态调整单元格高度
    iOS开发——随机数的使用
    iOS开发——使用基于Autolayout的ScrollView
    获取OS X中App Store更新后的安装包(如XCode)
    iOS开发——使用Autolayout弹出键盘
    iOS开发——给ImageView添加点击事件
    iOS开发——NSIBPrototypingLayoutConstraint原型约束造成的莫名问题
    iOS开发——异常:[__NSCFNumber length]: unrecognized selector sent to instance
    iOS开发——检测屏幕旋转
    iOS开发——使用Autolayout生成动态高度的TableViewCell单元格
  • 原文地址:https://www.cnblogs.com/helloworldtoyou/p/5147244.html
Copyright © 2011-2022 走看看