本文主要关注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;
}