linux设备i2c优先级,Linux设备之I2C

黄信厚
2023-12-01

在内核中已经提供I2C子系统,在linux系统中,I2C驱动结构如下图所示:

其中从上图可以I2C由三大部分组成:

1、I2C核心:I2C核心提供了总线驱动和设备驱动的注册、注销的方法,I2C通信方法,与具体适配器无关的代码以及检测设备地址的代码等。

2、I2C总线驱动:对I2C硬件体系结构中适配器的实现,控制I2C总线驱动的代码,控制I2C适配器以主控方式产生开始位,停止位,读写以及设备读写方式,产生ack等。

3、I2C客户驱动程序:是对I2C硬件体系结构设备端得实现。

我们在分析其子系统之前,还是来看看其数据结构

i2c_adapter结构体表示一个物理的i2c总线控制器

点击(此处)折叠或打开

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_algorithm,其对应一套通信方法

点击(此处)折叠或打开

struct i2c_algorithm {

int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,

int num);//I2C传输函数指针

int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,

unsigned short flags, char read_write,

u8 command, int size, union i2c_smbus_data *data);//SMBUS传输函数指针

u32 (*functionality) (struct i2c_adapter *);//返回适配器支持的功能

};

一个i2c适配器需要i2c_algorithm中提供的通信函数来控制适配器产生特定的访问函数。其中提供了关键的函数master_xfer()用于产生i2c访问周期需要的信号,以i2c_msg为单位

点击(此处)折叠或打开

struct i2c_msg {

__u16 addr;    /* slave address            */

__u16 flags;

__u16 len;        /* msg length                */

__u8 *buf;        /* pointer to msg data            */

};i2c_driver代表i2c从设备驱动

点击(此处)折叠或打开

struct i2c_driver {

unsigned int class;

int (*attach_adapter)(struct i2c_adapter *) __deprecated;//适配器函数指针

int (*detach_adapter)(struct i2c_adapter *) __deprecated;

/* Standard driver model interfaces */

int (*probe)(struct i2c_client *, const struct i2c_device_id *);

int (*remove)(struct i2c_client *);

/* driver model interfaces that don't relate to enumeration */

void (*shutdown)(struct i2c_client *);

int (*suspend)(struct i2c_client *, pm_message_t mesg);

int (*resume)(struct i2c_client *);

void (*alert)(struct i2c_client *, unsigned int data);

int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);

struct device_driver driver;

const struct i2c_device_id *id_table;

/* Device detection callback for automatic device creation */

int (*detect)(struct i2c_client *, struct i2c_board_info *);

const unsigned short *address_list;

struct list_head clients;

};i2c_client代表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;

};

i2c_driver与i2c_client是一对多的关系,一个i2c_driver上可以支持多个同等类型的i2c_client。i2c_adapter与i2c_client的关系与i2c硬件体系中适配器和从设备的关系一致,i2c_client依附在i2c_adapter。

下面来看看内核提供任何去注册一个i2c的设备驱动

1.直接使用i2c_register_board_unfo完成设备注册,这种方法适合于i2c总线上预先已知设备,因此可以预先声明i2c设备在哪条总线上,在通过数组结构i2c_board_info,内核实例为arch\arm\mach-omap2\board-h4.c。

点击(此处)折叠或打开

static struct i2c_board_info __initdata h4_i2c_board_info[] = {

{

I2C_BOARD_INFO("isp1301_omap", 0x2d),

.irq        = OMAP_GPIO_IRQ(125),

},

{    /* EEPROM on mainboard */

I2C_BOARD_INFO("24c01", 0x52),

.platform_data    = &m24c01,

},

{    /* EEPROM on cpu card */

I2C_BOARD_INFO("24c01", 0x57),

.platform_data    = &m24c01,

},

};

点击(此处)折叠或打开

static void __init omap_h4_init(void)

{

(...)

i2c_register_board_info(1, h4_i2c_board_info,

ARRAY_SIZE(h4_i2c_board_info));

(...)

}那么内核怎么完成匹配呢?首先来看看i2c_register_board_info()

点击(此处)折叠或打开

int __init

i2c_register_board_info(int busnum,

struct i2c_board_info const *info, unsigned len)

{

int status;

down_write(&__i2c_board_lock);

/* dynamic bus numbers will be assigned after the last static one */

if (busnum >= __i2c_first_dynamic_bus_num)

__i2c_first_dynamic_bus_num = busnum + 1;

for (status = 0; len; len--, info++) {

struct i2c_devinfo    *devinfo;

devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);

if (!devinfo) {

pr_debug("i2c-core: can't register boardinfo!\n");

status = -ENOMEM;

break;

}

devinfo->busnum = busnum;

devinfo->board_info = *info;

list_add_tail(&devinfo->list, &__i2c_board_list);

}

up_write(&__i2c_board_lock);

return status;

}这上面只做了一个非常重要的,将i2c_board_info放入到__i2c_board_list链表,而这个info中存放的是i2c通信非常重要的,设备名字和设备地址。那么链表何时使用呢?这个在i2c_scan_static_board_info的时候会调用

点击(此处)折叠或打开

static void i2c_scan_static_board_info(struct i2c_adapter *adapter)

