当前位置: 首页 > news >正文

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

目录

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 OF361          * 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                 else384                         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         else410                 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 whether526                  * IGNORE_NAK is set, we have to stop the operation and report527                  * 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         else439                 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         else473                 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         else306                 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 reasons797          * The initial implementation did not support kmalloc, only kzalloc798          */           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_LOCKDEP621 #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 #else632 #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 }

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 揭开Facebook AI的神秘面纱:如何利用人工智能提升社交体验
  • C++ | Leetcode C++题解之第395题至少有K个重复字符的最长子串
  • 还能买燃油车吗
  • Pygame中Sprite类实现多帧动画3-1
  • 一个例子彻底搞懂对线程模型的理解 !
  • 【Puppeteer】‘left‘ is already pressed, ‘${button}‘ is already pressed 的解决办法
  • Qt常用控件——QRadioButton和QCheckBox
  • 【VSCode v1.93.0】手动配置远程remote-ssh
  • 开源可视化大屏superset Docker环境部署
  • 计算机网络练级第一级————认识网络
  • VSTO常见的异常
  • 【自然语言处理】实验一:基于NLP工具的中文分词
  • 7.1图像平移
  • 安科瑞Acrel-1000DP分布式光伏监控系统平台的设计与应用-安科瑞 蒋静
  • 哈希表、算法
  • Google 是如何开发 Web 框架的
  • 【React系列】如何构建React应用程序
  • 【编码】-360实习笔试编程题(二)-2016.03.29
  • 4个实用的微服务测试策略
  • iOS 颜色设置看我就够了
  • JavaScript 无符号位移运算符 三个大于号 的使用方法
  • k8s如何管理Pod
  • laravel with 查询列表限制条数
  • Magento 1.x 中文订单打印乱码
  • VirtualBox 安装过程中出现 Running VMs found 错误的解决过程
  • windows下使用nginx调试简介
  • 快速体验 Sentinel 集群限流功能,只需简单几步
  • 面试遇到的一些题
  • 微服务入门【系列视频课程】
  • 一个6年java程序员的工作感悟,写给还在迷茫的你
  • 阿里云重庆大学大数据训练营落地分享
  • 积累各种好的链接
  • 移动端高清、多屏适配方案
  • ​Base64转换成图片,android studio build乱码,找不到okio.ByteString接腾讯人脸识别
  • ​什么是bug?bug的源头在哪里?
  • ​数据结构之初始二叉树(3)
  • !!【OpenCV学习】计算两幅图像的重叠区域
  • #NOIP 2014# day.1 T3 飞扬的小鸟 bird
  • #我与Java虚拟机的故事#连载09:面试大厂逃不过的JVM
  • (4)logging(日志模块)
  • (Matalb回归预测)PSO-BP粒子群算法优化BP神经网络的多维回归预测
  • (Note)C++中的继承方式
  • (定时器/计数器)中断系统(详解与使用)
  • (附源码)c#+winform实现远程开机(广域网可用)
  • (附源码)spring boot儿童教育管理系统 毕业设计 281442
  • (附源码)ssm考试题库管理系统 毕业设计 069043
  • (免费领源码)python#django#mysql校园校园宿舍管理系统84831-计算机毕业设计项目选题推荐
  • ./和../以及/和~之间的区别
  • .net core Redis 使用有序集合实现延迟队列
  • .NET Micro Framework初体验
  • .net websocket 获取http登录的用户_如何解密浏览器的登录密码?获取浏览器内用户信息?...
  • .Net 路由处理厉害了
  • .net 微服务 服务保护 自动重试 Polly
  • .net开发日常笔记(持续更新)
  • .NET平台开源项目速览(15)文档数据库RavenDB-介绍与初体验