[OMAP_I2C_CNT_REG] = 0x98, [OMAP_I2C_DATA_REG] = 0x9c, [OMAP_I2C_SYSC_REG] = 0x10, [OMAP_I2C_CON_REG] = 0xa4, [OMAP_I2C_OA_REG] = 0xa8, [OMAP_I2C_SA_REG] = 0xac, [OMAP_I2C_PSC_REG] = 0xb0, [OMAP_I2C_SCLL_REG] = 0xb4, [OMAP_I2C_SCLH_REG] = 0xb8, [OMAP_I2C_SYSTEST_REG] = 0xbC, [OMAP_I2C_BUFSTAT_REG] = 0xc0, [OMAP_I2C_IP_V2_REVNB_LO] = 0x00, [OMAP_I2C_IP_V2_REVNB_HI] = 0x04, [OMAP_I2C_IP_V2_IRQSTATUS_RAW] = 0x24, [OMAP_I2C_IP_V2_IRQENABLE_SET] = 0x2c, [OMAP_I2C_IP_V2_IRQENABLE_CLR] = 0x30, }; 与spec能够对应上。不过这个列表不是根据寄存器地址排序的,是根据:
enum { OMAP_I2C_REV_REG = 0, OMAP_I2C_IE_REG, OMAP_I2C_STAT_REG, OMAP_I2C_IV_REG, OMAP_I2C_WE_REG, OMAP_I2C_SYSS_REG, OMAP_I2C_BUF_REG, OMAP_I2C_CNT_REG, OMAP_I2C_DATA_REG, OMAP_I2C_SYSC_REG, OMAP_I2C_CON_REG, OMAP_I2C_OA_REG, OMAP_I2C_SA_REG, OMAP_I2C_PSC_REG, OMAP_I2C_SCLL_REG, OMAP_I2C_SCLH_REG, OMAP_I2C_SYSTEST_REG, OMAP_I2C_BUFSTAT_REG, /* only on OMAP4430 */ OMAP_I2C_IP_V2_REVNB_LO, OMAP_I2C_IP_V2_REVNB_HI, OMAP_I2C_IP_V2_IRQSTATUS_RAW, OMAP_I2C_IP_V2_IRQENABLE_SET, OMAP_I2C_IP_V2_IRQENABLE_CLR, }; 共计23个寄存器。接下来是获取FIFO信息:
if (!(dev->flags & OMAP_I2C_FLAG_NO_FIFO)) { u16 s; /* * OMAP_I2C_BUFSTAT_REG对应寄存器地图中的寄存器0xc0,即I2C_BUFSTAT寄存器。 * 其第14~15位代表FIFO大小:0x0-8字节,0x1-16字节,0x2-32字节,0x3-64字节,只读寄存器。 * 改变RX/TX FIFO可通过改写I2C_BUF 0x94寄存器 */ s = (omap_i2c_read_reg(dev, OMAP_I2C_BUFSTAT_REG) >> 14) & 0x3; dev->fifo_size = 0x8 << s; dev->fifo_size = (dev->fifo_size / 2); //折半是为了处理潜在事件 } 接下来是对I2C适配器的初始化:
/* reset ASAP, clearing any IRQs */ //尽快重置,清除所有中断位 omap_i2c_init(dev); 进入此函数后在对具体硬件操作前还进行了时钟的相关计算,由于代码比较冗长,这里直接根据实际情况提炼出部分代码进行分析:
static int omap_i2c_init(struct omap_i2c_dev *dev) { u16 psc = 0, scll = 0, sclh = 0; u16 fsscll = 0, fssclh = 0, hsscll = 0, hssclh = 0; unsigned long fclk_rate = 12000000; //12MHz unsigned long internal_clk = 0; struct clk *fclk; if (!(dev->flags & OMAP_I2C_FLAG_SIMPLE_CLOCK)) { //上边的代码中表示过,默认为100KHz。即标准模式,而此I2C适配器只能支持标准和快速,对于高速模式并不支持 internal_clk = 4000; fclk = clk_get(dev->dev, \ fclk_rate = clk_get_rate(fclk) / 1000; clk_put(fclk); /* Compute prescaler divisor */ psc = fclk_rate / internal_clk; //计算分频器系数,0~0xff表示1倍到256倍 psc = psc - 1; /* * SCLL为SCL低电平设置,持续时间tROW = (SCLL + 7) * ICLK,即SCLL = tROW / ICLK - 7 * SCLH为SCL高电平设置,持续时间tHIGH= (SCLH + 5) * ICLK,即SCLH = tHIGH/ ICLK - 5 */ /* Standard mode */ fsscll = internal_clk / (dev->speed * 2) - 7; fssclh = internal_clk / (dev->speed * 2) - 5; scll = (hsscll << OMAP_I2C_SCLL_HSSCLL) | fsscll; sclh = (hssclh << OMAP_I2C_SCLH_HSSCLH) | fssclh; } dev->iestate = (OMAP_I2C_IE_XRDY | OMAP_I2C_IE_RRDY | OMAP_I2C_IE_ARDY | OMAP_I2C_IE_NACK | OMAP_I2C_IE_AL) | ((dev->fifo_size) ? (OMAP_I2C_IE_RDR | OMAP_I2C_IE_XDR) : 0); //设置传输数据相关中断位 dev->pscstate = psc; dev->scllstate = scll; dev->sclhstate = sclh; __omap_i2c_init(dev); return 0; } 对一些最后的必要参数计算或匹配完后,通过最终的__omap_i2c_init(dev)进行最后的写入:
static void __omap_i2c_init(struct omap_i2c_dev *dev) { omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0); //重置控制器 /* Setup clock prescaler to obtain approx 12MHz I2C module clock: */ omap_i2c_write_reg(dev, OMAP_I2C_PSC_REG, dev->pscstate); //设置分频器参数 /* SCL low and high time values */ omap_i2c_write_reg(dev, OMAP_I2C_SCLL_REG, dev->scllstate); //设置SCL高低电平参数 omap_i2c_write_reg(dev, OMAP_I2C_SCLH_REG, dev->sclhstate); if (dev->rev >= OMAP_I2C_REV_ON_3430_3530) omap_i2c_write_reg(dev, OMAP_I2C_WE_REG, dev->westate); /* Take the I2C module out of reset: */ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN); //使能I2C适配器 /* * Don't write to this register if the IE state is 0 as it can * cause deadlock. */ if (dev->iestate) omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, dev->iestate); //设置中断使能位 } 到这里硬件模块的初始化工作就全部完成了。接下来继续,包含了中断处理程序注册、适配器注册等。
r = devm_request_threaded_irq(&pdev->dev, dev->irq, omap_i2c_isr, omap_i2c_isr_thread, IRQF_NO_SUSPEND | IRQF_ONESHOT, pdev->name, dev); //申请中断,并安装相应的handle及中断工作线程(主要包含传输工作) if (r) { dev_err(dev->dev, \ goto err_unuse_clocks; } adap = &dev->adapter; //开始准备适配器的注册工作 i2c_set_adapdata(adap, dev); //之前设置、计算的那些参数不能丢掉,要保存在adapter的dev->p->driver_data中。 adap->owner = THIS_MODULE; adap->class = I2C_CLASS_HWMON; strlcpy(adap->name, \adap->algo = &omap_i2c_algo; //此适配器的通讯算法 adap->dev.parent = &pdev->dev; adap->dev.of_node = pdev->dev.of_node; /* i2c device drivers may be active on return from add_adapter() */ adap->nr = pdev->id; //指定总线号 r = i2c_add_numbered_adapter(adap); //注册适配器 of_i2c_register_devices(adap); //注册在DTS中声明的I2C设备 至此此I2C适配器成功注册,属于他的I2C设备也即将通过注册。稍做休息,然后分析最最重要的adapter->algo成员。
static const struct i2c_algorithm omap_i2c_algo = { .master_xfer = omap_i2c_xfer, .functionality = omap_i2c_func, }; 先看简单的功能查询接口函数:
static u32 omap_i2c_func(struct i2c_adapter *adap) { return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) | I2C_FUNC_PROTOCOL_MANGLING; } 支持I2C、支持仿真SMBUS但不支持快速协议、支持协议编码(自定义协议)。在分析master_xfer成员前先熟悉一下i2c_msg的数据结构:
struct i2c_msg { __u16 addr; /* slave address */ __u16 flags; #define I2C_M_TEN 0x0010 /* this is a ten bit chip address */ //10bit从地址 #define I2C_M_RD 0x0001 /* read data, from slave to master */ //读数据 /* * 相关资料 https://www.kernel.org/doc/Documentation/i2c/i2c-protocol */ #define I2C_M_STOP 0x8000 /* if I2C_FUNC_PROTOCOL_MANGLING */ //每个消息后都会带有一个STOP位 #define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_NOSTART */ //多消息传输,在第二个消息前设置此位 #define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */ //切换读写标志位 #define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */ //no ACK位会被视为ACK #define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */ //读消息时候,主设备的ACK/no ACK位会被忽略 #define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */ __u16 len; /* msg length */ __u8 *buf; /* pointer to msg data */ }; ? addr即从设备地址