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

【u-boot】u-boot的I2C驱动框架剖析

本文基于u-boot版本v2019.04

在u-boot驱动模型中,实现了通用的驱动框架和标准接口:i2c-uclass,配置宏定义如下:

CONFIG_DM_I2C

框架代码:

./drivers/i2c/i2c-uclass.c

👀本文将描述u-boot 的I2C驱动框架的相关数据结构,以及总结重要的API接口,最后剖析一个具体的I2C设备驱动的实现案例。

一、描述i2c框架的数据结构

struct dm_i2c_ops用于描述I2C uclass的驱动操作接口,实现如下(/include/i2c.h):

struct dm_i2c_ops {/*** xfer() - 传输一组 I2C 消息** @bus:    要读取的总线* @msg:    待传输的消息列表* @nmsgs: 消息列表中的消息数量* @return 0 表示成功,如果从设备未应答某个字节返回 -EREMOTEIO,*         如果速度不支持返回 -ECOMM,*         如果芯片标志不支持返回 -EPROTO,*         其他错误返回其他负值*/int (*xfer)(struct udevice *bus, struct i2c_msg *msg, int nmsgs);/*** probe_chip() - 探测芯片地址是否存在** 此函数是可选的。如果未提供,uclass 会发送一个零长度消息作为替代。** @bus:       要探测的总线* @chip_addr: 要探测的芯片地址* @chip_flags: 探测标志 (enum dm_i2c_chip_flags)* @return 芯片存在返回 0,不存在返回 -EREMOTEIO,返回 -ENOSYS 表示回退到默认探测,*         其他错误返回其他负值*/int (*probe_chip)(struct udevice *bus, uint chip_addr, uint chip_flags);/*** set_bus_speed() - 设置总线速度(可选)** 如果该函数不返回错误,uclass 会更新总线速度值。此方法是可选的,* 如果未提供,驱动可以从 dev_get_uclass_priv(bus)->speed_hz 获取速度。** @bus:   要调整的总线* @speed: 请求的速度(单位 Hz)* @return 0 表示成功,-EINVAL 表示速度值无效*/int (*set_bus_speed)(struct udevice *bus, unsigned int speed);/*** get_bus_speed() - 获取总线速度(可选)** 通常 uclass 会提供此功能,但如果希望驱动通过查看硬件来获取速度,* 可以在这里实现。此方法是可选的。通常期望返回* dev_get_uclass_priv(bus)->speed_hz。** @bus: 要检查的总线* @return 总线速度(Hz),错误返回负值*/int (*get_bus_speed)(struct udevice *bus);/*** set_flags() - 设置芯片标志(可选)** 通常由 uclass 实现,但驱动也可以检查值以确保不使用不支持的选项。* 此方法是可选的。如果提供,当标志改变时总会调用此方法。** @dev:   要调整的芯片* @flags: 新的标志值* @return 0 表示成功,-EINVAL 表示值不支持*/int (*set_flags)(struct udevice *dev, uint flags);/*** deblock() - 恢复处于未知状态的总线** I2C 是同步协议,在访问过程中处理器重置可能会阻塞 I2C 总线,* 直到整个单元断电完成。这是因为从设备可能卡在等待一个永远不会完成的事务。* 重置 I2C 主设备并不能解决问题。唯一的方法是通过一系列总线转换,* 确保所有从设备完成事务。此方法在驱动支持时执行此“解锁”操作。** 此方法是可选的。*/int (*deblock)(struct udevice *bus);
};

struct dm_i2c_chip用于描述I2C芯片的信息,I2C芯片是I2C总线上的一个设备。它位于特定地址,通常支持7位或10位寻址。可使用dev_get_parent_platdata(dev)获取此结构体,其中dev是要查看的芯片。struct dm_i2c_chip结构实现如下(/include/i2c.h)

struct dm_i2c_chip {uint chip_addr; /* 芯片在总线上的地址 *//** 偏移量的长度(字节数)。单字节偏移可表示最多 256 字节,* 对于更大设备可能需要大于 1 的值。*/uint offset_len;/*  芯片的标志(dm_i2c_chip_flags) */uint flags;
#ifdef CONFIG_SANDBOXstruct udevice *emul; /* 该芯片地址的仿真器(仅用于仿真) */bool test_mode;
#endif
};

struct i2c_msg用于描述一条I2C消息,该结构实现如下(/include/i2c.h):

