uboot作为kernel启动的服务程序,内部支持很多的组件。部分需求会想让uboot支持eeprom存储或者rtc的功能支持,这样就需要uboot支持i2c的驱动。下下来的uboot里面就是有i2c的驱动例子的,这里就分析一下供移植模仿。
在uboot/drivers/i2c 下面就是几个i2c驱动的例子。
这里我们选择davinci_i2c.c来分析。这个是写的比较清楚和简单的。第一步我们需要先定义编译的条件,在uboot/include/configs/davinci_dm355evm.h 这个板级的配置文件里面定义i2c的配置:
/* I2C */
define CONFIG_HARD_I2C
define CONFIG_DRIVER_DAVINCI_I2C
define CONFIG_SYS_I2C_SPEED 400000
define CONFIG_SYS_I2C_SLAVE 0x10 /*
第二步:makefile 里添加编译
在uboot/drivers/i2c Makefile里添加
COBJS-$(CONFIG_DRIVER_DAVINCI_I2C) += davinci_i2c.o
比对第一步就可以知道这个功能就可以保证davinc_i2c.c可以被编译了。
第三步:完成两个必须的函数。这里要看一个关键的文件。因为uboot是没有i2c的框架的。里面只是用uboot/include/i2c.h 定义了操作函数,如果想用i2c只要include这个文件。就可以通过定义的/*
* Probe the given I2C chip address. Returns 0 if a chip responded,
* not 0 on failure.
*/
int i2c_probe(uchar chip);
/*
* Read/Write interface:
* chip: I2C chip address, range 0..127
* addr: Memory (register) address within the chip
* alen: Number of bytes to use for addr (typically 1, 2 for larger
* memories, 0 for register type devices with only one
* register)
* buffer: Where to read/write the data
* len: How many bytes to read/write
*
* Returns: 0 on success, not 0 on failure
*/
int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len);
int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len);
这样三个函数完成操作了。我们的任务就是实现这三个函数。转过来看看davinci_i2c.c的源代码
int i2c_probe(u_int8_t chip)
{
int rc = 1;
if (chip == REG(I2C_OA)) {
return(rc);
}
REG(I2C_CON) = 0;
if (wait_for_bus()) {return(1);}
/* try to read one byte from current (or only) address */
REG(I2C_CNT) = 1;
REG(I2C_SA) = chip;
REG(I2C_CON) = (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP);
udelay (50000);
if (!(REG(I2C_STAT) & I2C_STAT_NACK)) {
rc = 0;
flush_rx();
REG(I2C_STAT) = 0xffff;
} else {
REG(I2C_STAT) = 0xffff;
REG(I2C_CON) |= I2C_CON_STP;
udelay(20000);
if (wait_for_bus()) {return(1);}
}
flush_rx();
REG(I2C_STAT) = 0xffff;
REG(I2C_CNT) = 0;
return(rc);
}
int i2c_read(u_int8_t chip, u_int32_t addr, int alen, u_int8_t *buf, int len)
{
u_int32_t tmp;
int i;
if ((alen < 0) || (alen > 2)) {
printf("%s(): bogus address length %x\n", __FUNCTION__, alen);
return(1);
}
if (wait_for_bus()) {return(1);}
if (alen != 0) {
/* Start address phase */
tmp = I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX;
REG(I2C_CNT) = alen;
REG(I2C_SA) = chip;
REG(I2C_CON) = tmp;
tmp = poll_i2c_irq(I2C_STAT_XRDY | I2C_STAT_NACK);
CHECK_NACK();
switch (alen) {
case 2:
/* Send address MSByte */
if (tmp & I2C_STAT_XRDY) {
REG(I2C_DXR) = (addr >> 8) & 0xff;
} else {
REG(I2C_CON) = 0;
return(1);
}
tmp = poll_i2c_irq(I2C_STAT_XRDY | I2C_STAT_NACK);
CHECK_NACK();
/* No break, fall through */
case 1:
/* Send address LSByte */
if (tmp & I2C_STAT_XRDY) {
REG(I2C_DXR) = addr & 0xff;
} else {
REG(I2C_CON) = 0;
return(1);
}
tmp = poll_i2c_irq(I2C_STAT_XRDY | I2C_STAT_NACK | I2C_STAT_ARDY);
CHECK_NACK();
if (!(tmp & I2C_STAT_ARDY)) {
REG(I2C_CON) = 0;
return(1);
}
}
}
/* Address phase is over, now read ‘len‘ bytes and stop */
tmp = I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP;
REG(I2C_CNT) = len & 0xffff;
REG(I2C_SA) = chip;
REG(I2C_CON) = tmp;
for (i = 0; i < len; i++) {
tmp = poll_i2c_irq(I2C_STAT_RRDY | I2C_STAT_NACK | I2C_STAT_ROVR);
CHECK_NACK();
if (tmp & I2C_STAT_RRDY) {
buf[i] = REG(I2C_DRR);
} else {
REG(I2C_CON) = 0;
return(1);
}
}
tmp = poll_i2c_irq(I2C_STAT_SCD | I2C_STAT_NACK);
CHECK_NACK();
if (!(tmp & I2C_STAT_SCD)) {
REG(I2C_CON) = 0;
return(1);
}
flush_rx();
REG(I2C_STAT) = 0xffff;
REG(I2C_CNT) = 0;
REG(I2C_CON) = 0;
return(0);
}
int i2c_write(u_int8_t chip, u_int32_t addr, int alen, u_int8_t *buf, int len)
{
u_int32_t tmp;
int i;
if ((alen < 0) || (alen > 2)) {
printf("%s(): bogus address length %x\n", __FUNCTION__, alen);
return(1);
}
if (len < 0) {
printf("%s(): bogus length %x\n", __FUNCTION__, len);
return(1);
}
if (wait_for_bus()) {return(1);}
/* Start address phase */
tmp = I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX | I2C_CON_STP;
REG(I2C_CNT) = (alen == 0) ? len & 0xffff : (len & 0xffff) + alen;
REG(I2C_SA) = chip;
REG(I2C_CON) = tmp;
switch (alen) {
case 2:
/* Send address MSByte */
tmp = poll_i2c_irq(I2C_STAT_XRDY | I2C_STAT_NACK);
CHECK_NACK();
if (tmp & I2C_STAT_XRDY) {
REG(I2C_DXR) = (addr >> 8) & 0xff;
} else {
REG(I2C_CON) = 0;
return(1);
}
/* No break, fall through */
case 1:
/* Send address LSByte */
tmp = poll_i2c_irq(I2C_STAT_XRDY | I2C_STAT_NACK);
CHECK_NACK();
if (tmp & I2C_STAT_XRDY) {
REG(I2C_DXR) = addr & 0xff;
} else {
REG(I2C_CON) = 0;
return(1);
}
}
for (i = 0; i < len; i++) {
tmp = poll_i2c_irq(I2C_STAT_XRDY | I2C_STAT_NACK);
CHECK_NACK();
if (tmp & I2C_STAT_XRDY) {
REG(I2C_DXR) = buf[i];
} else {
return(1);
}
}
tmp = poll_i2c_irq(I2C_STAT_SCD | I2C_STAT_NACK);
CHECK_NACK();
if (!(tmp & I2C_STAT_SCD)) {
REG(I2C_CON) = 0;
return(1);
}
flush_rx();
REG(I2C_STAT) = 0xffff;
REG(I2C_CNT) = 0;
REG(I2C_CON) = 0;
return(0);
}
是不是也正好有这三个文件,并且函数类型和参数都是一样的呢。文件中的其他函数都只是这三个核心函数调用的。我们做移植的时候也只要实现对应这个标准接口的读写注册函数就可以了。
函数里面的内容因为没见soc厂商所用的ip不同,寄存器的配置也就不同。分析也没有什么意义,只要保证标准参数传进来之后我们能够正确解析对出正确的操作,回复给正确的状态就可以了。
版权声明:本文为博主原创文章,未经博主允许不得转载。