[linux 驱动]i2c总线设备驱动详解与实战

news2024/11/18 9:25:31

目录

1 描述

2 结构体

2.1 bus_type

2.2 i2c_bus_type

2.2.1 i2c_device_match

2.2.2 i2c_device_probe

2.2.3 i2c_device_remove

2.2.4 i2c_device_shutdown

2.2 i2c_adapter

2.3 i2c_algorithm

2.4 i2c_driver

2.5 i2c_client

3 i2c核心

3.1 注册i2c适配器

3.2 注册i2c设备驱动

3.3 i2c数据传输

3.3.1 i2c_transfer

3.3.2 i2c_master_send

3.3.3 i2c_master_recv

4 rk3399 i2c适配器驱动分析

4.1 设备树

4.2 结构体

4.2.1 rk3x_i2c

4.2.2 rk3x_i2c_soc_data

4.2.3 of_device_id

4.2.4 i2c_timings

4.2.5 platform_device

4.2.6 device

4.3 probe函数

4.4 函数解析

4.4.1 of_match_node

4.4.2 i2c_parse_fw_timings

4.4.3 spin_lock_init

4.4.4 init_waitqueue_head

4.4.5 register_pre_restart_handler

4.4.6 platform_get_resource

4.4.7 devm_ioremap_resource

4.4.8 syscon_regmap_lookup_by_phandle

4.4.9 of_alias_get_id

4.4.10 regmap_write

4.4.11 platform_get_irq

4.4.12 devm_request_irq

4.4.12 platform_set_drvdata

4.4.14 devm_clk_get

4.4.15 clk_prepare

4.4.16 PTR_ERR

4.4.17 clk_notifier_register

4.4.18 clk_get_rate

4.4.19 clk_enable

4.4.20 spin_lock_irqsave

4.5 rk3x_i2c_algorithm函数解析

5 spirit_mcu.c 驱动解析

5.1 驱动源码

5.2 设备树

5.3 驱动框架

5.4 函数解析

5.4.1 devm_kzalloc

5.4.2 devm_regmap_init_i2c

5.4.3 i2c_set_clientdata

5.4.4 i2c_get_clientdata

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 结构体来表示,这个结构体包含了该类型总线所需的所有信息和操作函数。

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

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

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

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设备。

 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 }
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设备。

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 设备进行通信。

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协议支持以及适配器功能性的查询等。

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从设备。

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对应于一套驱动方法,是用于辅助作用的数据结构,不对应于任何物理实体

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核心的中断映射。这主要用于与硬件或平台特定功能的兼容性。

 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。

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 适配器

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 适配器

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 设备,前提是这些设备与该驱动兼容。

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 设备驱动

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()函数真正驱动硬件流程。

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 }
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 从设备发送数据

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 从设备接收数据到主机

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 设备树

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

 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

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

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 总线的时序参数

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

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

        用于表示设备的结构体

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适配器初始化工作

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;
}
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 指针

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 };
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 结构体中

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

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 }
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,以便在系统重启之前被调用

54 struct notifier_block {
 55         notifier_fn_t notifier_call;
 56         struct notifier_block __rcu *next;
 57         int priority;
 58 };
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 等)

 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

功能

于在内核中映射一个设备资源到虚拟地址空间,并且自动管理这个映射的生命周期

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 失败:负数

功能

用于从设备树中获取设备别名的函数

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 失败负数

功能

请求中断

 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 }
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 }
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 }
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函数解析

1218 static const struct i2c_algorithm rk3x_i2c_algorithm = {
1219         .master_xfer            = rk3x_i2c_xfer,
1220         .functionality          = rk3x_i2c_func,
1221 };
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 设备通信的。

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 传输

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 控制器寄存器的值

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 控制器寄存器的值

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 驱动源码

spirit_mcu.c

spirit_mcu.h

5.2 设备树

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 驱动框架

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 可以用来分配任何类型的内存

功能

用于动态分配内存并清零

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 }
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)

 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)相关联

361 static inline void i2c_set_clientdata(struct i2c_client *dev, void *data)
362 {
363         dev_set_drvdata(&dev->dev, data);
364 }
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客户端设备相关联的私有数据

