Freescale P4080 I2C 驱动分析

来源:互联网 发布:卡盟外包系统源码 编辑:程序博客网 时间:2024/06/11 09:14

 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 int354 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_MTX409            | (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__ int421 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 int493 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() >= 0499         && i2c_write_addr(dev, I2C_WRITE_BIT, 0) != 0500         && __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的地址哦
原创粉丝点击