至少从linux2.6开始linux为方便管理众多的驱动引入了总线, 设备,驱动模型.总线(bus),总线由linux内核创建,设备,设备(device)由芯片厂商编写的驱动添加.驱动(driver), 驱动就是要驱动开发人员要编写的,比如陀螺仪驱动,触摸驱动等等.
如果不是芯片原厂,大多数情况下不必关心总线和设备的创建. 按照规定在设备树里添加iic设备的节点然后使用i2c_add_driver()添加驱动即可,非常的方便.这里介绍下总线和设备的创建.不需要可以跳过.
1.1iic总线创建:
在drivers/i2c/i2c-core-xxx.c (不同版本的内核有差异).目录创建了一个i2c总线.如下:
/* We must initialize early, because some subsystems register i2c drivers
* in subsys_initcall() code, but are linked (and initialized) before i2c.
*/
postcore_initcall(i2c_init);
static int __init i2c_init(void)
{
int retval;
retval = of_alias_get_highest_id("i2c");
down_write(&__i2c_board_lock);
if (retval >= __i2c_first_dynamic_bus_num)
__i2c_first_dynamic_bus_num = retval + 1;
up_write(&__i2c_board_lock);
retval = bus_register(&i2c_bus_type);
if (retval)
return retval;
is_registered = true;
#ifdef CONFIG_I2C_COMPAT
i2c_adapter_compat_class = class_compat_register("i2c-adapter");
if (!i2c_adapter_compat_class) {
retval = -ENOMEM;
goto bus_err;
}
#endif
retval = i2c_add_driver(&dummy_driver);
if (retval)
goto class_err;
if (IS_ENABLED(CONFIG_OF_DYNAMIC))
WARN_ON(of_reconfig_notifier_register(&i2c_of_notifier));
if (IS_ENABLED(CONFIG_ACPI))
WARN_ON(acpi_reconfig_notifier_register(&i2c_acpi_notifier));
return 0;
class_err:
#ifdef CONFIG_I2C_COMPAT
class_compat_unregister(i2c_adapter_compat_class);
bus_err:
#endif
is_registered = false;
bus_unregister(&i2c_bus_type);
return retval;
}
我们知道编译进内核的驱动的初始化函数是被自动调用的,linux内核启动后会跳转到一个"中断初始化表"里面,所有的驱动按照初始化的先后分成了0~7共八个等级.如果有必要可以手动指定驱动在哪一个先后等级执行.postcore_initcall(i2c_init);指定i2c总线注册的等级是2,
//include/linux/init.h
#define core_initcall(fn) __define_initcall(fn, 1)
#define core_initcall_sync(fn) __define_initcall(fn, 1s)
#define postcore_initcall(fn) __define_initcall(fn, 2)
#define postcore_initcall_sync(fn) __define_initcall(fn, 2s)
#define arch_initcall(fn) __define_initcall(fn, 3)
#define arch_initcall_sync(fn) __define_initcall(fn, 3s)
#define subsys_initcall(fn) __define_initcall(fn, 4)
#define subsys_initcall_sync(fn) __define_initcall(fn, 4s)
#define fs_initcall(fn) __define_initcall(fn, 5)
#define fs_initcall_sync(fn) __define_initcall(fn, 5s)
#define rootfs_initcall(fn) __define_initcall(fn, rootfs)
#define device_initcall(fn) __define_initcall(fn, 6)
#define device_initcall_sync(fn) __define_initcall(fn, 6s)
#define late_initcall(fn) __define_initcall(fn, 7)
#define late_initcall_sync(fn) __define_initcall(fn, 7s)
获取i2c最大的别名
of_alias_get_highest_id("i2c")获取i2c别名的最大值然后加一赋值给全局变量__i2c_first_dynamic_bus_num(i2c 第一个, 动态,总线,编号), 作用是什么暂时不关系.
bus_register注册总线
bus_register()函数是核心注册函数 ,i2c_bus_type 是一个struct bus_type 类型的全局结构体变量.代表一个总线.bus_register()函数会根据总线的kobject标签初始化总线以及总线所拥有的子系统例如devices,drivers等等.
总线结构体:
结构体里的数据项用到的时候再介绍.
/**
* struct bus_type - The bus type of the device
*
* @name: The name of the bus.
* @dev_name: Used for subsystems to enumerate devices like ("foo%u", dev->id).
* @dev_root: Default device to use as the parent.
* @bus_groups: Default attributes of the bus.
* @dev_groups: Default attributes of the devices on the bus.
* @drv_groups: Default attributes of the device drivers on the bus.
* @match: Called, perhaps multiple times, whenever a new device or driver
* is added for this bus. It should return a positive value if the
* given device can be handled by the given driver and zero
* otherwise. It may also return error code if determining that
* the driver supports the device is not possible. In case of
* -EPROBE_DEFER it will queue the device for deferred probing.
* @uevent: Called when a device is added, removed, or a few other things
* that generate uevents to add the environment variables.
* @probe: Called when a new device or driver add to this bus, and callback
* the specific driver's probe to initial the matched device.
* @remove: Called when a device removed from this bus.
* @shutdown: Called at shut-down time to quiesce the device.
*
* @online: Called to put the device back online (after offlining it).
* @offline: Called to put the device offline for hot-removal. May fail.
*
* @suspend: Called when a device on this bus wants to go to sleep mode.
* @resume: Called to bring a device on this bus out of sleep mode.
* @num_vf: Called to find out how many virtual functions a device on this
* bus supports.
* @pm: Power management operations of this bus, callback the specific
* device driver's pm-ops.
* @iommu_ops: IOMMU specific operations for this bus, used to attach IOMMU
* driver implementations to a bus and allow the driver to do
* bus-specific setup
* @p: The private data of the driver core, only the driver core can
* touch this.
* @lock_key: Lock class key for use by the lock validator
*
* A bus is a channel between the processor and one or more devices. For the
* purposes of the device model, all devices are connected via a bus, even if
* it is an internal, virtual, "platform" bus. Buses can plug into each other.
* A USB controller is usually a PCI device, for example. The device model
* represents the actual connections between buses and the devices they control.
* A bus is represented by the bus_type structure. It contains the name, the
* default attributes, the bus' methods, PM operations, and the driver core's
* private data.
*/
struct bus_type {
const char *name;
const char *dev_name;
struct device *dev_root;
const struct attribute_group **bus_groups;
const struct attribute_group **dev_groups;
const struct attribute_group **drv_groups;
int (*match)(struct device *dev, struct device_driver *drv);
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
int (*probe)(struct device *dev);
int (*remove)(struct device *dev);
void (*shutdown)(struct device *dev);
int (*online)(struct device *dev);
int (*offline)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
int (*num_vf)(struct device *dev);
const struct dev_pm_ops *pm;
const struct iommu_ops *iommu_ops;
struct subsys_private *p;
struct lock_class_key lock_key;
};
bus的私有数据体
申请一个struct subsys_private结构体实例,在struct bus_types结构体的最后有一个struct subsys_private类型的指针, 这就是用指向struct bus_types的私有数据. struct bus_types如下:
/**
* struct subsys_private - structure to hold the private to the driver core portions of the bus_type/class structure.
*
* @subsys - the struct kset that defines this subsystem
* @devices_kset - the subsystem's 'devices' directory
* @interfaces - list of subsystem interfaces associated
* @mutex - protect the devices, and interfaces lists.
*
* @drivers_kset - the list of drivers associated
* @klist_devices - the klist to iterate over the @devices_kset
* @klist_drivers - the klist to iterate over the @drivers_kset
* @bus_notifier - the bus notifier list for anything that cares about things
* on this bus.
* @bus - pointer back to the struct bus_type that this structure is associated
* with.
*
* @glue_dirs - "glue" directory to put in-between the parent device to
* avoid namespace conflicts
* @class - pointer back to the struct class that this structure is associated
* with.
*
* This structure is the one that is the actual kobject allowing struct
* bus_type/class to be statically allocated safely. Nothing outside of the
* driver core should ever touch these fields.
*/
struct subsys_private {
struct kset subsys;
struct kset *devices_kset;
struct list_head interfaces;
struct mutex mutex;
struct kset *drivers_kset;
struct klist klist_devices;
struct klist klist_drivers;
struct blocking_notifier_head bus_notifier;
unsigned int drivers_autoprobe:1;
struct bus_type *bus;
struct kset glue_dirs;
struct class *class;
};
子系统私有数据,服务于像bus_type和 class这样的容器类结构体.保存linux核心内核用到的一些数据,比如总线的keyset, 总线管理的device/driver的keyset.
bus_register函数实现
/**
* bus_register - register a driver-core subsystem
* @bus: bus to register
*
* Once we have that, we register the bus with the kobject
* infrastructure, then register the children subsystems it has:
* the devices and drivers that belong to the subsystem.
*/
int bus_register(struct bus_type *bus)
{
int retval;
struct subsys_private *priv;
struct lock_class_key *key = &bus->lock_key;
/**
*申请一个struct subsys_private结构体, 然后总线和私有数据相互绑定.
* priv->bus = bus;设置这个私有数据服务的总线,
* bus->p = priv;设置总线的私有数据
*/
priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->bus = bus;
bus->p = priv;
/*初始化总线的阻塞通知链,用于通知总线上的所有对象*/
BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
/*设置私有数据子系统名字,最终会体现在/sys/bus/,目录下*/
retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
if (retval)
goto out;
/*设置subsys的kobj的kset,也就是subsys所属的kset,
* bus_kset是全局变量 tatic struct kset *bus_kset;
* bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
* 以iic总线为例,它的父kset 是名为"bus"的kset,
* 所以i2c总线位于/sys/bys/目录下.
*drivers_autoprobe 默认属性,设置设备和驱动匹配后自动执行驱动的prob函数
*/
priv->subsys.kobj.kset = bus_kset;
priv->subsys.kobj.ktype = &bus_ktype;
priv->drivers_autoprobe = 1;
/**初始化并添加一个kset, 这里就是总线私有数据的kset,
* 创建完成后就会有/sys/bus/i2c目录了.kset在sysfs文件系统中会是一个目录.
*/
retval = kset_register(&priv->subsys);
if (retval)
goto out;
/*在总线基础上创建一个bus_attr_uevent 文件*/
retval = bus_create_file(bus, &bus_attr_uevent);
if (retval)
goto bus_uevent_fail;
/**创建总线的devices和drivers,体现在sysfs文件系统,每个bus下都有devices目录,用于保存
*总线拥有的所有设备,drivers目录用于保存总线所拥有的所有驱动.
* kset_create_and_add函数指定的
* 父设备是&priv->subsys.kobj, subsys是kset,是一个容器,
* 内核的对象管理的基础对象是kobject,
*/
priv->devices_kset = kset_create_and_add("devices", NULL,
&priv->subsys.kobj);
if (!priv->devices_kset) {
retval = -ENOMEM;
goto bus_devices_fail;
}
priv->drivers_kset = kset_create_and_add("drivers", NULL,
&priv->subsys.kobj);
if (!priv->drivers_kset) {
retval = -ENOMEM;
goto bus_drivers_fail;
}
/**初始化一些同步访问数据和链表
* 挂载在总线的设备和驱动是用链表管理的
*/
INIT_LIST_HEAD(&priv->interfaces);
__mutex_init(&priv->mutex, "subsys mutex", key);
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&priv->klist_drivers, NULL, NULL);
/**在总线下创建drivers_probe文件和drivers_autoprobe文件
* 软件可以cat drivers_autoprobe 检测卡是否开启了自动prob 前面
* 已经把 drivers_autoprobe 设置为1;
*/
retval = add_probe_files(bus);
if (retval)
goto bus_probe_files_fail;
/*添加总线的一些特有属性,这些属性定义在bus->bus_groups
* 对于i2c总线来说,bus->bus_groups = NULL, 什么也不创建.
*/
retval = bus_add_groups(bus, bus->bus_groups);
if (retval)
goto bus_groups_fail;
pr_debug("bus: '%s': registered\n", bus->name);
return 0;
bus_groups_fail:
remove_probe_files(bus);
bus_probe_files_fail:
kset_unregister(bus->p->drivers_kset);
bus_drivers_fail:
kset_unregister(bus->p->devices_kset);
bus_devices_fail:
bus_remove_file(bus, &bus_attr_uevent);
bus_uevent_fail:
kset_unregister(&bus->p->subsys);
out:
kfree(bus->p);
bus->p = NULL;
return retval;
}
iic添加设备
前面说过i2c设备是由芯片厂商编写,对于我们使用者来说只需要添加设备树然后添加驱动即可.这里已只看下设备是如何创建的,忽略具体的驱动实现.
一个芯片一般有一到多个iic控制器,在linux驱动中struct i2c_adapter adapter 代表一个i2c是控制器(这里翻译做适配器).如下:
/*
* i2c_adapter is the structure used to identify a physical i2c bus along
* with the access algorithms necessary to access it.
*/
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 */
const struct i2c_lock_operations *lock_ops;
struct rt_mutex bus_lock;
struct rt_mutex mux_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;
const struct i2c_adapter_quirks *quirks;
struct irq_domain *host_notify_domain;
};
linux把一个iic控制器抽象成一个i2c_adapter结构体,其中的struct i2c_algorithm *algo 访问i2c控制器的方法,这是芯片厂商实现的重点.如下:
/**
* struct i2c_algorithm - represent I2C transfer method
* @master_xfer: Issue a set of i2c transactions to the given I2C adapter
* defined by the msgs array, with num messages available to transfer via
* the adapter specified by adap.
* @smbus_xfer: Issue smbus transactions to the given I2C adapter. If this
* is not present, then the bus layer will try and convert the SMBus calls
* into I2C transfers instead.
* @functionality: Return the flags that this algorithm/adapter pair supports
* from the I2C_FUNC_* flags.
* @reg_slave: Register given client to I2C slave mode of this adapter
* @unreg_slave: Unregister given client from I2C slave mode of this adapter
*
* The following structs are for those who like to implement new bus drivers:
* i2c_algorithm is the interface to a class of hardware solutions which can
* be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584
* to name two of the most common.
*
* The return codes from the @master_xfer field should indicate the type of
* error code that occurred during the transfer, as documented in the kernel
* Documentation file Documentation/i2c/fault-codes.
*/
struct i2c_algorithm {
/* If an adapter algorithm can't do I2C-level access, set master_xfer
to NULL. If an adapter algorithm can do SMBus access, set
smbus_xfer. If set to NULL, the SMBus protocol is simulated
using common I2C messages */
/* master_xfer should return the number of messages successfully
processed, or a negative value on error */
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 *);
#if IS_ENABLED(CONFIG_I2C_SLAVE)
int (*reg_slave)(struct i2c_client *client);
int (*unreg_slave)(struct i2c_client *client);
#endif
};
主需要是实现iic的发送和接收实现,不同芯片差异很大, 这部分实现代码位于drivers/i2c/busses/目录下, 文件名 i2c-xxx.c xxx 是具体的芯片.这里以i2c-imx.c为例看下i2c设备是怎样添加进系统的.
i2c设备的添加
i2c设备的添加是通过平台驱动实现的,在设备树里定义了i2c 适配器的节点.如下:
i2c1: i2c@43f80000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx35-i2c", "fsl,imx1-i2c";
reg = <0x43f80000 0x4000>;
clocks = <&clks 51>;
clock-names = "ipg_per";
interrupts = <10>;
status = "disabled";
};
i2c2: i2c@43f98000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx35-i2c", "fsl,imx1-i2c";
reg = <0x43f98000 0x4000>;
clocks = <&clks 52>;
clock-names = "ipg_per";
interrupts = <4>;
status = "disabled";
};
i2c3: i2c@43f84000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx35-i2c", "fsl,imx1-i2c";
reg = <0x43f84000 0x4000>;
clocks = <&clks 53>;
clock-names = "ipg_per";
interrupts = <3>;
status = "disabled";
};
i2c每一个适配器对应一个节点,可以看到这些节点的compatible属性是相同的,所以这里一个驱动会初始化所有的这些i2c适配器.
直接看i2c适配器驱动的prob函数.
static int i2c_imx_probe(struct platform_device *pdev)
{
const struct of_device_id *of_id = of_match_device(i2c_imx_dt_ids,
&pdev->dev);
struct imx_i2c_struct *i2c_imx;
struct resource *res;
struct imxi2c_platform_data *pdata = dev_get_platdata(&pdev->dev);
void __iomem *base;
int irq, ret;
dma_addr_t phy_addr;
/*.....省略....*/
/* Setup i2c_imx driver structure */
strlcpy(i2c_imx->adapter.name, pdev->name, sizeof(i2c_imx->adapter.name));
i2c_imx->adapter.owner = THIS_MODULE;
i2c_imx->adapter.algo = &i2c_imx_algo;
i2c_imx->adapter.dev.parent = &pdev->dev;
i2c_imx->adapter.nr = pdev->id;
i2c_imx->adapter.dev.of_node = pdev->dev.of_node;
i2c_imx->base = base;
/*.....省略....*/
/* Init queue */
/*.....省略....*/;
/* Add I2C adapter */
ret = i2c_add_numbered_adapter(&i2c_imx->adapter);
if (ret < 0) {
dev_err(&pdev->dev, "registration failed\n");
goto clk_disable;
}
/*.....省略....*/
/* Init DMA config if supported */
i2c_imx_dma_request(i2c_imx, phy_addr);
return 0; /* Return OK */
/*.....省略....*/
}
由于i2c适配器会用到中断,dma, gpio,时钟等等,这里会有大量的和具体实现相关的代码,我们忽略. 重点是adapter的初始化, 这里设置adapter结构体的owmer=THIS_MODE, algo= &i2c_imx_algo.这是iic适配器的核心通信方法..dev.parent = &pdev->dev, 可以看到 adapter结构提是一个设备,这里制定了他的父设备是这个平台设备.nr = pdev->id, 指定adapter设备的编号..dev.of_node, 指定adaper的设备树节点.
重点是调用i2c_add_numbered_adapter()函数吧adapter设备添加到i2c总线.函数调用关系如下:
最终还是调用i2c核心驱动drivers/i2c/i2c-core-xxx.c文件的i2c_register_adapter()函数把adapter注册进i2c总线.函数实现如下:
static int i2c_register_adapter(struct i2c_adapter *adap)
{
int res = -EINVAL;
/* Can't register until after driver model init */
/**全局变量is_registered, 确保i2c总线已经初始化过了.
*/
if (WARN_ON(!is_registered)) {
res = -EAGAIN;
goto out_list;
}
/* Sanity checks */
if (WARN(!adap->name[0], "i2c adapter has no name"))
goto out_list;
if (!adap->algo) {
pr_err("adapter '%s': no algo supplied!\n", adap->name);
goto out_list;
}
/*指定锁定总线的方法,主要用于并发访问
* 初始化总线锁
*/
if (!adap->lock_ops)
adap->lock_ops = &i2c_adapter_lock_ops;
rt_mutex_init(&adap->bus_lock);
rt_mutex_init(&adap->mux_lock);
mutex_init(&adap->userspace_clients_lock);
INIT_LIST_HEAD(&adap->userspace_clients);
/* Set default timeout to 1 second if not already set */
if (adap->timeout == 0)
adap->timeout = HZ;
/* register soft irqs for Host Notify */
res = i2c_setup_host_notify_irq_domain(adap);
if (res) {
pr_err("adapter '%s': can't create Host Notify IRQs (%d)\n",
adap->name, res);
goto out_list;
}
/** 设置adapter 的deivce名字,格式是i2c-%d,后面的编号一般是设备树里的编号.
*i2c_bus_type , 指定adapter device的总线.
* dap->dev.type = &i2c_adapter_type; 设置device的默认属性
*/
dev_set_name(&adap->dev, "i2c-%d", adap->nr);
adap->dev.bus = &i2c_bus_type;
adap->dev.type = &i2c_adapter_type;
/*初始化并注册device到系统,创建完成后就会在/sys/bus/i2c/目录下创建相应的设备*/
res = device_register(&adap->dev);
if (res) {
pr_err("adapter '%s': can't register device (%d)\n", adap->name, res);
goto out_list;
}
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
pm_runtime_no_callbacks(&adap->dev);
pm_suspend_ignore_children(&adap->dev, true);
pm_runtime_enable(&adap->dev);
#ifdef CONFIG_I2C_COMPAT
res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,
adap->dev.parent);
if (res)
dev_warn(&adap->dev,
"Failed to create compatibility class link\n");
#endif
i2c_init_recovery(adap);
/* create pre-declared device nodes */
/*创建attribute子节点对应的device*/
of_i2c_register_devices(adap);
i2c_acpi_register_devices(adap);
i2c_acpi_install_space_handler(adap);
if (adap->nr < __i2c_first_dynamic_bus_num)
i2c_scan_static_board_info(adap);
/* Notify drivers */
mutex_lock(&core_lock);
bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
mutex_unlock(&core_lock);
return 0;
out_list:
mutex_lock(&core_lock);
idr_remove(&i2c_adapter_idr, adap->nr);
mutex_unlock(&core_lock);
return res;
}
of_i2c_register_devices(adap); 函数会搜索adap 节点下的iic设备节点然后创建设备实现如下:
void of_i2c_register_devices(struct i2c_adapter *adap)
{
struct device_node *bus, *node;
struct i2c_client *client;
/* Only register child devices if the adapter has a node pointer set */
if (!adap->dev.of_node)
return;
dev_dbg(&adap->dev, "of_i2c: walking child nodes\n");
/*通常在设备树里不会定义 名为 "i2c-bus" 的子节点.bus 为null
* 当然我们可以在 adatper 子节点下创建名为 "i2c-bus" 子节点然后把
* iic 设备节点放在 "i2c-bus" 节点下.
*/
bus = of_get_child_by_name(adap->dev.of_node, "i2c-bus");
if (!bus)
bus = of_node_get(adap->dev.of_node);
for_each_available_child_of_node(bus, node) {
if (of_node_test_and_set_flag(node, OF_POPULATED))
continue;
/*注册i2c设备,返回值是struct i2c_client (i2c 客户) */
client = of_i2c_register_device(adap, node);
if (IS_ERR(client)) {
dev_warn(&adap->dev,
"Failed to create I2C device for %pOF\n",
node);
of_node_clear_flag(node, OF_POPULATED);
}
}
of_node_put(bus);
}
of_i2c_register_device 函数实现如下:
static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
struct device_node *node)
{
struct i2c_client *result;
struct i2c_board_info info = {};
struct dev_archdata dev_ad = {};
const __be32 *addr_be;
u32 addr;
int len;
dev_dbg(&adap->dev, "of_i2c: register %pOF\n", node);
if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
dev_err(&adap->dev, "of_i2c: modalias failure on %pOF\n",
node);
return ERR_PTR(-EINVAL);
}
/*获取 iic 设备地址*/
addr_be = of_get_property(node, "reg", &len);
if (!addr_be || (len < sizeof(*addr_be))) {
dev_err(&adap->dev, "of_i2c: invalid reg on %pOF\n", node);
return ERR_PTR(-EINVAL);
}
addr = be32_to_cpup(addr_be);
if (addr & I2C_TEN_BIT_ADDRESS) {
addr &= ~I2C_TEN_BIT_ADDRESS;
info.flags |= I2C_CLIENT_TEN;
}
if (addr & I2C_OWN_SLAVE_ADDRESS) {
addr &= ~I2C_OWN_SLAVE_ADDRESS;
info.flags |= I2C_CLIENT_SLAVE;
}
/*检查iic 设备地址是否有效*/
if (i2c_check_addr_validity(addr, info.flags)) {
dev_err(&adap->dev, "of_i2c: invalid addr=%x on %pOF\n",
addr, node);
return ERR_PTR(-EINVAL);
}
info.addr = addr;
/*获取设备节点*/
info.of_node = of_node_get(node);
info.archdata = &dev_ad;
if (of_property_read_bool(node, "host-notify"))
info.flags |= I2C_CLIENT_HOST_NOTIFY;
if (of_get_property(node, "wakeup-source", NULL))
info.flags |= I2C_CLIENT_WAKE;
/*最终调用 i2c_new_device 创建一个iic device*/
result = i2c_new_device(adap, &info);
if (result == NULL) {
dev_err(&adap->dev, "of_i2c: Failure registering %pOF\n", node);
of_node_put(node);
return ERR_PTR(-EINVAL);
}
return result;
}
of_i2c_register_device函数内容不多, 定义了一个struct i2c_board_info info 结构体, 然后从设备树里获取iic设备的一些信息,例如iic地址,获取设备对应的设备树节点,等等,最终主要的工作由函数i2c_new_device(adap, &info);完成,注意i2c_new_device函数返回的是一个struct i2c_client 类型的结构体,它是核心, 设备和后面介绍的驱动匹配后驱动就会通过prob函数的参数得到设备的struct i2c_client结构体,struct i2c_client 介绍如下:
/**
* struct i2c_client - represent an I2C slave device 代表一个iic 从设备
* @flags: I2C_CLIENT_TEN indicates the device uses a ten bit chip address;
* I2C_CLIENT_PEC indicates it uses SMBus Packet Error Checking
标记,I2C_CLIENT_TEN表示这个设备是用十位iic从地址.
标记,I2C_CLIENT_PEC表示这个设备使用SMBus 校验错误.
* @addr: Address used on the I2C bus connected to the parent adapter.
iic从设备连接到iic物理总线上时的从地址.
* @name: Indicates the type of the device, usually a chip name that's
* generic enough to hide second-sourcing and compatible revisions. 表示deivce
的类型.
* @adapter: manages the bus segment hosting this I2C device, 指定处理iic 从设备的iic设备
* @dev: Driver model device node for the slave. i2c_client 是一个设备,这是它包含的device 实例
* @irq: indicates the IRQ generated by this device (if any) , 表示这个设备使用的中断,可有可无
* @detected: member of an i2c_driver.clients list or i2c-core's
* userspace_devices list
* @slave_cb: Callback when I2C slave mode of an adapter is used. The adapter
* calls it to pass on slave events to the slave driver.
*
* An i2c_client identifies a single device (i.e. chip) connected to an
* i2c bus. The behaviour exposed to Linux is defined by the driver
* managing the device.
* 一个 i2c_client 代表一个简单的 连接在物理i2c总线上的设备(例如mpu6050,触摸,等等)
*/
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 device dev; /* the device structure */
int irq; /* irq issued by device */
struct list_head detected;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
i2c_slave_cb_t slave_cb; /* callback for slave mode */
#endif
};
i2c_new_device函数完成最终的设备的添加函数实现如下:
/**
* i2c_new_device - instantiate an i2c device 新建一个 i2c 设备.
* @adap: the adapter managing the device, 指定操作这个deivce 的 adapter
* @info: describes one I2C device; bus_num is ignored , i2c 设备的描述信息.
* Context: can sleep
*
* Create an i2c device. Binding is handled through driver model
* 创建一个 i2c 设备,设备和驱动的绑定是通过后面介绍的驱动来实现的.
* probe()/remove() methods. A driver may be bound to this device when we
* return from this function, or any later moment (e.g. maybe hotplugging will
* load the driver module). This call is not appropriate for use by mainboard
* initialization logic, which usually runs during an arch_initcall() long
* before any i2c_adapter could exist.
*
* This returns the new i2c client, which may be saved for later use with
* i2c_unregister_device(); or NULL to indicate an error.
*/
struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
struct i2c_client *client;
int status;
/*创建clinet device*/
client = kzalloc(sizeof *client, GFP_KERNEL);
if (!client)
return NULL;
client->adapter = adap;
/* platform_data 是怎么获取的?*/
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;
if (!client->irq)
client->irq = i2c_dev_irq_from_resources(info->resources,
info->num_resources);
strlcpy(client->name, info->type, sizeof(client->name));
status = i2c_check_addr_validity(client->addr, client->flags);
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, i2c_encode_flags_to_addr(client));
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;
client->dev.fwnode = info->fwnode;
i2c_dev_set_name(adap, client);
if (info->properties) {
status = device_add_properties(&client->dev, info->properties);
if (status) {
dev_err(&adap->dev,
"Failed to add properties to client %s: %d\n",
client->name, status);
goto out_err;
}
}
/*最终还是调用device_register 注册一个设备,*/
status = device_register(&client->dev);
if (status)
goto out_free_props;
dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
client->name, dev_name(&client->dev));
return client;
out_free_props:
if (info->properties)
device_remove_properties(&client->dev);
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;
}
EXPORT_SYMBOL_GPL(i2c_new_device);
函数申请并初始化client 结构提实例,然后调用status = device_register(&client->dev); 添加了一个设设备.
总结一下:
添加i2c驱动
添加i2c驱动之前首先要在设备树里添加设备的信息, 不同的设备差异很大,但是至少要有以下属性:
&i2c0 {
clock-frequency = <400000>;
lm75@48 {
compatible = "nxp,lm75";
reg = <0x48>;
};
};
compatible属性用于设备和驱动的绑定,reg, 用于指定iic设备在物理i2c总线上的从地址.一般是7为地址(10位地址很少用,这里不使用8位地址)
例如你想为mpu6050添加一个驱动,参照上面添加好设备树后,新建一个驱动,然后在初始化函数里调用i2c_add_driver()即可创建添加一个i2c driver. 详细内容可以在 源码的driver 目录下 使用命令 grep -rn "i2c_add_driver" 查看别人的驱动是怎么写的~☺☺☺☺☺☺☺.
重点是i2c_add_driver 它会调用匹配函数, 通常在这个函数返回之前,驱动的prob函数就会执行完成(前提是设备和驱动成功绑定).具体真么实现的有时间再看......(待完善)