i2c 驱动:实例化设备

简介

这篇文章是针对 Documentation/i2c/instantiating-devices 的解释,部分增加自己的理解。为什么要将它解释下呢?因为他很好的说明了 linux3.4.xxx 内核中实例化设备的流程和方法。


具体内容

1. 根据 bus 号声明 i2c 的设备们

每一个i2c 总线的号之前知道,因此可以提前声明 i2c 设备们,方法是通过声明一个 struct i2c_borad_info结构体,并用 i2c_register_board_info() 注册。
例子(来自 omap2 h4):

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));
	(...)
}

上边的代码声明了bus1上的3个设备,包括他们各自的地址和他们私有数据。当被提到的i2c bus注册,i2c-core会自动实例化i2c device。
当i2c_bus被移除时,i2c_device也会unbond。
这里可以看到,这种方法一般用在 平台文件 中

2. 明确的实例化一个设备:

提前不知道i2c bus 号,用此方法。此方法通过 i2c的 adapter 创建设备。
例子 (来自 the sfe4001 network driver):

static struct i2c_board_info sfe4001_hwmon_info = {
	I2C_BOARD_INFO("max6647", 0x4e),
};

int sfe4001_init(struct efx_nic *efx)
{
	(...)
	efx->board_info.hwmon_client =
		i2c_new_device(&efx->i2c_adap, &sfe4001_hwmon_info);

	(...)
}

上边实例化一个 i2c 设备,这个i2c设备挂载 i2c bus ,bus上对应相应的adapter。根据的是adapter来创建的设备。
例子:
at24cxx_dev.c

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/regmap.h>
#include <linux/slab.h>


static struct i2c_board_info at24cxx_info = {	
	I2C_BOARD_INFO("at24c08", 0x50),
};

static struct i2c_client *at24cxx_client;

static int at24cxx_dev_init(void)
{
	struct i2c_adapter *i2c_adap;

	i2c_adap = i2c_get_adapter(0); // 0对应的是 dev/i2c-0
	at24cxx_client = i2c_new_device(i2c_adap, &at24cxx_info);
	i2c_put_adapter(i2c_adap);
	
	return 0;
}

static void at24cxx_dev_exit(void)
{
	i2c_unregister_device(at24cxx_client);
}


module_init(at24cxx_dev_init);
module_exit(at24cxx_dev_exit);
MODULE_LICENSE("GPL");

at24cxx_drv.c

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/regmap.h>
#include <linux/slab.h>


static int __devinit at24cxx_probe(struct i2c_client *client,
				  const struct i2c_device_id *id)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

static int __devexit at24cxx_remove(struct i2c_client *client)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

static const struct i2c_device_id at24cxx_id_table[] = {
	{ "at24c08", 0 },
	{}
};


/* 1. ·ÖÅä/ÉèÖÃi2c_driver */
static struct i2c_driver at24cxx_driver = {
	.driver	= {
		.name	= "100ask",
		.owner	= THIS_MODULE,
	},
	.probe		= at24cxx_probe,
	.remove		= __devexit_p(at24cxx_remove),
	.id_table	= at24cxx_id_table,
};

static int at24cxx_drv_init(void)
{
	/* 2. ×¢²ái2c_driver */
	i2c_add_driver(&at24cxx_driver);
	
	return 0;
}

static void at24cxx_drv_exit(void)
{
	i2c_del_driver(&at24cxx_driver);
}


module_init(at24cxx_drv_init);
module_exit(at24cxx_drv_exit);
MODULE_LICENSE("GPL");

Makefile

KERN_DIR = /work/system/linux-3.4.2

all:
	make -C $(KERN_DIR) M=`pwd` modules 

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order

obj-m	+= at24cxx_dev.o
obj-m	+= at24cxx_drv.o

一个变体,当你不知道一个设备是不是挂在总线上,或者地址发生了变化,可以用 i2c_new_probed_device() 代替 i2c_new_device()
Example (from the nxp OHCI driver):

 static const unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };

static int __devinit usb_hcd_nxp_probe(struct platform_device *pdev)
{
	(...)
	struct i2c_adapter *i2c_adap;
	struct i2c_board_info i2c_info;

	(...)
	i2c_adap = i2c_get_adapter(2);
	memset(&i2c_info, 0, sizeof(struct i2c_board_info));
	strlcpy(i2c_info.type, "isp1301_nxp", I2C_NAME_SIZE);
	isp1301_i2c_client = i2c_new_probed_device(i2c_adap, &i2c_info,
						   normal_i2c, NULL);
	i2c_put_adapter(i2c_adap);
	(...)
}

上边的代码实例化一个在 OHCI adapter上的i2c bus上的一个设备。他首先尝试地址 0x2c,如果没有回复,则尝试0x2d,如果都没发现,放弃。
例子:
at24cxx_dev.c

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/regmap.h>
#include <linux/slab.h>

