I2C硬件拓扑结构:
一个SOC,有多个i2c控制器,一个I2C可以引出一个2c接口.一个i2c接口可以接多i个I2C地址不同的设备.
同时I2C分master和device,对于SOC而已,其i2c接口都当作master.
总的来说,i2c 框架层,有这么几个虚拟设备:
i2c控制器 —>i2c_adapter---------->i2c资源的提供者—platform_device
i2c总线-------->i2c_bus
i2c外设-------->i2c_client ----------->i2c资源的消费者
同时,对于i2c_adapter,其需要提供本SOC手法数据的算法功能.Linux抽象为i2c_algorithm
对于i2c总线传输的数据,Linux也将其抽象为i2c_msg
整个的来说,还是Linux设备驱动模型那一套
对于i2c控制器i2c_adapter,分设备和驱动
设备:直接在dts中定义,比如.
i2c0: i2c@ff650000 {
compatible = “rockchip,rk3288-i2c”;
reg = <0x0 0xff650000 0x0 0x1000>;
interrupts = <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
#size-cells = <0>;
clock-names = “i2c”;
clocks = <&cru PCLK_I2C0>;
pinctrl-names = “default”;
pinctrl-0 = <&i2c0_xfer>;
status = “disabled”;
};
驱动:
Linux将SOC侧的设备都当作platform_device,对于平台设备来说,驱动主要做以下事情:
1.platform_device设备,本身作为一种抽象,这一类设备,可能不只有1个,可能有多个.因此,platform_device设备需要提供一套抽象的封装的api,用于处理这一类设备.
2.platform_device本身作为一种硬件资源,其存在的意义是供SOC外部设备调用,因此,其需要提供接口给device使用,并且,为了方便移植,这套接口应该是一种规范.以本地回调函数的方式实现(回调方法由SOC厂家实现,操作具体硬件寄存器,每家SOC可能不一样).
因此,platform-device设备的驱动,分为三部分:1.核心api实现层.2.具体的platform_device设备解析驱动层.3.回调函数实现
对于i2c控制器这个platform_device,Linux将其抽象成i2c_adapter(对于具体的SOC,可能还会在这基础上再封装一层,包含本SOC的一些变量信息,设备的状态信息,以及加入同步互斥等机制)
struct i2c_adapter {
struct module owner;
unsigned int class; / classes to allow probing for */
const struct i2c_algorithm algo; / the algorithm to access the bus */
void algo_data;
/ data fields that are valid for all devices /
struct rt_mutex bus_lock;
int timeout; / in jiffies /
int retries;
struct device dev; / the adapter device */
int nr;
char name[48];
struct completion dev_released;
struct mutex userspace_clients_lock;
struct list_head userspace_clients;
struct i2c_bus_recovery_info *bus_recovery_info;
const struct i2c_adapter_quirks *quirks;
};
Linux为i2c_adapter定义了一些列api
extern int i2c_add_adapter(struct i2c_adapter *);
extern void i2c_del_adapter(struct i2c_adapter *);
extern int i2c_add_numbered_adapter(struct i2c_adapter *);
static inline u32 i2c_get_functionality(struct i2c_adapter *adap)
static inline int i2c_check_functionality(struct i2c_adapter *adap, u32 func)
static inline int i2c_adapter_id(struct i2c_adapter *adap)
extern struct i2c_adapter *i2c_get_adapter(int nr);
extern void i2c_put_adapter(struct i2c_adapter *adap);
extern struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node);
对于必须又本地实现的i2c_algorithm,定义如下,
struct i2c_algorithm {
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num);
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data);
u32 (*functionality) (struct i2c_adapter *);
};
其中,master_xfer和functionality,函数指针.必须本地实现.例如.
static const struct i2c_algorithm rk3x_i2c_algorithm = {
.master_xfer = rk3x_i2c_xfer,//i2c 数据发送实现函数.
.functionality = rk3x_i2c_func,
};
因
此对于一个SOC,要实现i2c控制器,其主要工作如下:
1.在dts中定义i2cx节点,定义相应的寄存器,中断信息,clk,pinctrl等资源信息.
2.在driver/i2c定义SOC_i2c.c文件,作为该i2c控制器的驱动.
3.在该驱动中,编译该i2c控制器的platform_driver,并定义match_id,probe,remove等基本的驱动接口.
4.在probe中分配i2c_adapter结构变量,然后调用platform_get_resource,get_irq等解析dts中的设备节点信息,并初始化adapter,同时定义adapter的一些本地回调接口,中断函数,.然后就是调用核心层api i2c_add_adapter将其注册到系统中,生成相应的节点信息以及发出相关event.
5.实现本地回调函数(比如master_xfer,functionality等),中断函数等,以及需要本地实现的i2c开始/停止/read/write实现等.