Linux下DM644x设备驱动I2C之总线驱动(二)详解

本文均属自己阅读源码的点滴总结,转账请注明出处谢谢。

欢迎和大家交流。qq:1037701636 email:200803090209@zjut.comgzzaigcn2012@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上数据的传输。
 


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值