当前位置: 首页 > news >正文

IMX6ULL的I2C驱动详细分析

IMX6ULL的I2C驱动详细分析


文章目录

  • IMX6ULL的I2C驱动详细分析
  • i2c_imx_driver 的平台驱动注册
  • i2c_imx_probe注册函数
  • i2c_imx_algoI2C算法结构体
  • i2c_imx_start开始I2C
  • i2c_imx_stop停止I2C
  • i2c_imx_isr中断服务函数
  • i2c_imx_dma_writeDMA 进行写操作的 I2C 传输
  • 2c_imx_dma_read
  • i2c_imx_write
  • i2c_imx_read


在这里插入图片描述

i2c_imx_driver 的平台驱动注册

static struct platform_driver i2c_imx_driver = {.probe = i2c_imx_probe, // 注册函数.remove = i2c_imx_remove, // 注销函数.driver    = {.name = DRIVER_NAME, // 驱动名.owner = THIS_MODULE, // 模块拥有者.of_device_id = i2c_imx_dt_ids, // 设备树匹配表.pm = IMX_I2C_PM, // 电源管理},.id_table    = imx_i2c_devtype, // 设备ID表
};static int __init i2c_adap_imx_init(void)
{return platform_driver_register(&i2c_imx_driver); // 注册平台驱动
}
subsys_initcall(i2c_adap_imx_init); // 子系统初始化static void __exit i2c_adap_imx_exit(void)
{platform_driver_unregister(&i2c_imx_driver); // 注销平台驱动
}
module_exit(i2c_adap_imx_exit); // 模块退出

这段代码定义了一个名为 i2c_imx_driver 的平台驱动结构体,并实现了两个函数:i2c_adap_imx_init 和 i2c_adap_imx_exit。
首先,i2c_imx_driver 结构体中定义了以下字段:
probe:注册函数,用于在设备匹配成功时调用以进行设备初始化。
remove:注销函数,用于在设备被移除时调用以进行资源释放。
driver:驱动结构体,其中包含驱动的名称、模块拥有者、设备树匹配表和电源管理等信息。
id_table:设备ID表,用于指定支持的设备ID。
接下来,在 i2c_adap_imx_init 函数中,通过调用 platform_driver_register 函数来注册平台驱动 i2c_imx_driver。这将使得驱动在系统初始化期间被加载并可用。函数返回注册结果。
然后,在 i2c_adap_imx_exit 函数中,通过调用 platform_driver_unregister 函数来注销平台驱动 i2c_imx_driver。这将在模块退出时执行,用于释放已注册的平台驱动。函数没有返回值。
最后,使用 module_exit 宏将 i2c_adap_imx_exit 函数注册为模块的退出函数。这将在模块卸载时调用以执行平台驱动的注销操作。
这段代码的作用是实现了一个基于平台的 I2C 驱动,并提供了初始化和退出函数来注册和注销该驱动。驱动注册时将根据设备树的匹配信息进行初始化,并在模块退出时进行注销,以确保平台驱动的正确加载和释放。

i2c_imx_probe注册函数

这段代码是 I2C 设备探测函数的实现,它被用作 i2c_imx_driver 的 probe 成员。
函数的主要功能是在设备匹配成功时进行设备初始化。下面是该函数的主要步骤:
获取设备树匹配信息,用于判断是否有设备树匹配数据。
获取中断号和资源信息,并对 I2C 控制器的基地址进行映射。
分配并初始化 i2c_imx_struct 结构体,用于存储与该 I2C 设备相关的信息。
设置 I2C 适配器的名称、拥有者、算法等成员变量。
获取并使能 I2C 时钟。
请求中断,并设置中断处理函数。
初始化等待队列和适配器数据。
设置时钟分频和芯片寄存器的默认值。
添加 I2C 适配器。
设置平台驱动数据。
注销时钟,并打印调试信息。
如果支持 DMA,初始化 DMA 配置。
最后,函数返回0表示成功初始化设备,返回其他错误代码表示初始化失败。
该函数的作用是在 I2C 设备探测阶段完成必要的初始化操作,以确保 I2C 设备正常工作。

