网站建设教程吧,知乎的网络营销方式,网页制作培训教程,莱芜金点子租房信息港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_…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_imx_writei2c_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, cant 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, cant get I2C clock\n); // 获取时钟失败return PTR_ERR(i2c_imx-clk);}ret clk_prepare_enable(i2c_imx-clk); // 使能I2C时钟if (ret) {dev_err(pdev-dev, cant 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, cant 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: addr0x%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%d0x%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: addr0x%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%d0x%X\n,__func__, i, msgs-buf[i]); // 输出调试信息读取字节}return 0;
}