uboot移植i2c

迷霧綠洲發表於2015-08-13

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不同,暫存器的配置也就不同。分析也沒有什麼意義,只要保證標準引數傳進來之後我們能夠正確解析對出正確的操作,回覆給正確的狀態就可以了。

相關文章