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端,分配引脚。
! C, K2 {) {; E r! Q
代码分析: 首先定义一个i2c_dev,用于绑定到platfrom总线上。结构体中包含i2c_adapter,以及用于收发数据的buffer(p_send_buf和p_recv_buf),completion用于等待数据发送过程。 struct eeprom_i2c_dev{
W2 q9 c$ p) A, gstruct device *dev;8 g8 ^8 E, J0 s' Y; M1 _. j
struct i2c_adapter adapter;
! ^; K1 e- q; u I0 Xunsigned long size;
5 e' C6 ]6 B4 o/ nvoid __iomem *baddr;
1 @4 d0 e3 i$ n$ Xstruct i2c_msg *p_msg;
0 F1 Z( j2 Q! L* Y0 A% {1 Eunsigned char *p_send_buf;8 O& ~6 V0 T, G2 R- |; x
unsigned char *p_recv_buf;' x [: s F9 d3 j% p
unsigned int recv_cnt;
* d" a5 L1 ]% [) I2 Punsigned int send_cnt;
0 Q5 v' L2 N/ n1 L' ~int hold_falg;
8 F- B1 r& |8 m* @int irq;
M' E/ L' c/ `/ a$ Jstruct completion msg_completion;
7 X( `6 |, \1 q: w1 ispinlock_t xfer_lock;
0 h5 v9 l: w# \, z* du32 i2c_clk;
3 E0 U1 ]% _! Y4 ]; p, a( H' Tu32 input_clk;
% X. S4 i7 i/ R& Qstruct clk *clk;/ s# P1 ?% O: n* R) |/ R
u32 ctrl_reg;" x- P% l: G1 E$ s' C7 v" t
}; I2c_driver中主要包含了驱动注册和卸载函数。 static struct platform_driver eeprom_i2c_driver = {
. F0 [' i& Q6 o.driver={/ ^ d0 G- C' ]$ J0 [2 d P) b
.name=DRIVER_NAME,3 l* s3 q. r- @
.owner=THIS_MODULE,
, s1 y) E* k% b* M& h- u2 z& A.of_match_table=eeprom_i2c_of_match,
9 n) g! l! e6 f},( C# _+ ]/ D6 P P" n6 x% \
.probe=eeprom_i2c_probe,
, s0 E& w; K. \.remove=eeprom_i2c_remove,
" v' r9 }/ D R% j, i* m. U}; 现在分析probe函数: 首先从设备树中获得内存地址以及中断号,并分配IO虚拟内存。 static int eeprom_i2c_probe(struct platform_device *pdev){
& v" C: Z* B# `" r# s+ w0 G……2 g" G* q7 ?6 p5 O' O" u, `. G
res=platform_get_resource(pdev, IORESOURCE_MEM, 0);
8 S4 u/ I; ]* l- Q K……
9 ]. H# E# {% u3 Vbase=ioremap(res->start, resource_size(res));
: S* f* B0 e8 r6 [: g3 }……
5 ?# @( y& e1 Z, xres=platform_get_resource(pdev, IORESOURCE_IRQ, 0); 指定i2c_adapter的i2c_algorithm。 idev->adapter.owner = THIS_MODULE;9 O6 n, E- N( L. k% }" q- F
idev->adapter.dev.of_node = pdev->dev.of_node;/ ~6 ~, T3 u6 B/ N# E
idev->baddr=base;
# v9 y# V+ N6 E& u( G% r$ _idev->irq=irq;
$ C; q' o3 J, {9 L! V5 iidev->size=size;; J6 e5 L) Y8 j( T- I d/ Z# N
idev->dev=&pdev->dev;: ^. o& e) k/ ?( V
idev->adapter.timeout=msecs_to_jiffies(1000);" h9 }1 A1 a) B0 q8 c+ d' k/ x5 V% \
idev->adapter.algo=&eeprom_i2c_algo;, [4 N6 Y$ [# g" L" V
idev->adapter.algo_data = idev;8 ]/ P& f, p/ T* |! v; Q* i
idev->adapter.dev.parent = &pdev->dev;* l: @5 X& M) x% u
snprintf(idev->adapter.name, sizeof(idev->adapter.name), "EEPROM I2C at %08lx", strt_addr); 根据设备树中时钟配置IIC硬件中的时钟寄存器。 idev->clk = devm_clk_get(&pdev->dev, NULL);) W2 ?' i- C3 N+ I- H9 C+ S
if (IS_ERR(idev->clk)) {
2 P7 d) t6 V3 Ldev_err(&pdev->dev, "input clock not found./n");. {, l |3 P2 l
return PTR_ERR(idev->clk);
+ k; _! x* X- K5 `} ret=clk_prepare_enable(idev->clk);9 R9 u$ s4 n7 @9 a
if(ret) l) r" y7 [' F( ^3 R5 f
dev_err(&pdev->dev, "unable to enable clcok/n");- p& h4 p7 u& Y6 ~. I+ w' z
idev->input_clk=clk_get_rate(idev->clk);
* h4 U( [. \% S2 C8 I4 u& pidev->ctrl_reg = EEPROM_I2C_CR_ACK_EN | EEPROM_I2C_CR_NEA | EEPROM_I2C_CR_MS;3 K$ s7 w6 O! N3 d* b# y
ret=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 = {
3 R- I% ]: K- N( F; d% b& R0 I.master_xfer=eeprom_i2c_master_xfer,
: X! U5 C5 Z8 J* g8 q' v2 [. F0 k, V.functionality=eeprom_i2c_func,6 O3 s, K& N6 n4 A& E
}; 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了。
# i( z2 A7 |& M, r' q/ M- A* v* w |