zynq linux tcp 文件接收,Zynq linux的I2C驱动学习笔记(一)

I2C总线在传送数据过程中共有三种类型信号, 它们分别是:开始信号、结束信号和应答信号。

开始信号:SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。

结束信号:SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据。

应答信号:接收数据的IC在接收到8bit数据后,向发送数据的IC发出特定的低电平脉冲,表示已收到数据。CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为受控单元出现故障。

在LINUX系统初始化的过程中,通过 i2c_register_board_info,将所需要的I2C从设备加入一个名为_i2c_board_list双向循环链表,系统在成功加载I2C主设备adapt后,就会对这张链表里所有I2C从设备逐一地完成 i2c_client的注册。

也就是说,i2c_client和i2c_adapter都是由i2c_core来维护的。

在xilinx-linux中,i2c从设备是通过dts文件传递给内核的,内核通过zynq_init_machine函数注册所有的i2c从设备,i2c_client.

I2C的linux必须知道4个结构体:i2c_adapter,i2c_algorithm,i2c_client,i2c_driver

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;

};

i2c总线控制器数据依附于algo_data,比如xi2cps,s3c24xx_i2c。

struct device dev;成员表明i2c_adapter是一个硬件,对应SoC上的I2C控制器。而i2c_algorithm则是这个I2C控制器的底层驱动程序。

同理:

struct i2c_client {

unsigned short flags; /div., see below /

unsigned short addr; / chip address - NOTE: 7bit /

/ addresses are stored in the /

/ LOWER 7 bits/

char name[I2C_NAME_SIZE];

struct i2c_adapter adapter; / the adapter we sit on /

struct i2c_driver driver; / and our access routines /

struct device dev; / the device structure /

int irq; / irq issued by device/

struct list_head detected;

};

struct i2c_client代表一个挂载到i2c总线上的i2c从设备,该设备所需要的数据结构,其中包括

该i2c从设备所依附的i2c主设备 struct i2c_adapter adapter

该i2c从设备的驱动程序struct i2c_driverdriver

作为i2c从设备所通用的成员变量,比如addr, name等

该i2c从设备驱动所特有的数据,依附于dev->driver_data下,在i2c_driver中的probe函数中设置这个结构体成员。比如eeprom的eeprom_data。

所有i2c从设备组成的双向链表:detected

struct device dev表明struct i2c_client代表的是一个硬件,比如eeprom芯片,或则rtc芯片,通过i2c总线连接到i2c_adapter硬件上。

而i2c_driver则是这个i2c_client芯片硬件的驱动程序。

我们一般会对每个I2C字符设备定义一个私有信息结构体,而i2c_client一般被包含在这个私有信息结构体中。看过LDR3源代码的hacker应该比较清楚。

i2c_client依附于i2c_adapter,也就是I2C设备和I2C总线控制器的对应关系,一个i2c_adapter可以挂接多个i2c_client,i2c_adapter的struct list_head userspace_clients;结构成员就是所有client的链表。

linux的最新版本基本上支持目前所有的I2C适配器硬件和I2C从设备,但是对于工程师来说,可能要面临各种情况:为i2c_adapter和i2c_client编写驱动程序。

二、I2C核心

I2C核心是源码位于drivers/i2c/i2c-core.c,它并不依赖于硬件平台的接口函数,是I2C总线驱动和设备驱动的纽带。

增加/删除i2c_adapter

int i2c_add_adapter(struct i2c_adapter adapter) //调用i2c_register_adapter()

int i2c_del_adapter(struct i2c_adapteradapter)

增加/删除i2c_driver

int i2c_register_driver(struct module owner, struct i2c_driverdriver)

int i2c_add_driver(struct i2c_driver driver) //调用i2c_register_driver

void i2c_del_driver(struct i2c_driverdriver)

增加/删除i2c_client

struct i2c_client i2c_new_device(struct i2c_adapteradap, struct i2c_board_info const info)

