LinuxI2C
Linux$I^2C$体系结构 linux$I^2C$体系结构分为三部分:
$I^2C$核心
提供$I^2C$总线驱动和设备驱动的注册、注销等方法;$I^2C$通信方法上层的与具体适配器无关的代码;探测设备、检测设备地址的上层代码等
$I^2C$总线驱动
$I^2C$硬件体系结构中适配器端的实现。主要包含$I^2C$适配器数据结构i2c_adapter、$I^2C$适配器的算法数据结构i2c_algorithm和控制$I^2C$适配器产生通信信号的函数。
$I^2C$设备驱动
$I^2C$硬件体系结构中设备端的实现,$I^2C$设备驱动主要包含i2c_driver和i2c_client两个数据结构,由具体设备各自实现其中的成员。
linux$I^2C$子系统相对复杂,面对$I^2C$子系统,应该如何动手写驱动呢?哪些是内核已经提供的,哪些又是我们需要亲自做的呢?
一方面,适配器驱动可能是Linux内核本身还不包含的;另一方面,挂接在适配器上的具体设备驱动可能也是Linux内核还不包含的。因此,工程师要实现的主要工作如下:
提供$I^2C$适配器的硬件驱动,探测、初始化$I^2C$适配器、驱动CPU控制的$I^2C$适配器从硬件上产生各种信号以及处理$I^2C$中断等
提供$I^2C$适配器的Algorithm,用具体适配器的xxx_xfer()函数填充i2c_algorithm的master_xfer指针,并把i2c_algorithm指针赋值给i2c_adapter的algo指针
实现$I^2C$设备驱动中的i2c_driver接口,用具体设备的xxx_probe()、xxx_remove()、xxx_suspend()等和i2c_device_id设备ID表赋值给i2c_driver的相应指针
实现$I^2C$设备所对应类型的具体驱动,i2c_driver只是实现设备和总线的挂接,而挂接在总线上的设备则是千差万别的。
Linux$I^2C$核心 $I^2C$核心实现了一组不依赖于硬件平台的接口函数,这些一般不需要被工程师修改,但需要理解其中主要函数。主要函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 int i2c_add_adapter(struct i2c_adapter *adap); void i2c_del_adapter(struct i2c_adapter *adap); int i2c_register_driver(struct module *owner, struct i2c_driver *driver); void i2c_del_driver(struct i2c_driver *driver); int i2c_master_recv(const struct i2c_client *client, char *buf, int count); int i2c_master_send(const struct i2c_client *client, const char *buf, int count) int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
Linux$I^2C$适配器驱动 $I^2C$适配器驱动的注册与注销 由于$I^2C$总线控制器通常是在内存上的,所以它本身也是连接在platform总线上,要通过platform_driver和platform_device的匹配来执行。因此尽管$I^2C$适配器给别人提供了总线,它自己也被认为是挂接在platform总线上的设备。
由上分析,可得到如下的$I^2C$适配器驱动注册与注销的基本实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 static int i2c_au1550_probe(struct platform_device *pdev) { struct i2c_au1550_data *priv; struct resource *r; int ret; priv = devm_kzalloc(&pdev->dev, sizeof(struct i2c_au1550_data), GFP_KERNEL); if (!priv) return -ENOMEM; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); priv->psc_base = devm_ioremap_resource(&pdev->dev, r); if (IS_ERR(priv->psc_base)) return PTR_ERR(priv->psc_base); priv->xfer_timeout = 200; priv->adap.nr = pdev->id; priv->adap.algo = &au1550_algo; priv->adap.algo_data = priv; priv->adap.dev.parent = &pdev->dev; strlcpy(priv->adap.name, "Au1xxx PSC I2C", sizeof(priv->adap.name)); /* Now, set up the PSC for SMBus PIO mode. */ i2c_au1550_setup(priv); ret = i2c_add_numbered_adapter(&priv->adap); if (ret) { i2c_au1550_disable(priv); return ret; } platform_set_drvdata(pdev, priv); return 0; } static int i2c_au1550_remove(struct platform_device *pdev) { struct i2c_au1550_data *priv = platform_get_drvdata(pdev); i2c_del_adapter(&priv->adap); i2c_au1550_disable(priv); return 0; } static struct platform_driver au1xpsc_smbus_driver = { .driver = { .name = "au1xpsc_smbus", .pm = AU1XPSC_SMBUS_PMOPS, }, .probe = i2c_au1550_probe, .remove = i2c_au1550_remove, }; module_platform_driver(au1xpsc_smbus_driver);
$I^2C$总线的通信方法 要为特定的$I^2C$适配器实现通信方法,主要是实现i2c_algorithm的functionality()函数和master_xfer()函数。
functionality()用于返回algorithm所支持的通信协议。
master_xfer()函数在$I^2C$适配器上完成传递给它的i2c_msg数组中的每个$I^2C$消息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 static int au1550_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) { struct i2c_au1550_data *adap = i2c_adap->algo_data; struct i2c_msg *p; int i, err = 0; WR(adap, PSC_CTRL, PSC_CTRL_ENABLE); for (i = 0; !err && i < num; i++) { p = &msgs[i]; err = do_address(adap, p->addr, p->flags & I2C_M_RD, (p->len == 0)); if (err || !p->len) continue; if (p->flags & I2C_M_RD) err = i2c_read(adap, p->buf, p->len); else err = i2c_write(adap, p->buf, p->len); } /* Return the number of messages processed, or the error code. */ if (err == 0) err = num; WR(adap, PSC_CTRL, PSC_CTRL_SUSPEND); return err; }
Linux$I^2C$设备驱动 $I^2C$设备驱动与$I^2C$适配器驱动实现类似,主要区别是$I^2C$适配器驱动需要挂接到$I^2C$总线上。如下是一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 static int max7300_i2c_write(struct device *dev, unsigned int reg, unsigned int val) { struct i2c_client *client = to_i2c_client(dev); return i2c_smbus_write_byte_data(client, reg, val); } static int max7300_i2c_read(struct device *dev, unsigned int reg) { struct i2c_client *client = to_i2c_client(dev); return i2c_smbus_read_byte_data(client, reg); } static int max7300_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct max7301 *ts; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; ts = devm_kzalloc(&client->dev, sizeof(struct max7301), GFP_KERNEL); if (!ts) return -ENOMEM; ts->read = max7300_i2c_read; ts->write = max7300_i2c_write; ts->dev = &client->dev; return __max730x_probe(ts); } static int max7300_remove(struct i2c_client *client) { return __max730x_remove(&client->dev); } static const struct i2c_device_id max7300_id[] = { { "max7300", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, max7300_id); static struct i2c_driver max7300_driver = { .driver = { .name = "max7300", }, .probe = max7300_probe, .remove = max7300_remove, .id_table = max7300_id, }; static int __init max7300_init(void) { return i2c_add_driver(&max7300_driver); } subsys_initcall(max7300_init); static void __exit max7300_exit(void) { i2c_del_driver(&max7300_driver); } module_exit(max7300_exit);
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 yxhlfx@163.com
文章标题: LinuxI2C
本文作者: 红尘追风
发布时间: 2016-07-13, 19:51:17
原始链接: http://www.micernel.com/2016/07/13/LinuxI2C/
版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。