static struct i2c_client *at24cxx_client;

static const unsigned short addr_list[] = { 0x60, 0x50, I2C_CLIENT_END };

static int at24cxx_dev_init(void)
{
	struct i2c_adapter *i2c_adap;
	struct i2c_board_info at24cxx_info;

	memset(&at24cxx_info, 0, sizeof(struct i2c_board_info));	
	strlcpy(at24cxx_info.type, "at24c08", I2C_NAME_SIZE);

	i2c_adap = i2c_get_adapter(0);
	at24cxx_client = i2c_new_probed_device(i2c_adap, &at24cxx_info, addr_list, NULL);
	i2c_put_adapter(i2c_adap);

	if (at24cxx_client)
		return 0;
	else
		return -ENODEV;
}

static void at24cxx_dev_exit(void)
{
	i2c_unregister_device(at24cxx_client);
}


module_init(at24cxx_dev_init);
module_exit(at24cxx_dev_exit);
MODULE_LICENSE("GPL");

at24cxx_drv.c

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/regmap.h>
#include <linux/slab.h>


static int __devinit at24cxx_probe(struct i2c_client *client,
				  const struct i2c_device_id *id)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

static int __devexit at24cxx_remove(struct i2c_client *client)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

static const struct i2c_device_id at24cxx_id_table[] = {
	{ "at24c08", 0 },
	{}
};

static struct i2c_driver at24cxx_driver = {
	.driver	= {
		.name	= "100ask",    // 匹配不用这个
		.owner	= THIS_MODULE,
	},
	.probe		= at24cxx_probe,
	.remove		= __devexit_p(at24cxx_remove),
	.id_table	= at24cxx_id_table,
};

static int at24cxx_drv_init(void)
{
	i2c_add_driver(&at24cxx_driver);
	return 0;
}

static void at24cxx_drv_exit(void)
{
	i2c_del_driver(&at24cxx_driver);
}


module_init(at24cxx_drv_init);
module_exit(at24cxx_drv_exit);
MODULE_LICENSE("GPL");

Makefile

KERN_DIR = /work/system/linux-3.4.2

all:
	make -C $(KERN_DIR) M=`pwd` modules 

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order

obj-m	+= at24cxx_dev.o
obj-m	+= at24cxx_drv.o
#obj-m	+= i2c_bus_s3c2440.o
3. Probe一个针对于每一个设备的 i2c bus

有时,没有 i2c device 的足够的信息,甚至不能调用 i2c_new_probed_device(),典型的事例是 PC 主板上的硬件监控芯片。有很多模型,这些模型可以有25个不同的地址。考虑到有大量的主板,要构建一个详尽的硬件列表几乎是不可能的,幸运的是,大部分这类芯片都有 manufacturer (制造商)和 device ID(设备 ID)寄存器,以供我们可以识别。
既然那样,I2C devices即不声明,也不明确实例化。相反的,一旦 i2c driver 加载,i2c-core将探测这些设备,如果找到,I2C device 会自动实例化。为了阻止这个机制中的一些错误情况,有一下 的一些限制:

  • i2c 的设备驱动必须实现 detect() 方法,用这个方法来读设备的专有寄存器。
  • 只有可能含有支持设备,并同意 probe 的 bus 们才被用来 probe。

Example:
See lm90_driver and lm90_detect() in drivers/hwmon/lm90.c
当成功 probe 到 i2c device会自动实例化,当驱动检测到他们被移除了,device自动销毁,或者依赖的 i2c bus 自己销毁了。
再一次,方法3尽量避免使用,方法1和2更安全更快。

4. 从用户空间实例化

通常,内核应该知道哪些设备连接和他们的地址。然而,在某些情况,却不知道,所以一个 sysfs 接口被添加来让用户提供信息。这个接口由2个属性文件组成,new_device 和 delete_device ,两个文件都是只写的,你必须写正确的参数以实例化或者删除 i2c device
文件 new_device 需要2个参数:i2c设备的名字(字符串),一个i2c设备的地址(不标记是10进制,0x开头是16进制)
文件 delete_device 需要一个参数:地址
Example:
# echo eeprom 0x50 > /sys/bus/i2c/devices/i2c-3/new_device
然而这个接口应该在kernel 设备声明了,但是没有成功执行的时候使用,有一下各种各样情况可能这个接口有帮助:

  • 用方法 4.3 detects 设备,但是你设备依赖的 bus segment 没有合适的类的位的设置,这样检测不能被触发
  • i2c 驱动通常 detects 设备,但是地址设置的不对。
  • i2c 驱动通常 detects 设备,但是你的设备因为检测程序太严格,而没有检测到,或者你的设备还不是官方支持的,但是你知道,他能够使用。
  • 你在开发一个驱动在一个测试的板子上。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值