// I2C设备探测函数
static int i2c_imx_probe(struct platform_device *pdev)
{// 获取设备树匹配信息const struct of_device_id *of_id = of_match_device(i2c_imx_dt_ids,&pdev->dev);// 定义I2C设备结构体指针struct imx_i2c_struct *i2c_imx;// 定义资源结构体指针struct resource *res;// 定义I2C平台数据结构体指针struct imxi2c_platform_data *pdata = dev_get_platdata(&pdev->dev);// 定义I2C控制器基地址指针void __iomem *base;// 定义中断号int irq, ret;// 定义DMA物理地址dma_addr_t phy_addr;// 打印调试信息dev_dbg(&pdev->dev, "<%s>\n", __func__);// 获取中断号irq = platform_get_irq(pdev, 0);if (irq < 0) {dev_err(&pdev->dev, "can't get irq number\n");return irq;}// 获取资源res = platform_get_resource(pdev, IORESOURCE_MEM, 0);// 映射I2C控制器基地址base = devm_ioremap_resource(&pdev->dev, res);if (IS_ERR(base))return PTR_ERR(base);// 获取DMA物理地址phy_addr = (dma_addr_t)res->start;// 分配I2C设备结构体内存i2c_imx = devm_kzalloc(&pdev->dev, sizeof(*i2c_imx), GFP_KERNEL);if (!i2c_imx)return -ENOMEM;// 判断是否有设备树匹配信息if (of_id)i2c_imx->hwdata = of_id->data;elsei2c_imx->hwdata = (struct imx_i2c_hwdata *)platform_get_device_id(pdev)->driver_data;/* Setup i2c_imx driver structure */// 复制设备名到I2C适配器结构体中strlcpy(i2c_imx->adapter.name, pdev->name, sizeof(i2c_imx->adapter.name));// 设置I2C适配器结构体中的成员变量i2c_imx->adapter.owner        = THIS_MODULE;i2c_imx->adapter.algo        = &i2c_imx_algo;i2c_imx->adapter.dev.parent    = &pdev->dev;i2c_imx->adapter.nr        = pdev->id;i2c_imx->adapter.dev.of_node    = pdev->dev.of_node;i2c_imx->base            = base;/* 获取I2C时钟 */i2c_imx->clk = devm_clk_get(&pdev->dev, NULL); // 获取I2C时钟if (IS_ERR(i2c_imx->clk)) {dev_err(&pdev->dev, "can't get I2C clock\n"); // 获取时钟失败return PTR_ERR(i2c_imx->clk);}ret = clk_prepare_enable(i2c_imx->clk); // 使能I2C时钟if (ret) {dev_err(&pdev->dev, "can't enable I2C clock\n"); // 使能时钟失败return ret;}/* 请求中断 */ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr,IRQF_NO_SUSPEND, pdev->name, i2c_imx); // 请求中断if (ret) {dev_err(&pdev->dev, "can't claim irq %d\n", irq); // 请求中断失败goto clk_disable;}/* 初始化队列 */init_waitqueue_head(&i2c_imx->queue); // 初始化等待队列/* 设置适配器数据 */i2c_set_adapdata(&i2c_imx->adapter, i2c_imx); // 设置适配器数据/* 设置时钟分频 */i2c_imx->bitrate = IMX_I2C_BIT_RATE;ret = of_property_read_u32(pdev->dev.of_node,"clock-frequency", &i2c_imx->bitrate);if (ret < 0 && pdata && pdata->bitrate)i2c_imx->bitrate = pdata->bitrate;/* 设置芯片寄存器为默认值 */imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,i2c_imx, IMX_I2C_I2CR);imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);/* 添加I2C适配器 */ret = i2c_add_numbered_adapter(&i2c_imx->adapter);if (ret < 0) {dev_err(&pdev->dev, "registration failed\n");goto clk_disable;}/* 设置平台驱动数据 */platform_set_drvdata(pdev, i2c_imx);clk_disable_unprepare(i2c_imx->clk);dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d\n", irq);dev_dbg(&i2c_imx->adapter.dev, "device resources: %pR\n", res);dev_dbg(&i2c_imx->adapter.dev, "adapter name: \"%s\"\n",i2c_imx->adapter.name);dev_info(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n");/* 如果支持DMA,则初始化DMA配置 */i2c_imx_dma_request(i2c_imx, phy_addr);return 0;   /* 返回OK */clk_disable:clk_disable_unprepare(i2c_imx->clk);return ret;
}

i2c_imx_algoI2C算法结构体

函数 i2c_imx_xfer 是一个 I2C 传输函数,用于执行一系列的 I2C 数据传输操作。以下是该函数的主要步骤:
执行 I2C 传输的准备工作,包括启动 I2C 传输。
针对每个传输消息进行读写操作。根据消息的标志位,如果是读操作,则调用 i2c_imx_read 函数进行读取;如果是写操作,则根据数据长度决定是否使用 DMA 进行写操作,或者使用普通的写操作函数 i2c_imx_write。
如果发生错误,跳转到 fail0 标签处进行错误处理。
完成所有消息的传输后,调用 i2c_imx_stop 函数停止 I2C 传输。
打印调试信息,然后根据传输结果返回传输的消息数或错误代码。
函数 i2c_imx_func 是用于返回 I2C 总线所支持的功能的函数。它指定了 I2C 总线支持的功能,包括基本的 I2C 功能、SMBus 模拟功能以及读取块数据的功能。
这两个函数一起实现了在 I2C 总线上进行数据传输的功能,并提供了对 I2C 总线所支持功能的查询。