356 static inline void *i2c_get_clientdata(const struct i2c_client *dev)
357 {
358         return dev_get_drvdata(&dev->dev);
359 }
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)设备

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 }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2133084.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

windows电脑怎么录屏?电脑录屏全攻略,轻松捕捉精彩瞬间

在数字化时代&#xff0c;屏幕录制已成为我们日常生活和工作中不可或缺的一部分。无论是记录游戏的高光时刻、制作教学视频&#xff0c;还是保存重要的在线会议内容&#xff0c;Windows电脑都为我们提供了多种高效便捷的录屏方式&#xff0c;如果你还不知道怎么录屏&#xff0c…

什么是领域驱动设计?

什么是领域驱动设计&#xff1f; 领域驱动设计&#xff08;Domain-Driven Design&#xff0c;简称DDD&#xff09;是一种面向对象的软件开发方法&#xff0c;它强调将软件系统的设计和实现过程与业务领域紧密结合&#xff0c;通过深入理解和建模业务领域&#xff0c;从而实现高…

抓包工具检测手把手教学 - 某招聘网站

大家好&#xff0c;我是南枫~~~ 先问大家一个问题&#xff0c;你们有没有遇到过想爬一个网站&#xff0c;想用老方法&#xff0c;直接右键打开抓包工具&#xff0c;此时&#xff0c;突然&#xff01;整个页面都变得空白&#xff0c;什么数据都没有了的情况…… 如果你没遇到过…

探索MySQL视图的无限可能:优化查询、增强数据安全与简化数据访问

作者简介&#xff1a;我是团团儿&#xff0c;是一名专注于云计算领域的专业创作者&#xff0c;感谢大家的关注 座右铭&#xff1a; 云端筑梦&#xff0c;数据为翼&#xff0c;探索无限可能&#xff0c;引领云计算新纪元 个人主页&#xff1a;团儿.-CSDN博客 目录 前言&#…

OpenAI发布o1预览模型:推理能力更强可达理科博士生水准

近日OpenAI宣布推出了新一代 AI 模型系列 OpenAI o1&#xff0c;按照官方技术博客说法&#xff0c;o1 在推理能力上代表了人工智能最强的水平。 那究竟是怎么一回事呢&#xff1f; OpenAI CEO Sam Altman 表示&#xff1a;o1 系列的推出代表了 AI 能力的新起点&#xff0c;能…

用Python设置PDF中图片的透明度

在PDF文档的设计与内容创作过程中&#xff0c;图像的透明度设置是一个重要的操作。尤其是在处理图文密集型PDF文档时&#xff0c;设置适当的图片透明度能够极大地提升视觉表达的层次感与专业性。设置PDF图像的透明度能够让图像更好地融入背景&#xff0c;实现平滑过渡的效果&am…

PAT甲级-1028 List Sorting

题目 题目大意 输入给出学生数目和C值&#xff0c;以及每个学生的信息。要求按照C值对学生信息作出不同的排序&#xff0c;如果C为1&#xff0c;就将学号升序排列&#xff1b;如果C为2&#xff0c;将学生姓名非递减排序&#xff1b;如果C为3&#xff0c;将学生分数非递减排序。…

SldWorks问题1: 在装配体中获取零件的面

问题 我为零件的某个面进行了命名&#xff0c;以此查找&#xff0c;进行配合。 在使用先前写好的查找方法时&#xff0c;有时会出现找不到的情况。 然后捣鼓了半天&#xff0c;发现&#xff1a; 只有当“零件文档显示的配置&#xff0c;和已放置的零件配置一致”时&#xff…

《C++PrimerPlus》第10章:类和对象

文章目录 文章介绍目录重点知识10.2 抽象和类10.3 类的构造函数和析构函数10.4 this指针10.5 对象数组10.6 类作用域 文章介绍 目录 重点知识 10.2 抽象和类 类开发人员&#xff1a;设计类 类调用人员&#xff1a;使用类 10.3 类的构造函数和析构函数 10.4 this指针 10.5 对…

微服务杂谈

几个概念 还是第一次听说Spring Cloud Alibaba &#xff0c;真是孤陋寡闻了&#xff0c;以前只知道 SpringCloud 是为了搭建微服务的&#xff0c;spring boot 则是快速创建一个项目&#xff0c;也可以是一个微服务 。那么SpringCloud 和 Spring boot 有什么区别呢&#xff1f;S…

dirty pages , swapiness 查看SWAP占用进程

文章说了这么多的意思 就是不要过度分配不用的内存。虽然脏块不会写入swap&#xff0c;但是占了物理内存&#xff0c;浪费空间&#xff0c;可能导致进行了很多不必要的交换&#xff08;虽然判断很少要进swap&#xff0c;判断要不要也要时间。。。&#xff09;。 To verify whic…

(机器学习必看视频)机器学习-吴恩达笔记汇总

最近将吴恩达老师在网易课程上的机器学习视频看了第二遍&#xff0c;同时整理了一下笔记&#xff0c;仅供学习实用&#xff0c;也放到了Github。主要是参考了下面几位大佬的书籍和作品&#xff0c;表示感谢&#xff01; 李航《统计学习方法》周志华 《机器学习》黄海广博士 ima…

NAS黑群晖7.21折腾笔记

黑群晖引导制作 https://post.smzdm.com/p/a96d62xe/ 黑群晖基本使用教程 https://www.bilibili.com/video/BV1A3411f7WK/?spm_id_from333.337.search-card.all.click 重点&#xff1a; 1&#xff0c;存储管理器 --创建存储池 RAID类型选择&#xff1a; 2&#xff0c…

【2024.08】图模互补:知识图谱与大模型融合综述-笔记

阅读目的&#xff1a;假设已有一个知识图谱&#xff0c;如何利用图谱增强模型的问答&#xff0c;如何检索知识图谱、知识图谱与模型的文本如何相互交互、如何利用知识图谱增强模型回答的可解释性。 从综述中抽取感兴趣的论文进一步阅读。 来源&#xff1a;图模互补&#xff1…

天下苦英伟达久矣!PyTorch官方免CUDA加速推理,Triton时代要来?

在做大语言模型(LLM)的训练、微调和推理时,使用英伟达的 GPU 和 CUDA 是常见的做法。在更大的机器学习编程与计算范畴,同样严重依赖 CUDA,使用它加速的机器学习模型可以实现更大的性能提升。 虽然 CUDA 在加速计算领域占据主导地位,并成为英伟达重要的护城河之一。但其他…

AV1 Bitstream Decoding Process Specification--[4]:语法结构

原文地址&#xff1a;https://aomediacodec.github.io/av1-spec/av1-spec.pdf没有梯子的下载地址&#xff1a;AV1 Bitstream & Decoding Process Specification摘要&#xff1a;这份文档定义了开放媒体联盟&#xff08;Alliance for Open Media&#xff09;AV1视频编解码器…

动态规划:汉诺塔问题|循环汉诺塔

目录 1. 汉诺塔游戏简介 2.算法原理 3.循环汉诺塔 1. 汉诺塔游戏简介 汉诺塔游戏是一个经典的数学智力游戏&#xff0c;其目标是将塔上不同大小的圆盘全部移动到另一个塔上&#xff0c;且在移动过程中必须遵守以下规则&#xff1a; 每次只能移动一个圆盘较大的圆盘不能放在…

linux cmake版本升级教程(Centos7)

有时候,当前系统的cmake版本,并一定能满足编译要求,所以需要进行升级到高于某个版本才能正常编译。本章教程,主要在centos7上进行升级cmake版本。 一、查看当前的cmake版本 cmake --version二、下载指定版本的cmake wget https://github.com/Kitware/CMake/releases/down…

2.2 vc-align源码分析 -- ant-design-vue系列

vc-align源码分析 源码地址&#xff1a;https://github.com/vueComponent/ant-design-vue/tree/main/components/vc-align 1 基础代码 1.1 名词约定 需要对齐的节点叫source&#xff0c;对齐的目标叫target。 1.2 props 提供了两个参数&#xff1a; align&#xff1a;对…

华为ensp中vlan与静态路由技术的实现

vlan 同一网段的设备&#xff0c;可以互通&#xff1b; 虚拟局域网&#xff1a;将局域网从逻辑上划分为多个局域网&#xff0c;不同通过vlan编号区分&#xff1b; 实现网络隔离。提高了网络安全性&#xff1b; vlan编号为12位&#xff1b; 范围1-4094可以用来配置 默认处于…