【linux iic子系统】i2c-designware框架分析(六)

9 篇文章 5 订阅

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。

  • 5
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux的I2C子系统是用于在Linux内核中管理和操作I2C总线的子系统。它提供了一组API和驱动程序,允许用户空间应用程序与连接到I2C总线上的设备进行通信。 在Linux中,I2C子系统由以下几个主要组件组成: 1. I2C核心驱动程序:这是I2C子系统的核心部分,负责管理I2C总线和设备的注册、协议处理等功能。它提供了一组API供其他驱动程序或用户空间应用程序使用。 2. I2C适配器驱动程序:这些驱动程序用于支持特定的硬件I2C适配器,如FPGA、SOC等。它们与I2C核心驱动程序紧密配合,负责将硬件特定的操作转换为通用的I2C操作。 3. I2C设备驱动程序:这些驱动程序用于支持连接到I2C总线上的具体设备。每个I2C设备都有一个对应的设备驱动程序,负责处理设备的初始化、通信协议等。在Linux中,这些设备驱动程序通常作为内核模块存在。 4. I2C工具和库:除了内核驱动程序外,Linux还提供了一些用户空间工具和库,用于与I2C设备进行交互。例如,`i2cdetect`工具用于检测I2C总线上的设备地址,`i2cget`和`i2cset`工具用于读取和写入I2C设备的寄存器值。 用户空间应用程序可以使用I2C子系统提供的API和工具来访问和控制连接到I2C总线上的设备。通过打开适当的设备节点文件,并使用相应的读写操作,可以向设备发送命令和数据,以及从设备读取响应和数据。 总而言之,Linux的I2C子系统提供了一套完整的解决方案,使用户能够方便地在Linux环境中操作和管理I2C设备。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值