// I2C算法结构体
static struct i2c_algorithm i2c_imx_algo = {.master_xfer    = i2c_imx_xfer, // 主机传输函数.functionality    = i2c_imx_func, // 返回总线支持的功能
};
static int i2c_imx_xfer(struct i2c_adapter *adapter,struct i2c_msg *msgs, int num)
{unsigned int i, temp;int result;bool is_lastmsg = false;struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter);dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);/* Start I2C transfer */result = i2c_imx_start(i2c_imx); // 开始I2C传输if (result)goto fail0;/* read/write data */for (i = 0; i < num; i++) { // 读写数据if (i == num - 1)is_lastmsg = true;if (i) {dev_dbg(&i2c_imx->adapter.dev,"<%s> repeated start\n", __func__);temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);temp |= I2CR_RSTA; // 重复启动imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);result =  i2c_imx_bus_busy(i2c_imx, 1);if (result)goto fail0;}dev_dbg(&i2c_imx->adapter.dev,"<%s> transfer message: %d\n", __func__, i);/* write/read data */
#ifdef CONFIG_I2C_DEBUG_BUStemp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);dev_dbg(&i2c_imx->adapter.dev,"<%s> CONTROL: IEN=%d, IIEN=%d, MSTA=%d, MTX=%d, TXAK=%d, RSTA=%d\n",__func__,(temp & I2CR_IEN ? 1 : 0), (temp & I2CR_IIEN ? 1 : 0),(temp & I2CR_MSTA ? 1 : 0), (temp & I2CR_MTX ? 1 : 0),(temp & I2CR_TXAK ? 1 : 0), (temp & I2CR_RSTA ? 1 : 0));temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);dev_dbg(&i2c_imx->adapter.dev,"<%s> STATUS: ICF=%d, IAAS=%d, IBB=%d, IAL=%d, SRW=%d, IIF=%d, RXAK=%d\n",__func__,(temp & I2SR_ICF ? 1 : 0), (temp & I2SR_IAAS ? 1 : 0),(temp & I2SR_IBB ? 1 : 0), (temp & I2SR_IAL ? 1 : 0),(temp & I2SR_SRW ? 1 : 0), (temp & I2SR_IIF ? 1 : 0),(temp & I2SR_RXAK ? 1 : 0));
#endifif (msgs[i].flags & I2C_M_RD)result = i2c_imx_read(i2c_imx, &msgs[i], is_lastmsg); // 读取else {if (i2c_imx->dma && msgs[i].len >= DMA_THRESHOLD)result = i2c_imx_dma_write(i2c_imx, &msgs[i]); // DMA写elseresult = i2c_imx_write(i2c_imx, &msgs[i]); // 写}if (result)goto fail0;}fail0:/* Stop I2C transfer */i2c_imx_stop(i2c_imx); // 停止I2C传输dev_dbg(&i2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__,(result < 0) ? "error" : "success msg",(result < 0) ? result : num);return (result < 0) ? result : num;
}// 返回I2C总线支持的功能
static u32 i2c_imx_func(struct i2c_adapter *adapter)
{return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL| I2C_FUNC_SMBUS_READ_BLOCK_DATA;
}

i2c_imx_start开始I2C

函数 i2c_imx_start 用于开始一次 I2C 事务。以下是该函数的主要步骤:
设置 I2C 时钟并准备使能该时钟。
将分频值写入相应寄存器。
启用 I2C 控制器,清除相关寄存器的状态。
等待控制器稳定,延时一段时间。
设置控制器为主机模式,并检查总线是否繁忙。
将控制器的配置设置为发送模式,并开启中断和发送应答信号。
返回执行结果。
函数的主要目的是初始化和配置 I2C 控制器,以准备开始一次 I2C 事务。它确保时钟和寄存器的设置正确,并将控制器设置为主机模式。函数执行成功后,即可开始进行 I2C 数据传输。

// 开始I2C事务
static int i2c_imx_start(struct imx_i2c_struct *i2c_imx)
{unsigned int temp = 0;int result;dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);// 设置I2C时钟i2c_imx_set_clk(i2c_imx);// 准备并使能I2C时钟result = clk_prepare_enable(i2c_imx->clk);if (result)return result;// 写入分频值imx_i2c_write_reg(i2c_imx->ifdr, i2c_imx, IMX_I2C_IFDR);/* 启用I2C控制器 */imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode, i2c_imx, IMX_I2C_I2CR);/* 等待控制器稳定 */udelay(50);/* 开始I2C事务 */temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);temp |= I2CR_MSTA;imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);result = i2c_imx_bus_busy(i2c_imx, 1);if (result)return result;i2c_imx->stopped = 0;temp |= I2CR_IIEN | I2CR_MTX | I2CR_TXAK;temp &= ~I2CR_DMAEN;imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);return result;
}

i2c_imx_stop停止I2C

函数 i2c_imx_stop 用于停止当前的 I2C 事务。以下是该函数的主要步骤:
检查是否已停止当前的 I2C 事务。如果是,则直接返回。
如果尚未停止,则执行以下操作:
清除控制器的主机模式和发送模式标志位,如果使用 DMA,则还会清除 DMA 使能标志位。
在某些 i.MXL 硬件错误的情况下,添加延迟以确保生成 “STOP” 位。
标记总线非繁忙,并将 stopped 标志设置为 1,表示 I2C 事务已停止。
禁用 I2C 控制器并释放相关资源。
函数的主要目的是完成 I2C 事务的停止过程。它会清除控制器的相关标志位,并释放使用的资源,以便下次进行新的 I2C 事务。

/** 停止I2C事务*/
static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx)
{unsigned int temp = 0;if (!i2c_imx->stopped) {/* 停止I2C事务 */dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);temp &= ~(I2CR_MSTA | I2CR_MTX);if (i2c_imx->dma)temp &= ~I2CR_DMAEN;imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);}if (is_imx1_i2c(i2c_imx)) {/** 这个延迟是由于i.MXL硬件错误引起的。* 如果没有(或太短的)延迟,将不会生成“STOP”位。*/udelay(i2c_imx->disable_delay);}if (!i2c_imx->stopped) {i2c_imx_bus_busy(i2c_imx, 0);i2c_imx->stopped = 1;}/* 禁用I2C控制器 */temp = i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);clk_disable_unprepare(i2c_imx->clk);
}

i2c_imx_isr中断服务函数

该函数是 I2C 控制器的中断服务例程。
当 I2C 控制器产生中断时,会调用该函数来处理中断。
函数首先读取状态寄存器 I2SR,并检查中断标志位 IIF 是否被设置。
如果中断标志位已经被设置,则保存状态寄存器的值,并清除中断标志位。
然后,函数会根据硬件数据表中的操作码,将清除中断标志位的操作码写入状态寄存器。
最后,函数会唤醒等待队列,以通知等待中的线程中断已经处理完毕。
如果中断标志位未被设置,函数返回 IRQ_NONE,表示未处理中断。
如果中断标志位已被设置并成功处理,函数返回 IRQ_HANDLED,表示中断已被处理。

static irqreturn_t i2c_imx_isr(int irq, void *dev_id)
{struct imx_i2c_struct *i2c_imx = dev_id;unsigned int temp;temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);if (temp & I2SR_IIF) {/* 保存状态寄存器 */i2c_imx->i2csr = temp;temp &= ~I2SR_IIF;temp |= (i2c_imx->hwdata->i2sr_clr_opcode & I2SR_IIF);imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2SR);wake_up(&i2c_imx->queue); // 唤醒等待队列return IRQ_HANDLED;}return IRQ_NONE;
}

i2c_imx_dma_writeDMA 进行写操作的 I2C 传输

以下是函数 i2c_imx_dma_write 的概要总结:该函数用于使用 DMA 进行写操作的 I2C 传输。
首先,函数对 DMA 相关参数进行设置,包括选择发送通道、传输方向、数据方向和传输长度。
然后,函数调用
i2c_imx_dma_xfer 函数执行 DMA 传输,将消息数据传输到设备。
接下来,函数开启 DMA 传输,将 I2C 控制器的
I2CR 寄存器中的 DMA 使能位置为1。
函数将从设备地址写入到
I2DR 寄存器,其中第一个字节必须由 CPU 传输。
函数使用等待完成的方式等待传输完成,同时检查是否超时。
一旦传输完成,函数关闭 DMA 传输,将 I2C 控制器的 I2CR 寄存器中的 DMA 使能位置为0。
最后,函数将最后一个数据字节由 CPU 传输到 I2DR 寄存器,并检查传输的完成情况。
如果传输过程中发生错误,函数将返回相应的错误代码。
如果传输成功完成,则调用
i2c_imx_acked 函数以确认从设备的应答状态,并返回结果。

static int i2c_imx_dma_write(struct imx_i2c_struct *i2c_imx,struct i2c_msg *msgs)
{int result;unsigned long time_left;unsigned int temp = 0;unsigned long orig_jiffies = jiffies;struct imx_i2c_dma *dma = i2c_imx->dma;struct device *dev = &i2c_imx->adapter.dev;// 设置DMA通道为发送通道dma->chan_using = dma->chan_tx;// DMA传输方向为从内存到设备dma->dma_transfer_dir = DMA_MEM_TO_DEV;// DMA数据方向为发送dma->dma_data_dir = DMA_TO_DEVICE;// DMA传输长度为消息长度减1dma->dma_len = msgs->len - 1;result = i2c_imx_dma_xfer(i2c_imx, msgs);if (result)return result;// 开启DMA传输temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);temp |= I2CR_DMAEN;imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);/** 写从设备地址。* 第一个字节必须由CPU传输。*/imx_i2c_write_reg(msgs->addr << 1, i2c_imx, IMX_I2C_I2DR);reinit_completion(&i2c_imx->dma->cmd_complete);time_left = wait_for_completion_timeout(&i2c_imx->dma->cmd_complete,msecs_to_jiffies(DMA_TIMEOUT));if (time_left == 0) {dmaengine_terminate_all(dma->chan_using);return -ETIMEDOUT;}/* 等待传输完成 */while (1) {temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);if (temp & I2SR_ICF) // 如果传输完成break;if (time_after(jiffies, orig_jiffies +msecs_to_jiffies(DMA_TIMEOUT))) { // 如果超时dev_dbg(dev, "<%s> Timeout\n", __func__);return -ETIMEDOUT;}schedule(); // 等待}temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);temp &= ~I2CR_DMAEN; // 关闭DMA传输imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);/* 最后一个数据字节必须由CPU传输 */imx_i2c_write_reg(msgs->buf[msgs->len-1],i2c_imx, IMX_I2C_I2DR);result = i2c_imx_trx_complete(i2c_imx);if (result)return result;return i2c_imx_acked(i2c_imx);
}

2c_imx_dma_read

函数i2c_imx_dma_read是用于在i.MX系列芯片上进行I2C数据读取的函数。它使用DMA进行数据传输,并处理了传输完成、超时等情况。
函数的主要步骤如下:
开启DMA传输:设置I2C控制寄存器的DMAEN位,启用DMA传输。

设置DMA通道为接收通道:将DMA通道设置为接收通道,并指定数据传输方向为从设备到内存。

执行DMA传输:调用i2c_imx_dma_xfer函数执行DMA传输。

等待DMA传输完成:使用等待完成机制等待DMA传输完成,如果超时则终止DMA传输并返回超时错误。

等待数据传输完成:循环检查I2C状态寄存器,直到传输完成或超时。如果超时,则返回超时错误。

关闭DMA传输:将I2C控制寄存器的DMAEN位清除,关闭DMA传输。

读取数据:从I2C数据寄存器读取n-1个字节的数据,并调用i2c_imx_trx_complete函数完成传输。

处理最后一个消息:如果是最后一个消息,清除主模式和传输模式位,将总线设置为空闲状态,并标记传输已停止。否则,设置传输模式为主模式接收,并读取最后一个字节的数据。

返回0:传输完成,返回0表示成功。

总体而言,函数i2c_imx_dma_read使用DMA进行I2C数据读取,处理了传输中断、超时和最后一个消息的情况,并最终返回传输结果。

