您的位置:首页 > 运维架构 > Linux

linux的I2C驱动——读写操作

2017-08-06 16:12 477 查看

一、体系结构

接下来开始整体的介绍I2C,主要参考《Linux设备驱动开发详解》。


1、I2C核心

  I2C核心提供了I2C总线驱动和设备驱动的注册、注销方法,I2C通信方法。

2、I2C总线驱动

  I2C总线驱动是对I2C硬件体系结构中适配器的实现。

  I2C总线驱动主要包含I2C适配器数据结构i2c_adapter、I2C适配器的algorithm数据结构i2c_algorithm和控制I2C适配器产生通信信号的函数。

  经由I2C总线驱动的代码,我们可以控制I2C适配器以主控方式产生开始位、停止位、读写周期,以及以从设备方式被读写、产生ACK等。

3、I2C设备驱动

  I2C设备驱动主要包含数据结构i2c_driver和i2c_client。

二、I2C总线

  代码路径:drivers/I2C/busses/i2c_tegra.c

每个代码架构都有他对应的总线代码,下面介绍i2c_adapter、i2c_algorithm、i2c_msg三个结构体

  定义总线适配器

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;
};


数据传输结构体,决定I2C的通信方式

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);

/* To determine what the adapter supports */
u32 (*functionality) (struct i2c_adapter *);
};


数据格式

struct i2c_msg {
__u16 addr; /* 从地址          */
__u16 flags;
#define I2C_M_TEN       0x0010  /* this is a ten bit chip address */
#define I2C_M_RD        0x0001  /* read data, from slave to master */
#define I2C_M_NOSTART       0x4000  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR  0x2000  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK    0x1000  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK     0x0800  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN      0x0400  /* length will be first received byte */
__u16 len;      /* 数据长度             */
__u8 *buf;      /* 数据指针         */
};


I2C总线的初始化

static int __init tegra_i2c_init_driver(void)
{
return platform_driver_register(&tegra_i2c_driver);
}
subsys_initcall(tegra_i2c_init_driver);


int platform_driver_register(struct platform_driver *drv)
{
drv->driver.bus = &platform_bus_type;
if (drv->probe)
drv->driver.probe = platform_drv_probe;
if (drv->remove)
drv->driver.remove = platform_drv_remove;
if (drv->shutdown)
drv->driver.shutdown = platform_drv_shutdown;

return driver_register(&drv->driver);
}
EXPORT_SYMBOL_GPL(platform_driver_register);


总线会像普通设备那个去注册驱动,并调用probe函数。

tegra_i2c_probe函数用于初始化i2c_adapter和i2c_algorithm结构体

static int tegra_i2c_probe(struct platform_device *pdev)
{
struct tegra_i2c_dev *i2c_dev;
struct tegra_i2c_platform_data *pdata = pdev->dev.platform_data;
struct resource *res;
struct resource *iomem;
struct clk *clk;
struct clk *i2c_clk;
void *base;
int irq;
int ret = 0;

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
……
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
……
i2c_dev = kzalloc(sizeof(struct tegra_i2c_dev), GFP_KERNEL);
if (!i2c_dev) {
ret = -ENOMEM;
goto err_i2c_clk_put;
}

i2c_dev->base = base;
i2c_dev->clk = clk;
i2c_dev->i2c_clk = i2c_clk;
i2c_dev->iomem = iomem;
i2c_dev->adapter.algo = &tegra_i2c_algo;
i2c_dev->irq = irq;
i2c_dev->cont_id = pdev->id;
i2c_dev->dev = &pdev->dev;
i2c_dev->bus_clk_rate = pdata ? pdata->bus_clk_rate : 100000;

if (pdev->id == 3)
i2c_dev->is_dvc = 1;
init_completion(&i2c_dev->msg_complete);

platform_set_drvdata(pdev, i2c_dev);

ret = tegra_i2c_init(i2c_dev);
……
i2c_set_adapdata(&i2c_dev->adapter, i2c_dev);
i2c_dev->adapter.owner = THIS_MODULE;
i2c_dev->adapter.class = I2C_CLASS_HWMON;
strlcpy(i2c_dev->adapter.name, "Tegra I2C adapter",
sizeof(i2c_dev->adapter.name));
i2c_dev->adapter.algo = &tegra_i2c_algo;
i2c_dev->adapter.dev.parent = &pdev->dev;
i2c_dev->adapter.nr = pdev->id;

ret = i2c_add_numbered_adapter(&i2c_dev->adapter);
if (ret) {
dev_err(&pdev->dev, "Failed to add I2C adapter\n");
goto err_free_irq;
}

return 0;
……
}


通过i2c_set_adapdata函数设置i2c_adapter;

通过i2c_dev->adapter.algo = &tegra_i2c_algo去指定i2c_algorithm结构体

/*drivers/I2C/busses/i2c-tegra.c*/
static const struct i2c_algorithm tegra_i2c_algo = {
.master_xfer    = tegra_i2c_xfer,
.functionality  = tegra_i2c_func,
};


master_xfer定义了数据传输函数。

三、上层调用

上层通过read、write接口函数去调用驱动中对应的函数,在驱动中会定义file_operations


struct file_operations at24cxx_fops =
{
.owner = THIS_MODULE,
.re
9cd7
ad = at24cxx_read,
.write = at24cxx_write,
};


在at24cxx_read函数中会调用i2c_transfer函数,进行数据的读取。

/*drivers/I2C/i2c-core.c*/
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
unsigned long orig_jiffies;
int ret, try;

if (adap->algo->master_xfer) {
if (in_atomic() || irqs_disabled()) {
ret = i2c_trylock_adapter(adap);
if (!ret)
/* I2C activity is ongoing. */
return -EAGAIN;
} else {
i2c_lock_adapter(adap);
}

/* 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;
}
i2c_unlock_adapter(adap);

return ret;
} else {
dev_dbg(&adap->dev, "I2C level transfers not supported\n");
return -EOPNOTSUPP;
}
}
EXPORT_SYMBOL(i2c_transfer);


在i2c_transfer函数中调用adap->algo->master_xfer所指定的函数进行数据的传输。即调用总线去传输数据。

I2C设备的整体运行过程如下:

  上层通过read、write函数去调用驱动中对应的接口,然后由接口函数再去调用总线的相关函数,然后实现与设备的通信。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: