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的地址哦