static int i2c_imx_dma_read(struct imx_i2c_struct *i2c_imx,struct i2c_msg *msgs, bool is_lastmsg)
{int result;unsigned long time_left;unsigned int temp;unsigned long orig_jiffies = jiffies;struct imx_i2c_dma *dma = i2c_imx->dma;struct device *dev = &i2c_imx->adapter.dev;// 开启DMA传输temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);temp |= I2CR_DMAEN;imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);// 设置DMA通道为接收通道dma->chan_using = dma->chan_rx;// DMA传输方向为从设备到内存dma->dma_transfer_dir = DMA_DEV_TO_MEM;// DMA数据方向为接收dma->dma_data_dir = DMA_FROM_DEVICE;// 最后两个数据字节必须由CPU传输dma->dma_len = msgs->len - 2;result = i2c_imx_dma_xfer(i2c_imx, msgs);if (result)return result;// 等待DMA传输完成reinit_completion(&i2c_imx->dma->cmd_complete);time_left = wait_for_completion_timeout(&i2c_imx->dma->cmd_complete,msecs_to_jiffies(DMA_TIMEOUT));if (time_left == 0) {dmaengine_terminate_all(dma->chan_using);return -ETIMEDOUT;}/* 等待传输完成 */while (1) {temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR); // 读取I2SR寄存器if (temp & I2SR_ICF) // 如果传输完成break;if (time_after(jiffies, orig_jiffies +msecs_to_jiffies(DMA_TIMEOUT))) { // 如果超时dev_dbg(dev, "<%s> Timeout\n", __func__); // 调试信息return -ETIMEDOUT;}schedule(); // 等待}temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); // 读取I2CR寄存器temp &= ~I2CR_DMAEN; // 关闭DMA传输imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); // 写入I2CR寄存器/* 读取n-1个字节的数据 */temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); // 读取I2CR寄存器temp |= I2CR_TXAK; // 设置NACKimx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); // 写入I2CR寄存器msgs->buf[msgs->len-2] = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); // 读取n-1个字节的数据/* 读取n个字节的数据 */result = i2c_imx_trx_complete(i2c_imx); // 传输完成if (result)return result;if (is_lastmsg) { // 如果是最后一个消息/** 在读取I2DR之前,必须生成STOP以防止控制器生成另一个时钟周期*/dev_dbg(dev, "<%s> clear MSTA\n", __func__); // 调试信息temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); // 读取I2CR寄存器temp &= ~(I2CR_MSTA | I2CR_MTX); // 清除MSTA和MTX位imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); // 写入I2CR寄存器i2c_imx_bus_busy(i2c_imx, 0); // 总线空闲i2c_imx->stopped = 1; // 停止} else {/** 对于I2C主接收器,重复重启操作如下:* 读取->重复MSTA->读/写* 在第一个读操作中,在读取最后一个字节之前,控制器必须设置MTX,否则第一个读操作会多花费一个时钟周期。*/temp = readb(i2c_imx->base + IMX_I2C_I2CR); // 读取I2CR寄存器temp |= I2CR_MTX; // 设置MTX位writeb(temp, i2c_imx->base + IMX_I2C_I2CR); // 写入I2CR寄存器}msgs->buf[msgs->len-1] = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); // 读取最后一个字节的数据return 0; // 返回0
}

i2c_imx_write

函数i2c_imx_write是用于在i.MX系列芯片上进行I2C数据写入的函数。
函数的主要步骤如下:
输出调试信息:打印从设备地址。

写入从设备地址:将从设备地址左移一位并写入I2C数据寄存器。

等待传输完成:调用i2c_imx_trx_complete函数等待传输完成。

检查ACK:调用i2c_imx_acked函数检查是否收到ACK。

输出调试信息:打印写入数据的调试信息。

写入数据:使用循环将数据逐个字节写入I2C数据寄存器。

等待传输完成:调用i2c_imx_trx_complete函数等待传输完成。

检查ACK:调用i2c_imx_acked函数检查是否收到ACK。

返回0:传输完成,返回0表示成功。

总体而言,函数i2c_imx_write用于通过I2C总线向从设备写入数据。它将从设备地址和数据逐个字节写入I2C数据寄存器,并在每个字节写入后等待传输完成和检查ACK。最后,函数返回传输结果。

static int i2c_imx_write(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs)
{int i, result;dev_dbg(&i2c_imx->adapter.dev, "<%s> write slave address: addr=0x%x\n",__func__, msgs->addr << 1); // 输出调试信息,写从设备地址/* write slave address */imx_i2c_write_reg(msgs->addr << 1, i2c_imx, IMX_I2C_I2DR); // 写从设备地址result = i2c_imx_trx_complete(i2c_imx); // 传输完成if (result)return result;result = i2c_imx_acked(i2c_imx); // 是否收到ACKif (result)return result;dev_dbg(&i2c_imx->adapter.dev, "<%s> write data\n", __func__); // 输出调试信息,写数据/* write data */for (i = 0; i < msgs->len; i++) {dev_dbg(&i2c_imx->adapter.dev,"<%s> write byte: B%d=0x%X\n",__func__, i, msgs->buf[i]); // 输出调试信息,写字节imx_i2c_write_reg(msgs->buf[i], i2c_imx, IMX_I2C_I2DR); // 写字节result = i2c_imx_trx_complete(i2c_imx); // 传输完成if (result)return result;result = i2c_imx_acked(i2c_imx); // 是否收到ACKif (result)return result;}return 0; // 返回0
}

i2c_imx_read

函数i2c_imx_read是用于在i.MX系列芯片上进行I2C数据读取的函数。
函数的主要步骤如下:
输出调试信息:打印从设备地址。

写入从设备地址:将从设备地址左移一位,并将最低位设置为1,表示读取操作。

等待传输完成:调用i2c_imx_trx_complete函数等待传输完成。

检查ACK:调用i2c_imx_acked函数检查是否收到ACK。

输出调试信息:打印设置总线的调试信息。

设置总线以读取数据:读取I2CR寄存器的值,并清除MTX位,表示将进行读取操作。

重置I2CR_TXAK标志位:对于SMBus块读或读取长度大于1的情况,重置I2CR_TXAK标志位。

写入I2CR寄存器:将修改后的值写入I2CR寄存器。

读取I2DR寄存器:执行虚拟读取,读取I2DR寄存器的值(此读取不会返回实际的数据)。

输出调试信息:打印读取数据的调试信息。

如果使用DMA传输且满足使用DMA传输的条件,则调用i2c_imx_dma_read函数进行数据读取。

循环读取数据:使用循环读取数据。

等待传输完成:调用i2c_imx_trx_complete函数等待传输完成。

对于SMBus块读的第一个字节,读取长度并添加到msgs->len中。

如果是最后一个消息:

如果is_lastmsg为true,生成STOP信号并清除MSTA和MTX标志位,以防止控制器生成另一个时钟周期。
如果is_lastmsg为false,进行重复重新启动操作。
对于倒数第二个字节,设置I2CR_TXAK标志位。

如果是SMBus块读的第一个字节,将长度存储在msgs->buf[0]中。

否则,将读取的字节存储在msgs->buf[i]中。

输出调试信息:打印读取的字节信息。

返回0:传输完成,返回0表示成功。

总体而言,函数i2c_imx_read用于通过I2C总线从从设备读取数据。它发送从设备地址,并根据读取长度和是否使用DMA传输等条件进行数据读取。在循环读取数据时,它会检查是否是SMBus块读的第一个字节,并根据是否是最后一个消息进行相应的操作。最后,函数返回传输结果。

static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, bool is_lastmsg)
{int i, result;unsigned int temp;int block_data = msgs->flags & I2C_M_RECV_LEN; // 判断是否为SMBus块读dev_dbg(&i2c_imx->adapter.dev,"<%s> write slave address: addr=0x%x\n",__func__, (msgs->addr << 1) | 0x01); // 输出调试信息,写从设备地址/* write slave address */imx_i2c_write_reg((msgs->addr << 1) | 0x01, i2c_imx, IMX_I2C_I2DR); // 写从设备地址result = i2c_imx_trx_complete(i2c_imx); // 传输完成if (result)return result;result = i2c_imx_acked(i2c_imx); // 是否收到ACKif (result)return result;dev_dbg(&i2c_imx->adapter.dev, "<%s> setup bus\n", __func__); // 输出调试信息,设置总线/* setup bus to read data */temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); // 读取I2CR寄存器temp &= ~I2CR_MTX; // 清除MTX位/** Reset the I2CR_TXAK flag initially for SMBus block read since the* length is unknown*/if ((msgs->len - 1) || block_data)temp &= ~I2CR_TXAK; // 如果是SMBus块读,则重置I2CR_TXAK标志位imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); // 写入I2CR寄存器imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); /* dummy read */ // 读取I2DR寄存器dev_dbg(&i2c_imx->adapter.dev, "<%s> read data\n", __func__); // 输出调试信息,读取数据if (i2c_imx->dma && msgs->len >= DMA_THRESHOLD && !block_data) // 如果使用DMA传输return i2c_imx_dma_read(i2c_imx, msgs, is_lastmsg); // 使用DMA传输/* read data */for (i = 0; i < msgs->len; i++) { // 循环读取数据u8 len = 0; // 初始化长度为0result = i2c_imx_trx_complete(i2c_imx); // 传输完成if (result)return result;/** First byte is the length of remaining packet* in the SMBus block data read. Add it to* msgs->len.*/if ((!i) && block_data) { // 如果是SMBus块读len = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); // 读取长度if ((len == 0) || (len > I2C_SMBUS_BLOCK_MAX)) // 如果长度为0或者大于最大长度return -EPROTO; // 返回错误dev_dbg(&i2c_imx->adapter.dev,"<%s> read length: 0x%X\n",__func__, len); // 输出调试信息,读取长度msgs->len += len; // 将长度加入到msgs->len中}// 如果是最后一个消息if (i == (msgs->len - 1)) {if (is_lastmsg) {/** 在读取I2DR之前,必须生成STOP,以防止控制器生成另一个时钟周期*/dev_dbg(&i2c_imx->adapter.dev,"<%s> clear MSTA\n", __func__);temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);temp &= ~(I2CR_MSTA | I2CR_MTX);imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);i2c_imx_bus_busy(i2c_imx, 0);i2c_imx->stopped = 1;} else {/** 对于i2c主接收器,重复重新启动操作如下:* 读取->重复MSTA->读/写* 在第一个读操作中,在读取最后一个字节之前,控制器必须设置MTX,否则第一个读操作会多花费一个时钟周期。*/temp = readb(i2c_imx->base + IMX_I2C_I2CR);temp |= I2CR_MTX;writeb(temp, i2c_imx->base + IMX_I2C_I2CR);}} else if (i == (msgs->len - 2)) {dev_dbg(&i2c_imx->adapter.dev,"<%s> set TXAK\n", __func__);temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);temp |= I2CR_TXAK;imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);}if ((!i) && block_data)msgs->buf[0] = len;elsemsgs->buf[i] =  imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);dev_dbg(&i2c_imx->adapter.dev,"<%s> read byte: B%d=0x%X\n",__func__, i, msgs->buf[i]); // 输出调试信息,读取字节}return 0;
}

相关文章:

IMX6ULL的I2C驱动详细分析

IMX6ULL的I2C驱动详细分析 文章目录 IMX6ULL的I2C驱动详细分析i2c_imx_driver 的平台驱动注册i2c_imx_probe注册函数i2c_imx_algoI2C算法结构体i2c_imx_start开始I2Ci2c_imx_stop停止I2Ci2c_imx_isr中断服务函数i2c_imx_dma_writeDMA 进行写操作的 I2C 传输2c_imx_dma_readi2c_…...

日志迁移到 logback

目标 本次迁移的目标就是用 slf4jlogback 的日志框架来取代目前的 slf4jlog4j 如何迁移 基于 slf4j 的无痛迁移 项目本身是采用的 slf4jlog4j 日志架构&#xff0c;所以迁移到 logback 基本无痛 修改依赖 原本是 slf4jlog4&#xff0c;依赖如下 <dependency><gro…...

开源字节 CRM 系统

开源字节CRM是一款SaaS模式的客户关系管理软件&#xff0c;基于钉钉平台进行研发&#xff0c;以客户管理为核心&#xff0c;包含客户管理、销售全流程管理&#xff0c;合同订单、工单管理、移动审批、数据分析六大模块。 旨在助力企业销售全流程精细化、数字化管理&#xff0c…...

七、Spring Cloud Alibaba-Sentinel

一、引言 1、了解服务可用性问题&#xff0c;服务挂掉原因 缓存击穿、单点故障、流量激增、线程池爆满、CPU飙升、DB超时、缺乏容错机制或保护机制、负载不均、服务雪崩、异常没处理等。 服务雪崩效应&#xff1a;因服务提供者的不可用导致服务调用者的不可用&#xff0c;并将…...

机器学习与深度学习——通过knn算法分类鸢尾花数据集iris求出错误率并进行可视化

什么是knn算法&#xff1f; KNN算法是一种基于实例的机器学习算法&#xff0c;其全称为K-最近邻算法&#xff08;K-Nearest Neighbors Algorithm&#xff09;。它是一种简单但非常有效的分类和回归算法。 该算法的基本思想是&#xff1a;对于一个新的输入样本&#xff0c;通过…...

【MySQL】MySQL基础知识详解

文章目录 1. MySQL概述1.1 数据库相关概念1.1.1 数据库、数据库管理系统与SQL1.1.2 关系型数据库与数据模型 1.2 MySQL数据库1.2.1 MySQL的安装与配置1.2.2 MySQL服务的启动与停止1.2.3 连接MySQL服务端 2. SQL2.1 SQL简介2.2 DDL2.2.1 数据库操作2.2.2 表操作2.2.2.1 创建表2.…...

RabbitMQ日常使用小结

一、使用场景 削峰、解耦、异步。 基于AMQP(高级消息队列协议)协议来统一数据交互,通过channel(网络信道)传递信息。erlang语言开发&#xff0c;并发量12000&#xff0c;支持持久化&#xff0c;稳定性好&#xff0c;集群不支持动态扩展。 RabbitMQ的基本概念 二、组成及工作流…...

​​​​​​​博物馆文物馆藏环境空气质量无线监控系统方案

博物馆文物馆藏环境空气质量无线监控系统方案 博物馆无线环境测控系统 博物馆恒温恒湿消毒净化系统 现代化博物馆空气质量一体化3D可视化管控平台 博物馆温湿度在线监控系统 博物馆光照在线监控系统 博物馆二氧化碳在线监控系统 博物馆在线监控系统 博物馆紫外线在线监控…...

测试理论----Bug的严重程度(Severity)和优先级(Priority)的分类

【原文链接】测试理论----Bug的严重程度&#xff08;Severity&#xff09;和优先级&#xff08;Priority&#xff09;的分类 一、Bug的严重程度&#xff08;Severity&#xff09; Bug的Severity&#xff08;严重程度&#xff09;指的是一个Bug对软件系统功能影响的程度&#…...

斯坦福、Nautilus Chain等联合主办的 Hackathon 活动,现已接受报名

由 Stanford Blockchain Accelerator、Zebec Protocol、 Nautilus Chain、Rootz Lab 共同主办的黑客松活动&#xff0c;现已接受优秀项目提交参赛申请。 在加密行业发展早期&#xff0c;密码极客们就始终在对区块链世界基础设施&#xff0c;在发展方向的无限可能性进行探索。而…...

00后卷王,把我们这些老油条卷的辞职信都写好了........

现在的小年轻真的卷得过分了。 前段时间我们公司来了个00年的&#xff0c;工作没两年&#xff0c;跳槽到我们公司起薪18K&#xff0c;都快接近我了。 后来才知道人家是个卷王&#xff0c;从早干到晚就差搬张床到工位睡觉了。 最近和他聊了一次天&#xff0c;原来这位小老弟家…...

JavaEE(系列12) -- 常见锁策略

