Freescale P4080 I2C 驅動分析

 Freescale P4080是一款8核心網絡處理器,功能強大,外設齊全,基於powerpc e500 core。在嵌入式網絡應用上被廣泛使用。

今天只討論P4080的I2C部分。

P4080片上集成了4個I2C 控制器,在我們的應用中,這4個I2C Controller 都是作爲master來使用。

先來一張I2C 控制器的結構圖

這張圖描述了一I2C 控制器應該有的寄存器,描述了各寄存器應有的功能。

其實,實現I2C驅動的關鍵就在於按照文檔進行配置這些寄存器。不單是I2C,大部分驅動程序都是這樣,比如DDR,SPI,DMA等都是按照文檔,

提供一個接口(函數)來訪問硬件,這就是所謂的驅動啦。

來看一下p4080上是怎麼規定這麼資源(registers)的地址的

規定了各I2C控制器的基地址,以及各控制器中的寄存器偏移。我們要按照這個地址進行組織代碼,寫一個結構體進行

描述I2C控制器,下面的結構體來自freescale

typedef struct fsl_i2c {

    u8 adr;     /* I2C slave address */
    u8 res0[3];
#define I2C_ADR     0xFE
#define I2C_ADR_SHIFT   1
#define I2C_ADR_RES ~(I2C_ADR)

    u8 fdr;     /* I2C frequency divider register */
    u8 res1[3];
#define IC2_FDR     0x3F
#define IC2_FDR_SHIFT   0
#define IC2_FDR_RES ~(IC2_FDR)

    u8 cr;      /* I2C control redister */
    u8 res2[3];
#define I2C_CR_MEN  0x80
#define I2C_CR_MIEN 0x40
#define I2C_CR_MSTA 0x20
#define I2C_CR_MTX  0x10
#define I2C_CR_TXAK 0x08
#define I2C_CR_RSTA 0x04
#define I2C_CR_BCST 0x01

    u8 sr;      /* I2C status register */
    u8 res3[3];
#define I2C_SR_MCF  0x80
#define I2C_SR_MAAS 0x40
#define I2C_SR_MBB  0x20
#define I2C_SR_MAL  0x10
#define I2C_SR_BCSTM    0x08
#define I2C_SR_SRW  0x04
#define I2C_SR_MIF  0x02
#define I2C_SR_RXAK 0x01

    u8 dr;      /* I2C data register */
    u8 res4[3];
#define I2C_DR      0xFF
#define I2C_DR_SHIFT    0
#define I2C_DR_RES  ~(I2C_DR)

    u8 dfsrr;   /* I2C digital filter sampling rate register */
    u8 res5[3];
#define I2C_DFSRR   0x3F
#define I2C_DFSRR_SHIFT 0
#define I2C_DFSRR_RES   ~(I2C_DR)

    /* Fill out the reserved block */
    u8 res6[0xE8];
} fsl_i2c_t;


看到了吧,是不是嚴格按照上圖的順序來的。這樣,在配置寄存器的時候就方便多了,只需要知道一個指向該控制器的結構體指針,

然後配置裏面的各項,實際上正好是配置了各個寄存器,這也是在驅動中常用的方法。

四個寄存器的基地址也是定義好的

#define CONFIG_SYS_I2C_OFFSET       0x118000
#define CONFIG_SYS_I2C2_OFFSET      0x118100
第三,第四個控制器爲0x119000, 0x119100
在加上系統的基地址(這個是所有控制器都要加上的,在這裏,控制器的base address相當於在系統上的一個偏移,哈哈,類似分頁了)
定義四個控制器結構體
66 #if defined CONFIG_SYS_NUM_OF_I2C
 67 static unsigned int i2c_bus_speed[CONFIG_SYS_NUM_OF_I2C] = {CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SPEED,CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SPEED };
 68 
 69  const struct fsl_i2c *i2c_dev[CONFIG_SYS_NUM_OF_I2C] = {
 70     (struct fsl_i2c *) (CONFIG_SYS_IMMR + CONFIG_SYS_I2C_OFFSET),
 71     (struct fsl_i2c *) (CONFIG_SYS_IMMR + CONFIG_SYS_I2C_OFFSET + 0x100),
 72 #if defined(CONFIG_PPC_P4080)                // (dual) i2c module #2
 73     (struct fsl_i2c *) (CONFIG_SYS_IMMR + CONFIG_SYS_I2C_OFFSET + 0x1000),
 74     (struct fsl_i2c *) (CONFIG_SYS_IMMR + CONFIG_SYS_I2C_OFFSET + 0x1000 + 0x100)
 75 #endif
 76 };

 