{

struct i2c_devinfo    *devinfo;

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_board_list链表上面的i2c设备的信息,也就是我们在启动的时候指出的i2c设备的信息,如果指定设备位于adapter所在的i2c总线上,那么就调用i2c_new_device()。

2.直接使用2c_new_device, i2c_new_probed_device

点击(此处)折叠或打开

struct i2c_client *

i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)

{

struct i2c_client    *client;

int            status;

client = kzalloc(sizeof *client, GFP_KERNEL);

if (!client)

return NULL;

client->adapter = adap;

client->dev.platform_data = info->platform_data;

if (info->archdata)

client->dev.archdata = *info->archdata;

client->flags = info->flags;

client->addr = info->addr;

client->irq = info->irq;

strlcpy(client->name, info->type, sizeof(client->name));

/* Check for address validity */

status = i2c_check_client_addr_validity(client);

if (status) {

dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",

client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);

goto out_err_silent;

}

/* Check for address business */

status = i2c_check_addr_busy(adap, client->addr);

if (status)

goto out_err;

client->dev.parent = &client->adapter->dev;

client->dev.bus = &i2c_bus_type;

client->dev.type = &i2c_client_type;

client->dev.of_node = info->of_node;

/* For 10-bit clients, add an arbitrary offset to avoid collisions */

dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),

client->addr | ((client->flags & I2C_CLIENT_TEN)

? 0xa000 : 0));

status = device_register(&client->dev);

if (status)

goto out_err;

dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",

client->name, dev_name(&client->dev));

return client;

out_err:

dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x "

"(%d)\n", client->name, client->addr, status);

out_err_silent:

kfree(client);

return NULL;

}该函数只是device_register,但是上面出现了一个新的结构,i2c_client,其实它就是一个struct device的i2c设备的封装。在client里保存该设备的相关信息,client->adapter指向了它所在的adapter。clent->dev所在的bus为i2c_bus_type,在device_register注册的时候,会调用总线的match函数。

点击(此处)折叠或打开

static int i2c_device_match(struct device *dev, struct device_driver *drv)

{

struct i2c_client    *client = i2c_verify_client(dev);

struct i2c_driver    *driver;

if (!client)

return 0;

/* Attempt an OF style match */

if (of_driver_match_device(dev, drv))

return 1;

driver = to_i2c_driver(drv);

/* match on an id table if there is one */

if (driver->id_table)

return i2c_match_id(driver->id_table, client) != NULL;

return 0;

}

点击(此处)折叠或打开

static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,

const struct i2c_client *client)

{

while (id->name[0]) {

if (strcmp(client->name, id->name) == 0)

return id;

id++;

}

return NULL;

}上面是匹配driver的id_table的名字和client的名字是否相同,那么会调用驱动的probe函数。

下面在分析下i2c_new_probed_device有啥不一样,前面一个认为设备肯定存在,而后面的是对于已经识别出来的设备,才会创建。

点击(此处)折叠或打开

for (i = 0; addr_list[i] != I2C_CLIENT_END; i++) {

/* Check address validity */

if (i2c_check_addr_validity(addr_list[i]) < 0) {

dev_warn(&adap->dev, "Invalid 7-bit address "

"0x%02x\n", addr_list[i]);

continue;

}

/* Check address availability */

if (i2c_check_addr_busy(adap, addr_list[i])) {

dev_dbg(&adap->dev, "Address 0x%02x already in "

"use, not probing\n", addr_list[i]);

continue;

}

/* Test address responsiveness */

if (probe(adap, addr_list[i]))

break;

}3.直接i2c_add_driver

前面的2中方法,都要实现确定适配器,如果我们不知道这个i2c设备在那个适配器上,怎么办?内核提供了一种去class表示在所有的适配器上查找一些i2c设备的地址。

点击(此处)折叠或打开

static struct i2c_driver at24_driver = {

.driver = {

.name = "at24",

.owner = THIS_MODULE,

},

.probe = at24_probe,

.remove = __devexit_p(at24_remove),

.id_table = at24_ids,

};

点击(此处)折叠或打开

static int __init at24_init(void)

{

if (!io_limit) {

pr_err("at24: io_limit must not be 0!\n");

return -EINVAL;

}

io_limit = rounddown_pow_of_two(io_limit);

return i2c_add_driver(&at24_driver);

}而i2c_add_diver只是调用i2c_register_driver了

点击(此处)折叠或打开

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)

{

int res;

/* Can't register until after driver model init */

if (unlikely(WARN_ON(!i2c_bus_type.p)))

return -EAGAIN;

/* add the driver to the list of i2c drivers in the driver core */

driver->driver.owner = owner;

driver->driver.bus = &i2c_bus_type;

/* When registration returns, the driver core

* will have called probe() for all matching-but-unbound devices.

*/

res = driver_register(&driver->driver);

if (res)

return res;

/* Drivers should switch to dev_pm_ops instead. */

if (driver->suspend)

pr_warn("i2c-core: driver [%s] using legacy suspend method\n",

driver->driver.name);

if (driver->resume)

pr_warn("i2c-core: driver [%s] using legacy resume method\n",

driver->driver.name);

pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);

INIT_LIST_HEAD(&driver->clients);

/* Walk the adapters that are already present */

i2c_for_each_dev(driver, __process_new_driver);

return 0;

}上面比较主要的是driver_register,同事遍历driver->client链表,会调用driver->detect的函数。

 类似资料: