i2c-designware框架怎么注册i2c适配器?
两种方式,一种是通过platform_bus_type平台总线注册,另一种是通pci_bus_type总线注册。
pci方式
I2c-designware-pcidrv.c文件
内核启动时会扫描所有pci外设为它们分配内存与io空间,并读取配置空间信息填充pci_dev结构体注册到pci总线(设备驱动模型当中的总线),然后就是pci设备与pci驱动的匹配过程,使用厂商号、设备号、次厂商号、次设备号完成匹配,如下匹配列表:
static const struct pci_device_id i2_designware_pci_ids[] = {
/* Medfield */
{ PCI_VDEVICE(INTEL, 0x0817), medfield },
{ PCI_VDEVICE(INTEL, 0x0818), medfield },
{ PCI_VDEVICE(INTEL, 0x0819), medfield },
{ PCI_VDEVICE(INTEL, 0x082C), medfield },
{ PCI_VDEVICE(INTEL, 0x082D), medfield },
{ PCI_VDEVICE(INTEL, 0x082E), medfield },
/* Merrifield */
{ PCI_VDEVICE(INTEL, 0x1195), merrifield },
{ PCI_VDEVICE(INTEL, 0x1196), merrifield },
/* Baytrail */
{ PCI_VDEVICE(INTEL, 0x0F41), baytrail },
……
}
当pci设备与上述列表匹配成功后,将会执行i2c-designware-pcidrv.c中的probe函数:
static int i2c_dw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{ //根据匹配id获取controller数据,里面有传输速度、收发数据长度等
controller = &dw_pci_controllers[id->driver_data];
r = pcim_enable_device(pdev);//使能pci设备内存和IO空间
r = pcim_iomap_regions(pdev, 1 << 0, pci_name(pdev));//内存/io空间映射
dev = devm_kzalloc(&pdev->dev, sizeof(struct dw_i2c_dev), GFP_KERNEL);
//以下填充dev数据
dev->clk = NULL;
dev->controller = controller;
dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz;
dev->base = pcim_iomap_table(pdev)[0];//设置寄存器基地址
dev->dev = &pdev->dev;
dev->irq = pdev->irq;//设置中断号
dev->flags |= controller->flags;
dev->functionality = controller->functionality | //支持默认传输方式
DW_IC_DEFAULT_FUNCTIONALITY;
dev->master_cfg = controller->bus_cfg; //传输速度、主从方式、使能重复开始
if (controller->scl_sda_cfg) {//设置标准和快速i2c的clk高低电平最少保持时间
cfg = controller->scl_sda_cfg;
dev->ss_hcnt = cfg->ss_hcnt;
dev->fs_hcnt = cfg->fs_hcnt;
dev->ss_lcnt = cfg->ss_lcnt;
dev->fs_lcnt = cfg->fs_lcnt;
dev->sda_hold_time = cfg->sda_hold;//设置sda高电平最少保持时间
}
dev->tx_fifo_depth = controller->tx_fifo_depth;//设置发送buf能发几个字节
dev->rx_fifo_depth = controller->rx_fifo_depth;//设置接收buf能接收几个字节
adap = &dev->adapter;
adap->owner = THIS_MODULE;
adap->class = 0;
adap->nr = controller->bus_num;//总线号-1,表示动态注册i2c适配器
//以上都是填充dev数据,最终给下面的函数使用
r = i2c_dw_probe(dev);//调用初始化核心函数
}
可以看到主要是记录i2c适配器的配置,供后续使用。
i2c_dw_probe函数两种注册方式都会调用,后面一起分析。
platform方式
I2c-designware-platdrv.c文件
platform平台总线是一条虚拟的总线,即没有像i2c、usb、pci总线有对应的实体,内核为了让无法归类的设备也有挂接的总线,因此提出了platform虚拟总线。I2c-designware-platdrv.c注册了一个平台驱动,提供了三种匹配规则,如下:
static struct platform_driver dw_i2c_driver = {
.probe = dw_i2c_plat_probe,
.remove = dw_i2c_plat_remove,
.driver = {
.name = "i2c_designware",//设备名称与驱动名称匹配
.of_match_table = of_match_ptr(dw_i2c_of_match),//设备树方式匹配,一般arm用
.acpi_match_table = ACPI_PTR(dw_i2c_acpi_match),//acpi方式匹配
.pm = DW_I2C_DEV_PMOPS,
},
};
任何一种匹配方式匹配成功后将调用I2c-designware-platdrv.c文件中的probe函数:
static int dw_i2c_plat_probe(struct platform_device *pdev)
{
irq = platform_get_irq(pdev, 0);//获取中断号
//获取含有i2c控制器寄存器物理基地址的resource
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dev->base = devm_ioremap_resource(&pdev->dev, mem);//物理地址映射成虚拟地址保存
dev->dev = &pdev->dev;
dev->irq = irq;
if (pdata) {//如果平台设备私有数据不为空,则获取传输速度
dev->clk_freq = pdata->i2c_scl_freq;
} else {//从设备树中获取传输速度、sda与scl高低电平保持时间
device_property_read_u32(&pdev->dev, "i2c-sda-hold-time-ns", &ht);
device_property_read_u32(&pdev->dev, "i2c-sda-falling-time-ns",&dev->sda_falling_time);
device_property_read_u32(&pdev->dev, "i2c-scl-falling-time-ns", &dev->scl_falling_time);
device_property_read_u32(&pdev->dev, "clock-frequency", &dev->clk_freq);
}
acpi_speed = i2c_acpi_find_bus_speed(&pdev->dev);//从acpi中获取i2c传输速度
for (i = 1; i < ARRAY_SIZE(supported_speeds); i++) {
if (acpi_speed < supported_speeds[i]) break;
}//对齐标准速度,100k、400k、1mk、3.4mk
acpi_speed = supported_speeds[i - 1];
if (acpi_speed && dev->clk_freq)//都不为空,取最小速度
dev->clk_freq = min(dev->clk_freq, acpi_speed);
else if (acpi_speed || dev->clk_freq)//取最大速度
dev->clk_freq = max(dev->clk_freq, acpi_speed);
else dev->clk_freq = 400000;//都为空则设置成400k
//设置收发数据buf长度,获取acpi中设置的sda高电平时间并根据传输速度选择
if (has_acpi_companion(&pdev->dev))//如果设备存在acpi信息
dw_i2c_acpi_configure(pdev);
//判断是否为从机,若是则按从机配置
if (i2c_detect_slave_mode(&pdev->dev)) i2c_dw_configure_slave(dev);
else//配置为主机模式、配置速度与使能重复开始
i2c_dw_configure_master(dev);
adap = &dev->adapter;
adap->owner = THIS_MODULE;
adap->class = I2C_CLASS_DEPRECATED;
adap->dev.of_node = pdev->dev.of_node;
//根据模式选择对应probe函数
if (dev->mode == DW_IC_SLAVE) ret = i2c_dw_probe_slave(dev);
else ret = i2c_dw_probe(dev);//一般调用这个
}
可以看到同样也主要是记录i2c适配器的配置,供后续使用。
两者最终都会调用到i2c_dw_probe函数。
i2c_dw_probe
int i2c_dw_probe(struct dw_i2c_dev *dev)
{
init_completion(&dev->cmd_complete);//初始化信号量,收发函数和中断使用
dev->init = i2c_dw_init_master;//设置初始化函数
dev->disable = i2c_dw_disable;
dev->disable_int = i2c_dw_disable_int;
ret = dev->init(dev);//调用初始化函数
//以下配置adapter相关
snprintf(adap->name, sizeof(adap->name),"Synopsys DesignWare I2C adapter");
adap->retries = 3;
adap->algo = &i2c_dw_algo;//设置了i2c通信方法
adap->dev.parent = dev->dev;
i2c_set_adapdata(adap, dev);
if (dev->pm_disabled) {//选择中断模式
dev_pm_syscore_device(dev->dev, true); irq_flags = IRQF_NO_SUSPEND;
} else { irq_flags = IRQF_SHARED | IRQF_COND_SUSPEND; }
i2c_dw_disable_int(dev);//先关闭中断
ret = devm_request_irq(dev->dev, dev->irq, i2c_dw_isr, irq_flags,
dev_name(dev->dev), dev);//注册中断,中断函数为i2c_dw_isr
ret = i2c_dw_init_recovery_info(dev);//未知
ret = i2c_add_numbered_adapter(adap);//注册i2c控制器,也就是i2c_adapter
}
先看看初始化函数。
static int i2c_dw_init_master(struct dw_i2c_dev *dev)
{
/* 根据速度类型设置clk的时间参数 */
dw_writel(dev, hcnt, DW_IC_S/F/H-S_SCL_HCNT);
dw_writel(dev, lcnt, DW_IC_S/F/H-S_SCL_LCNT);
/* Configure SDA Hold Time if required */
reg = dw_readl(dev, DW_IC_COMP_VERSION);
if (reg >= DW_IC_SDA_HOLD_MIN_VERS) {
if (!dev->sda_hold_time) {
/* Keep previous hold time setting if no one set it */
dev->sda_hold_time = dw_readl(dev, DW_IC_SDA_HOLD);
}
if (!(dev->sda_hold_time & DW_IC_SDA_HOLD_RX_MASK))//
dev->sda_hold_time |= 1 << DW_IC_SDA_HOLD_RX_SHIFT;
dw_writel(dev, dev->sda_hold_time, DW_IC_SDA_HOLD);
} else if (dev->sda_hold_time) {//硬件太老
dev_warn(dev->dev,
"Hardware too old to adjust SDA hold time.\n");
}
i2c_dw_configure_fifo_master(dev);//配置IIC_CON寄存器,也就是设置模式、速度等
}
主要是检查各类参数是否存在,若没有则使用默认值。根据速度类型配置clk时间参数,配置i2c控制器模式、i2c传输速度、使能重复开始等。这个函数在传输超时时还会调用。
看见了吧,使用记录的配置初始化i2c适配器后调用i2c_add_numbered_adapter函数向i2c_bus_type注册i2c_adapter结构,正好对应之前看过的图,再放一次。
I2c-designware框架下如何收发数据?
简要过程如下
详细分析如下
收发数据相关函数均在i2c-designware-master.c文件
static int i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
{
struct dw_i2c_dev *dev = i2c_get_adapdata(adap);//从adapter获取私有数据
pm_runtime_get_sync(dev->dev);//电源管理相关
reinit_completion(&dev->cmd_complete);//初始化信号量
dev->msgs = msgs; //保存收发数据结构,以下初始化成员变量
dev->msgs_num = num; dev->cmd_err = 0; dev->msg_write_idx = 0;
dev->msg_read_idx = 0; dev->msg_err = 0; dev->status = STATUS_IDLE;
dev->abort_source = 0; dev->rx_outstanding = 0;
ret = i2c_dw_acquire_lock(dev);//如果有锁,则加锁
ret = i2c_dw_wait_bus_not_busy(dev);//总线是否繁忙
/* Start the transfers */
i2c_dw_xfer_init(dev);//使能传输
/* Wait for tx to complete *///等待超时
if (!wait_for_completion_timeout(&dev->cmd_complete, adap->timeout)) {
/* i2c_dw_init implicitly disables the adapter */
i2c_recover_bus(&dev->adapter);//复位总线?
i2c_dw_init_master(dev);//再次初始化i2c控制器
}
__i2c_dw_disable_nowait(dev);//关闭i2c控制器
if (dev->msg_err) {//有错误,返回错误码
ret = dev->msg_err;
}
/* No error *///若无错误则返回传输msg个数
if (likely(!dev->cmd_err && !dev->status)) {
ret = num;
}
}
先将收发数据结构msg保存至adapter私有数据结构中,然后检查总线是否被使用等,接着启动i2c传输后,等待信号量被释放,若等待超时则重新初始化i2c控制器,启动i2c传输函数如下:
static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
{
struct i2c_msg *msgs = dev->msgs;
u32 ic_con, ic_tar = 0;
/* Disable the adapter */
__i2c_dw_disable(dev);
/* If the slave address is ten bit address, enable 10BITADDR */
ic_con = dw_readl(dev, DW_IC_CON);
if (msgs[dev->msg_write_idx].flags & I2C_M_TEN) {
ic_con |= DW_IC_CON_10BITADDR_MASTER;
ic_tar = DW_IC_TAR_10BITADDR_MASTER;
} else {
ic_con &= ~DW_IC_CON_10BITADDR_MASTER;
}
dw_writel(dev, ic_con, DW_IC_CON);//根据上面选择设置是否为10位地址
dw_writel(dev, msgs[dev->msg_write_idx].addr | ic_tar, DW_IC_TAR);//设置从机地址
/* Enforce disabled interrupts (due to HW issues) */
i2c_dw_disable_int(dev);//先关闭中断
/* Enable the adapter */
__i2c_dw_enable(dev);//使能i2c控制器内部调用dw_writel(dev, 1, DW_IC_ENABLE);
/* Dummy read to avoid the register getting stuck on Bay Trail */
dw_readl(dev, DW_IC_ENABLE_STATUS);
/* Clear and enable interrupts */
dw_readl(dev, DW_IC_CLR_INTR);
dw_writel(dev, DW_IC_INTR_MASTER_MASK, DW_IC_INTR_MASK);//之后立刻产生发送为空中断
}这个函数设置了当前传输是否为10位地址方式,并启用了i2c控制器和中断,实际的数据收发过程在中断函数中完成!
前面说到注册的中断函数为i2c_de_isr,其代码如下:
static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
{
struct dw_i2c_dev *dev = dev_id;
u32 stat, enabled;
enabled = dw_readl(dev, DW_IC_ENABLE);
stat = dw_readl(dev, DW_IC_RAW_INTR_STAT);
dev_dbg(dev->dev, "enabled=%#x stat=%#x\n", enabled, stat);
if (!enabled || !(stat & ~DW_IC_INTR_ACTIVITY))//判断是否开启传输后产生的中断
return IRQ_NONE;
i2c_dw_irq_handler_master(dev);//调用真正处理函数
return IRQ_HANDLED;
}
如果中断是开启传输之后产生的中断,则调用真正的收发处理函数:
static int i2c_dw_irq_handler_master(struct dw_i2c_dev *dev)
{
stat = i2c_dw_read_clear_intrbits(dev);//读取中断状态位
if (stat & DW_IC_INTR_TX_ABRT) {//发送过程中出现失败
dev->cmd_err |= DW_IC_ERR_TX_ABRT;
dev->status = STATUS_IDLE;
dw_writel(dev, 0, DW_IC_INTR_MASK);
}
if (stat & DW_IC_INTR_RX_FULL)
i2c_dw_read(dev);//接收满了调用读函数
if (stat & DW_IC_INTR_TX_EMPTY)
i2c_dw_xfer_msg(dev);//发送为空,即开始传输数据
//发送成功产生停止位、发送失败或其他错误均唤醒左侧发送等待
if ((stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET)) || dev->msg_err)
complete(&dev->cmd_complete);//唤醒等待完成量的进程
else if (unlikely(dev->flags & ACCESS_INTR_MASK)) {//未知处理方式
stat = dw_readl(dev, DW_IC_INTR_MASK); i2c_dw_disable_int(dev);
dw_writel(dev, stat, DW_IC_INTR_MASK);
}
}
static void i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
{
u32 addr = msgs[dev->msg_write_idx].addr;
//设置初始能产生哪些中断类型,发送空、接收满、发送意外停止、正常结束
intr_mask = DW_IC_INTR_MASTER_MASK;
for (; dev->msg_write_idx < dev->msgs_num; dev->msg_write_idx++) {//开始传输
//若地址与之前不一致,则错误
if (msgs[dev->msg_write_idx].addr != addr) { dev->msg_err = -EINVAL; break;}
//若长度为0,则错误
if (msgs[dev->msg_write_idx].len == 0) { dev->msg_err = -EINVAL; break; }
//若上一个发送还未完成,则不执行下面
if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) {
buf = msgs[dev->msg_write_idx].buf; buf_len = msgs[dev->msg_write_idx].len;
if ((dev->master_cfg & DW_IC_CON_RESTART_EN) &&(dev->msg_write_idx > 0))
need_restart = true;
}
//单次传输最多能发送或接收几个有效字节
tx_limit = dev->tx_fifo_depth - dw_readl(dev, DW_IC_TXFLR);
rx_limit = dev->rx_fifo_depth - dw_readl(dev, DW_IC_RXFLR);
//填充发送buf,开始发送
while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) {
u32 cmd = 0;
if (dev->msg_write_idx == dev->msgs_num - 1 &&
buf_len == 1 && !(flags & I2C_M_RECV_LEN))
cmd |= BIT(9);//若是最后一位数据,产生STOP信号
if (need_restart) {//若需要产生重复开始信号
cmd |= BIT(10); need_restart = false;
}
if (msgs[dev->msg_write_idx].flags & I2C_M_RD) {
if (dev->rx_outstanding >= dev->rx_fifo_depth)
break;//若待接收个数大于最大接收个数,则下次再接收
dw_writel(dev, cmd | 0x100, DW_IC_DATA_CMD);//切换读
rx_limit--;//接收buf所能接收长度减一
dev->rx_outstanding++;//待接收个数
} else
dw_writel(dev, cmd | *buf++, DW_IC_DATA_CMD);//将数据放入发送buf
tx_limit--; buf_len--;//所能发送数据长度减一,需要发送的len减一
}
dev->tx_buf = buf;//存储当前发送位置
dev->tx_buf_len = buf_len;//存储当前还需要发送的长度len
*///当次数据未能全部发送,设置正在发送状态
if (buf_len > 0 || flags & I2C_M_RECV_LEN) {
dev->status |= STATUS_WRITE_IN_PROGRESS; break;
} else
dev->status &= ~STATUS_WRITE_IN_PROGRESS;
}
//如果数据均放到了发送Buf,则不产生发送为空中断
if (dev->msg_write_idx == dev->msgs_num) intr_mask &= ~DW_IC_INTR_TX_EMPTY;
if (dev->msg_err) intr_mask = 0;
//再次设置能产生哪些中断,等待下一次中断
dw_writel(dev, intr_mask, DW_IC_INTR_MASK);
}//若单次未发送完一个msg,则还会产生发送为空中断,将继续发送完剩余数据,不断循环直至所有msg发送完毕。
接收函数较简单,这里不再分析。
i2c_adapter的一种奇怪注册方式
看一个图片。
为什么属于pci设备(i2c-4/i2c-5)的i2c控制器会匹配platform总线上的驱动(i2c-4/device下面的driver指向)?
查看一下i2c-4对应的pci设备匹配的驱动名称,如下图。
匹配的驱动名称为intel-lpass。
查看pci厂商号与设备号。
找到对应驱动文件——Intel-lpss-pci.c。
文件中有一个pci_device_id匹配列表,部分内容如下:
static const struct pci_device_id intel_lpss_pci_ids[] = {
……
{ PCI_VDEVICE(INTEL, 0xa127), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa128), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa129), (kernel_ulong_t)&spt_info },
{ PCI_VDEVICE(INTEL, 0xa12a), (kernel_ulong_t)&spt_info },
{ PCI_VDEVICE(INTEL, 0xa160), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa161), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa162), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa166), (kernel_ulong_t)&spt_uart_info },
……
};
看匹配列表中有i2c-4对应pci设备的设备号0xa160,因此属于pci设备的i2c控制器会匹配上这个pci驱动,进而执行该驱动probe函数:
static int intel_lpss_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
ret = pcim_enable_device(pdev);//使能pci设备内存/io空间
info->mem = &pdev->resource[0];//保存存有内存/io空间地址的resource
info->irq = pdev->irq;//保存中断号
ret = intel_lpss_probe(&pdev->dev, info);
}
int intel_lpss_probe(struct device *dev, const struct intel_lpss_platform_info *info)
{
if (!info || !info->mem || info->irq <= 0)
return -EINVAL;
lpss->priv = devm_ioremap(dev, info->mem->start + LPSS_PRIV_OFFSET,
LPSS_PRIV_SIZE);//映射内存空间(i2c控制器寄存器基地址)
lpss->info = info;
lpss->dev = dev;
lpss->caps = readl(lpss->priv + LPSS_PRIV_CAPS);//获取设备类型
//若是I2c设备,设置名字为“i2c_designware”,重点
ret = intel_lpss_assign_devs(lpss);//根据设备类型选择配置包
lpss->cell->properties = info->properties;//未知
intel_lpss_init_dev(lpss);//未知
lpss->devid = ida_simple_get(&intel_lpss_devid_ida, 0, 0, GFP_KERNEL);
ret = intel_lpss_register_clock(lpss);//未知
intel_lpss_ltr_expose(lpss);//未知
ret = intel_lpss_debugfs_add(lpss);//未知
if (intel_lpss_has_idma(lpss)) {
/*Ensure the DMA driver is loaded before the host
* controller device appears, so that the host controller
* driver can request its DMA channels as early as
* possible.
*
* If the DMA module is not there that's OK as well.
*/
intel_lpss_request_dma_module(LPSS_IDMA64_DRIVER_NAME);
ret = mfd_add_devices(dev, lpss->devid, &intel_lpss_idma64_cell,
1, info->mem, info->irq, NULL);
}
ret = mfd_add_devices(dev, lpss->devid, lpss->cell,
1, info->mem, info->irq, NULL);//i2c设备执行这个函数
}
int mfd_add_devices(struct device *parent, int id, const struct mfd_cell *cells, int n_devs, struct resource *mem_base, int irq_base, struct irq_domain *domain)
{
for (i = 0; i < n_devs; i++) {
atomic_set(&cnts[i], 0);
ret = mfd_add_device(parent, id, cells + i, cnts + i, mem_base,
irq_base, domain);
}
}
往下调用mfd_add_device函数,注意少了一个s:
static int mfd_add_device(struct device *parent, int id,
const struct mfd_cell *cell, atomic_t *usage_count,
struct resource *mem_base, int irq_base, struct irq_domain *domain)
{
//分配一个平台设备,使用cell->name名字,也就是“i2c_designware”
pdev = platform_device_alloc(cell->name, platform_id);
//分配resources空间
res = kcalloc(cell->num_resources, sizeof(*res), GFP_KERNEL);
//以下初始化平台设备结构体
pdev->dev.parent = parent;
pdev->dev.type = &mfd_dev_type;
pdev->dev.dma_mask = parent->dma_mask;
pdev->dev.dma_parms = parent->dma_parms;
pdev->dev.coherent_dma_mask = parent->coherent_dma_mask;
mfd_acpi_add_device(cell, pdev);//acpi相关,作用未知
if (cell->pdata_size) {//设置平台设备相关数据
ret = platform_device_add_data(pdev,
cell->platform_data, cell->pdata_size);
}
//初始化平台设备的resources
for (r = 0; r < cell->num_resources; r++) {
res[r].name = cell->resources[r].name;
res[r].flags = cell->resources[r].flags;
/* Find out base to use */
if ((cell->resources[r].flags & IORESOURCE_MEM) && mem_base) {
res[r].parent = mem_base;
res[r].start = mem_base->start + cell->resources[r].start;
res[r].end = mem_base->start + cell->resources[r].end;
} else if (cell->resources[r].flags & IORESOURCE_IRQ) {
if (domain) {
/* Unable to create mappings for IRQ ranges. */
WARN_ON(cell->resources[r].start != cell->resources[r].end);
res[r].start = res[r].end = irq_create_mapping(
domain, cell->resources[r].start);
} else {
res[r].start = irq_base + cell->resources[r].start;
res[r].end = irq_base + cell->resources[r].end;
}
} else {
res[r].parent = cell->resources[r].parent;
res[r].start = cell->resources[r].start;
res[r].end = cell->resources[r].end;
}
if (!cell->ignore_resource_conflicts) {
if (has_acpi_companion(&pdev->dev)) {
ret = acpi_check_resource_conflict(&res[r]);
}
}
}
//将resources添加到平台结构体中,供平台总线上驱动使用!
ret = platform_device_add_resources(pdev, res, cell->num_resources);
//根据当前系统i2c_designware设备注册个数修改设备名并注册设备到平台总线
ret = platform_device_add(pdev);//名字格式 i2c_designware.x
}
因此在该pci驱动中并没有直接注册i2c_adapter,而是先注册了平台设备,再经平台驱动匹配进而注册i2c_adapter。