至此,I2C Controller在內存中的表示就已經完成了,剩下的就是如何配置按照文檔進行配置它們,使之能正常工作。

 

需要配置的是速度,這個在文檔中也是有規定的,只需要按照方法一步步來就可以了。

然後實現reset函數,就是像寄存器cr中寫固定的值,這些都是硬件決定的,沒什麼好講,其實驅動就是這個樣子,你可能在上層實現的優雅一點,但是

對於最底層,誰也無能爲力,比如實現reset的函數

239 void i2c_soft_reset(const struct fsl_i2c *dev )
240 {
241     volatile u8 cTmp;
242 
243         debug("\t@%08x:  I2CCSR:%02x I2CCCR:%02x  ", (int) dev, readb(&dev->sr), readb(&dev->cr));
244 
245         /* per 11.5.6 of 8548 UM */
246         writeb(0x20, &dev->cr); //這裏都是硬件規定,不要問爲啥是0x20, 00100000這個值中已經申明瞭某一位置位
247         udelay(1000);
248         writeb(0xA0, &dev->cr);          /* start condition */
249         udelay(1000);
250 
251         cTmp =  readb(&dev->dr);         /* kick off the read 8 data + ack */
252         debug("I2CCDR:%02x %s \n", cTmp, __FUNCTION__);
253 
254         writeb(0x0, &dev->cr);             /* disable and leave it alone */
255         udelay(1000);
256 
257 }
硬件給提供的接口就是你配置寄存器(或寄存器的某一位,某幾位)就能實現神馬功能,就是這樣!
有了上述兩操作,可以提供init函數了,就是設置速度,然後reset。
 
接着,終於步入最關鍵的, 實現讀寫啊!
 
無論是讀還是寫之前,先要看看總線是否空閒
353 static int
354 i2c_wait4bus(void)
355 {
356     unsigned long long timeval = get_ticks();
357     const unsigned long long timeout = usec2ticks(CONFIG_I2C_MBB_TIMEOUT);
358 
359     while (readb(&i2c_dev[i2c_bus_num]->sr) & I2C_SR_MBB) {
360         if ((get_ticks() - timeval) > timeout)
361             return -1;
362     }
363 
364     return 0;
365 }
 
接着,就要跟I2C協議扯上關係了,比如一個讀的過程,一寫的過程,具體有哪幾步,哈,還是看協議吧,我是看了就忘
406 static int i2c_write_addr (u8 dev, u8 dir, int rsta)
407 {
408     writeb(I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_MTX
409            | (rsta ? I2C_CR_RSTA : 0),
410            &i2c_dev[i2c_bus_num]->cr);
411 
412     writeb((dev << 1) | dir, &i2c_dev[i2c_bus_num]->dr);
413 
414     if (i2c_wait(I2C_WRITE_BIT) < 0)
415         return 0;
416 
417     return 1;
418 }
419 
420 // static __inline__ int
421 static int __i2c_write(u8 *data, int length)
422 {
423     int i;
424 
425     for (i = 0; i < length; i++) {
426         writeb(data[i], &i2c_dev[i2c_bus_num]->dr);
427 
428         if (i2c_wait(I2C_WRITE_BIT) < 0)
429             break;
430     }
431 
432     return i;
433 }
上面兩個函數就是,一個發地址,一個發數據
492 int
493 i2c_write(u8 dev, uint addr, int alen, u8 *data, int length)
494 {
495     int i = -1; /* signal error */
496     u8 *a = (u8*)&addr;
497 
498     if (i2c_wait4bus() >= 0
499         && i2c_write_addr(dev, I2C_WRITE_BIT, 0) != 0
500         && __i2c_write(&a[4 - alen], alen) == alen) {
501         i = __i2c_write(data, length);
502     }
503 
504     writeb(I2C_CR_MEN, &i2c_dev[i2c_bus_num]->cr);
505     if (i2c_wait4bus()) /* Wait until STOP */
506         debug("i2c_write: wait4bus timed out\n");
507 
508     if (i == length)
509         return 0;
510 
511     return -1;
512 }
這個接口就可以提供給上層使用啦。dev是slave的地址哦
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章