目录
[1 描述](#1 描述)
[2 结构体](#2 结构体)
[2.1 bus_type](#2.1 bus_type)
[2.2 i2c_bus_type](#2.2 i2c_bus_type)
[2.2.1 i2c_device_match](#2.2.1 i2c_device_match)
[2.2.2 i2c_device_probe](#2.2.2 i2c_device_probe)
[2.2.3 i2c_device_remove](#2.2.3 i2c_device_remove)
[2.2.4 i2c_device_shutdown](#2.2.4 i2c_device_shutdown)
[2.2 i2c_adapter](#2.2 i2c_adapter)
[2.3 i2c_algorithm](#2.3 i2c_algorithm)
[2.4 i2c_driver](#2.4 i2c_driver)
[2.5 i2c_client](#2.5 i2c_client)
[3 i2c核心](#3 i2c核心)
[3.1 注册i2c适配器](#3.1 注册i2c适配器)
[3.2 注册i2c设备驱动](#3.2 注册i2c设备驱动)
[3.3 i2c数据传输](#3.3 i2c数据传输)
[3.3.1 i2c_transfer](#3.3.1 i2c_transfer)
[3.3.2 i2c_master_send](#3.3.2 i2c_master_send)
[3.3.3 i2c_master_recv](#3.3.3 i2c_master_recv)
[4 rk3399 i2c适配器驱动分析](#4 rk3399 i2c适配器驱动分析)
[4.1 设备树](#4.1 设备树)
[4.2 结构体](#4.2 结构体)
[4.2.1 rk3x_i2c](#4.2.1 rk3x_i2c)
[4.2.2 rk3x_i2c_soc_data](#4.2.2 rk3x_i2c_soc_data)
[4.2.3 of_device_id](#4.2.3 of_device_id)
[4.2.4 i2c_timings](#4.2.4 i2c_timings)
[4.2.5 platform_device](#4.2.5 platform_device)
[4.2.6 device](#4.2.6 device)
[4.3 probe函数](#4.3 probe函数)
[4.4 函数解析](#4.4 函数解析)
[4.4.1 of_match_node](#4.4.1 of_match_node)
[4.4.2 i2c_parse_fw_timings](#4.4.2 i2c_parse_fw_timings)
[4.4.3 spin_lock_init](#4.4.3 spin_lock_init)
[4.4.4 init_waitqueue_head](#4.4.4 init_waitqueue_head)
[4.4.5 register_pre_restart_handler](#4.4.5 register_pre_restart_handler)
[4.4.6 platform_get_resource](#4.4.6 platform_get_resource)
[4.4.7 devm_ioremap_resource](#4.4.7 devm_ioremap_resource)
[4.4.8 syscon_regmap_lookup_by_phandle](#4.4.8 syscon_regmap_lookup_by_phandle)
[4.4.9 of_alias_get_id](#4.4.9 of_alias_get_id)
[4.4.10 regmap_write](#4.4.10 regmap_write)
[4.4.11 platform_get_irq](#4.4.11 platform_get_irq)
[4.4.12 devm_request_irq](#4.4.12 devm_request_irq)
[4.4.12 platform_set_drvdata](#4.4.12 platform_set_drvdata)
[4.4.14 devm_clk_get](#4.4.14 devm_clk_get)
[4.4.15 clk_prepare](#4.4.15 clk_prepare)
[4.4.16 PTR_ERR](#4.4.16 PTR_ERR)
[4.4.17 clk_notifier_register](#4.4.17 clk_notifier_register)
[4.4.18 clk_get_rate](#4.4.18 clk_get_rate)
[4.4.19 clk_enable](#4.4.19 clk_enable)
[4.4.20 spin_lock_irqsave](#4.4.20 spin_lock_irqsave)
[4.5 rk3x_i2c_algorithm函数解析](#4.5 rk3x_i2c_algorithm函数解析)
[5 spirit_mcu.c 驱动解析](#5 spirit_mcu.c 驱动解析)
[5.1 驱动源码](#5.1 驱动源码)
[5.2 设备树](#5.2 设备树)
[5.3 驱动框架](#5.3 驱动框架)
[5.4 函数解析](#5.4 函数解析)
[5.4.1 devm_kzalloc](#5.4.1 devm_kzalloc)
[5.4.2 devm_regmap_init_i2c](#5.4.2 devm_regmap_init_i2c)
[5.4.3 i2c_set_clientdata](#5.4.3 i2c_set_clientdata)
[5.4.4 i2c_get_clientdata](#5.4.4 i2c_get_clientdata)
[5.4.5 misc_register](#5.4.5 misc_register)
1 描述
所有i2c设备都在文件系统中显示,存在于/sys/bus/i2c/目录。
k3399_Android11:/sys/bus/i2c # ls
devices drivers drivers_autoprobe drivers_probe uevent
rk3399_Android11:/sys/bus/i2c #
rk3399_Android11:/sys/bus/i2c # ls devices/
0-001b 0-0040 0-0041 1-0010 4-0015 4-0035 4-0051 i2c-0 i2c-1 i2c-10 i2c-4 i2c-9
rk3399_Android11:/sys/bus/i2c #
rk3399_Android11:/sys/bus/i2c # ls drivers
DIO5632 bq25700-charger dummy es8396 gc2355 gsensor_lsm303d gsensor_mpu6880 gslX680 gyro_lsm330 light_cm3218 ov5695 rk630 tc35874x
ES8323 compass_akm8963 dw9714 fan53555-regulator gc2385 gsensor_lsm330 gsensor_mxc6655 gslX680-pad gyro_mpu6500 light_stk3410 ov8858 rk808 tps65132
Goodix-TS compass_akm8975 es7202 fts_ts gc4c33 gsensor_mc3230 gsensor_sc7660 gslX6801 gyro_mpu6880 lp8752 proximity_stk3410 rt5640 vm149c
Goodix-TS-GT1X cst2xxse es7210 fusb302 gc8034 gsensor_mir3da gsensor_sc7a20 gsl_thzy i2c_hid mp8865 rk1000-ctl rtc_hym8563 wacom
LT6911UXC cw201x es7243e gc0312 gsensor_bma2x2 gsensor_mma7660 gsensor_sc7a30 gyro_ewtsa jw_mcu ov13850 rk1000-tve sgm3784 xz3216
LT8619C cx2072x es8311 gc032a gsensor_kxtj9 gsensor_mma8452 gsl3673 gyro_l3g20d jw_mcu_isp ov2680 rk618 sii902x
act8865 cyttsp5_i2c_adapter es8316 gc2145 gsensor_lis3dh gsensor_mpu6500 gsl3673_800x1280 gyro_l3g4200d light_cm3217 ov5648 rk628 spirit_mcu
rk3399_Android11:/sys/bus/i2c #
i2c 的框架如下所示, i2c核心提供了I2C总线驱动和设备驱动的注册、注销方法,i2c通信方法(即Algorithm)上层的与具体适配器无关的代码以及探测设备、检测设备地址的上层代码等。
2 结构体
2.1 bus_type
struct bus_type 在 Linux 内核中是一个非常重要的结构体,它用于表示和管理不同类型的总线。在 Linux 的设备模型中,总线(bus)是一个关键的抽象概念,它用于将设备和驱动程序连接起来。每种类型的总线(如 platporm、PCI、USB、I2C 等)都由一个 bus_type 结构体来表示,这个结构体包含了该类型总线所需的所有信息和操作函数。
cpp
122 struct bus_type {
123 const char *name;
124 const char *dev_name;
125 struct device *dev_root;
126 const struct attribute_group **bus_groups;
127 const struct attribute_group **dev_groups;
128 const struct attribute_group **drv_groups;
129
130 int (*match)(struct device *dev, struct device_driver *drv);
131 int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
132 int (*probe)(struct device *dev);
133 void (*sync_state)(struct device *dev);
134 int (*remove)(struct device *dev);
135 void (*shutdown)(struct device *dev);
136
137 int (*online)(struct device *dev);
138 int (*offline)(struct device *dev);
139
140 int (*suspend)(struct device *dev, pm_message_t state);
141 int (*resume)(struct device *dev);
142
143 int (*num_vf)(struct device *dev);
144
145 int (*dma_configure)(struct device *dev);
146
147 const struct dev_pm_ops *pm;
148
149 const struct iommu_ops *iommu_ops;
150
151 struct subsys_private *p;
152 struct lock_class_key lock_key;
153
154 bool need_parent_lock;
155
156 ANDROID_KABI_RESERVE(1);
157 ANDROID_KABI_RESERVE(2);
158 ANDROID_KABI_RESERVE(3);
159 ANDROID_KABI_RESERVE(4);
160 };
bus_type结构体的成员解释
name 是总线的名称,如 "pci"、"usb" 等,用于在系统中唯一标识总线类型。
dev_name 通常用于构造设备在系统中的名称,但在这个结构体定义中,它可能不是所有情况下都使用或必需。其确切用途可能取决于内核版本和具体实现。
dev_root:指向该总线类型下所有设备的根设备。在一些总线类型中,设备可能会以树状结构组织,其中 dev_root 表示这棵树的根节点。
bus_groups, dev_groups, drv_groups:这些字段分别指向属性组的数组,这些属性组可以通过 sysfs 接口被用户空间访问。bus_groups 包含总线级别的属性,dev_groups 包含设备级别的属性,而 drv_groups 包含驱动程序级别的属性。
match、uevent、probe、sync_state、remove、shutdown、online、offline、suspend、resume等函数指针分别指向处理设备添加、移除、状态同步、电源管理等操作的函数。这些函数定义了当设备或驱动程序与总线交互时应执行的行为。
num_vf 可能用于某些支持虚拟功能(如 SR-IOV)的总线,用于返回设备的虚拟功能数量。
dma_configure 用于配置设备的 DMA(直接内存访问)特性。
pm:指向 dev_pm_ops 结构体的指针,该结构体包含了一组用于电源管理的函数指针,如设备挂起、恢复等操作。
iommu_ops:指向 iommu_ops 结构体的指针,该结构体定义了一组用于管理输入/输出内存管理单元(IOMMU)的函数。IOMMU 用于在物理内存和设备地址空间之间建立映射。
p:指向 subsys_private 结构体的指针,这通常是一个私有数据结构,用于存储与总线子系统相关的私有信息。
lock_key:用于锁类别的键,这有助于调试和性能分析,确保锁的正确使用和避免死锁。
need_parent_lock:一个布尔值,指示在访问总线上的设备时是否需要锁定其父设备。这有助于处理总线层次结构中的并发访问。
ANDROID_KABI_RESERVE:这些是 Android 特有的保留字段,用于确保在 Android 和 Linux 内核之间的接口保持稳定。这些字段当前未使用,但保留以供将来扩展。
2.2 i2c_bus_type
I2C 总线的数据结构为 i2c_bus_type
cpp
505 struct bus_type i2c_bus_type = {
506 .name = "i2c",
507 .match = i2c_device_match,
508 .probe = i2c_device_probe,
509 .remove = i2c_device_remove,
510 .shutdown = i2c_device_shutdown,
511 };
i2c_bus_type结构体的成员解释
**.name:**总线的名称,这里是 "i2c"。
**.match:**一个函数指针,用于匹配设备和驱动程序,确保它们能够配对。
**.probe:**一个函数指针,用于初始化设备,通常在设备连接时调用。
.**remove:**一个函数指针,用于处理设备被移除时的清理工作。
.shutdown:一个函数指针,用于设备关机时的处理。
2.2.1 i2c_device_match
cpp
103 static int i2c_device_match(struct device *dev, struct device_driver *drv)
104 {
105 struct i2c_client *client = i2c_verify_client(dev);
106 struct i2c_driver *driver;
107
108
109 /* Attempt an OF style match */
110 if (i2c_of_match_device(drv->of_match_table, client))
111 return 1;
112
113 /* Then ACPI style match */
114 if (acpi_driver_match_device(dev, drv))
115 return 1;
116
117 driver = to_i2c_driver(drv);
118
119 /* Finally an I2C match */
120 if (i2c_match_id(driver->id_table, client))
121 return 1;
122
123 return 0;
124 }
i2c_of_match_device 函数用于完成设备树中定义的设备与驱动匹配过程。比较 I2C 设备节点的 compatible 属性和 of_device_id 中的 compatible 属性是否相等,如果相当的话就表示 I2C 设备和驱动匹配
acpi_driver_match_device 函数用于 ACPI 形式的匹配
i2c_match_id 函数用于传统的、无设备树的 I2C 设备和驱动匹配过程。比较 I2C设备名字和 i2c_device_id 的 name 字段是否相等,相等的话就说明 I2C 设备和驱动匹配成功
2.2.2 i2c_device_probe
cpp
324 static int i2c_device_probe(struct device *dev)
325 {
326 struct i2c_client *client = i2c_verify_client(dev);
327 struct i2c_driver *driver;
328 int status;
329
330 if (!client)
331 return 0;
332
333 driver = to_i2c_driver(dev->driver);
334
335 if (!client->irq && !driver->disable_i2c_core_irq_mapping) {
336 int irq = -ENOENT;
337
338 if (client->flags & I2C_CLIENT_HOST_NOTIFY) {
339 dev_dbg(dev, "Using Host Notify IRQ\n");
340 /* Keep adapter active when Host Notify is required */
341 pm_runtime_get_sync(&client->adapter->dev);
342 irq = i2c_smbus_host_notify_to_irq(client);
343 } else if (dev->of_node) {
344 irq = of_irq_get_byname(dev->of_node, "irq");
345 if (irq == -EINVAL || irq == -ENODATA)
346 irq = of_irq_get(dev->of_node, 0);
347 } else if (ACPI_COMPANION(dev)) {
348 irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), 0);
349 }
350 if (irq == -EPROBE_DEFER)
351 return irq;
352
353 if (irq < 0)
354 irq = 0;
355
356 client->irq = irq;
357 }
358
359 /*
360 * An I2C ID table is not mandatory, if and only if, a suitable OF
361 * or ACPI ID table is supplied for the probing device.
362 */
363 if (!driver->id_table &&
364 !i2c_acpi_match_device(dev->driver->acpi_match_table, client) &&
365 !i2c_of_match_device(dev->driver->of_match_table, client))
366 return -ENODEV;
367
368 if (client->flags & I2C_CLIENT_WAKE) {
369 int wakeirq = -ENOENT;
370
371 if (dev->of_node) {
372 wakeirq = of_irq_get_byname(dev->of_node, "wakeup");
373 if (wakeirq == -EPROBE_DEFER)
374 return wakeirq;
375 }
376
377 device_init_wakeup(&client->dev, true);
378
379 if (wakeirq > 0 && wakeirq != client->irq)
380 status = dev_pm_set_dedicated_wake_irq(dev, wakeirq);
381 else if (client->irq > 0)
382 status = dev_pm_set_wake_irq(dev, client->irq);
383 else
384 status = 0;
385
386 if (status)
387 dev_warn(&client->dev, "failed to set up wakeup irq\n");
388 }
389
390 dev_dbg(dev, "probe\n");
391
392 status = of_clk_set_defaults(dev->of_node, false);
393 if (status < 0)
394 goto err_clear_wakeup_irq;
395
396 status = dev_pm_domain_attach(&client->dev, true);
397 if (status)
398 goto err_clear_wakeup_irq;
399
400 /*
401 * When there are no more users of probe(),
402 * rename probe_new to probe.
403 */
404 if (driver->probe_new)
405 status = driver->probe_new(client);
406 else if (driver->probe)
407 status = driver->probe(client,
408 i2c_match_id(driver->id_table, client));
409 else
410 status = -EINVAL;
411
412 if (status)
413 goto err_detach_pm_domain;
414
415 return 0;
416
417 err_detach_pm_domain:
418 dev_pm_domain_detach(&client->dev, true);
419 err_clear_wakeup_irq:
420 dev_pm_clear_wake_irq(&client->dev);
421 device_init_wakeup(&client->dev, false);
422 return status;
423 }
2.2.3 i2c_device_remove
函数i2c_device_remove是用于从系统中移除一个I2C设备的函数。它接收一个指向device结构的指针作为参数,这个device结构代表了要被移除的I2C设备。
cpp
425 static int i2c_device_remove(struct device *dev)
426 {
427 struct i2c_client *client = i2c_verify_client(dev);
428 struct i2c_driver *driver;
429 int status = 0;
430
431 if (!client || !dev->driver)
432 return 0;
433
434 driver = to_i2c_driver(dev->driver);
435 if (driver->remove) {
436 dev_dbg(dev, "remove\n");
437 status = driver->remove(client);
438 }
439
440 dev_pm_domain_detach(&client->dev, true);
441
442 dev_pm_clear_wake_irq(&client->dev);
443 device_init_wakeup(&client->dev, false);
444
445 client->irq = client->init_irq;
446 if (client->flags & I2C_CLIENT_HOST_NOTIFY)
447 pm_runtime_put(&client->adapter->dev);
448
449 return status;
450 }
cpp
531 struct i2c_client *i2c_verify_client(struct device *dev)
532 {
533 return (dev->type == &i2c_client_type)
534 ? to_i2c_client(dev)
535 : NULL;
536 }
2.2.4 i2c_device_shutdown
函数i2c_device_shutdown是用于在系统关闭或重启时,对I2C设备进行特定处理的函数。它接收一个指向device结构的指针作为参数,这个device结构代表了I2C设备。
cpp
452 static void i2c_device_shutdown(struct device *dev)
453 {
454 struct i2c_client *client = i2c_verify_client(dev);
455 struct i2c_driver *driver;
456
457 if (!client || !dev->driver)
458 return;
459 driver = to_i2c_driver(dev->driver);
460 if (driver->shutdown)
461 driver->shutdown(client);
462 else if (client->irq > 0)
463 disable_irq(client->irq);
464 }
2.2 i2c_adapter
i2c_adapter对应于物理上的一个适配器,i2c_adapter 结构体是 Linux 内核中 I2C 子系统的一个核心组件,它封装了与 I2C 总线适配器相关的所有必要信息,使得驱动程序能够高效、安全地与 I2C 设备进行通信。
cpp
672 struct i2c_adapter {
673 struct module *owner;
674 unsigned int class; /* classes to allow probing for */
675 const struct i2c_algorithm *algo; /* the algorithm to access the bus */
676 void *algo_data;
677
678 /* data fields that are valid for all devices */
679 const struct i2c_lock_operations *lock_ops;
680 struct rt_mutex bus_lock;
681 struct rt_mutex mux_lock;
682
683 int timeout; /* in jiffies */
684 int retries;
685 struct device dev; /* the adapter device */
686
687 int nr;
688 char name[48];
689 struct completion dev_released;
690
691 struct mutex userspace_clients_lock;
692 struct list_head userspace_clients;
693
694 struct i2c_bus_recovery_info *bus_recovery_info;
695 const struct i2c_adapter_quirks *quirks;
696
697 struct irq_domain *host_notify_domain;
698 };
struct module *owner;
指向拥有此适配器的模块的指针。这用于跟踪哪些模块正在使用此适配器,并在需要时进行卸载处理。
unsigned int class;
表示此适配器支持的 I2C 设备类。这个类可以用来过滤掉不感兴趣的 I2C 设备,或者作为探测时的一个指导。
const struct i2c_algorithm *algo;
指向 I2C 算法结构的指针,该算法定义了如何与 I2C 总线进行通信。这个算法包含了一系列函数,如发送和接收数据等。
void *algo_data;
一个指向特定于算法的数据的指针。这个数据可能对于算法来说是必需的,但它的具体内容取决于算法的实现。
const struct i2c_lock_operations *lock_ops;
指向一组锁操作函数的指针,这些函数用于控制对 I2C 总线的访问。这可以确保在并发环境中,对总线的访问是安全的。
struct rt_mutex bus_lock; 和 struct rt_mutex mux_lock;
实时互斥锁,分别用于保护总线访问和复用器(如果有的话)的访问。这些锁确保了在高负载或并发环境下,对总线的访问是同步的。
int timeout; 和 int retries;
分别表示 I2C 事务的超时时间和重试次数。这些值在发送 I2C 请求时用作参数,以确保系统不会因为长时间等待或多次失败而挂起。
struct device dev;
表示适配器设备的 device 结构体。这个结构体包含了设备在 Linux 设备模型中的所有信息,如设备类型、父设备、驱动程序等。
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;
指向一组特定于适配器的怪癖(quirks)的指针。这些怪癖是适配器的特殊行为或限制,驱动程序可能需要了解这些信息才能正确操作适配器。
struct irq_domain *host_notify_domain;
指向中断域的指针,该中断域用于管理由适配器或连接到它的设备触发的中断。
2.3 i2c_algorithm
结构体 struct i2c_algorithm 是Linux内核中I2C(Inter-Integrated Circuit)总线通信机制的一部分,它定义了一个I2C适配器(adapter)所支持的算法或操作集合。这个结构体为I2C适配器提供了基本的通信接口,包括主设备(master)模式下的数据传输、SMBus协议支持以及适配器功能性的查询等。
cpp
519 struct i2c_algorithm {
520 /* If an adapter algorithm can't do I2C-level access, set master_xfer
521 to NULL. If an adapter algorithm can do SMBus access, set
522 smbus_xfer. If set to NULL, the SMBus protocol is simulated
523 using common I2C messages */
524 /* master_xfer should return the number of messages successfully
525 processed, or a negative value on error */
526 int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
527 int num);
528 int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
529 unsigned short flags, char read_write,
530 u8 command, int size, union i2c_smbus_data *data);
531
532 /* To determine what the adapter supports */
533 u32 (*functionality) (struct i2c_adapter *);
534
535 #if IS_ENABLED(CONFIG_I2C_SLAVE)
536 int (*reg_slave)(struct i2c_client *client);
537 int (*unreg_slave)(struct i2c_client *client);
538 #endif
539 };
i2c_algorithm对应于一套通信方法,一个i2c适配器需要i2c_algorithm提供的通信函数来控制适配器上产生特定的访问周期。
master_xfer用于产生i2c访问周期需要的信号,以i2c_msg为消息单位
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
这是一个函数指针,指向一个函数,该函数负责在主设备模式下执行I2C消息传输。adap是指向当前I2C适配器的指针,msgs是指向I2C消息数组的指针,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);
这也是一个函数指针,指向一个函数,该函数提供了对SMBus协议的支持。SMBus是I2C总线的一个子集,用于简化系统内部通信。该函数允许通过SMBus协议发送和接收数据。参数包括目标设备的地址、标志、读写操作、命令、数据大小和指向数据缓冲区的指针。
u32 (*functionality) (struct i2c_adapter *);
这是一个函数指针,指向一个函数,该函数返回适配器支持的功能的位掩码。这些功能包括适配器是否支持I2C总线上的快速模式、10位地址寻址等。
#if IS_ENABLED(CONFIG_I2C_SLAVE) 条件编译块
这个条件编译块包含了两个函数指针,它们仅在内核配置为支持I2C从设备(slave)模式时可用。
**int (*reg_slave)(struct i2c_client *client);:**用于注册一个I2C从设备。
**int (*unreg_slave)(struct i2c_client *client);:**用于注销一个I2C从设备。
cpp
69 struct i2c_msg {
70 __u16 addr; /* slave address */
71 __u16 flags;
72 #define I2C_M_RD 0x0001 /* read data, from slave to master */
73 /* I2C_M_RD is guaranteed to be 0x0001! */
74 #define I2C_M_TEN 0x0010 /* this is a ten bit chip address */
75 #define I2C_M_DMA_SAFE 0x0200 /* the buffer of this message is DMA safe */
76 /* makes only sense in kernelspace */
77 /* userspace buffers are copied anyway */
78 #define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
79 #define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */
80 #define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
81 #define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
82 #define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_NOSTART */
83 #define I2C_M_STOP 0x8000 /* if I2C_FUNC_PROTOCOL_MANGLING */
84 __u16 len; /* msg length */
85 __u8 *buf; /* pointer to msg data */
86 };
2.4 i2c_driver
struct i2c_driver 是Linux内核中用于表示I2C设备驱动的结构体。它定义了驱动程序必须实现的接口和包含的属性,以便与I2C总线上的设备进行交互。i2c_driver对应于一套驱动方法,是用于辅助作用的数据结构,不对应于任何物理实体
cpp
267 struct i2c_driver {
268 unsigned int class;
269
270 /* Standard driver model interfaces */
271 int (*probe)(struct i2c_client *, const struct i2c_device_id *);
272 int (*remove)(struct i2c_client *);
273
274 /* New driver model interface to aid the seamless removal of the
275 * current probe()'s, more commonly unused than used second parameter.
276 */
277 int (*probe_new)(struct i2c_client *);
278
279 /* driver model interfaces that don't relate to enumeration */
280 void (*shutdown)(struct i2c_client *);
281
282 /* Alert callback, for example for the SMBus alert protocol.
283 * The format and meaning of the data value depends on the protocol.
284 * For the SMBus alert protocol, there is a single bit of data passed
285 * as the alert response's low bit ("event flag").
286 * For the SMBus Host Notify protocol, the data corresponds to the
287 * 16-bit payload data reported by the slave device acting as master.
288 */
289 void (*alert)(struct i2c_client *, enum i2c_alert_protocol protocol,
290 unsigned int data);
291
292 /* a ioctl like command that can be used to perform specific functions
293 * with the device.
294 */
295 int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
296
297 struct device_driver driver;
298 const struct i2c_device_id *id_table;
299
300 /* Device detection callback for automatic device creation */
301 int (*detect)(struct i2c_client *, struct i2c_board_info *);
302 const unsigned short *address_list;
303 struct list_head clients;
304
305 bool disable_i2c_core_irq_mapping;
306 };
unsigned int class;:
这个成员可能用于指定驱动程序的类别或类型,但在最新的内核代码中,它的使用可能不那么直接或标准,因为I2C设备驱动通常通过其他机制(如设备树或模块参数)来区分设备类型。
int (*probe)(struct i2c_client *, const struct i2c_device_id *);:
这是当设备被I2C总线探测到时调用的函数。它用于初始化设备、注册设备驱动,并可能进行任何必要的设备特定设置。如果设备不是期望的设备,函数应该返回错误。
int (*remove)(struct i2c_client *);:
当设备从I2C总线上移除或驱动程序被卸载时,此函数被调用。它用于执行清理工作,如释放资源。
int (*probe_new)(struct i2c_client *);:
这是一个新的驱动程序模型接口,旨在替代传统的probe函数,特别是去除了对第二个不常用参数的依赖。
void (*shutdown)(struct i2c_client *);:
在系统关闭或重启之前,如果驱动需要执行任何特定的清理工作,可以使用此函数。
void (*alert)(struct i2c_client *, enum i2c_alert_protocol protocol, unsigned int data);:
当设备发送警报时(如SMBus警报协议),此函数被调用。它允许驱动程序对警报做出响应。
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);:
这类似于文件系统中的ioctl操作,允许用户空间向驱动程序发送特定命令和参数。
struct device_driver driver;:
这是一个嵌套的device_driver结构体,它包含了设备驱动程序在Linux设备模型中的表示。这是驱动程序与内核设备系统交互的关键部分。
const struct i2c_device_id *id_table;:
这是一个指向ID表的指针,该表包含了一组匹配规则,用于将探测到的设备与特定的驱动程序匹配。
int (*detect)(struct i2c_client *, struct i2c_board_info *);:
这个回调函数用于自动设备创建过程中的设备检测。它通常不是必需的,但在某些情况下,它允许驱动程序动态地检测和报告其支持的设备。
const unsigned short *address_list;:
这是一个指向地址列表的指针,列出了驱动程序可能尝试访问的I2C设备地址。然而,随着设备树(Device Tree)的普及,这个成员的使用变得不那么常见了。
struct list_head clients;:
这是一个链表头,用于链接到该驱动程序支持的所有I2C客户端设备。这允许驱动程序轻松地遍历其所有连接的设备。
bool disable_i2c_core_irq_mapping;:
这个标志用于指示驱动程序是否希望禁用I2C核心的中断映射。这主要用于与硬件或平台特定功能的兼容性。
cpp
298 struct device_driver {
299 const char *name;
300 struct bus_type *bus;
301
302 struct module *owner;
303 const char *mod_name; /* used for built-in modules */
304
305 bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
306 enum probe_type probe_type;
307
308 const struct of_device_id *of_match_table;
309 const struct acpi_device_id *acpi_match_table;
310
311 int (*probe) (struct device *dev);
312 void (*sync_state)(struct device *dev);
313 int (*remove) (struct device *dev);
314 void (*shutdown) (struct device *dev);
315 int (*suspend) (struct device *dev, pm_message_t state);
316 int (*resume) (struct device *dev);
317 const struct attribute_group **groups;
318
319 const struct dev_pm_ops *pm;
320 void (*coredump) (struct device *dev);
321
322 struct driver_private *p;
323
324 ANDROID_KABI_RESERVE(1);
325 ANDROID_KABI_RESERVE(2);
326 ANDROID_KABI_RESERVE(3);
327 ANDROID_KABI_RESERVE(4);
328 };
name:驱动的名称
***bus:**指向该驱动所属的总线类型的指针(例如i2c、spi、usb等)
***owner:**如果驱动是作为模块加载的,则此字段指向该模块的 struct module 结构体。这允许内核在需要时追踪到哪个模块提供了该驱动。
*of_match_table:设备树匹配表,用于基于设备树信息来识别和绑定设备到驱动
2.5 i2c_client
struct i2c_client 是Linux内核中用于表示I2C总线上一个客户端设备(即从设备)的结构体。这个结构体包含了客户端设备的关键信息,以及它如何与I2C总线及其适配器(master设备)进行交互的细节。
i2c_client对应于真实的物理设备,每个i2c设备都需要一个i2c_client描述。i2c_client 用于描述 I2C 总线下的设备,i2c_driver 则用于描述 I2C 总线下的设备驱动,类似于 platform 总线下的 platform_device 和 platform_driver。
cpp
328 struct i2c_client {
329 unsigned short flags; /* div., see below */
330 unsigned short addr; /* chip address - NOTE: 7bit */
331 /* addresses are stored in the */
332 /* _LOWER_ 7 bits */
333 char name[I2C_NAME_SIZE];
334 struct i2c_adapter *adapter; /* the adapter we sit on */
335 struct device dev; /* the device structure */
336 int init_irq; /* irq set at initialization */
337 int irq; /* irq issued by device */
338 struct list_head detected;
339 #if IS_ENABLED(CONFIG_I2C_SLAVE)
340 i2c_slave_cb_t slave_cb; /* callback for slave mode */
341 #endif
342 };
unsigned short flags;:
这个成员用于存储一些标志位,这些标志位可以表示设备的不同状态或能力。例如,它可能包含用于控制设备行为(如10位地址支持、SMBus块读写模式等)的标志。不过,具体的标志位及其含义依赖于内核的实现和文档。
unsigned short addr;:
这是客户端设备的I2C地址。注意,尽管I2C协议支持7位和10位地址,但在这个字段中,通常只存储7位地址(如果设备支持10位地址,则需要通过其他方式处理)。I2C地址的低7位被存储在这个字段中。
char name[I2C_NAME_SIZE];:
这是一个字符数组,用于存储客户端设备的名称。I2C_NAME_SIZE是一个在内核中定义的宏,指定了数组的最大长度。这个名称用于在系统中唯一标识设备。
struct i2c_adapter *adapter;:
这是一个指向i2c_adapter结构体的指针,i2c_adapter表示I2C适配器(即master设备)。通过这个指针,客户端设备可以与I2C总线及其适配器进行通信。
struct device dev;:
这是一个嵌套的device结构体,它代表了Linux设备模型中的一个设备。这个结构体包含了设备在系统中的许多重要信息,如设备类、父设备、设备驱动等。这使得设备可以与内核的其他部分(如电源管理、热插拔等)进行交互。
int init_irq;:
这个成员在设备初始化时设置,通常用于记录设备初始化的中断号(如果有的话)。然而,在实际使用中,这个成员的使用可能取决于具体的驱动程序和设备。
int irq;:
这是设备在正常运行时发出的中断号。如果设备支持中断模式,则驱动程序可以通过这个中断号来响应设备的各种事件。
struct list_head detected;:
这是一个链表头,可能用于将客户端设备链接到某个检测列表中。这允许驱动程序或I2C子系统在需要时遍历所有已检测到的客户端设备。然而,具体的用途可能取决于内核的实现和文档。
#if IS_ENABLED(CONFIG_I2C_SLAVE)...#endif:
这是一个条件编译块,它仅在内核配置了I2C从设备支持(CONFIG_I2C_SLAVE)时才包含slave_cb成员。
i2c_slave_cb_t slave_cb;
是一个回调函数指针,用于在从设备模式下接收和处理来自master设备的请求。当master设备向从设备发送数据时,从设备的驱动程序可以使用这个回调函数来响应。
3 i2c核心
3.1 注册i2c适配器
|------|------------------------------|-------------------------------------------------------------------------------------------|
| 函数原型 | int i2c_add_adapter(struct i2c_adapter *adapter) ||
| 参数 | struct i2c_adapter *adapter | 指向 i2c_adapter 结构体的指针,这个结构体包含了描述一个 I2C 适配器的所有必要信息,比如适配器的名称、地址范围、算法(用于控制硬件的特定函数集)、数据传输速率等。 |
| 返回值 | int | 成功:0 失败:错误码 |
| 功能 | 向 I2C 子系统注册一个新的 I2C 适配器 ||
cpp
1361 int i2c_add_adapter(struct i2c_adapter *adapter)
1362 {
1363 struct device *dev = &adapter->dev;
1364 int id;
1365
1366 if (dev->of_node) {
1367 id = of_alias_get_id(dev->of_node, "i2c");
1368 if (id >= 0) {
1369 adapter->nr = id;
1370 return __i2c_add_numbered_adapter(adapter);
1371 }
1372 }
1373
1374 mutex_lock(&core_lock);
1375 id = idr_alloc(&i2c_adapter_idr, adapter,
1376 __i2c_first_dynamic_bus_num, 0, GFP_KERNEL);
1377 mutex_unlock(&core_lock);
1378 if (WARN(id < 0, "couldn't get idr"))
1379 return id;
1380
1381 adapter->nr = id;
1382
1383 return i2c_register_adapter(adapter);
1384 }
|------|------------------------------|-------------------------------------------------------------------------------------------|
| 函数原型 | void i2c_del_adapter(struct i2c_adapter *adap) ||
| 参数 | struct i2c_adapter *adapter | 指向 i2c_adapter 结构体的指针,这个结构体包含了描述一个 I2C 适配器的所有必要信息,比如适配器的名称、地址范围、算法(用于控制硬件的特定函数集)、数据传输速率等。 |
| 返回值 | int | |
| 功能 | 向 I2C 子系统删除一个的 I2C 适配器 ||
cpp
1465 void i2c_del_adapter(struct i2c_adapter *adap)
1466 {
1467 struct i2c_adapter *found;
1468 struct i2c_client *client, *next;
1469
1470 /* First make sure that this adapter was ever added */
1471 mutex_lock(&core_lock);
1472 found = idr_find(&i2c_adapter_idr, adap->nr);
1473 mutex_unlock(&core_lock);
1474 if (found != adap) {
1475 pr_debug("attempting to delete unregistered adapter [%s]\n", adap->name);
1476 return;
1477 }
1478
1479 i2c_acpi_remove_space_handler(adap);
1480 /* Tell drivers about this removal */
1481 mutex_lock(&core_lock);
1482 bus_for_each_drv(&i2c_bus_type, NULL, adap,
1483 __process_removed_adapter);
1484 mutex_unlock(&core_lock);
1485
1486 /* Remove devices instantiated from sysfs */
1487 mutex_lock_nested(&adap->userspace_clients_lock,
1488 i2c_adapter_depth(adap));
1489 list_for_each_entry_safe(client, next, &adap->userspace_clients,
1490 detected) {
1491 dev_dbg(&adap->dev, "Removing %s at 0x%x\n", client->name,
1492 client->addr);
1493 list_del(&client->detected);
1494 i2c_unregister_device(client);
1495 }
1496 mutex_unlock(&adap->userspace_clients_lock);
1497
1498 /* Detach any active clients. This can't fail, thus we do not
1499 * check the returned value. This is a two-pass process, because
1500 * we can't remove the dummy devices during the first pass: they
1501 * could have been instantiated by real devices wishing to clean
1502 * them up properly, so we give them a chance to do that first. */
1503 device_for_each_child(&adap->dev, NULL, __unregister_client);
1504 device_for_each_child(&adap->dev, NULL, __unregister_dummy);
1505
1506 #ifdef CONFIG_I2C_COMPAT
1507 class_compat_remove_link(i2c_adapter_compat_class, &adap->dev,
1508 adap->dev.parent);
1509 #endif
1510
1511 /* device name is gone after device_unregister */
1512 dev_dbg(&adap->dev, "adapter [%s] unregistered\n", adap->name);
1513
1514 pm_runtime_disable(&adap->dev);
1515
1516 i2c_host_notify_irq_teardown(adap);
1517
1518 /* wait until all references to the device are gone
1519 *
1520 * FIXME: This is old code and should ideally be replaced by an
1521 * alternative which results in decoupling the lifetime of the struct
1522 * device from the i2c_adapter, like spi or netdev do. Any solution
1523 * should be thoroughly tested with DEBUG_KOBJECT_RELEASE enabled!
1524 */
1525 init_completion(&adap->dev_released);
1526 device_unregister(&adap->dev);
1527 wait_for_completion(&adap->dev_released);
1528
1529 /* free bus id */
1530 mutex_lock(&core_lock);
1531 idr_remove(&i2c_adapter_idr, adap->nr);
1532 mutex_unlock(&core_lock);
1533
1534 /* Clear the device structure in case this adapter is ever going to be
1535 added again */
1536 memset(&adap->dev, 0, sizeof(adap->dev));
1537 }
3.2 注册i2c设备驱动
一般 SoC 的 I2C 总线驱动都是由半导体厂商编写的,比如 RK3568 的 I2C 适配器驱动 RK 官方已经编写好了,这个不需要用户去编写。因此 I2C 总线驱动对我们这些 SoC 使用者来说是被屏蔽掉的,我们只要专注于 I2C 设备驱动即可,除非是在半导体公司上班,工作内容就是写 I2C 适配器驱动。
|------|----------------------------|--------------------------------------------------------------------------------------------------------------|
| 函数原型 | int i2c_register_driver(struct module *owner, struct i2c_driver *driver) #define i2c_add_driver(driver) i2c_register_driver(THIS_MODULE, driver) ||
| 参数 | struct module *owner | 指向拥有该驱动的内核模块的指针。这个参数允许内核在需要时追踪到哪个模块注册了这个驱动,这在卸载模块或处理错误时特别有用。如果驱动是静态编译进内核的(而非作为模块加载),则这个参数通常设置为 THIS_MODULE 宏 |
| 参数 | struct i2c_driver *driver | 指向 i2c_driver 结构体的指针,该结构体包含了描述一个 I2C 设备驱动的所有必要信息,比如驱动的名称、支持的 I2C 设备 ID 表、用于探测、附加、分离和移除设备的回调函数等 |
| 返回值 | | 成功:0 失败:错误码 |
| 功能 | 向 I2C 子系统注册一个新的 I2C 设备驱动。通过这个函数,内核能够识别并管理通过特定 I2C 适配器连接的 I2C 设备,前提是这些设备与该驱动兼容。 ||
cpp
1620 int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
1621 {
1622 int res;
1623
1624 /* Can't register until after driver model init */
1625 if (WARN_ON(!is_registered))
1626 return -EAGAIN;
1627
1628 /* add the driver to the list of i2c drivers in the driver core */
1629 driver->driver.owner = owner;
1630 driver->driver.bus = &i2c_bus_type;
1631 INIT_LIST_HEAD(&driver->clients);
1632
1633 /* When registration returns, the driver core
1634 * will have called probe() for all matching-but-unbound devices.
1635 */
1636 res = driver_register(&driver->driver);
1637 if (res)
1638 return res;
1639
1640 pr_debug("driver [%s] registered\n", driver->driver.name);
1641
1642 /* Walk the adapters that are already present */
1643 i2c_for_each_dev(driver, __process_new_driver);
1644
1645 return 0;
1646 }
|------|----------------------------|------------------------------------------------------------------------------------------------|
| 函数原型 | void i2c_del_driver(struct i2c_driver *driver) ||
| 参数 | struct i2c_driver *driver | 指向 i2c_driver 结构体的指针,该结构体包含了描述一个 I2C 设备驱动的所有必要信息,比如驱动的名称、支持的 I2C 设备 ID 表、用于探测、附加、分离和移除设备的回调函数等 |
| 返回值 | | |
| 功能 | 向 I2C 子系统注销一个 I2C 设备驱动 ||
cpp
1661 void i2c_del_driver(struct i2c_driver *driver)
1662 {
1663 i2c_for_each_dev(driver, __process_removed_driver);
1664
1665 driver_unregister(&driver->driver);
1666 pr_debug("driver [%s] unregistered\n", driver->driver.name);
1667 }
3.3 i2c数据传输
3.3.1 i2c_transfer
|------|---------------------------|-------------------------------------------------------------------------------------------------------|
| 函数原型 | int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) ||
| 参数 | struct i2c_adapter *adap | 指向 I2C 适配器(也称为总线)的指针。在 Linux 中,每个 I2C 总线都由一个 i2c_adapter 结构体表示,该结构体包含了总线的基本信息和操作函数 |
| 参数 | struct i2c_msg *msgs | 指向一个 i2c_msg 结构体数组的指针,每个 i2c_msg 结构体代表一个要传输的消息。这个数组定义了传输的具体内容,包括目标设备的地址、传输的方向(读或写)、要传输的数据缓冲区以及传输的字节数。 |
| 参数 | int num | msgs 数组中 i2c_msg 结构体的数量,即要传输的消息数量。 |
| 返回值 | int | 成功:传输的消息数量 失败:错误码 |
| 功能 | 用于在 I2C 总线上执行一系列的消息传输 ||
i2c_transfer()函数本身不具备驱动适配器物理硬件完成消息交互的能力,只是寻找到i2c_adapter 对应的i2c_algorithm,并使用i2c_algorithm的master_xfer()函数真正驱动硬件流程。
cpp
1967 int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
1968 {
1969 int ret;
1987
1988 if (adap->algo->master_xfer) {
1989 #ifdef DEBUG
1990 for (ret = 0; ret < num; ret++) {
1991 dev_dbg(&adap->dev,
1992 "master_xfer[%d] %c, addr=0x%02x, len=%d%s\n",
1993 ret, (msgs[ret].flags & I2C_M_RD) ? 'R' : 'W',
1994 msgs[ret].addr, msgs[ret].len,
1995 (msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");
1996 }
1997 #endif
1998
1999 if (in_atomic() || irqs_disabled()) {
2000 ret = i2c_trylock_bus(adap, I2C_LOCK_SEGMENT);
2001 if (!ret)
2002 /* I2C activity is ongoing. */
2003 return -EAGAIN;
2004 } else {
2005 i2c_lock_bus(adap, I2C_LOCK_SEGMENT);
2006 }
2007
2008 ret = __i2c_transfer(adap, msgs, num);
2009 i2c_unlock_bus(adap, I2C_LOCK_SEGMENT);
2010
2011 return ret;
2012 } else {
2013 dev_dbg(&adap->dev, "I2C level transfers not supported\n");
2014 return -EOPNOTSUPP;
2015 }
2016 }
cpp
1908 int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
1909 {
1910 unsigned long orig_jiffies;
1911 int ret, try;
1912
1913 if (WARN_ON(!msgs || num < 1))
1914 return -EINVAL;
1915
1916 if (adap->quirks && i2c_check_for_quirks(adap, msgs, num))
1917 return -EOPNOTSUPP;
1918
1919 /*
1920 * i2c_trace_msg_key gets enabled when tracepoint i2c_transfer gets
1921 * enabled. This is an efficient way of keeping the for-loop from
1922 * being executed when not needed.
1923 */
1924 if (static_branch_unlikely(&i2c_trace_msg_key)) {
1925 int i;
1926 for (i = 0; i < num; i++)
1927 if (msgs[i].flags & I2C_M_RD)
1928 trace_i2c_read(adap, &msgs[i], i);
1929 else
1930 trace_i2c_write(adap, &msgs[i], i);
1931 }
1932
1933 /* Retry automatically on arbitration loss */
1934 orig_jiffies = jiffies;
1935 for (ret = 0, try = 0; try <= adap->retries; try++) {
1936 ret = adap->algo->master_xfer(adap, msgs, num);
1937 if (ret != -EAGAIN)
1938 break;
1939 if (time_after(jiffies, orig_jiffies + adap->timeout))
1940 break;
1941 }
1942
1943 if (static_branch_unlikely(&i2c_trace_msg_key)) {
1944 int i;
1945 for (i = 0; i < ret; i++)
1946 if (msgs[i].flags & I2C_M_RD)
1947 trace_i2c_reply(adap, &msgs[i], i);
1948 trace_i2c_result(adap, num, ret);
1949 }
1950
1951 return ret;
1952 }
3.3.2 i2c_master_send
|------|----------------------------------|--------------------------------------------------------------------------------------------|
| 函数原型 | static inline int i2c_master_send(const struct i2c_client *client, const char *buf, int count) ||
| 参数 | const struct i2c_client *client | 指向 i2c_client 结构体的指针,该结构体代表了一个 I2C 设备客户端(即从设备)。它包含了设备的地址、适配器(I2C 控制器)以及可能的其他信息,如设备特定的标志或数据 |
| 参数 | const char *buf | 指向要发送数据的缓冲区的指针 |
| 参数 | int count | 要发送的字节数 |
| 返回值 | int | 成功:实际发送的字节数 失败:错误码 |
| 功能 | 从 I2C 主机(通常是 CPU 或微控制器)向指定的 I2C 从设备发送数据 ||
cpp
108 static inline int i2c_master_send(const struct i2c_client *client, const char *buf, int count)
110 {
111 return i2c_transfer_buffer_flags(client, (char *)buf, count, 0);
112 };
2029 int i2c_transfer_buffer_flags(const struct i2c_client *client, char *buf,
2030 int count, u16 flags)
2031 {
2032 int ret;
2033 struct i2c_msg msg = {
2034 .addr = client->addr,
2035 .flags = flags | (client->flags & I2C_M_TEN),
2036 .len = count,
2037 .buf = buf,
2038 };
2039
2040 ret = i2c_transfer(client->adapter, &msg, 1);
2041
2042 /*
2043 * If everything went ok (i.e. 1 msg transferred), return #bytes
2044 * transferred, else error code.
2045 */
2046 return (ret == 1) ? count : ret;
2047 }
3.3.3 i2c_master_recv
|------|----------------------------------|--------------------------------------------------------------------------------------------|
| 函数原型 | static inline int i2c_master_recv(const struct i2c_client *client, char *buf, int count) ||
| 参数 | const struct i2c_client *client | 指向 i2c_client 结构体的指针,该结构体代表了一个 I2C 设备客户端(即从设备)。它包含了设备的地址、适配器(I2C 控制器)以及可能的其他信息,如设备特定的标志或数据 |
| 参数 | const char *buf | 指向要发送数据的缓冲区的指针 |
| 参数 | int count | 要发送的字节数 |
| 返回值 | int | 成功:实际发送的字节数 失败:错误码 |
| 功能 | 从指定的 I2C 从设备接收数据到主机 ||
cpp
78 static inline int i2c_master_recv(const struct i2c_client *client,
79 char *buf, int count)
80 {
81 return i2c_transfer_buffer_flags(client, buf, count, I2C_M_RD);
82 };
2029 int i2c_transfer_buffer_flags(const struct i2c_client *client, char *buf,
2030 int count, u16 flags)
2031 {
2032 int ret;
2033 struct i2c_msg msg = {
2034 .addr = client->addr,
2035 .flags = flags | (client->flags & I2C_M_TEN),
2036 .len = count,
2037 .buf = buf,
2038 };
2039
2040 ret = i2c_transfer(client->adapter, &msg, 1);
2041
2042 /*
2043 * If everything went ok (i.e. 1 msg transferred), return #bytes
2044 * transferred, else error code.
2045 */
2046 return (ret == 1) ? count : ret;
2047 }
4 rk3399 i2c适配器驱动分析
4.1 设备树
cpp
18 / {
1241 i2c4: i2c@ff3d0000 {
1242 compatible = "rockchip,rk3399-i2c";
1243 reg = <0x0 0xff3d0000 0x0 0x1000>;
1244 assigned-clocks = <&pmucru SCLK_I2C4_PMU>;
1245 assigned-clock-rates = <200000000>;
1246 clocks = <&pmucru SCLK_I2C4_PMU>, <&pmucru PCLK_I2C4_PMU>;
1247 clock-names = "i2c", "pclk";
1248 interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH 0>;
1249 pinctrl-names = "default";
1250 pinctrl-0 = <&i2c4_xfer>;
1251 #address-cells = <1>;
1252 #size-cells = <0>;
1253 status = "disabled";
1254 };
3605 };
4.2 结构体
4.2.1 rk3x_i2c
cpp
201 struct rk3x_i2c {
202 struct i2c_adapter adap;
203 struct device *dev;
204 const struct rk3x_i2c_soc_data *soc_data;
205
206 /* Hardware resources */
207 void __iomem *regs;
208 struct clk *clk;
209 struct clk *pclk;
210 struct notifier_block clk_rate_nb;
211
212 /* Settings */
213 struct i2c_timings t;
214
215 /* Synchronization & notification */
216 spinlock_t lock;
217 wait_queue_head_t wait;
218 bool busy;
219
220 /* Current message */
221 struct i2c_msg *msg;
222 u8 addr;
223 unsigned int mode;
224 bool is_last_msg;
225
226 /* I2C state machine */
227 enum rk3x_i2c_state state;
228 unsigned int processed;
229 int error;
230 unsigned int suspended:1;
231
232 struct notifier_block i2c_restart_nb;
233 bool system_restarting;
234 };
4.2.2 rk3x_i2c_soc_data
cpp
172 struct rk3x_i2c_soc_data {
173 int grf_offset;
174 int (*calc_timings)(unsigned long, struct i2c_timings *,
175 struct rk3x_i2c_calced_timings *);
176 };
4.2.3 of_device_id
cpp
241 struct of_device_id {
242 char name[32];
243 char type[32];
244 char compatible[128];
245 const void *data;
246 };
4.2.4 i2c_timings
i2c_timings用于配置 I2C 总线的时序参数
cpp
564 struct i2c_timings {
565 u32 bus_freq_hz;
566 u32 scl_rise_ns;
567 u32 scl_fall_ns;
568 u32 scl_int_delay_ns;
569 u32 sda_fall_ns;
570 u32 sda_hold_ns;
571 };
**bus_freq_hz:**I2C 总线的频率,以赫兹 (Hz) 为单位。
scl_rise_ns: SCL 信号上升沿的时间,单位为纳秒 (ns)。
**scl_fall_ns:**SCL 信号下降沿的时间,单位为纳秒 (ns)。
scl_int_delay_ns: SCL 信号在上升沿和下降沿之间的内部延迟,单位为纳秒 (ns)。
sda_fall_ns: SDA 信号下降沿的时间,单位为纳秒 (ns)。
**sda_hold_ns:**SDA 信号在 SCL 下降沿之后的保持时间,单位为纳秒 (ns)。
4.2.5 platform_device
cpp
23 struct platform_device {
24 const char *name;
25 int id;
26 bool id_auto;
27 struct device dev;
28 u32 num_resources;
29 struct resource *resource;
30
31 const struct platform_device_id *id_entry;
32 char *driver_override; /* Driver name to force a match */
33
34 /* MFD cell pointer */
35 struct mfd_cell *mfd_cell;
36
37 /* arch specific additions */
38 struct pdev_archdata archdata;
39 };
4.2.6 device
用于表示设备的结构体
cpp
1031 struct device {
1032 struct device *parent;
1033
1034 struct device_private *p;
1035
1036 struct kobject kobj;
1037 const char *init_name; /* initial name of the device */
1038 const struct device_type *type;
1039
1040 struct mutex mutex; /* mutex to synchronize calls to
1041 * its driver.
1042 */
1043
1044 struct bus_type *bus; /* type of bus device is on */
1045 struct device_driver *driver; /* which driver has allocated this
1046 device */
1047 void *platform_data; /* Platform specific data, device
1048 core doesn't touch it */
1049 void *driver_data; /* Driver data, set and get with
1050 dev_set/get_drvdata */
1051 struct dev_links_info links;
1052 struct dev_pm_info power;
1053 struct dev_pm_domain *pm_domain;
1054
1055 #ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
1056 struct irq_domain *msi_domain;
1057 #endif
1058 #ifdef CONFIG_PINCTRL
1059 struct dev_pin_info *pins;
1060 #endif
1061 #ifdef CONFIG_GENERIC_MSI_IRQ
1062 struct list_head msi_list;
1063 #endif
1064
1065 #ifdef CONFIG_NUMA
1066 int numa_node; /* NUMA node this device is close to */
1067 #endif
1068 const struct dma_map_ops *dma_ops;
1069 u64 *dma_mask; /* dma mask (if dma'able device) */
1070 u64 coherent_dma_mask;/* Like dma_mask, but for
1071 alloc_coherent mappings as
1072 not all hardware supports
1073 64 bit addresses for consistent
1074 allocations such descriptors. */
1075 u64 bus_dma_mask; /* upstream dma_mask constraint */
1076 unsigned long dma_pfn_offset;
1077
1078 struct device_dma_parameters *dma_parms;
1079
1080 struct list_head dma_pools; /* dma pools (if dma'ble) */
1081
1082 struct dma_coherent_mem *dma_mem; /* internal for coherent mem
1083 override */
1084 #ifdef CONFIG_DMA_CMA
1085 struct cma *cma_area; /* contiguous memory area for dma
1086 allocations */
1087 #endif
1088 struct removed_region *removed_mem;
1089 /* arch specific additions */
1090 struct dev_archdata archdata;
1091
1092 struct device_node *of_node; /* associated device tree node */
1093 struct fwnode_handle *fwnode; /* firmware device node */
1094
1095 dev_t devt; /* dev_t, creates the sysfs "dev" */
1096 u32 id; /* device instance */
1097
1098 spinlock_t devres_lock;
1099 struct list_head devres_head;
1100
1101 struct klist_node knode_class;
1102 struct class *class;
1103 const struct attribute_group **groups; /* optional groups */
1104
1105 void (*release)(struct device *dev);
1106 struct iommu_group *iommu_group;
1107 struct iommu_fwspec *iommu_fwspec;
1108
1109 bool offline_disabled:1;
1110 bool offline:1;
1111 bool of_node_reused:1;
1112 bool state_synced:1;
1113
1114 ANDROID_KABI_RESERVE(1);
1115 ANDROID_KABI_RESERVE(2);
1116 ANDROID_KABI_RESERVE(3);
1117 ANDROID_KABI_RESERVE(4);
1118 ANDROID_KABI_RESERVE(5);
1119 ANDROID_KABI_RESERVE(6);
1120 ANDROID_KABI_RESERVE(7);
1121 ANDROID_KABI_RESERVE(8);
1122 };
const char *init_name: 设备的初始化名称,用于在设备创建时标识设备。
struct device *parent: 指向设备的父设备。用于表示设备的层次结构,通常父设备是包含或控制子设备的设备。
**struct device_driver *driver:**指向设备当前驱动的指针。驱动程序用于控制设备的操作。
struct device_node *of_node: 用于设备树的节点,表示设备在设备树中的位置和属性。
const struct device_type *type: 设备的类型,定义了设备的行为和特性。
**struct kobject kobj:**设备的内核对象,用于设备的系统文件创建和管理。
**struct device *parent:**设备的父设备,表示设备树中的层次关系。
**struct device_attribute *dev_attrs:**设备属性,用于与设备交互的属性列表。
**struct class *class:**设备所在的类,用于组织设备的类别。
**void *platform_data:**指向平台特定数据的指针,设备驱动可以通过它访问特定的配置信息。
void *driver_data: 指向驱动程序数据的指针,用于驱动程序存储设备特定的数据。
4.3 probe函数
rk3x_i2c_probe完成i2c适配器初始化工作
cpp
static int rk3x_i2c_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *match;
struct rk3x_i2c *i2c;
struct resource *mem;
int ret = 0;
u32 value;
int irq;
unsigned long clk_rate;
i2c = devm_kzalloc(&pdev->dev, sizeof(struct rk3x_i2c), GFP_KERNEL);
if (!i2c)
return -ENOMEM;
match = of_match_node(rk3x_i2c_match, np);
i2c->soc_data = match->data;
/* use common interface to get I2C timing properties */
i2c_parse_fw_timings(&pdev->dev, &i2c->t, true);
strlcpy(i2c->adap.name, "rk3x-i2c", sizeof(i2c->adap.name));
i2c->adap.owner = THIS_MODULE;
i2c->adap.algo = &rk3x_i2c_algorithm;
i2c->adap.retries = 3;
i2c->adap.dev.of_node = np;
i2c->adap.algo_data = i2c;
i2c->adap.dev.parent = &pdev->dev;
i2c->dev = &pdev->dev;
spin_lock_init(&i2c->lock);
init_waitqueue_head(&i2c->wait);
i2c->i2c_restart_nb.notifier_call = rk3x_i2c_restart_notify;
i2c->i2c_restart_nb.priority = 128;
ret = register_pre_restart_handler(&i2c->i2c_restart_nb);
if (ret) {
dev_err(&pdev->dev, "failed to setup i2c restart handler.\n");
return ret;
}
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
i2c->regs = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(i2c->regs))
return PTR_ERR(i2c->regs);
/*
* Switch to new interface if the SoC also offers the old one.
* The control bit is located in the GRF register space.
*/
if (i2c->soc_data->grf_offset >= 0) {
struct regmap *grf;
grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
if (!IS_ERR(grf)) {
int bus_nr;
/* Try to set the I2C adapter number from dt */
bus_nr = of_alias_get_id(np, "i2c");
if (bus_nr < 0) {
dev_err(&pdev->dev, "rk3x-i2c needs i2cX alias");
return -EINVAL;
}
if (i2c->soc_data == &rv1108_soc_data && bus_nr == 2)
/* rv1108 i2c2 set grf offset-0x408, bit-10 */
value = BIT(26) | BIT(10);
else if (i2c->soc_data == &rv1126_soc_data &&
bus_nr == 2)
/* rv1126 i2c2 set pmugrf offset-0x118, bit-4 */
value = BIT(20) | BIT(4);
else
/* rk3xxx 27+i: write mask, 11+i: value */
value = BIT(27 + bus_nr) | BIT(11 + bus_nr);
ret = regmap_write(grf, i2c->soc_data->grf_offset,
value);
if (ret != 0) {
dev_err(i2c->dev, "Could not write to GRF: %d\n",
ret);
return ret;
}
}
}
/* IRQ setup */
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "cannot find rk3x IRQ\n");
return irq;
}
ret = devm_request_irq(&pdev->dev, irq, rk3x_i2c_irq,
0, dev_name(&pdev->dev), i2c);
if (ret < 0) {
dev_err(&pdev->dev, "cannot request IRQ\n");
return ret;
}
platform_set_drvdata(pdev, i2c);
if (i2c->soc_data->calc_timings == rk3x_i2c_v0_calc_timings) {
/* Only one clock to use for bus clock and peripheral clock */
i2c->clk = devm_clk_get(&pdev->dev, NULL);
i2c->pclk = i2c->clk;
} else {
i2c->clk = devm_clk_get(&pdev->dev, "i2c");
i2c->pclk = devm_clk_get(&pdev->dev, "pclk");
}
if (IS_ERR(i2c->clk)) {
ret = PTR_ERR(i2c->clk);
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev, "Can't get bus clk: %d\n", ret);
return ret;
}
if (IS_ERR(i2c->pclk)) {
ret = PTR_ERR(i2c->pclk);
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev, "Can't get periph clk: %d\n", ret);
return ret;
}
ret = clk_prepare(i2c->clk);
if (ret < 0) {
dev_err(&pdev->dev, "Can't prepare bus clk: %d\n", ret);
return ret;
}
ret = clk_prepare(i2c->pclk);
if (ret < 0) {
dev_err(&pdev->dev, "Can't prepare periph clock: %d\n", ret);
goto err_clk;
}
i2c->clk_rate_nb.notifier_call = rk3x_i2c_clk_notifier_cb;
ret = clk_notifier_register(i2c->clk, &i2c->clk_rate_nb);
if (ret != 0) {
dev_err(&pdev->dev, "Unable to register clock notifier\n");
goto err_pclk;
}
clk_rate = clk_get_rate(i2c->clk);
rk3x_i2c_adapt_div(i2c, clk_rate);
ret = i2c_add_adapter(&i2c->adap);
if (ret < 0)
goto err_clk_notifier;
return 0;
err_clk_notifier:
clk_notifier_unregister(i2c->clk, &i2c->clk_rate_nb);
err_pclk:
clk_unprepare(i2c->pclk);
err_clk:
clk_unprepare(i2c->clk);
return ret;
}
cpp
900 static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, unsigned long clk_rate)
901 {
902 struct i2c_timings *t = &i2c->t;
903 struct rk3x_i2c_calced_timings calc;
904 u64 t_low_ns, t_high_ns;
905 unsigned long flags;
906 u32 val;
907 int ret;
908
909 ret = i2c->soc_data->calc_timings(clk_rate, t, &calc);
910 WARN_ONCE(ret != 0, "Could not reach SCL freq %u", t->bus_freq_hz);
911
912 clk_enable(i2c->pclk);
913
914 spin_lock_irqsave(&i2c->lock, flags);
915 val = i2c_readl(i2c, REG_CON);
916 val &= ~REG_CON_TUNING_MASK;
917 val |= calc.tuning;
918 i2c_writel(i2c, val, REG_CON);
919 i2c_writel(i2c, (calc.div_high << 16) | (calc.div_low & 0xffff),
920 REG_CLKDIV);
921 spin_unlock_irqrestore(&i2c->lock, flags);
922
923 clk_disable(i2c->pclk);
924
925 t_low_ns = div_u64(((u64)calc.div_low + 1) * 8 * 1000000000, clk_rate);
926 t_high_ns = div_u64(((u64)calc.div_high + 1) * 8 * 1000000000,
927 clk_rate);
928 dev_dbg(i2c->dev,
929 "CLK %lukhz, Req %uns, Act low %lluns high %lluns\n",
930 clk_rate / 1000,
931 1000000000 / t->bus_freq_hz,
932 t_low_ns, t_high_ns);
933 }
4.4 函数解析
4.4.1 of_match_node
|------|-------------------------------------|----------------------------------------------|
| 函数原型 | const struct of_device_id *of_match_node(const struct of_device_id *matches, const struct device_node *node) ||
| 参数 | const struct of_device_id *matches | of_device_id 结构体数组 matches,包含了设备与驱动程序之间的匹配信息 |
| 参数 | const struct device_node *node | device_node 指针 node,表示当前要匹配的设备节点 |
| 返回值 | struct of_device_id * | 成功:of_device_id 指针 失败:NULL |
| 功能 | 用于在设备树中匹配设备节点。函数会遍历 matches 数组,查找与 node 兼容的条目,并返回匹配的 of_device_id 指针 ||
cpp
1258 static const struct of_device_id rk3x_i2c_match[] = {
1259 {
1260 .compatible = "rockchip,rv1108-i2c",
1261 .data = &rv1108_soc_data
1262 },
1263 {
1264 .compatible = "rockchip,rv1126-i2c",
1265 .data = &rv1126_soc_data
1266 },
1267 {
1268 .compatible = "rockchip,rk3066-i2c",
1269 .data = &rk3066_soc_data
1270 },
1271 {
1272 .compatible = "rockchip,rk3188-i2c",
1273 .data = &rk3188_soc_data
1274 },
1275 {
1276 .compatible = "rockchip,rk3228-i2c",
1277 .data = &rk3228_soc_data
1278 },
1279 {
1280 .compatible = "rockchip,rk3288-i2c",
1281 .data = &rk3288_soc_data
1282 },
1283 {
1284 .compatible = "rockchip,rk3399-i2c",
1285 .data = &rk3399_soc_data
1286 },
1287 {},
1288 };
cpp
1253 static const struct rk3x_i2c_soc_data rk3399_soc_data = {
1254 .grf_offset = -1,
1255 .calc_timings = rk3x_i2c_v1_calc_timings,
1256 };
4.4.2 i2c_parse_fw_timings
i2c_parse_fw_timings 函数设置 I2C 频率, 如果不设置"clock-frequency"则使用默认值 100KHZ,如果设备树节点设置了"clock-frequency"属性的话 I2C 频率就使用 clock-frequency 属性值
|------|------------------------|---------------------------------------------|
| 函数原型 | void i2c_parse_fw_timings(struct device *dev, struct i2c_timings *t, bool use_defaults) ||
| 参数 | struct device *dev | 指向设备结构体的指针 |
| 参数 | struct i2c_timings *t | 用于存储解析后的时序信息 |
| 参数 | bool use_defaults | use_defaults 是一个布尔值,指示如果固件中没有提供时序信息时是否使用默认值 |
| 返回值 | | |
| 功能 | 从设备的固件(或设备树)中解析 I2C 总线的时序参数,并将其存储在 i2c_timings 结构体中 ||
cpp
1556 void i2c_parse_fw_timings(struct device *dev, struct i2c_timings *t, bool use_defaults)
1557 {
1558 int ret;
1559
1560 memset(t, 0, sizeof(*t));
1561
1562 ret = device_property_read_u32(dev, "clock-frequency", &t->bus_freq_hz);
1563 if (ret && use_defaults)
1564 t->bus_freq_hz = 100000;
1565
1566 ret = device_property_read_u32(dev, "i2c-scl-rising-time-ns", &t->scl_rise_ns);
1567 if (ret && use_defaults) {
1568 if (t->bus_freq_hz <= 100000)
1569 t->scl_rise_ns = 1000;
1570 else if (t->bus_freq_hz <= 400000)
1571 t->scl_rise_ns = 300;
1572 else
1573 t->scl_rise_ns = 120;
1574 }
1575
1576 ret = device_property_read_u32(dev, "i2c-scl-falling-time-ns", &t->scl_fall_ns);
1577 if (ret && use_defaults) {
1578 if (t->bus_freq_hz <= 400000)
1579 t->scl_fall_ns = 300;
1580 else
1581 t->scl_fall_ns = 120;
1582 }
1583
1584 device_property_read_u32(dev, "i2c-scl-internal-delay-ns", &t->scl_int_delay_ns);
1585
1586 ret = device_property_read_u32(dev, "i2c-sda-falling-time-ns", &t->sda_fall_ns);
1587 if (ret && use_defaults)
1588 t->sda_fall_ns = t->scl_fall_ns;
1589
1590 device_property_read_u32(dev, "i2c-sda-hold-time-ns", &t->sda_hold_ns);
1591 }
|------|-----------------------|---------------------------------------------------------------|
| 函数原型 | static inline int device_property_read_u32(struct device *dev, const char *propname, u32 *val) ||
| 参数 | struct device *dev | 指向 struct device 结构体的指针,表示要从中读取属性的设备。这个结构体通常包含设备的描述信息和设备的属性数据 |
| 参数 | const char *propname | 设备属性的名称 |
| 参数 | u32 *val | 用于存储读取到的属性值 |
| 返回值 | int | 成功:0 失败:负数 |
| 功能 | 从设备的属性中读取 u32 类型的值 ||
4.4.3 spin_lock_init
|------|------------------|-----|
| 函数原型 | #define spin_lock_init(_lock) \ do { \ spinlock_check(_lock); \ raw_spin_lock_init(&(_lock)->rlock); \ } while (0) ||
| 参数 | spinlock_t _lock | 自旋锁 |
| 返回值 | | |
| 功能 | 初始化自旋锁 _lock。自旋锁是一种用于保护共享资源的同步机制,它在锁被占用时会忙等待(即持续检查锁状态),直到锁变为可用 ||
4.4.4 init_waitqueue_head
|------|----------------------------------|---------------------------------------------------------|
| 函数原型 | #define init_waitqueue_head(wq_head) \ do { \ static struct lock_class_key __key; \ \ __init_waitqueue_head((wq_head), #wq_head, &__key); \ } while (0) void __init_waitqueue_head(struct wait_queue_head *wq_head, const char *name, struct lock_class_key *key) ||
| 参数 | struct wait_queue_head *wq_head | 指向等待队列头(wait_queue_head)结构体的指针 |
| 参数 | const char *name | 该字符串为等待队列提供了一个名称。这个名称主要用于调试目的,它可以帮助开发者在调试内核时更容易地识别出等待队列 |
| 参数 | struct lock_class_key *key | 用于标识和分类锁。这个参数与内核的锁调试功能相关 |
| 返回值 | | |
| 功能 | 用于初始化等待队列头 wq_head ||
cpp
8 void __init_waitqueue_head(struct wait_queue_head *wq_head, const char *name, struct lock_class_key *key)
9 {
10 spin_lock_init(&wq_head->lock);
11 lockdep_set_class_and_name(&wq_head->lock, key, name);
12 INIT_LIST_HEAD(&wq_head->head);
13 }
cpp
127 static inline void
128 INIT_LIST_HEAD(struct list_head *list)
129 {
130 list->next = list->prev = list;
131 }
4.4.5 register_pre_restart_handler
|------|----------------------------|------------------------------------------------------------------------|
| 函数原型 | int register_pre_restart_handler(struct notifier_block *nb) ||
| 参数 | struct notifier_block *nb | notifier_block 结构体包含了处理特定事件的回调函数。当系统准备重启时,所有注册的处理程序都会被调用,以执行必要的清理或保存操作 |
| 返回值 | | |
| 功能 | 用于注册一个通知处理程序 nb,以便在系统重启之前被调用 ||
cpp
54 struct notifier_block {
55 notifier_fn_t notifier_call;
56 struct notifier_block __rcu *next;
57 int priority;
58 };
cpp
1291 static int rk3x_i2c_probe(struct platform_device *pdev)
1292 {
1325 i2c->i2c_restart_nb.notifier_call = rk3x_i2c_restart_notify;
1326 i2c->i2c_restart_nb.priority = 128;
1327 ret = register_pre_restart_handler(&i2c->i2c_restart_nb);
1328 if (ret) {
1329 dev_err(&pdev->dev, "failed to setup i2c restart handler.\n");
1330 return ret;
1331 }
1449 }
1147 static int rk3x_i2c_restart_notify(struct notifier_block *this,
1148 unsigned long mode, void *cmd)
1149 {
1150 struct rk3x_i2c *i2c = container_of(this, struct rk3x_i2c,
1151 i2c_restart_nb);
1152 int tmo = WAIT_TIMEOUT * USEC_PER_MSEC;
1153 u32 val;
1154
1155 if (i2c->state != STATE_IDLE) {
1156 i2c->system_restarting = true;
1157 /* complete the unfinished job */
1158 while (tmo-- && i2c->busy) {
1159 udelay(1);
1160 rk3x_i2c_irq(0, i2c);
1161 }
1162 }
1163
1164 if (tmo <= 0) {
1165 dev_err(i2c->dev, "restart timeout, ipd: 0x%02x, state: %d\n",
1166 i2c_readl(i2c, REG_IPD), i2c->state);
1167
1168 /* Force a STOP condition without interrupt */
1169 i2c_writel(i2c, 0, REG_IEN);
1170 val = i2c_readl(i2c, REG_CON) & REG_CON_TUNING_MASK;
1171 val |= REG_CON_EN | REG_CON_STOP;
1172 i2c_writel(i2c, val, REG_CON);
1173
1174 udelay(10);
1175 i2c->state = STATE_IDLE;
1176 }
1177
1178 return NOTIFY_DONE;
1179 }
4.4.6 platform_get_resource
|------|------------------------------|--------------------------------------------------------------------------|
| 函数原型 | struct resource *platform_get_resource(struct platform_device *dev,unsigned int type, unsigned int num) ||
| 参数 | struct platform_device *dev | 指向 platform_device 结构体的指针,表示你要获取资源的设备。这通常是在设备驱动程序中创建的设备实例 |
| 参数 | unsigned int type | 资源的类型,通常是一个资源类型的枚举值,如 IORESOURCE_MEM(表示内存资源)或 IORESOURCE_IO(表示 I/O 端口资源) |
| 参数 | unsigned int num | 资源的索引,用于指定特定的资源。例如,如果一个设备有多个内存区域(内存资源),num 可以用来选择其中的一个 |
| 返回值 | struct resource * | 成功:指向 resource 结构体的指针 失败:NULL |
| 功能 | 用于获取平台设备资源的函数。它主要用于在内核中访问与平台设备相关联的硬件资源(如内存区域、I/O 端口、IRQ 等) ||
cpp
68 struct resource *platform_get_resource(struct platform_device *dev,
69 unsigned int type, unsigned int num)
70 {
71 u32 i;
72
73 for (i = 0; i < dev->num_resources; i++) {
74 struct resource *r = &dev->resource[i];
75
76 if (type == resource_type(r) && num-- == 0)
77 return r;
78 }
79 return NULL;
80 }
4.4.7 devm_ioremap_resource
|------|-----------------------------|--------------------------------------------------|
| 函数原型 | void __iomem *devm_ioremap_resource(struct device *dev,const struct resource *res) ||
| 参数 | struct device *dev | 指向 device 结构体的指针,表示设备实例,用于管理和关联资源 |
| 参数 | const struct resource *res | 指向 resource 结构体的指针,描述了要映射的硬件资源的起始地址、结束地址及资源类型等信息 |
| 返回值 | void __iomem * | 成功:指向映射后的虚拟地址的指针 (void __iomem *) 失败:NULL |
| 功能 | 于在内核中映射一个设备资源到虚拟地址空间,并且自动管理这个映射的生命周期 ||
cpp
153 void __iomem *devm_ioremap_resource(struct device *dev,
154 const struct resource *res)
155 {
156 resource_size_t size;
157 const char *name;
158 void __iomem *dest_ptr;
159
160 BUG_ON(!dev);
161
162 if (!res || resource_type(res) != IORESOURCE_MEM) {
163 dev_err(dev, "invalid resource\n");
164 return IOMEM_ERR_PTR(-EINVAL);
165 }
166
167 size = resource_size(res);
168 name = res->name ?: dev_name(dev);
169
170 if (!devm_request_mem_region(dev, res->start, size, name)) {
171 dev_err(dev, "can't request region for resource %pR\n", res);
172 return IOMEM_ERR_PTR(-EBUSY);
173 }
174
175 dest_ptr = devm_ioremap(dev, res->start, size);
176 if (!dest_ptr) {
177 dev_err(dev, "ioremap failed for resource %pR\n", res);
178 devm_release_mem_region(dev, res->start, size);
179 dest_ptr = IOMEM_ERR_PTR(-ENOMEM);
180 }
181
182 return dest_ptr;
183 }
4.4.8 syscon_regmap_lookup_by_phandle
|------|-------------------------|------------------------------------------------------|
| 函数原型 | struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np, const char *property) ||
| 参数 | struct device_node *np | 指向 device_node 结构体的指针,表示设备树中的节点 |
| 参数 | const char *property | 一个字符串,指定要查找的设备树属性名 |
| 返回值 | struct regmap * | 成功:一个 struct regmap * 类型的指针,指向找到的 regmap 对象 失败:NULL |
| 功能 | 用于查找系统控制寄存器映射的函数。它从设备树节点中获取与特定属性关联的系统控制寄存器的 regmap 对象 ||
4.4.9 of_alias_get_id
|------|-------------------------|-------------------------------------------------------|
| 函数原型 | int of_alias_get_id(struct device_node *np, const char *stem) ||
| 参数 | struct device_node *np | 指向 device_node 结构体的指针,表示设备树节点。一般来说,这个节点是设备树的根节点或某个父节点 |
| 参数 | const char *stem | 一个字符串,表示设备别名的前缀(即"stem")。这个前缀用于匹配设备树中定义的别名 |
| 返回值 | int | 成功:给定 stem 匹配的设备别名的 ID 失败:负数 |
| 功能 | 用于从设备树中获取设备别名的函数 ||
cpp
1978 int of_alias_get_id(struct device_node *np, const char *stem)
1979 {
1980 struct alias_prop *app;
1981 int id = -ENODEV;
1982
1983 mutex_lock(&of_mutex);
1984 list_for_each_entry(app, &aliases_lookup, link) {
1985 if (strcmp(app->stem, stem) != 0)
1986 continue;
1987
1988 if (np == app->np) {
1989 id = app->id;
1990 break;
1991 }
1992 }
1993 mutex_unlock(&of_mutex);
1994
1995 return id;
1996 }
4.4.10 regmap_write
|------|---------------------|-------------|
| 函数原型 | int regmap_write(struct regmap *map, unsigned int reg, unsigned int val) ||
| 参数 | struct regmap *map | regmap结构体指针 |
| 参数 | unsigned int reg | 写的寄存器地址 |
| 参数 | unsigned int val | 写寄存器的值 |
| 返回值 | int | 成功:0 失败:负数 |
| 功能 | 从设备的寄存器中写数据。regmap框架提供了一种统一的方式来访问设备的寄存器,无论这些寄存器是通过i2c、spi、内存映射还是其他方式访问的 ||
4.4.11 platform_get_irq
|------|------------------------------|--------------------------------------|
| 函数原型 | int platform_get_irq(struct platform_device *dev, unsigned int num) ||
| 参数 | struct platform_device *dev | 指向 platform_device 结构体的指针,表示要获取中断的设备 |
| 参数 | unsigned int num | 中断编号 |
| 返回值 | | 成功:获取到的中断号 失败:负数 |
| 功能 | 用于从平台设备中获取中断号 ||
4.4.12 devm_request_irq
|------|------------------------|----------------------|
| 函数原型 | static inline int __must_check devm_request_irq(struct device *dev, unsigned int irq, irq_handler_t handler,unsigned long irqflags, const char *devname, void *dev_id) ||
| 参数 | struct device *dev | 请求中断的设备 |
| 参数 | unsigned int irq | 请求的中断号 |
| 参数 | rq_handler_t handler | 中断处理函数 |
| 参数 | unsigned long irqflags | 中断标志位 |
| | const char *devname | 描述设备的名字 |
| | void *dev_id | 通常用于识别中断处理程序对应的设备或数据 |
| 返回值 | int | 成功:0 失败负数 |
| 功能 | 请求中断 ||
cpp
502 static irqreturn_t rk3x_i2c_irq(int irqno, void *dev_id)
503 {
504 struct rk3x_i2c *i2c = dev_id;
505 unsigned int ipd;
506
507 spin_lock(&i2c->lock);
508
509 ipd = i2c_readl(i2c, REG_IPD);
510 if (i2c->state == STATE_IDLE) {
511 dev_warn_ratelimited(i2c->dev,
512 "irq in STATE_IDLE, ipd = 0x%x\n",
513 ipd);
514 rk3x_i2c_clean_ipd(i2c);
515 goto out;
516 }
517
518 dev_dbg(i2c->dev, "IRQ: state %d, ipd: %x\n", i2c->state, ipd);
519
520 /* Clean interrupt bits we don't care about */
521 ipd &= ~(REG_INT_BRF | REG_INT_BTF);
522
523 if (ipd & REG_INT_NAKRCV) {
524 /*
525 * We got a NACK in the last operation. Depending on whether
526 * IGNORE_NAK is set, we have to stop the operation and report
527 * an error.
528 */
529 i2c_writel(i2c, REG_INT_NAKRCV, REG_IPD);
530
531 ipd &= ~REG_INT_NAKRCV;
532
533 if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
534 rk3x_i2c_stop(i2c, -ENXIO);
535 goto out;
536 }
537 }
538
539 /* is there anything left to handle? */
540 if ((ipd & REG_INT_ALL) == 0)
541 goto out;
542
543 switch (i2c->state) {
544 case STATE_WRITE:
545 rk3x_i2c_handle_write(i2c, ipd);
546 break;
547 case STATE_READ:
548 rk3x_i2c_handle_read(i2c, ipd);
549 break;
550 case STATE_STOP:
551 rk3x_i2c_handle_stop(i2c, ipd);
552 break;
553 case STATE_IDLE:
554 break;
555 }
556
557 out:
558 spin_unlock(&i2c->lock);
559 return IRQ_HANDLED;
560 }
cpp
423 static void rk3x_i2c_handle_write(struct rk3x_i2c *i2c, unsigned int ipd)
424 {
425 if (!(ipd & REG_INT_MBTF)) {
426 rk3x_i2c_stop(i2c, -EIO);
427 dev_err(i2c->dev, "unexpected irq in WRITE: 0x%x\n", ipd);
428 rk3x_i2c_clean_ipd(i2c);
429 return;
430 }
431
432 /* ack interrupt */
433 i2c_writel(i2c, REG_INT_MBTF, REG_IPD);
434
435 /* are we finished? */
436 if (i2c->processed == i2c->msg->len)
437 rk3x_i2c_stop(i2c, i2c->error);
438 else
439 rk3x_i2c_fill_transmit_buf(i2c, true);
440 }
cpp
442 static void rk3x_i2c_handle_read(struct rk3x_i2c *i2c, unsigned int ipd)
443 {
444 unsigned int i;
445 unsigned int len = i2c->msg->len - i2c->processed;
446 u32 uninitialized_var(val);
447 u8 byte;
448
449 /* we only care for MBRF here. */
450 if (!(ipd & REG_INT_MBRF))
451 return;
452
453 /* ack interrupt (read also produces a spurious START flag, clear it too) */
454 i2c_writel(i2c, REG_INT_MBRF | REG_INT_START, REG_IPD);
455
456 /* Can only handle a maximum of 32 bytes at a time */
457 if (len > 32)
458 len = 32;
459
460 /* read the data from receive buffer */
461 for (i = 0; i < len; ++i) {
462 if (i % 4 == 0)
463 val = i2c_readl(i2c, RXBUFFER_BASE + (i / 4) * 4);
464
465 byte = (val >> ((i % 4) * 8)) & 0xff;
466 i2c->msg->buf[i2c->processed++] = byte;
467 }
468
469 /* are we finished? */
470 if (i2c->processed == i2c->msg->len)
471 rk3x_i2c_stop(i2c, i2c->error);
472 else
473 rk3x_i2c_prepare_read(i2c);
474 }
cpp
476 static void rk3x_i2c_handle_stop(struct rk3x_i2c *i2c, unsigned int ipd)
477 {
478 unsigned int con;
479
480 if (!(ipd & REG_INT_STOP)) {
481 rk3x_i2c_stop(i2c, -EIO);
482 dev_err(i2c->dev, "unexpected irq in STOP: 0x%x\n", ipd);
483 rk3x_i2c_clean_ipd(i2c);
484 return;
485 }
486
487 /* ack interrupt */
488 i2c_writel(i2c, REG_INT_STOP, REG_IPD);
489
490 /* disable STOP bit */
491 con = i2c_readl(i2c, REG_CON);
492 con &= ~REG_CON_STOP;
493 i2c_writel(i2c, con, REG_CON);
494
495 i2c->busy = false;
496 i2c->state = STATE_IDLE;
497
498 /* signal rk3x_i2c_xfer that we are finished */
499 rk3x_i2c_wake_up(i2c);
500 }
4.4.12 platform_set_drvdata
|------|-------------------------------|-----------------------------------------------------------|
| 函数原型 | static inline void platform_set_drvdata(struct platform_device *pdev, void *data) ||
| 参数 | struct platform_device *pdev | 指向 platform_device 结构体的指针。这个结构体代表一个平台设备,它用于设备的管理和驱动程序的关联。 |
| 参数 | void *data | 指向要存储在设备驱动数据中的任意数据指针 |
| 返回值 | | |
| 功能 | 将 data 指针与 pdev 关联起来。这个数据指针可以在设备的生命周期内通过 platform_get_drvdata 函数检索到。这种机制使得驱动程序能够存储和访问设备特定的数据,而不需要通过全局变量或其他方法管理这些信息 ||
4.4.14 devm_clk_get
|------|---------------------|-------------------------------------------------|
| 函数原型 | struct clk *devm_clk_get(struct device *dev, const char *id) ||
| 参数 | struct device *dev | 指向 device 结构体的指针。这个结构体表示一个设备,它用于管理设备的资源和状态 |
| | const char *id | 一个字符串,表示要获取的时钟的标识符(通常是时钟的名字)。这个标识符用于查找设备所需的具体时钟 |
| 返回值 | struct clk * | 成功:struct clk 失败:错误指针 |
| 功能 | 用于获取设备时钟 ||
4.4.15 clk_prepare
|------|------------------|--------------------------------------------------------|
| 函数原型 | int clk_prepare(struct clk *clk) ||
| 参数 | struct clk *clk | 指向 clk 结构体的指针,这个结构体表示一个时钟源。它通常是通过 clk_get 或其他时钟管理函数获取的 |
| 返回值 | int | 成功:0 失败:负数 |
| 功能 | 用于准备时钟以便它能够被启用。准备过程包括初始化时钟状态,使其处于可以启用的状态。它通常在启用时钟之前调用 ||
4.4.16 PTR_ERR
|------|--------------------------|---|
| 函数原型 | static inline long __must_check PTR_ERR(__force const void *ptr) ||
| 参数 | __force const void *ptr | |
| 返回值 | | |
| 功能 | 用于从错误指针中提取错误码 ||
4.4.17 clk_notifier_register
|------|----------------------------|------------------------------------------------------------------------|
| 函数原型 | int clk_notifier_register(struct clk *clk, struct notifier_block *nb) ||
| 参数 | struct clk *clk | 指向 clk 结构体的指针,这个结构体表示一个时钟源。它通常是通过 clk_get 或其他时钟管理函数获取的 |
| 参数 | struct notifier_block *nb | notifier_block 结构体包含了处理特定事件的回调函数。当系统准备重启时,所有注册的处理程序都会被调用,以执行必要的清理或保存操作 |
| 返回值 | int | 成功:0 失败:负数 |
| 功能 | 用于将一个 notifier_block 结构体(回调函数)注册到指定的时钟上,以便当时钟状态发生变化时通知回调函数。回调函数可以用于处理时钟状态变化,例如时钟启用或禁用。 ||
4.4.18 clk_get_rate
|------|------------------|--------------------------------------------------------|
| 函数原型 | unsigned long clk_get_rate(struct clk *clk) ||
| 参数 | struct clk *clk | 指向 clk 结构体的指针,这个结构体表示一个时钟源。它通常是通过 clk_get 或其他时钟管理函数获取的 |
| 返回值 | unsigned long | 成功:返回时钟的当前频率 失败:负数 |
| 功能 | 用于获取指定时钟的当前频率,以赫兹(Hz)为单位 ||
4.4.19 clk_enable
|------|------------------|--------------------------------------------------------|
| 函数原型 | int clk_enable(struct clk *clk) ||
| 参数 | struct clk *clk | 指向 clk 结构体的指针,这个结构体表示一个时钟源。它通常是通过 clk_get 或其他时钟管理函数获取的 |
| 返回值 | int | 成功:0 失败:负数 |
| 功能 | 启动时钟 ||
4.4.20 spin_lock_irqsave
|------|------|---------------------------|
| 函数原型 | #define spin_lock_irqsave mtx_lock_irqsave #define mtx_lock_irqsave(lock, x) mtx_lock(lock) ||
| 参数 | lock | 指向 mutex 结构体的指针,表示要锁定的互斥锁 |
| 返回值 | int | 成功:0 失败:负数 |
| 功能 | 在获取互斥锁的同时保存和禁用中断 ||
4.5 rk3x_i2c_algorithm函数解析
cpp
1218 static const struct i2c_algorithm rk3x_i2c_algorithm = {
1219 .master_xfer = rk3x_i2c_xfer,
1220 .functionality = rk3x_i2c_func,
1221 };
cpp
static u32 rk3x_i2c_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
}
i2c_transfer 函数最终会调用 I2C 适配器中 i2c_algorithm 里面的 master_xfer 函数。i2c最终就是通过rk3x_i2c_xfer函数来完成与 I2C 设备通信的。
cpp
static int rk3x_i2c_xfer(struct i2c_adapter *adap,
struct i2c_msg *msgs, int num)
{
struct rk3x_i2c *i2c = (struct rk3x_i2c *)adap->algo_data;
unsigned long timeout, flags;
u32 val;
int ret = 0;
int i;
if (i2c->suspended)
return -EACCES;
spin_lock_irqsave(&i2c->lock, flags);
clk_enable(i2c->clk);
clk_enable(i2c->pclk);
i2c->is_last_msg = false;
/*
* Process msgs. We can handle more than one message at once (see
* rk3x_i2c_setup()).
*/
for (i = 0; i < num; i += ret) {
ret = rk3x_i2c_setup(i2c, msgs + i, num - i);
if (ret < 0) {
dev_err(i2c->dev, "rk3x_i2c_setup() failed\n");
break;
}
if (i + ret >= num)
i2c->is_last_msg = true;
rk3x_i2c_start(i2c);
spin_unlock_irqrestore(&i2c->lock, flags);
timeout = wait_event_timeout(i2c->wait, !i2c->busy,
msecs_to_jiffies(WAIT_TIMEOUT));
spin_lock_irqsave(&i2c->lock, flags);
if (timeout == 0) {
dev_err(i2c->dev, "timeout, ipd: 0x%02x, state: %d\n",
i2c_readl(i2c, REG_IPD), i2c->state);
/* Force a STOP condition without interrupt */
rk3x_i2c_disable_irq(i2c);
val = i2c_readl(i2c, REG_CON) & REG_CON_TUNING_MASK;
val |= REG_CON_EN | REG_CON_STOP;
i2c_writel(i2c, val, REG_CON);
i2c->state = STATE_IDLE;
ret = -ETIMEDOUT;
break;
}
if (i2c->error) {
ret = i2c->error;
break;
}
}
rk3x_i2c_disable_irq(i2c);
rk3x_i2c_disable(i2c);
clk_disable(i2c->pclk);
clk_disable(i2c->clk);
spin_unlock_irqrestore(&i2c->lock, flags);
return ret < 0 ? ret : num;
}
启动 I2C 传输
cpp
277 static void rk3x_i2c_start(struct rk3x_i2c *i2c)
278 {
279 u32 val = i2c_readl(i2c, REG_CON) & REG_CON_TUNING_MASK;
280 int length = 0;
281
282 /* enable appropriate interrupts */
283 if (i2c->mode == REG_CON_MOD_TX) {
284 i2c_writel(i2c, REG_INT_MBTF | REG_INT_NAKRCV, REG_IEN);
285 i2c->state = STATE_WRITE;
286 length = rk3x_i2c_fill_transmit_buf(i2c, false);
287 } else {
288 /* in any other case, we are going to be reading. */
289 i2c_writel(i2c, REG_INT_MBRF | REG_INT_NAKRCV, REG_IEN);
290 i2c->state = STATE_READ;
291 }
292
293 /* enable adapter with correct mode, send START condition */
294 val |= REG_CON_EN | REG_CON_MOD(i2c->mode) | REG_CON_START;
295
296 /* if we want to react to NACK, set ACTACK bit */
297 if (!(i2c->msg->flags & I2C_M_IGNORE_NAK))
298 val |= REG_CON_ACTACK;
299
300 i2c_writel(i2c, val, REG_CON);
301
302 /* enable transition */
303 if (i2c->mode == REG_CON_MOD_TX)
304 i2c_writel(i2c, length, REG_MTXCNT);
305 else
306 rk3x_i2c_prepare_read(i2c);
307 }
|------|-----------------------|-------------------------------------------|
| 函数原型 | static inline u32 i2c_readl(struct rk3x_i2c *i2c, unsigned int offset) ||
| 参数 | struct rk3x_i2c *i2c | 表示 I2C 控制器的设备实例。这个结构体通常包含 I2C 控制器的寄存器映射地址 |
| 参数 | unsigned int offset) | 表示要读取的寄存器的偏移量 |
| 返回值 | u32 | 寄存器的值 |
| 功能 | 用于读取 I2C 控制器寄存器的值 ||
cpp
251 static inline u32 i2c_readl(struct rk3x_i2c *i2c, unsigned int offset)
252 {
253 return readl(i2c->regs + offset);
254 }
|------|-----------------------|-------------------------------------------|
| 函数原型 | static inline void i2c_writel(struct rk3x_i2c *i2c, u32 value,, unsigned int offset) ||
| 参数 | struct rk3x_i2c *i2c | 表示 I2C 控制器的设备实例。这个结构体通常包含 I2C 控制器的寄存器映射地址 |
| 参数 | u32 value | 需要写入寄存器的 32 位无符号整数值 |
| 参数 | unsigned int offset) | 表示写的寄存器的偏移量 |
| 返回值 | | |
| 功能 | 用于写 I2C 控制器寄存器的值 ||
cpp
245 static inline void i2c_writel(struct rk3x_i2c *i2c, u32 value,
246 unsigned int offset)
247 {
248 writel(value, i2c->regs + offset);
249 }
5 spirit_mcu.c 驱动解析
5.1 驱动源码
5.2 设备树
cpp
21 / {
201 i2c@c240000{
202 status = "okay";
203
204 spirit_mcu: spirit_mcu@13 {
205 compatible = "spirit_mcu";
206 reg = <0x13>;
207 #clock-cells = <0>;
208 status = "okay";
209 watchdog-feed = <&tegra_main_gpio TEGRA234_MAIN_GPIO(R, 5) GPIO_ACTIVE_LOW>;
210 };
238 };
375 };
5.3 驱动框架
cpp
int mcu_process(struct spirit_mcu *spirit_mcu,struct mcu_req *req,unsigned char *value)
{
int ret = 0;
unsigned int mcu_value = 0,req_value = 0;
unsigned int reg = 0;
req_value = (unsigned int)req->mode;
reg = req->opcode & (~OP_READ_BIT);
switch(reg)
{
case OP_ALARM_RTC:
reg = MCU_RTC_WAKE;
break;
case OP_AUTO_POWERON:
reg = MCU_POWER_LOSS;
break;
case OP_NET_WAKE:
reg = MCU_WOL_WAKE;
break;
case OP_WATCHDOG:
reg = MCU_WDT_CONTROL;
break;
case OP_WATCHDOG_TIME:
reg = MCU_WDT_TIMOUT;
break;
default:
break;
}
if(reg > 0){
ret = regmap_read(spirit_mcu->regmap, reg, &mcu_value);
if (ret) {
dev_err(&spirit_mcu->i2c->dev, "read 0x%x failed\n", reg);
return ret;
}
printk("read reg:%02x : %02x\n",reg,mcu_value);
if((req->opcode & OP_READ_BIT) != 0) req_value = mcu_value;
if(mcu_value != req_value){
ret = regmap_write(spirit_mcu->regmap, reg, req_value);
if (ret) {
dev_err(&spirit_mcu->i2c->dev, "write 0x%x failed\n", reg);
return ret;
}
}
}
*value = (unsigned char)mcu_value;
return ret;
}
static long spirit_mcu_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int ret = 0;
unsigned char value;
struct mcu_req req;
struct DeviceInfomation deviceInfo;
struct spirit_mcu *spirit_mcu = i2c_get_clientdata(mcu_i2c_client);
void __user *argp = (void __user *)arg;
printk("spirit_mcu_ioctl cmd[%d]\n",cmd);
mutex_lock(&spirit_mcu->m_lock);
switch(cmd) {
case MCU_CMD_SYNC:
if (copy_from_user(&req, argp, sizeof(struct mcu_req))) {
printk(KERN_ERR "copy_from_user failed.\n");
ret = -EFAULT;
break;
}
ret = mcu_process(spirit_mcu,&req,&value);
if((req.opcode & OP_READ_BIT) != 0){
req.mode = value;
if (unlikely(copy_to_user(argp, &req, sizeof (struct mcu_req)))) {
printk(KERN_ERR "copy_to_user failed.\n");
ret = -EFAULT;
}
}
break;
case MCU_CMD_DEVICE_INFO:
if (copy_from_user(&deviceInfo, argp, sizeof(struct DeviceInfomation))) {
printk(KERN_ERR "copy_from_user failed.\n");
ret = -EFAULT;
break;
}
deviceInfo = spirit_mcu->deviceInfo;
if (unlikely(copy_to_user(argp, &deviceInfo, sizeof(struct DeviceInfomation)))) {
printk(KERN_ERR "copy_to_user failed.\n");
ret = -EFAULT;
break;
}
break;
case MCU_WDT_FEED_CONTROL:
if (copy_from_user(&req, argp, sizeof(struct mcu_req))) {
printk(KERN_ERR "copy_from_user failed.\n");
ret = -EFAULT;
break;
}
if(req.mode == 1){
spirit_mcu->userfeed = 0;
schedule_delayed_work(&spirit_mcu->work, msecs_to_jiffies(WATCHDOG_FEED_COUNT));
}else {
spirit_mcu->userfeed = 1;
mcu_watchdog_feed(spirit_mcu);
cancel_delayed_work_sync(&spirit_mcu->work);
}
break;
case MCU_WDT_USER_FEED:
if(spirit_mcu->userfeed == 1){
mcu_watchdog_feed(spirit_mcu);
}
break;
default:
break;
}
mutex_unlock(&spirit_mcu->m_lock);
printk("mcu ioctrl end\n");
return ret;
}
const struct file_operations spirit_mcu_operations = {
.owner = THIS_MODULE,
.open = spirit_mcu_open,
.release = spirit_mcu_release,
.unlocked_ioctl = spirit_mcu_ioctl,
};
static struct miscdevice spirit_mcu_misc_driver = {
.minor = MISC_DYNAMIC_MINOR,
.name = "spirit_mcu",
.fops = &spirit_mcu_operations
};
static const struct regmap_config mcu_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = MCU_REG_MAX,
.cache_type = REGCACHE_NONE,
.volatile_reg = mcu_is_volatile_reg,
};
static int spirit_mcu_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret = 0;
struct spirit_mcu *spirit_mcu;
struct device_node *np = client->dev.of_node;
printk("%s: probe\n", __FUNCTION__);
spirit_mcu = devm_kzalloc(&client->dev, sizeof(struct spirit_mcu), GFP_KERNEL);
if (!spirit_mcu)
return -ENOMEM;
spirit_mcu->regmap = devm_regmap_init_i2c(client, &mcu_regmap_config);
if (IS_ERR(spirit_mcu->regmap)) {
dev_err(&client->dev, "regmap initialization failed\n");
return PTR_ERR(spirit_mcu->regmap);
}
spirit_mcu->userfeed = 0;
i2c_set_clientdata(client, spirit_mcu);
spirit_mcu->i2c = client;
spirit_mcu->np = np;
mcu_i2c_client = client;
ret = misc_register(&spirit_mcu_misc_driver);
if(ret)
{
printk(KERN_ERR "register mcu misc device error\n");
goto failed;
}
return 0;
failed:
return ret;
}
static const struct i2c_device_id spirit_mcu_id[] = {
{ "spirit_mcu", 0 },
{ }
};
static struct i2c_driver spirit_mcu_driver = {
.driver = {
.name = "spirit_mcu",
.owner = THIS_MODULE,
},
.probe = spirit_mcu_probe,
.id_table = spirit_mcu_id,
};
static int __init spirit_mcu_init(void)
{
return i2c_add_driver(&spirit_mcu_driver);
}
static void __exit spirit_mcu_exit(void)
{
unregister_reboot_notifier(&mcu_reboot_notifier);
i2c_del_driver(&spirit_mcu_driver);
}
5.4 函数解析
5.4.1 devm_kzalloc
|------|---------------------|-----------------------------------------------------------------------|
| 函数原型 | static inline void *devm_kzalloc(struct device *dev, size_t size, gfp_t gfp) ||
| 参数 | struct device *dev | 指向 struct device 的指针,它代表了要进行内存分配的设备。这个参数允许内核跟踪分配的内存,以便在设备被移除时自动释放这些内存 |
| 参数 | size_t size | 要分配的内存大小(以字节为单位) |
| 参数 | gfp_t gfp | 分配标志(GFP),这些标志控制内存分配的行为,比如是否允许睡眠、内存是从哪个区域分配的等 |
| 返回值 | void * | 指向任意类型数据的指针,因为 devm_kzalloc 可以用来分配任何类型的内存 |
| 功能 | 用于动态分配内存并清零 ||
cpp
709 static inline void *devm_kzalloc(struct device *dev, size_t size, gfp_t gfp)
710 {
711 return devm_kmalloc(dev, size, gfp | __GFP_ZERO);
712 }
cpp
786 void * devm_kmalloc(struct device *dev, size_t size, gfp_t gfp)
787 {
788 struct devres *dr;
789
790 /* use raw alloc_dr for kmalloc caller tracing */
791 dr = alloc_dr(devm_kmalloc_release, size, gfp, dev_to_node(dev));
792 if (unlikely(!dr))
793 return NULL;
794
795 /*
796 * This is named devm_kzalloc_release for historical reasons
797 * The initial implementation did not support kmalloc, only kzalloc
798 */
799 set_node_dbginfo(&dr->node, "devm_kzalloc_release", size);
800 devres_add(dev, dr->data);
801 return dr->data;
802 }
5.4.2 devm_regmap_init_i2c
|------|-------------------------------------|-------------------------------------------------------------------------------|
| 函数原型 | struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config); #define devm_regmap_init_i2c(i2c, config) \ __regmap_lockdep_wrapper(__devm_regmap_init_i2c, #config, \ i2c, config) ||
| 参数 | struct i2c_client *i2c | i2c客户端指针 |
| 参数 | const struct regmap_config *config | regmap_config结构体指针,该结构体包含了初始化寄存器映射的配置信息,如寄存器的大小(8、16、32位)、读写操作的回调函数、寄存器的缓存配置等 |
| 返回值 | struct regmap * | 成功:新创建的regmap结构体指针 失败:错误码 |
| 功能 | 通过i2c接口初始化寄存器映射(regmap) ||
cpp
620 #ifdef CONFIG_LOCKDEP
621 #define __regmap_lockdep_wrapper(fn, name, ...) \
622 ( \
623 ({ \
624 static struct lock_class_key _key; \
625 fn(__VA_ARGS__, &_key, \
626 KBUILD_BASENAME ":" \
627 __stringify(__LINE__) ":" \
628 "(" name ")->lock"); \
629 }) \
630 )
631 #else
632 #define __regmap_lockdep_wrapper(fn, name, ...) fn(__VA_ARGS__, NULL, NULL)
633 #endif
5.4.3 i2c_set_clientdata
|------|-------------------------|--------------------------------------------------------------------------------|
| 函数原型 | static inline void i2c_set_clientdata(struct i2c_client *dev, void *data) ||
| 参数 | struct i2c_client *dev | 指向struct i2c_client结构体的指针。这个结构体包含了I2C客户端设备的所有相关信息,比如设备的地址、适配器(adapter)指针、设备名称等 |
| 参数 | void *data | 指向任意类型数据的指针。这个指针将被与dev指向的I2C客户端设备相关联 |
| 返回值 | | |
| 功能 | 将一个指向数据的指针(void *data)与特定的I2C客户端设备(struct i2c_client *dev)相关联 ||
cpp
361 static inline void i2c_set_clientdata(struct i2c_client *dev, void *data)
362 {
363 dev_set_drvdata(&dev->dev, data);
364 }
cpp
1184 static inline void dev_set_drvdata(struct device *dev, void *data)
1185 {
1186 dev->driver_data = data;
1187 }
5.4.4 i2c_get_clientdata
|------|-------------------------|--------------------------------------------------------------------------------|
| 函数原型 | static inline void *i2c_get_clientdata(const struct i2c_client *dev) ||
| 参数 | struct i2c_client *dev | 指向struct i2c_client结构体的指针。这个结构体包含了I2C客户端设备的所有相关信息,比如设备的地址、适配器(adapter)指针、设备名称等 |
| 返回值 | | |
| 功能 | 获取与特定I2C客户端设备相关联的私有数据 ||
cpp
356 static inline void *i2c_get_clientdata(const struct i2c_client *dev)
357 {
358 return dev_get_drvdata(&dev->dev);
359 }
cpp
1179 static inline void *dev_get_drvdata(const struct device *dev)
1180 {
1181 return dev->driver_data;
1182 }
5.4.5 misc_register
注册一个杂项(misc)设备的函数misc_register的实现。杂项设备是一种特殊的字符设备,它们使用固定的主设备号MISC_MAJOR和可选的静态或动态分配的次设备号。这个函数的主要作用是将一个杂项设备添加到系统中,并为其分配必要的资源。
|------|--------------------------|------------------------------------------------|
| 函数原型 | int misc_register(struct miscdevice *misc) ||
| 参数 | struct miscdevice *misc | 指向要注册的杂项设备结构体的指针。这个结构体包含了设备的次设备号、设备名、文件操作函数等信息 |
| 返回值 | int | |
| 功能 | 注册一个杂项(misc)设备 ||
cpp
173 int misc_register(struct miscdevice *misc)
174 {
175 dev_t dev;
176 int err = 0;
177 bool is_dynamic = (misc->minor == MISC_DYNAMIC_MINOR);
178
179 INIT_LIST_HEAD(&misc->list);
180
181 mutex_lock(&misc_mtx);
182
183 if (is_dynamic) {
184 int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);
185
186 if (i >= DYNAMIC_MINORS) {
187 err = -EBUSY;
188 goto out;
189 }
190 misc->minor = DYNAMIC_MINORS - i - 1;
191 set_bit(i, misc_minors);
192 } else {
193 struct miscdevice *c;
194
195 list_for_each_entry(c, &misc_list, list) {
196 if (c->minor == misc->minor) {
197 err = -EBUSY;
198 goto out;
199 }
200 }
201 }
202
203 dev = MKDEV(MISC_MAJOR, misc->minor);
204
205 misc->this_device =
206 device_create_with_groups(misc_class, misc->parent, dev,
207 misc, misc->groups, "%s", misc->name);
208 if (IS_ERR(misc->this_device)) {
209 if (is_dynamic) {
210 int i = DYNAMIC_MINORS - misc->minor - 1;
211
212 if (i < DYNAMIC_MINORS && i >= 0)
213 clear_bit(i, misc_minors);
214 misc->minor = MISC_DYNAMIC_MINOR;
215 }
216 err = PTR_ERR(misc->this_device);
217 goto out;
218 }
219
220 /*
221 * Add it to the front, so that later devices can "override"
222 * earlier defaults
223 */
224 list_add(&misc->list, &misc_list);
225 out:
226 mutex_unlock(&misc_mtx);
227 return err;
228 }