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