struct i2c_msg {uint addr;  /* 从设备地址 */uint flags; /* 标志位(参见枚举 dm_i2c_msg_flags) */uint len;	/* 缓冲区长度(字节),探测时可以为 0 */u8 *buf;	/* 发送/接收缓冲区,如果没有数据可为 NULL */
};

struct i2c_msg_list用于描述一组 I2C 消息的列表,实现如下:

struct i2c_msg_list {struct i2c_msg *msgs; /*  指向i2c_msg数组的指针 */uint nmsgs;			  /*  数组中的元素数量 */
};

二、I2C驱动框架操作接口

/*** dm_i2c_read() - 从I2C芯片读取字节** 要根据I2C总线地址获取I2C设备(称为“芯片”),可以使用i2c_get_chip()。* 要根据总线号获取总线,可以使用 uclass_get_device_by_seq(UCLASS_I2C, <总线号>)。** 要设置设备的地址长度,可以使用i2c_set_addr_len(),默认值为1。** @dev:    要读取的芯片* @offset: 芯片内的起始偏移地址* @buffer: 存放读取数据的缓冲区* @len:    要读取的字节数** @return  成功返回 0,失败返回负值*/
int dm_i2c_read(struct udevice *dev, uint offset, uint8_t *buffer, int len);
/*** dm_i2c_write() - 向I2C芯片写入字节** 详细说明请参考dm_i2c_read()的说明。** @dev:    要写入的芯片* @offset: 芯片内的起始偏移地址* @buffer: 存放待写入数据的缓冲区* @len:    要写入的字节数** @return  成功返回0,失败返回负值*/
int dm_i2c_write(struct udevice *dev, uint offset, const uint8_t *buffer,int len);
/*** dm_i2c_probe() - 探测特定芯片地址** 该函数用于检查总线上某个芯片是否存在。* 通常通过向总线写入芯片地址,并检查芯片是否返回 ACK 来实现。** @bus:       要探测的总线* @chip_addr: 要探测的 7 位地址(不支持 10 位及其他类型地址)* @chip_flags: 探测标志(参见枚举 dm_i2c_chip_flags)* @devp:      返回找到的设备,如果未找到则返回 NULL* @return     如果在该地址找到芯片返回 0,否则返回负值*/
int dm_i2c_probe(struct udevice *bus, uint chip_addr, uint chip_flags,struct udevice **devp);
/*** dm_i2c_reg_read() - 从I2C寄存器读取值** 该函数从指定I2C芯片的地址读取一个值。** @dev:  用于传输的设备* @addr: 要读取的地址* @return 读取到的值,出错返回负值*/
int dm_i2c_reg_read(struct udevice *dev, uint offset);
/*** dm_i2c_reg_write() - 向I2C寄存器写入值** 该函数向指定 I2C 芯片的地址写入一个值。** @dev:  用于传输的设备* @addr: 要写入的地址* @val:  要写入的值(通常为一个字节)* @return 成功返回 0,出错返回负值*/
int dm_i2c_reg_write(struct udevice *dev, uint offset, unsigned int val);
/*** dm_i2c_xfer() - 通过 I2C 传输消息** 该函数用于传输原始消息。通常建议使用 dm_i2c_reg_read/write()。** @dev:   用于传输的设备* @msg:   待传输的消息列表* @nmsgs: 消息数量* @return 成功返回 0,出错返回负值*/
int dm_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs);
/*** dm_i2c_set_bus_speed() - 设置某条总线的速度** @bus:   要调整的总线* @speed: 请求的速度(Hz)* @return 成功返回0,速度值无效返回-EINVAL*/
int dm_i2c_set_bus_speed(struct udevice *bus, unsigned int speed);
/*** dm_i2c_get_bus_speed() - 获取某条总线的速度** @bus:  要检查的总线* @return 选定I2C总线的速度(Hz),出错返回负值*/
int dm_i2c_get_bus_speed(struct udevice *bus);
/*** i2c_set_chip_flags() - 设置某个芯片的标志位** 通常地址为7位,但对于10位地址,应将flags设置为DM_I2C_CHIP_10BIT。* 这样所有访问将使用10位寻址。** @dev:    要调整的芯片* @flags:  新的标志位* @return  成功返回0,值不支持返回-EINVAL,其他错误返回负值*/
int i2c_set_chip_flags(struct udevice *dev, uint flags);
/*** i2c_get_chip_flags() - 获取某个芯片的标志位** @dev:     要检查的芯片* @flagsp:  用于存放标志位的指针* @return   成功返回 0,出错返回负值*/
int i2c_get_chip_flags(struct udevice *dev, uint *flagsp);
/*** i2c_set_offset_len() - 设置某个芯片的偏移长度** 用于访问芯片的偏移量最多可达 4 个字节。通常情况下,它只有 1 个字节,* 这对于具有 256 字节存储或寄存器的芯片来说已经足够。* 默认值为 1,但可以通过调用此函数进行修改。** @offset_len: 新的偏移长度值(通常为 1 或 2)*/
int i2c_set_chip_offset_len(struct udevice *dev, uint offset_len);
/*** i2c_get_offset_len() - 获取某个芯片的偏移长度** @return: 当前的偏移长度值(通常为 1 或 2)*/
int i2c_get_chip_offset_len(struct udevice *dev);
/*** i2c_deblock() - 恢复处于未知状态的 I2C 总线** 详细说明参见结构体struct dm_i2c_ops中的 deblock() 方法。** @bus:    要恢复的总线* @return  成功返回 0,出错返回负值*/
int i2c_deblock(struct udevice *bus);

三、I2C驱动实例剖析

本小节来具体分析一个I2C驱动实例的实现,以rockchip实现的I2C驱动进行描述(/drivers/i2c/rk_i2c.c):

// SPDX-License-Identifier: GPL-2.0+
/** (C) Copyright 2015 Google, Inc** (C) Copyright 2008-2014 Rockchip Electronics* Peter, Software Engineering, <superpeter.cai@gmail.com>.*/#include <common.h>
#include <clk.h>
#include <dm.h>
#include <errno.h>
#include <i2c.h>
#include <asm/io.h>
#include <asm/arch-rockchip/clock.h>
#include <asm/arch-rockchip/i2c.h>
#include <asm/arch-rockchip/periph.h>
#include <dm/pinctrl.h>
#include <linux/sizes.h>/* i2c timerout */
#define I2C_TIMEOUT_MS		100
#define I2C_RETRY_COUNT		3/* rk i2c fifo max transfer bytes */
#define RK_I2C_FIFO_SIZE	32struct rk_i2c {struct clk clk;struct i2c_regs *regs;unsigned int speed;
};enum {RK_I2C_LEGACY,RK_I2C_NEW,
};/*** @controller_type: i2c controller type*/
struct rk_i2c_soc_data {int controller_type
http://www.dtcms.com/a/548899.html

相关文章:

  • JFrog vs Nexus vs Hadess,制品管理工具一文纵评
  • 【Docker】容器常用命令
  • Linux《Socket编程UDP》
  • Java IO 流进阶:Buffer 与 Channel 核心概念解析及与传统 IO 的本质区别
  • 【Linux基础开发工具 (一)】详解Linux软件生态与包管理器:从yum / apt原理到镜像源实战
  • 镇江网站营销推广电商怎么做如何从零开始视频
  • opencv 学习: 01 初识图片处理
  • 从 Wot UI 出发谈 VSCode 插件的自动化发布
  • Rust专项——用 Weak 打破引用环:树与图结构实战
  • c#调Lua返回个字符串
  • 单元测试(JUnit、Mockito、PowerMock )
  • 不只是语法糖:解构作为 Rust 安全与清晰度的基石
  • 企业微信消息群发助手(企业微信自建应用)winform.netcore实现(详细配置)
  • 基于Vue的教育学习网站04y8688l(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
  • 移动端网站生成器中国电商平台排行榜前100
  • Excel正则表达式.获取字符
  • K8s 资源管理与操作
  • 如何在 Azure 虚拟机上部署 Elasticsearch
  • Go切片的赋值
  • Go语言设计模式:原型模式详解
  • 泉州网站建设-泉州网站建设石家庄网站建设招聘
  • [MySQL]表——权限控制
  • 把AI“灌”进奶瓶:1KB决策树让婴儿温奶器自己学会「恒温+计时」
  • 视频网站怎么做移动广告联盟
  • 高速DIC技术用于无人机旋翼动态变形与轨迹姿态分析
  • Node.js环境变量配置
  • Docker 部署 Node.js + Playwright 项目,实现浏览器截图、打印和下载
  • 所有权与解构:一次把“拆”与“留”写进类型系统的旅程 ——从语法糖到零拷贝 AST
  • 基于ASM1042通信接口芯片的两轮车充电机性能优化研究
  • hadoop之MapReduce的map工作流程