i2c驱动程序(基于总线,设备,驱动,模型.)

        至少从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函数就会执行完成(前提是设备和驱动成功绑定).具体真么实现的有时间再看......(待完善)

  • 9
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值