EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
本文主要介绍基于zynq的IIC的驱动架构,通过代码编写来深入了解IIC驱动的内容和机制。 1. IIC驱动架构 IIC驱动包含两部分:IIC总线驱动和设备驱动。总线驱动是对硬件设备适配器端的实现,主要包含i2c_adapter,i2c_algorithm和控制i2c适配器产生通信信号的函数。I2c_adapter和一套i2c读写algorithm绑定,i2c_algorithm包含对i2c总线的访问方式。IIC总线驱动完成i2c_driver的注册,IO空间分配,IIC硬件设备时钟的设定,主从模式以及收发模式的设定等。通过i2c_adapter可以控制I2C总线上开始,结束等信号的产生。 IIC设备驱动提供用户空间和i2c总线驱动的交互,其定义了i2c_client数据结构以及文件操作ioctrl,open,write等。I2c_client依附于i2c_adapter,用户通过i2c_client结构来调用相应的i2c_adapter,来实现IIC控制。具体层次结构如下: User ioctrl---------> I2c_client------------> I2c_adapter------------> I2c_algorithm---------------> IIC硬件 2. zynq上IIC驱动试验 基于黑金的zynq7020开发板,通过IIC来读写EEPROM,首先在PS端选择IIC端口,导出到PL端,分配引脚。
' b$ B5 p, r* F1 n, @
代码分析: 首先定义一个i2c_dev,用于绑定到platfrom总线上。结构体中包含i2c_adapter,以及用于收发数据的buffer(p_send_buf和p_recv_buf),completion用于等待数据发送过程。 struct eeprom_i2c_dev{" Z* T5 ^- O; a6 ~, V2 ?2 p8 M
struct device *dev;
5 c0 r7 {& ~* _; |struct i2c_adapter adapter;8 ]1 w4 T1 B+ ]+ l C1 ^9 s1 _
unsigned long size;
: T2 {0 J# z7 w) R; Wvoid __iomem *baddr;9 t; r" L3 E v
struct i2c_msg *p_msg;
$ u+ I7 j# o ?' T5 X: qunsigned char *p_send_buf;
7 s: u3 ^ T7 a% X! {0 L, b, Funsigned char *p_recv_buf;" S& X, O' W) T; ?/ ]# }2 z0 c
unsigned int recv_cnt;
; k. ]9 Z8 Q) K( b" {9 Wunsigned int send_cnt;
* q; e" F0 E% n# |6 ~6 hint hold_falg;
; r2 a, a+ C! C2 `8 Yint irq;* ~4 p" P# H* w( _( U( N
struct completion msg_completion;$ V5 E* R- w# E" w$ F
spinlock_t xfer_lock;# H0 x0 X/ ~$ S1 b* |
u32 i2c_clk;% S* Y9 a% C7 h
u32 input_clk;
- o* U. x4 [, P4 F7 l% U, ~8 ?struct clk *clk;
' |* j3 ?1 p; J6 l/ T: [u32 ctrl_reg;
/ Y& f0 G4 I( `; U5 L}; I2c_driver中主要包含了驱动注册和卸载函数。 static struct platform_driver eeprom_i2c_driver = {
0 ?4 n4 W3 f! z& r% V2 y.driver={
5 ^( ?0 c$ n* F1 d& y( X \.name=DRIVER_NAME,
3 m8 W* t$ J$ d! R6 m.owner=THIS_MODULE,) X3 }2 A. t7 u6 V- ]
.of_match_table=eeprom_i2c_of_match,5 t/ N F! x: X3 `: O. i& Z
},
9 t' [& h" j5 u5 c.probe=eeprom_i2c_probe,
$ V. m. y* F, z$ j. H.remove=eeprom_i2c_remove,
" ^: o6 j8 S8 Z* `* L. t}; 现在分析probe函数: 首先从设备树中获得内存地址以及中断号,并分配IO虚拟内存。 static int eeprom_i2c_probe(struct platform_device *pdev){ ?2 Z6 z1 x- B; ?4 }# l$ _
……1 `: \ s# X( c; t# _& E. \9 N; a
res=platform_get_resource(pdev, IORESOURCE_MEM, 0);. E! N* a# f! T3 _# K
……4 T- B/ E" v+ f: H
base=ioremap(res->start, resource_size(res));. Y% }5 D+ k* {& D0 F
……) R+ k' r& u3 ?0 \
res=platform_get_resource(pdev, IORESOURCE_IRQ, 0); 指定i2c_adapter的i2c_algorithm。 idev->adapter.owner = THIS_MODULE;. E8 v5 W5 g" a+ Y. b
idev->adapter.dev.of_node = pdev->dev.of_node;8 S% `* W+ W$ @) W5 B. j) y
idev->baddr=base;6 A9 q% E) m1 I3 k& X* D" S) v
idev->irq=irq;
) E: R" N d1 r9 i1 m5 Y& T) } ^/ n6 Zidev->size=size;* x; F1 E$ V d+ `1 k. U& ^ T$ g$ L5 z
idev->dev=&pdev->dev;( K! V; v8 A0 X1 d3 u
idev->adapter.timeout=msecs_to_jiffies(1000); c0 C6 f$ I& N: w, I. |+ J( O
idev->adapter.algo=&eeprom_i2c_algo;+ y+ D3 o! s6 ^3 M2 o$ |5 x
idev->adapter.algo_data = idev;
$ C9 R! P4 d- y2 y; `/ yidev->adapter.dev.parent = &pdev->dev;
/ |6 l2 W' k! j- J+ \snprintf(idev->adapter.name, sizeof(idev->adapter.name), "EEPROM I2C at %08lx", strt_addr); 根据设备树中时钟配置IIC硬件中的时钟寄存器。 idev->clk = devm_clk_get(&pdev->dev, NULL);
" r" n7 f) H/ {if (IS_ERR(idev->clk)) {. N, c! G/ J; m) l. ~% A/ H1 D
dev_err(&pdev->dev, "input clock not found./n");& p7 `2 B; Q" N% n2 L
return PTR_ERR(idev->clk);
* V9 Y3 o& Q# C. X) v} ret=clk_prepare_enable(idev->clk);& J* y0 r e! ]: U- T( ?
if(ret)0 M( o, w( v7 ^& x3 E
dev_err(&pdev->dev, "unable to enable clcok/n");$ O2 ?/ p7 s1 P; y9 P6 W4 d. Y
idev->input_clk=clk_get_rate(idev->clk);
. ^* F% n+ M; v. X$ oidev->ctrl_reg = EEPROM_I2C_CR_ACK_EN | EEPROM_I2C_CR_NEA | EEPROM_I2C_CR_MS;
* _5 o) R; `2 |$ _. pret=eeprom_i2c_setclk(idev->input_clk, idev); IIC接收数据要申请中断。 ret=devm_request_irq(&pdev->dev, irq, eeprom_i2c_irq, 0, DRIVER_NAME, idev); i2c_add_adapter用于将i2c_adapter加入到总线中。这个时候i2c_dev.c是可以通过通知连来检测到这个i2c_adapter的,并能为其分配设备号。然后用户就可以通过直接操作这个设备来实现IIC读写了。 接下来看i2c_algorithm: static const struct i2c_algorithm eeprom_i2c_algo = {
2 V5 G. x7 ?! k.master_xfer=eeprom_i2c_master_xfer,
# t# ^3 L0 b; B/ D% k5 g) c' z) o.functionality=eeprom_i2c_func,' e0 V/ o2 n: g4 T
}; eeprom_i2c_master_xfer是通信函数,其中有: eeprom_i2c_mrcv用于处理接收到IIC设备数据,eeprom_i2c_msend用于处理发送数据,函数主要通过配置IIC硬件设备中寄存器来实现。 现在来看用户如何操作IIC: i2c_rdwr_ioctl_data是i2c_dev.c中用于存储的收发数据的结构,通过它可以传递数据。在work_queue中的msgs中设定好slave_addr,len等值,然后通过ioctrl可以读写IIC了。 ( n- J& P6 w8 C8 V/ I2 m6 @
|