本文均属自己阅读源码的点滴总结,转账请注明出处谢谢。
欢迎和大家交流。qq:1037701636 email:200803090209@zjut.com,gzzaigcn2012@gmail.com
I2总线驱动的另一块内容就是适配器对应的algorithm结构体,在这个结构体中的主要内容是完成I2C控制器数据的传输与接收和硬件密切相关
static struct i2c_algorithm i2c_davinci_algo = { //主要涉及相关的I2C通信协议以及数据的传输和接受
.name = "DAVINCI I2C algorithm",
.id = I2C_ALGO_EXP,
.master_xfer = i2c_davinci_xfer,
.smbus_xfer = NULL,
.slave_send = NULL,
.slave_recv = NULL,
.algo_control = NULL,
.functionality = i2c_davinci_func,
};
涉及的内容主要是master_xfer指针函数i2c_davinci_xfer
i2c_davinci_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) //num=1
{
int count;
int ret = 0;
char retries = 5;
DEB1("msgs: %d", num);
if (num < 1 || num > MAX_MESSAGES)
return -EINVAL;
/* Check for valid parameters in messages */
for (count = 0; count < num; count++)
if (msgs[count].buf == NULL) //消息数据
return -EINVAL;
if ((ret = i2c_davinci_wait_for_bb(1, adap)) < 0) //检测I2C忙信号
return ret;
for (count = 0; count < num; count++) {
DEB1("msg: %d, addr: 0x%04x, len: %d, flags: 0x%x",
count, msgs[count].addr, msgs[count].len,
msgs[count].flags); //addr=0x1a,len=2,flags=0
do {
ret = i2c_davinci_xfer_msg(adap, &msgs[count],
(count == (num - 1)));
if (ret < 0) {
struct i2c_davinci_device *dev = i2c_get_adapdata(adap);
DEB1("i2c: retry %d - icstr = 0x%x",
retries, dev->regs->icstr);
mdelay (1);
retries--;
} else
break;
} while (retries);
DEB1("ret: %d", ret);
if (ret != msgs[count].len)
break;
}
if (ret >= 0 && num >= 1)
ret = num;
DEB1("ret: %d", ret);
return ret;
}
从传入的函数参数来看主要内容i2_meg这个i2传输信息结构体以及需要处理的信息个数
struct i2c_msg {
__u16 addr; /* slave address */
__u16 flags;
#define I2C_M_TEN 0x10 /* we have a ten bit chip address */
#define I2C_M_RD 0x01
#define I2C_M_NOSTART 0x4000
#define I2C_M_REV_DIR_ADDR 0x2000
#define I2C_M_IGNORE_NAK 0x1000
#define I2C_M_NO_RD_ACK 0x0800
__u16 len; /* msg length */
__u8 *buf; /* pointer to msg data */
};
主要内容包括I2C传输时:从机的地址,信息标志,信息的长度,以及信息的内容
在i2c_davinci_xfer函数内首先调用i2c_davinci_wait_for_bb来对I2C BUS进行忙检测。核心内容是调用内核的定时模块,在规定时间内对I2C做忙检测
while ((i2c_davinci_dev.regs->icstr) & DAVINCI_I2C_ICSTR_BB_MASK)
核心内容调用i2c_davinci_xfer_msg最终完成I2C数据的处理
static int
i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop) //stop=0,不设置停止标志
{
struct i2c_davinci_device *dev = i2c_get_adapdata(adap);
u8 zero_byte = 0;
u32 flag = 0, stat = 0;
int i;
DEB1("addr: 0x%04x, len: %d, flags: 0x%x, stop: %d",
msg->addr, msg->len, msg->flags, stop);
/* Introduce a 100musec delay. Required for Davinci EVM board only */
udelay(100); //延时100ms
/* set the slave address */
dev->regs->icsar = msg->addr; //0x1A
/* Sigh, seems we can't do zero length transactions. Thus, we
* can't probe for devices w/o actually sending/receiving at least
* a single byte. So we'll set count to 1 for the zero length
* transaction case and hope we don't cause grief for some
* arbitrary device due to random byte write/read during
* probes.
*/
if (msg->len == 0) {
dev->buf = &zero_byte;
dev->buf_len = 1;
} else {
dev->buf = msg->buf;
dev->buf_len = msg->len; //msg->len=2
}
dev->regs->iccnt = dev->buf_len; //数据的长度写入ICCNT计数寄存器
dev->cmd_complete = 0;
dev->cmd_err = 0;
/* Clear any pending interrupts by reading the IVR */
stat = dev->regs->icivr; //读取中断向量寄存器,完成中断产生的标志清零
/* Take I2C out of reset, configure it as master and set the start bit */
flag =
DAVINCI_I2C_ICMDR_IRS_MASK | DAVINCI_I2C_ICMDR_MST_MASK |
DAVINCI_I2C_ICMDR_STT_MASK; //I2C使能,Master模式,启动开始信号
/* if the slave address is ten bit address, enable XA bit */
if (msg->flags & I2C_M_TEN)
flag |= DAVINCI_I2C_ICMDR_XA_MASK; //支持10位地址
if (!(msg->flags & I2C_M_RD))
flag |= DAVINCI_I2C_ICMDR_TRX_MASK; //配置为传输模式
if (stop)
flag |= DAVINCI_I2C_ICMDR_STP_MASK; //是否使能停止标志位
/* Enable receive and transmit interrupts */
if (msg->flags & I2C_M_RD) //读数据标志
dev->regs->icimr |= DAVINCI_I2C_ICIMR_ICRRDY_MASK;
else
dev->regs->icimr |= DAVINCI_I2C_ICIMR_ICXRDY_MASK; //使能传输数据准备中断
/* write the data into mode register */
dev->regs->icmdr = flag; //启动数据传输,产生一个启动信号等待中断信号
/* wait for the transaction to complete */
wait_event_timeout (dev->cmd_wait, dev->cmd_complete, DAVINCI_I2C_TIMEOUT); //传输完成则唤醒等待队列dev->cmd_wait
dev->buf_len = 0;
if (!dev->cmd_complete) { //未完成数据的处理
i2c_warn("i2c: cmd complete failed: complete = 0x%x, \
icstr = 0x%x\n", dev->cmd_complete,
dev->regs->icstr);
/* Send the NACK to the slave */
dev->regs->icmdr |= DAVINCI_I2C_ICMDR_NACKMOD_MASK;
/* Disable I2C */
PINMUX1 &= (~(1 << 7));
/* Set the GPIO direction register */
gpio_set_direction (43, GIO_DIR_OUTPUT);
/* Send high and low on the SCL line */
for (i = 0; i < 10; i++) {
gpio_set_value (43, GIO_STATE_HIGH);
udelay(25);
gpio_set_value (43, GIO_STATE_LOW);
udelay(25);
}
/* Re-enable I2C */
PINMUX1 |= (1 << 7);
i2c_davinci_reset(dev);
dev->cmd_complete = 0;
return -ETIMEDOUT;
}
dev->cmd_complete = 0; //完成传输后标志位清零
/* no error */
if (!dev->cmd_err)
return msg->len;
/* We have an error */
if (dev->cmd_err & DAVINCI_I2C_ICSTR_NACK_MASK) {
if (msg->flags & I2C_M_IGNORE_NAK)
return msg->len;
if (stop)
dev->regs->icmdr |= DAVINCI_I2C_ICMDR_STP_MASK;
return -EREMOTEIO;
}
if (dev->cmd_err & DAVINCI_I2C_ICSTR_AL_MASK) {
i2c_davinci_reset(dev);
return -EIO;
}
return msg->len;
}
/*
* Prepare controller for a transaction and call i2c_davinci_xfer_msg
相关内容的代码可以查看我的注释,主要通过设置相应I2C的寄存器的值,完成对I2C的中断配置,控制配置,工作模式的配置。最终启动I2C,进入进程睡眠状态。
数据的传输与接收处理在中断中进行,中断函数内容如下:
static irqreturn_t
i2c_davinci_isr(int this_irq, void *dev_id, struct pt_regs *reg) //完成数据的传输
{
struct i2c_davinci_device *dev = dev_id;
u32 stat;
DEB1("i2c_davinci_isr()");
while ((stat = dev->regs->icivr) != 0) { //读取中断向量寄存器,判断何事件引起I2C中断,并清除中断标志
switch (stat) {
case DAVINCI_I2C_ICIVR_INTCODE_AL:
dev->cmd_err |= DAVINCI_I2C_ICSTR_AL_MASK;
i2c_warn("i2c: AL detected");
i2c_davinci_complete_cmd(dev);
break;
case DAVINCI_I2C_ICIVR_INTCODE_NACK:
dev->cmd_err |= DAVINCI_I2C_ICSTR_NACK_MASK;
//i2c_warn("i2c: NACK detected");
i2c_davinci_complete_cmd(dev);
break;
case DAVINCI_I2C_ICIVR_INTCODE_RAR:
// i2c_warn("i2c: RAR detected");
dev->regs->icstr |= DAVINCI_I2C_ICSTR_ARDY_MASK;
i2c_davinci_complete_cmd(dev);
break;
case DAVINCI_I2C_ICIVR_INTCODE_RDR:
if (dev->buf_len) {
*dev->buf++ = dev->regs->icdrr;
dev->buf_len--;
if (dev->buf_len) {
continue;
} else {
dev->regs->icimr &=
~DAVINCI_I2C_ICIMR_ICRRDY_MASK;
}
}
break;
case DAVINCI_I2C_ICIVR_INTCODE_TDR: //传输数据准备好后产生的中断
if (dev->buf_len) {
dev->regs->icdxr = *dev->buf++; //将要传输的数据写入ICDXR数据传输的寄存器
dev->buf_len--;
if (dev->buf_len)
continue; //继续while循环
else {
dev->regs->icimr &=
~DAVINCI_I2C_ICIMR_ICXRDY_MASK; //数据发送结束后清除中断使能位ICXRDY
}
}
break;
case DAVINCI_I2C_ICIVR_INTCODE_SCD:
dev->regs->icstr |= DAVINCI_I2C_ICSTR_SCD_MASK;
i2c_davinci_complete_cmd(dev);
break;
case DAVINCI_I2C_ICIVR_INTCODE_AAS:
i2c_warn("i2c: AAS detected");
break;
default:
i2c_warn("i2c: unknown status: %d", stat);
break;
} /* switch */
} /* while */
return IRQ_HANDLED;
}
通过读取I2C的中断向量表(stat = dev->regs->icivr)的内容。选择对应的操作
在这里介绍DAVINCI_I2C_ICIVR_INTCODE_TDR中断情况,完成数据的传输,实际只需向其dev->regs->icdxr = *dev->buf++;写入数据就可以完成I2C上数据的传输。

3250

被折叠的 条评论
为什么被折叠?