目录 1. 乐观锁和悲观锁 2. 轻量级锁与重量级锁 3. 自旋锁和挂起等待锁 4. 互斥锁和读写锁 5. 可重入锁与不可重入锁 6. 死锁 6.1 死锁的必要条件 6.2 如何避免死锁 7. 公平锁和非公平锁 8. Synchronized原理及加锁过程 8.1 Synchronized 小结 8.2 加锁工作过程 8.2.1 偏向锁…...

前端nginx接口跨域

前提&#xff1a;vue项目本地接口通过proxy都可使用&#xff0c;但是项目部署在服务器上后发现所有接口出现503如下状况 简而言之&#xff1a;页面部署在域名为https://aa.bb.cc.com/vehicle/#/下&#xff0c;但是我接口需访问的是https:// azz.qqv.com/permission/company/gro…...

【国产虚拟仪器】基于 ZYNQ 的电能质量系统高速数据采集系统设计

随着电网中非线性负荷用户的不断增加 &#xff0c; 电能质量问题日益严重 。 高精度数据采集系统能够为电能质 量分析提供准确的数据支持 &#xff0c; 是解决电能质量问题的关键依据 。 通过对比现有高速采集系统的设计方案 &#xff0c; 主 控电路多以 ARM 微控制器搭配…...

Java前缀和算法

一.什么是前缀和算法 通俗来讲&#xff0c;前缀和算法就是使用一个新数组来储存原数组中前n-1个元素的和&#xff08;如果新数组的当前元素的下标为n&#xff0c;计算当前元素的值为原数组中从0到n-1下标数组元素的和&#xff09;&#xff0c;可能这样讲起来有点抽象&#xff0…...

pico 的两个双核相关函数的延时问题

pico高级API函数中&#xff0c; multicore_fifo_pop_timeout_us 和 multicore_fifo_push_timeout_us 的延时参数&#xff0c; 如修改为500微秒以上时&#xff0c;其延时似乎远远超过设定值&#xff0c;其反馈速度似乎被主核的交互所左右 &#xff0c;而修改为200以下时&#x…...

Doxygen源码分析: QCString类依赖的qstr系列C函数浅析

2023-05-20 17:02:21 ChrisZZ imzhuofoxmailcom Hompage https://github.com/zchrissirhcz 文章目录 1. doxygen 版本2. QCString 类简介3. qstr 系列函数浅析qmemmove()qsnprintfqstrdup()qstrfree()qstrlen()qstrcpy()qstrncpy()qisempty()qstrcmp()qstrncmp()qisspace()qstr…...

华为OD机试之一种字符串压缩表示的解压(Java源码)

一种字符串压缩表示的解压 题目描述 有一种简易压缩算法&#xff1a;针对全部由小写英文字母组成的字符串&#xff0c;将其中连续超过两个相同字母的部分压缩为连续个数加该字母&#xff0c;其他部分保持原样不变。 例如&#xff1a;字符串“aaabbccccd”经过压缩成为字符串“…...

Microsoft Project Online部署方案

目录 一、前言 二、Microsoft Project Online简介 三、Microsoft Project Online的优势 1、云端部署 2、多设备支持...

飞浆AI studio人工智能课程学习(3)-在具体场景下优化Prompt

文章目录 在具体场景下优化Prompt营销场景办公效率场景日常生活场景海报背景图生成办公效率场景预设Prompt 生活场景中日常学习Prompt: 给写完的代码做文档 将优质Prompt模板化Prompt 1:Prompt 1:Prompt 2步骤文本过长而导致遗失信息的示例修改后 特殊示例 如何提升安全性主要目…...

微信小程序之bind和catch

这两个呢&#xff0c;都是绑定事件用的&#xff0c;具体使用有些小区别。 官方文档&#xff1a; 事件冒泡处理不同 bind&#xff1a;绑定的事件会向上冒泡&#xff0c;即触发当前组件的事件后&#xff0c;还会继续触发父组件的相同事件。例如&#xff0c;有一个子视图绑定了b…...

Objective-C常用命名规范总结

【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名&#xff08;Class Name)2.协议名&#xff08;Protocol Name)3.方法名&#xff08;Method Name)4.属性名&#xff08;Property Name&#xff09;5.局部变量/实例变量&#xff08;Local / Instance Variables&…...

ardupilot 开发环境eclipse 中import 缺少C++

目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比

目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec&#xff1f; IPsec VPN 5.1 IPsec传输模式&#xff08;Transport Mode&#xff09; 5.2 IPsec隧道模式&#xff08;Tunne…...

视觉slam十四讲实践部分记录——ch2、ch3

ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式

今天是关于AI如何在教学中增强学生的学习体验&#xff0c;我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育&#xff0c;这并非炒作&#xff0c;而是已经发生的巨大变革。教育机构和教育者不能忽视它&#xff0c;试图简单地禁止学生使…...

Java数值运算常见陷阱与规避方法

整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...

uniapp 开发ios, xcode 提交app store connect 和 testflight内测

uniapp 中配置 配置manifest 文档&#xff1a;manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号&#xff1a;4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...

GO协程(Goroutine)问题总结

在使用Go语言来编写代码时&#xff0c;遇到的一些问题总结一下 [参考文档]&#xff1a;https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现&#xff1a; 今天在看到这个教程的时候&#xff0c;在自己的电…...

通过MicroSip配置自己的freeswitch服务器进行调试记录

之前用docker安装的freeswitch的&#xff0c;启动是正常的&#xff0c; 但用下面的Microsip连接不上 主要原因有可能一下几个 1、通过下面命令可以看 [rootlocalhost default]# docker exec -it freeswitch fs_cli -x "sofia status profile internal"Name …...