void i2c_unregister_device(struct i2c_clientclient)

注:在2.6.30版本之前使用的是i2c_attach_client()和i2c_detach_client()函数。之后attach被merge到了i2c_new_device中,而detach直接被unregister取代。实际上这两个函数内部都是调用了device_register()和device_unregister()

I2C传输、发送接收

int i2c_transfer(struct i2c_adapter adap, struct i2c_msgmsgs, int num)

int i2c_master_send(struct i2c_client client,const charbuf ,int count)

int i2c_master_recv(struct i2c_client client, charbuf ,int count)

i2c_transfer()函数用于进行I2C 适配器和I2C 设备之间的一组消息交互,i2c_master_send()函数和i2c_master_recv()函数内部会调用i2c_transfer()函数分别完成一条写消息和一条读消息。

i2c_transfer()本身不能和硬件完成消息交互,它寻找i2c_adapter对应的i2c_algorithm,要实现数据传送就要实现i2c_algorithm的master_xfer(),这个函数与具体的硬件有关,大部分时间由厂商完成。

i2c_transfer()通过调用__i2c_transfer()完成I2C通讯:

int __i2c_transfer(struct i2c_adapter adap, struct i2c_msgmsgs, int num)

{

unsigned long orig_jiffies;

int ret, try;

/Retry automatically on arbitration loss/

orig_jiffies = jiffies;

for (ret = 0, try = 0; try <= adap->retries; try++) {

ret = adap->algo->master_xfer(adap, msgs, num);

if (ret != -EAGAIN)

break;

if (time_after(jiffies, orig_jiffies + adap->timeout))

break;

}

return ret;

}

可见retries为重传尝试次数,timeout为超时时间。

三、Linux I2C总线驱动

1、I2C适配器的加载和卸除

加载:申请硬件资源,比如IO地址,中断号,调用i2c_add_adapter加载适配器

i2c_add_adapter中会调用i2c_register_adapter函数

static int i2c_register_adapter(struct i2c_adapter *adap)

{

device_register(&adap->dev); //完成I2C主设备adapter的注册,即注册object和发送uevent等

i2c_scan_static_board_info(adap); //注册i2c_clienlt

}

static void i2c_scan_static_board_info(struct i2c_adapter adapter)

{

struct i2c_devinfodevinfo;

down_read(&__i2c_board_lock);

list_for_each_entry(devinfo, &i2c_board_list, list) {

if (devinfo->busnum == adapter->nr

&& !i2c_new_device(adapter,

&devinfo->board_info))

dev_err(&adapter->dev,

“Can’t create device at 0x%02x\n”,

devinfo->board_info.addr);

}

up_read(&i2c_board_lock);

}

i2c_new_device调用device_register注册i2c从设备。

那么,这个I2C从设备组成的双向循环链表,是什么时候通过什么方式建立起来的呢?

以 /arch/ARM/mach-pxa/saar.c 为例

static void __init saar_init(void)

{

saar_init_i2c();

}

static void __init saar_init_i2c(void)

{

pxa_set_i2c_info(NULL);

i2c_register_board_info(0, ARRAY_AND_SIZE(saar_i2c_info));

}

static struct i2c_board_info saar_i2c_info[] = {

[0] = {

.type = “da9034”,

.addr = 0x34,

.platform_data = &saar_da9034_info,

.irq = PXA_GPIO_TO_IRQ(mfp_to_gpio(MFP_PIN_GPIO83)),

},

};

/drivers/i2c/i2c-boardinfo.c/

int __init i2c_register_board_info(int busnum, structi2c_board_info const info, unsigned len)

{

struct i2c_devinfodevinfo;

devinfo->board_info = *info;

list_add_tail(&devinfo->list, &__i2c_board_list); //将I2C从设备加入该链表中

}

原文:https://blog.51cto.com/14441885/2427678

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值