最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Linux Pinctrl子系統(tǒng)使用及說(shuō)明

 更新時(shí)間:2026年04月13日 15:59:19   作者:su_ym8110  
這篇文章主要介紹了Linux Pinctrl子系統(tǒng)使用及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

第一部分:核心概念與工作機(jī)制

1. Pinctrl 子系統(tǒng)概述

Pinctrl(Pin Control)子系統(tǒng)是Linux內(nèi)核中用于管理和配置SoC引腳的標(biāo)準(zhǔn)框架。

它解決了傳統(tǒng)直接操作寄存器配置引腳復(fù)用和電氣特性所帶來(lái)的繁瑣、易錯(cuò)、功能沖突等問(wèn)題。

1.1 主要功能

  • 引腳復(fù)用(Pin Muxing):將同一物理引腳切換為不同的功能(如GPIO、I2C、UART、SPI等)。
  • 引腳配置(Pin Configuration):設(shè)置引腳的電氣特性,包括上拉/下拉、驅(qū)動(dòng)能力、開(kāi)漏、壓擺率等。
  • 引腳分組(Pin Groups):將一組引腳定義為邏輯組,方便整體配置。
  • 狀態(tài)管理(State Management):為設(shè)備定義多個(gè)引腳狀態(tài)(如default、sleepidle),并在運(yùn)行時(shí)動(dòng)態(tài)切換。

1.2 架構(gòu)分層

Pinctrl子系統(tǒng)采用服務(wù)端(Provider)— 客戶端(Consumer) 模型:

  • 服務(wù)端(Provider):由芯片廠商實(shí)現(xiàn),負(fù)責(zé)描述SoC中所有引腳、引腳組、可復(fù)用功能以及硬件操作函數(shù)(struct pinctrl_desc + ops)。
  • 客戶端(Consumer):由設(shè)備驅(qū)動(dòng)編寫(xiě)者使用,通過(guò)在設(shè)備樹(shù)中聲明所需引腳狀態(tài),讓Pinctrl子系統(tǒng)自動(dòng)完成配置。

下圖展示了子系統(tǒng)內(nèi)部模塊間的協(xié)作關(guān)系:

2. 核心數(shù)據(jù)結(jié)構(gòu)

Pinctrl子系統(tǒng)通過(guò)一系列精心設(shè)計(jì)的數(shù)據(jù)結(jié)構(gòu),將服務(wù)端能力、客戶端需求以及狀態(tài)配置連接起來(lái)。

2.1 服務(wù)端核心結(jié)構(gòu)

struct pinctrl_desc—— 引腳控制器的描述符

每個(gè)Pinctrl控制器驅(qū)動(dòng)都必須提供一個(gè)pinctrl_desc實(shí)例,用于向內(nèi)核描述該控制器的資源與操作能力。

struct pinctrl_desc {
    const char *name;                      // 控制器名稱
    const struct pinctrl_pin_desc *pins;   // 引腳數(shù)組
    unsigned int npins;                    // 引腳數(shù)量
    const struct pinctrl_ops *pctlops;     // 全局控制操作
    const struct pinmux_ops *pmxops;       // 復(fù)用操作
    const struct pinconf_ops *confops;     // 配置操作
    struct module *owner;
    // ... 其他參數(shù)
};

三大操作集(ops)是驅(qū)動(dòng)必須實(shí)現(xiàn)的核心函數(shù)表:

  • pinctrl_ops:提供引腳組信息、設(shè)備樹(shù)節(jié)點(diǎn)到映射的轉(zhuǎn)換(dt_node_to_map)等。
  • pinmux_ops:提供復(fù)用功能數(shù)量、名稱、組以及set_mux回調(diào)(最終寫(xiě)寄存器)。
  • pinconf_ops:提供引腳或引腳組的電氣特性讀取/設(shè)置(如pin_config_set)。
struct pinctrl_dev—— 控制器實(shí)例

內(nèi)核通過(guò)pinctrl_register()pinctrl_desc注冊(cè)為一個(gè)pinctrl_dev對(duì)象,并掛載到全局鏈表中。

2.2 客戶端核心結(jié)構(gòu)

struct dev_pin_info—— 掛載在struct device中的引腳信息

每個(gè)設(shè)備(struct device)如果啟用了CONFIG_PINCTRL,都會(huì)包含一個(gè)dev_pin_info指針,記錄該設(shè)備所使用的Pinctrl句柄及各種狀態(tài)。

struct dev_pin_info {
    struct pinctrl *p;                     // 該設(shè)備的pinctrl句柄
    struct pinctrl_state *default_state;   // 默認(rèn)狀態(tài)(如“default”)
    struct pinctrl_state *init_state;      // 初始化狀態(tài)(可選)
#ifdef CONFIG_PM
    struct pinctrl_state *sleep_state;     // 睡眠狀態(tài)
    struct pinctrl_state *idle_state;      // 空閑狀態(tài)
#endif
};
struct pinctrl—— 客戶端句柄

代表一個(gè)設(shè)備獲取到的完整Pinctrl資源,內(nèi)部包含了該設(shè)備所有可能的狀態(tài)(states鏈表)以及從設(shè)備樹(shù)解析出的映射表(dt_maps)。

struct pinctrl {
    struct list_head node;
    struct device *dev;
    struct list_head states;               // 該設(shè)備的所有狀態(tài)
    struct pinctrl_state *state;           // 當(dāng)前激活的狀態(tài)
    struct list_head dt_maps;
    struct kref users;
};
struct pinctrl_state與struct pinctrl_setting
  • 每個(gè)狀態(tài)(如“default”)對(duì)應(yīng)一個(gè)pinctrl_state對(duì)象,內(nèi)部settings鏈表包含多個(gè)pinctrl_setting
  • pinctrl_setting表示一個(gè)具體的配置項(xiàng):要么是復(fù)用設(shè)置(哪個(gè)group切換到哪個(gè)function),要么是配置設(shè)置(某個(gè)引腳的上下拉/驅(qū)動(dòng)能力)。
struct pinctrl_setting {
    struct list_head node;
    enum pinctrl_map_type type;            // MUX_GROUP 或 CONFIGS_PIN/CONFIGS_GROUP
    struct pinctrl_dev *pctldev;
    union {
        struct pinctrl_setting_mux mux;    // 復(fù)用信息:group + func
        struct pinctrl_setting_configs configs; // 配置信息:引腳/組 + 配置值數(shù)組
    } data;
};

3. 設(shè)備樹(shù)描述與解析

3.1 服務(wù)端設(shè)備樹(shù)節(jié)點(diǎn)

芯片廠商會(huì)在.dtsi中定義Pinctrl控制器節(jié)點(diǎn),例如rk3568.dtsi

pinctrl: pinctrl {
    compatible = "rockchip,rk3568-pinctrl";
    rockchip,grf = <&grf>;
    rockchip,pmu = <&pmugrf>;
    #address-cells = <2>;
    #size-cells = <2>;
    ranges;

    gpio0: gpio@fdd60000 {
        compatible = "rockchip,gpio-bank";
        reg = <0x0 0xfdd60000 0x0 0x100>;
        interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
        gpio-controller;
        #gpio-cells = <2>;
        gpio-ranges = <&pinctrl 0 0 32>;
    };
    // ... 其他 GPIO 組
};

同時(shí),在rk3568-pinctrl.dtsi中定義了所有可用的引腳功能組(groups)和配置(pinconf),例如I2C3的兩種引腳組:

i2c3m0_xfer: i2c3m0-xfer {
    rockchip,pins =
        <1 RK_PA1 1 &pcfg_pull_none_smt>,   // i2c3_sclm0
        <1 RK_PA0 1 &pcfg_pull_none_smt>;   // i2c3_sdam0
};

i2c3m1_xfer: i2c3m1-xfer {
    rockchip,pins =
        <3 RK_PB5 4 &pcfg_pull_none_smt>,
        <3 RK_PB6 4 &pcfg_pull_none_smt>;
};

每個(gè)rockchip,pins屬性包含四個(gè)字段:

  • PIN_BANK:GPIO組(0~4)
  • PIN_BANK_IDX:組內(nèi)引腳索引(如RK_PA0=0,RK_PC0=16)
  • MUX:復(fù)用值(0表示GPIO,1表示功能1,依芯片手冊(cè))
  • phandle:指向引腳配置節(jié)點(diǎn)(如&pcfg_pull_none_smt),定義上下拉、驅(qū)動(dòng)能力等。

3.2 客戶端設(shè)備樹(shù)節(jié)點(diǎn)

外設(shè)驅(qū)動(dòng)在它的節(jié)點(diǎn)中通過(guò)pinctrl-*屬性聲明所需引腳狀態(tài)。例如一個(gè)I2C控制器:

&i2c3 {
    pinctrl-names = "default", "sleep";
    pinctrl-0 = <&i2c3m0_xfer>;
    pinctrl-1 = <&i2c3m1_xfer>;
    status = "okay";
};
  • pinctrl-names:定義狀態(tài)名稱列表,內(nèi)核預(yù)定義了PINCTRL_STATE_DEFAULT(“default”)、PINCTRL_STATE_SLEEP(“sleep”)等,也可以自定義。
  • pinctrl-0:對(duì)應(yīng)第一個(gè)狀態(tài)(“default”)的引腳組引用,可以是單個(gè)或多個(gè)phandle。
  • pinctrl-1:對(duì)應(yīng)第二個(gè)狀態(tài)(“sleep”)的引腳組。

3.3 從設(shè)備樹(shù)到內(nèi)部映射

當(dāng)內(nèi)核解析客戶端設(shè)備樹(shù)時(shí),會(huì)為設(shè)備創(chuàng)建pinctrl句柄,并調(diào)用服務(wù)端驅(qū)動(dòng)的dt_node_to_map回調(diào),將設(shè)備樹(shù)描述轉(zhuǎn)換為pinctrl_map數(shù)組。

以Rockchip驅(qū)動(dòng)為例,rockchip_dt_node_to_map會(huì)做兩件事:

根據(jù)設(shè)備樹(shù)節(jié)點(diǎn)名找到對(duì)應(yīng)的rockchip_pin_group。

創(chuàng)建兩個(gè)映射條目:

  • 第一個(gè)是PIN_MAP_TYPE_MUX_GROUP,記錄function名稱(取自父節(jié)點(diǎn)名)和group名稱(取自節(jié)點(diǎn)名)。
  • 隨后為組內(nèi)每個(gè)引腳創(chuàng)建PIN_MAP_TYPE_CONFIGS_PIN條目,記錄引腳名和從pcfg_xxx解析出的配置值。
static int rockchip_dt_node_to_map(...)
{
    // 找到 group
    grp = pinctrl_name_to_group(info, np->name);
    // 分配 new_map,數(shù)量為 1(復(fù)用) + npins(配置)
    new_map[0].type = PIN_MAP_TYPE_MUX_GROUP;
    new_map[0].data.mux.function = parent->name;
    new_map[0].data.mux.group = np->name;
    // 填充每個(gè)引腳的配置 map
    for (i = 0; i < grp->npins; i++) {
        new_map[i+1].type = PIN_MAP_TYPE_CONFIGS_PIN;
        new_map[i+1].data.configs.configs = grp->data[i].configs;
        ...
    }
}

這些pinctrl_map隨后會(huì)被add_setting轉(zhuǎn)換為pinctrl_setting,并掛載到對(duì)應(yīng)狀態(tài)的settings鏈表中,供后續(xù)激活使用。

4. 控制器驅(qū)動(dòng)初始化流程

以Rockchip pinctrl驅(qū)動(dòng)(pinctrl-rockchip.c)為例,展示一個(gè)Pinctrl服務(wù)端是如何初始化的。

4.1 驅(qū)動(dòng)入口

static struct platform_driver rockchip_pinctrl_driver = {
    .probe = rockchip_pinctrl_probe,
    .driver = {
        .name = "rockchip-pinctrl",
        .of_match_table = rockchip_pinctrl_dt_match,
    },
};
postcore_initcall(rockchip_pinctrl_drv_register);

驅(qū)動(dòng)使用postcore_initcall,在系統(tǒng)初始化早期完成注冊(cè),確保其他設(shè)備probe時(shí)Pinctrl已經(jīng)可用。

4.2 probe函數(shù)核心步驟

rockchip_pinctrl_probe完成以下關(guān)鍵工作:

分配私有數(shù)據(jù)結(jié)構(gòu)struct rockchip_pinctrl *info

獲取硬件資源regmap(通過(guò)syscon_node_to_regmapdevm_ioremap_resource

解析SOC數(shù)據(jù)rockchip_pinctrl_get_soc_data,填充rockchip_pin_ctrl

注冊(cè)Pinctrl控制器:調(diào)用rockchip_pinctrl_register,該函數(shù)內(nèi)部:

  • 填充pinctrl_desc的各個(gè)ops(rockchip_pctrl_ops, rockchip_pmx_ops, rockchip_pinconf_ops
  • 調(diào)用devm_pinctrl_register -> pinctrl_init_controller -> 創(chuàng)建pinctrl_dev

注冊(cè)GPIO子設(shè)備of_platform_populate,讓每個(gè)GPIO bank作為一個(gè)獨(dú)立平臺(tái)設(shè)備被probe。

static int rockchip_pinctrl_probe(struct platform_device *pdev)
{
    struct rockchip_pinctrl *info;
    // ... 分配內(nèi)存、獲取regmap
    ctrl = rockchip_pinctrl_get_soc_data(info, pdev);
    info->ctrl = ctrl;
    ret = rockchip_pinctrl_register(pdev, info);
    ret = of_platform_populate(np, rockchip_bank_match, NULL, NULL);
    return 0;
}

4.3pinctrl_init_controller內(nèi)部初始化

該函數(shù)分配struct pinctrl_dev,初始化radix tree(用于快速查找引腳描述符、組、函數(shù)),并調(diào)用pinctrl_check_ops驗(yàn)證驅(qū)動(dòng)是否提供了必要的回調(diào)函數(shù)。

5. 客戶端引腳自動(dòng)綁定流程(核心機(jī)制)

這是Pinctrl最核心的自動(dòng)化機(jī)制:在設(shè)備驅(qū)動(dòng)自身的probe函數(shù)被調(diào)用之前,內(nèi)核已經(jīng)根據(jù)設(shè)備樹(shù)完成了該設(shè)備所有引腳的復(fù)用和配置。

5.1 調(diào)用鏈

當(dāng)設(shè)備和驅(qū)動(dòng)匹配成功后,內(nèi)核的drivers/base/dd.c中的really_probe函數(shù)會(huì)執(zhí)行:

static int really_probe(struct device *dev, struct device_driver *drv)
{
    // ... 
    ret = pinctrl_bind_pins(dev);    // 關(guān)鍵!綁定并配置引腳
    if (ret)
        goto pinctrl_bind_failed;
    // ... 然后才調(diào)用驅(qū)動(dòng)的probe
    ret = drv->probe(dev);
}

5.2pinctrl_bind_pins實(shí)現(xiàn)

pinctrl_bind_pins位于drivers/base/pinctrl.c,它執(zhí)行以下步驟:

  • 調(diào)用devm_pinctrl_get(dev)獲取設(shè)備的pinctrl句柄(如果尚未獲?。?/li>
  • 查找PINCTRL_STATE_DEFAULTPINCTRL_STATE_INIT狀態(tài)。
  • 優(yōu)先選擇init狀態(tài)(若存在),否則選擇default狀態(tài),然后調(diào)用pinctrl_select_state激活該狀態(tài)。
int pinctrl_bind_pins(struct device *dev)
{
    dev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL);
    dev->pins->p = devm_pinctrl_get(dev);
    dev->pins->default_state = pinctrl_lookup_state(dev->pins->p, PINCTRL_STATE_DEFAULT);
    dev->pins->init_state = pinctrl_lookup_state(dev->pins->p, PINCTRL_STATE_INIT);
    if (!IS_ERR(dev->pins->init_state))
        ret = pinctrl_select_state(dev->pins->p, dev->pins->init_state);
    else
        ret = pinctrl_select_state(dev->pins->p, dev->pins->default_state);
    // ...
}

5.3pinctrl_select_state激活硬件配置

pinctrl_select_state會(huì)遍歷目標(biāo)狀態(tài)下的所有pinctrl_setting,對(duì)每個(gè)setting:

  • 若是MUX_GROUP類型,調(diào)用pinmux_enable_setting,最終通過(guò)驅(qū)動(dòng)提供的set_mux回調(diào)寫(xiě)入復(fù)用寄存器。
  • 若是CONFIGS_GROUP或CONFIGS_PIN類型,調(diào)用pinconf_apply_setting,通過(guò)pin_config_set回調(diào)寫(xiě)入配置寄存器。

下圖展示了從設(shè)備樹(shù)到硬件配置完成的完整流程:

6. 動(dòng)態(tài)API使用(在驅(qū)動(dòng)中主動(dòng)切換狀態(tài))

除了自動(dòng)綁定,驅(qū)動(dòng)還可以在運(yùn)行時(shí)主動(dòng)切換引腳狀態(tài),常用于電源管理(suspend/resume)。

主要API函數(shù)如下:

函數(shù)作用
devm_pinctrl_get(dev) / pinctrl_get(dev)獲取設(shè)備的pinctrl句柄
pinctrl_put(p)釋放句柄(通常用devm_版本自動(dòng)管理)
pinctrl_lookup_state(p, name)根據(jù)名稱(如“sleep”)查找狀態(tài)對(duì)象
pinctrl_select_state(p, state)將指定狀態(tài)應(yīng)用到硬件

典型用法示例(攝像頭驅(qū)動(dòng)suspend時(shí)切換為sleep狀態(tài)):

static int ov5670_suspend(struct device *dev)
{
    struct ov5670 *ov5670 = dev_get_drvdata(dev);
    if (!IS_ERR_OR_NULL(ov5670->pins_sleep)) {
        pinctrl_select_state(ov5670->pinctrl, ov5670->pins_sleep);
    }
    return 0;
}

static int ov5670_resume(struct device *dev)
{
    struct ov5670 *ov5670 = dev_get_drvdata(dev);
    if (!IS_ERR_OR_NULL(ov5670->pins_default)) {
        pinctrl_select_state(ov5670->pinctrl, ov5670->pins_default);
    }
    return 0;
}

驅(qū)動(dòng)通常在probe中獲取句柄和狀態(tài):

ov5670->pinctrl = devm_pinctrl_get(dev);
ov5670->pins_default = pinctrl_lookup_state(ov5670->pinctrl, "default");
ov5670->pins_sleep = pinctrl_lookup_state(ov5670->pinctrl, "sleep");

7. 調(diào)試方法(Debugfs)

Pinctrl子系統(tǒng)提供了豐富的調(diào)試接口,位于/sys/kernel/debug/pinctrl/下,對(duì)排查引腳沖突、配置錯(cuò)誤非常有用。

文件內(nèi)容說(shuō)明
pinctrl-devices列出所有已注冊(cè)的Pinctrl控制器,顯示是否支持pinmux/pinconf
pinctrl-handles最重要:顯示每個(gè)客戶端設(shè)備當(dāng)前請(qǐng)求的引腳狀態(tài)、每個(gè)狀態(tài)下的復(fù)用組和配置詳情??梢郧宄吹侥硞€(gè)引腳被哪個(gè)設(shè)備占用。
<controller>/pins該控制器下所有引腳的列表(如pin 0 (GPIO_0))
<controller>/pingroups該控制器定義的引腳組及其包含的引腳
<controller>/pinmux-functions每個(gè)功能(function)對(duì)應(yīng)哪些引腳組(groups)
<controller>/pinconf-groups每個(gè)引腳組的電氣配置參數(shù)(上下拉、驅(qū)動(dòng)能力等)

示例輸出片段pinctrl-handles):

device: 78b6000.i2c
  state: i2c_active
    type: MUX_GROUP controller 1000000.pinctrl group: gpio6 (6) function: blsp_i2c2 (12)
    type: CONFIGS_GROUP controller 1000000.pinctrl group gpio6 (6) 00000000 00020009
  state: i2c_sleep
    type: MUX_GROUP controller 1000000.pinctrl group: gpio6 (6) function: gpio (1)
    type: CONFIGS_GROUP controller 1000000.pinctrl group gpio6 (6) 00010004 00020009

通過(guò)分析pinctrl-handles可以快速定位:

  • 哪個(gè)設(shè)備占用了哪個(gè)引腳組
  • 該引腳組當(dāng)前處于active還是sleep狀態(tài)
  • 復(fù)用功能是否正確(例如期望I2C,實(shí)際卻配置為GPIO)

總結(jié):

Linux Pinctrl子系統(tǒng)通過(guò)標(biāo)準(zhǔn)化的服務(wù)端/客戶端模型,將引腳配置從硬件細(xì)節(jié)中抽象出來(lái)。其核心工作流程可概括為:

  1. 芯片廠商實(shí)現(xiàn)pinctrl_desc + ops,并注冊(cè)控制器。
  2. 設(shè)備樹(shù)編寫(xiě)者在客戶端節(jié)點(diǎn)中聲明pinctrl-namespinctrl-0/1/...。
  3. 內(nèi)核Pinctrl Core在設(shè)備probe前自動(dòng)解析設(shè)備樹(shù),調(diào)用驅(qū)動(dòng)回調(diào)完成硬件配置。
  4. 驅(qū)動(dòng)開(kāi)發(fā)者也可以在運(yùn)行時(shí)調(diào)用pinctrl_select_state動(dòng)態(tài)切換狀態(tài)(如電源管理)。

這種設(shè)計(jì)極大簡(jiǎn)化了驅(qū)動(dòng)開(kāi)發(fā),避免了引腳配置沖突,是Linux SoC支持中的關(guān)鍵基礎(chǔ)設(shè)施。理解Pinctrl,對(duì)于閱讀和編寫(xiě)嵌入式Linux板級(jí)支持包(BSP)至關(guān)重要。

第二部分:Rockchip pinctrl 驅(qū)動(dòng)

1. 引言

在第一部分中,我們介紹了 Pinctrl 子系統(tǒng)的通用框架、核心數(shù)據(jù)結(jié)構(gòu)以及客戶端自動(dòng)綁定的流程。第二部分將以 Rockchip RK3568 平臺(tái)的 pinctrl 驅(qū)動(dòng)(pinctrl-rockchip.c)為例,深入分析 服務(wù)端(Provider) 驅(qū)動(dòng)的具體實(shí)現(xiàn)細(xì)節(jié),包括:

  • 驅(qū)動(dòng)數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)與 SoC 專屬描述
  • 如何從設(shè)備樹(shù)構(gòu)建引腳組(groups)和功能(functions)
  • 復(fù)用(mux)與配置(conf)硬件寄存器的底層操作
  • 設(shè)備樹(shù)節(jié)點(diǎn)到映射(dt_node_to_map)的完整轉(zhuǎn)換過(guò)程

通過(guò)學(xué)習(xí)這部分,你將掌握如何為新的 SoC 編寫(xiě)或理解 pinctrl 驅(qū)動(dòng)。

2. Rockchip pinctrl 驅(qū)動(dòng)概覽

Rockchip 的 pinctrl 驅(qū)動(dòng)位于 drivers/pinctrl/pinctrl-rockchip.c 及其頭文件 pinctrl-rockchip.h。它實(shí)現(xiàn)了:

  • 支持多個(gè) SoC 系列(RK3188, RK3288, RK3399, RK3568 等)
  • 通過(guò) rockchip_pin_ctrl 數(shù)據(jù)結(jié)構(gòu)區(qū)分不同 SoC 的引腳布局、寄存器偏移和功能映射
  • 利用 regmap 統(tǒng)一訪問(wèn) GRF(通用寄存器文件)和 PMUGRF(電源管理單元寄存器文件)
  • 提供 pinctrl_ops, pinmux_ops, pinconf_ops 三個(gè)操作集,供 Pinctrl Core 調(diào)用

下圖展示了 Rockchip pinctrl 驅(qū)動(dòng)與內(nèi)核其他模塊的關(guān)系:

3. 關(guān)鍵數(shù)據(jù)結(jié)構(gòu)

3.1rockchip_pinctrl—— 驅(qū)動(dòng)私有數(shù)據(jù)

該結(jié)構(gòu)體保存了驅(qū)動(dòng)運(yùn)行時(shí)的所有上下文信息。

struct rockchip_pinctrl {
    struct regmap *regmap_base;      // GRF 寄存器映射
    int reg_size;                    // 寄存器區(qū)域大小
    struct regmap *regmap_pull;      // 拉取寄存器映射(舊綁定)
    struct regmap *regmap_pmu;       // PMUGRF 寄存器映射
    struct device *dev;
    struct rockchip_pin_ctrl *ctrl;  // SoC 專屬引腳控制描述
    struct pinctrl_desc pctl;        // 通用 pinctrl 描述符
    struct pinctrl_dev *pctl_dev;
    struct rockchip_pin_group *groups;   // 該 SoC 的所有引腳組
    unsigned int ngroups;
    struct rockchip_pmx_func *functions; // 該 SoC 的所有復(fù)用功能
    unsigned int nfunctions;
};

其中 rockchip_pin_ctrl 是 SoC 相關(guān)的配置核心。

3.2rockchip_pin_ctrl—— SoC 引腳控制器描述

該結(jié)構(gòu)體包含了不同 SoC 的差異化信息,例如引腳總數(shù)、寄存器布局、驅(qū)動(dòng)強(qiáng)度計(jì)算方式等。

struct rockchip_pin_ctrl {
    enum rockchip_pinctrl_type type;        // 芯片類型(RK3568等)
    struct pinctrl_pin_desc *pins;          // 引腳描述符數(shù)組
    unsigned int npins;                     // 引腳數(shù)量
    unsigned int grf_mux_offset;            // 復(fù)用寄存器偏移
    unsigned int pmu_mux_offset;            // PMU 復(fù)用寄存器偏移
    int (*pull_calc_reg)(...);              // 計(jì)算上下拉寄存器的函數(shù)
    int (*drv_calc_reg)(...);               // 計(jì)算驅(qū)動(dòng)能力寄存器的函數(shù)
    const struct rockchip_mux_recalced_data *recalced_data;
    unsigned int num_recalced;
    // ... 更多 SoC 特有回調(diào)
};

以 RK3568 為例,驅(qū)動(dòng)中定義了 rk3568_pin_ctrl 靜態(tài)實(shí)例,其中包含引腳數(shù)組、寄存器偏移、以及專用的計(jì)算函數(shù)。

3.3rockchip_pin_group與rockchip_pmx_func

  • rockchip_pin_group:描述一個(gè)引腳組(group),包含組內(nèi)引腳編號(hào)列表、每個(gè)引腳的配置數(shù)據(jù)數(shù)組(上下拉、驅(qū)動(dòng)能力等)。
  • rockchip_pmx_func:描述一個(gè)復(fù)用功能(function),包含功能名稱以及屬于該功能的組名列表。

這兩者都是從設(shè)備樹(shù)中解析出來(lái)的。

4. 驅(qū)動(dòng)初始化深入

4.1rockchip_pinctrl_probe步驟詳解

static int rockchip_pinctrl_probe(struct platform_device *pdev)
{
    struct rockchip_pinctrl *info;
    struct rockchip_pin_ctrl *ctrl;
    struct device_node *np = pdev->dev.of_node;
    
    // 1. 分配私有數(shù)據(jù)
    info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
    
    // 2. 獲取 SoC 專屬配置(根據(jù) compatible 匹配)
    ctrl = rockchip_pinctrl_get_soc_data(info, pdev);
    info->ctrl = ctrl;
    
    // 3. 獲取 regmap:首先嘗試 "rockchip,grf"  phandle,否則 ioremap
    node = of_parse_phandle(np, "rockchip,grf", 0);
    if (node)
        info->regmap_base = syscon_node_to_regmap(node);
    else
        // 回退:直接映射 IORESOURCE_MEM
        info->regmap_base = devm_regmap_init_mmio(...);
    
    // 同樣處理 PMUGRF
    node = of_parse_phandle(np, "rockchip,pmu", 0);
    if (node)
        info->regmap_pmu = syscon_node_to_regmap(node);
    
    // 4. 注冊(cè) pinctrl 控制器
    ret = rockchip_pinctrl_register(pdev, info);
    
    // 5. 創(chuàng)建 GPIO bank 子設(shè)備(每個(gè) GPIO 控制器作為一個(gè) platform_device)
    ret = of_platform_populate(np, rockchip_bank_match, NULL, NULL);
    
    return 0;
}

關(guān)鍵點(diǎn)

  • rockchip_pinctrl_get_soc_data 通過(guò) of_match_device 獲取匹配的 rockchip_pin_ctrl 指針(存儲(chǔ)在 platform_driverdata 字段)。
  • 寄存器訪問(wèn)使用 regmap,可以統(tǒng)一處理位寬、緩存等,并兼容多個(gè)寄存器區(qū)域。
  • of_platform_populate 會(huì)為每個(gè) gpio 子節(jié)點(diǎn)(如 gpio0)創(chuàng)建 platform 設(shè)備,其驅(qū)動(dòng) rockchip_gpio_probe 會(huì)進(jìn)一步注冊(cè) GPIO 控制器。

4.2rockchip_pinctrl_register—— 填充并注冊(cè) pinctrl_desc

static int rockchip_pinctrl_register(struct platform_device *pdev,
                                     struct rockchip_pinctrl *info)
{
    struct pinctrl_desc *ctrldesc = &info->pctl;
    struct rockchip_pin_ctrl *ctrl = info->ctrl;
    
    ctrldesc->name = "rockchip-pinctrl";
    ctrldesc->pins = ctrl->pins;
    ctrldesc->npins = ctrl->npins;
    ctrldesc->pctlops = &rockchip_pctrl_ops;
    ctrldesc->pmxops = &rockchip_pmx_ops;
    ctrldesc->confops = &rockchip_pinconf_ops;
    ctrldesc->owner = THIS_MODULE;
    
    // 解析設(shè)備樹(shù),構(gòu)建 groups 和 functions
    ret = rockchip_pinctrl_parse_dt(pdev, info);
    
    // 注冊(cè)到 pinctrl core
    info->pctl_dev = devm_pinctrl_register(&pdev->dev, ctrldesc, info);
    return PTR_ERR_OR_ZERO(info->pctl_dev);
}

4.3rockchip_pinctrl_parse_dt—— 構(gòu)建設(shè)備樹(shù)映射

該函數(shù)遍歷 pinctrl 控制器節(jié)點(diǎn)下的所有子節(jié)點(diǎn)(每個(gè)子節(jié)點(diǎn)代表一個(gè) function,如 i2c3),然后再遍歷每個(gè) function 下的引腳組子節(jié)點(diǎn)(如 i2c3m0_xfer),最終填充 info->groupsinfo->functions。

static int rockchip_pinctrl_parse_dt(struct platform_device *pdev,
                                     struct rockchip_pinctrl *info)
{
    struct device_node *np = pdev->dev.of_node;
    struct device_node *child;
    int ret;
    
    // 第一遍:統(tǒng)計(jì) groups 和 functions 的數(shù)量
    for_each_child_of_node(np, child) {
        // 每個(gè) child 代表一個(gè) function 節(jié)點(diǎn)(如 i2c3, uart0)
        info->nfunctions++;
        for_each_child_of_node(child, grp) {
            info->ngroups++;
        }
    }
    // 分配內(nèi)存
    info->groups = devm_kcalloc(dev, info->ngroups, sizeof(*info->groups), ...);
    info->functions = devm_kcalloc(dev, info->nfunctions, sizeof(*info->functions), ...);
    
    // 第二遍:填充實(shí)際內(nèi)容,解析每個(gè) group 的 rockchip,pins 屬性
    // 將引腳編號(hào)、復(fù)用值、配置值存入 group 結(jié)構(gòu)
}

在解析 rockchip,pins 屬性時(shí),會(huì)調(diào)用 rockchip_pinctrl_parse_pin 將設(shè)備樹(shù)中的 bank、pin、mux 和 config phandle 轉(zhuǎn)換為:

  • 全局引腳編號(hào)(如 (bank-1)*32 + pin_index
  • 復(fù)用值(mux)
  • 配置參數(shù)(從 &pcfg_pull_none 等節(jié)點(diǎn)讀取 bias、drive-strength 等)

這些配置參數(shù)會(huì)最終存儲(chǔ)在 rockchip_pin_groupdata[i].configs 數(shù)組中。

5. 復(fù)用操作(pinmux_ops)的實(shí)現(xiàn)

Rockchip pinctrl 驅(qū)動(dòng)實(shí)現(xiàn)了 rockchip_pmx_ops 結(jié)構(gòu)體:

static const struct pinmux_ops rockchip_pmx_ops = {
    .get_functions_count = rockchip_pmx_get_funcs_count,
    .get_function_name = rockchip_pmx_get_func_name,
    .get_function_groups = rockchip_pmx_get_groups,
    .set_mux = rockchip_pmx_set,
};

其中最核心的是 rockchip_pmx_set,它負(fù)責(zé)將某個(gè)引腳組切換到指定的復(fù)用功能。

5.1rockchip_pmx_set實(shí)現(xiàn)分析

static int rockchip_pmx_set(struct pinctrl_dev *pctldev,
                            unsigned int func_selector,
                            unsigned int group_selector)
{
    struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
    struct rockchip_pin_group *group = &info->groups[group_selector];
    struct rockchip_pmx_func *func = &info->functions[func_selector];
    int i;
    
    for (i = 0; i < group->npins; i++) {
        unsigned int pin = group->pins[i];
        unsigned int mux = group->data[i].mux;   // 該引腳在該 group 中的復(fù)用值
        int ret = rockchip_pinctrl_set_mux(info, pin, mux);
        if (ret)
            return ret;
    }
    return 0;
}

實(shí)際寫(xiě)寄存器的函數(shù)是 rockchip_pinctrl_set_mux,它根據(jù)引腳所在的 bank 和 pin 索引計(jì)算出 IOMUX 寄存器的地址和位域,然后通過(guò) regmap 更新對(duì)應(yīng)位。

static int rockchip_pinctrl_set_mux(struct rockchip_pinctrl *info,
                                    unsigned int pin, int mux)
{
    struct rockchip_pin_bank *bank = pin_to_bank(info, pin);
    int pin_offset = pin - bank->pin_base;
    int reg, bit, ret;
    
    // 根據(jù) SoC 類型計(jì)算復(fù)用寄存器偏移(GRF 或 PMU)
    rockchip_get_mux(bank, pin_offset, &reg, &bit, &bank->pin_sel_type[pin_offset]);
    
    // 寫(xiě)寄存器:清除原來(lái)的 2 位(或 3 位)復(fù)用值,再寫(xiě)入新值
    ret = regmap_update_bits(info->regmap_base, reg, 0x3 << bit, mux << bit);
    return ret;
}

不同 Rockchip SoC 的復(fù)用寄存器布局不同,驅(qū)動(dòng)通過(guò) bank->pin_sel_type 數(shù)組(在 GPIO bank 驅(qū)動(dòng)初始化時(shí)填充)來(lái)區(qū)分是 2 位復(fù)用(如 RK3288)還是 3 位復(fù)用(如 RK3399),以及寄存器是否位于 PMU 域。

6. 配置操作(pinconf_ops)的實(shí)現(xiàn)

Rockchip 的 pinconf_ops 提供引腳電氣特性的配置:

static const struct pinconf_ops rockchip_pinconf_ops = {
    .pin_config_get = rockchip_pinconf_get,
    .pin_config_set = rockchip_pinconf_set,
    .is_generic = true,
};

6.1 支持的配置參數(shù)

Rockchip 驅(qū)動(dòng)通過(guò)通用 pinconf 框架支持以下參數(shù)(定義在 pinctrl-generic.h):

  • PIN_CONFIG_BIAS_DISABLE:關(guān)閉上下拉
  • PIN_CONFIG_BIAS_PULL_UP:上拉
  • PIN_CONFIG_BIAS_PULL_DOWN:下拉
  • PIN_CONFIG_DRIVE_STRENGTH:驅(qū)動(dòng)能力(mA)
  • PIN_CONFIG_SLEW_RATE:壓擺率
  • PIN_CONFIG_INPUT_ENABLE:輸入使能

驅(qū)動(dòng)中通過(guò) rockchip_pinconf_set 解析這些通用參數(shù),并調(diào)用 SoC 專用的計(jì)算函數(shù)(如 pull_calc_reg)找到對(duì)應(yīng)的寄存器地址和位域,最后更新硬件。

6.2rockchip_pinconf_set核心流程

static int rockchip_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
                                unsigned long *configs, unsigned int num_configs)
{
    struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
    struct rockchip_pin_bank *bank = pin_to_bank(info, pin);
    int i, ret = 0;
    
    for (i = 0; i < num_configs; i++) {
        enum pin_config_param param = pinconf_to_config_param(configs[i]);
        u32 arg = pinconf_to_config_argument(configs[i]);
        
        switch (param) {
        case PIN_CONFIG_BIAS_PULL_UP:
        case PIN_CONFIG_BIAS_PULL_DOWN:
        case PIN_CONFIG_BIAS_DISABLE:
            ret = rockchip_set_pull(info, bank, pin - bank->pin_base, param);
            break;
        case PIN_CONFIG_DRIVE_STRENGTH:
            ret = rockchip_set_drive(info, bank, pin - bank->pin_base, arg);
            break;
        // ... 其他參數(shù)
        }
    }
    return ret;
}

rockchip_set_pullrockchip_set_drive 內(nèi)部會(huì)利用 rockchip_pin_ctrl 提供的回調(diào)函數(shù)(如 pull_calc_reg)來(lái)定位寄存器。

例如,RK3568 的上下拉寄存器位于 PMUGRF(部分引腳)或 GRF(其他引腳),驅(qū)動(dòng)通過(guò) info->regmap_pmuinfo->regmap_base 分別訪問(wèn)。

7. 設(shè)備樹(shù)到映射的轉(zhuǎn)換(dt_node_to_map)

客戶端設(shè)備在解析其 pinctrl-0 屬性時(shí),Pinctrl Core 會(huì)調(diào)用服務(wù)端驅(qū)動(dòng)的 dt_node_to_map 函數(shù)。

Rockchip 驅(qū)動(dòng)實(shí)現(xiàn)了 rockchip_dt_node_to_map,該函數(shù)將設(shè)備樹(shù)中的引腳組描述(如 <&i2c3m0_xfer>)轉(zhuǎn)換為 pinctrl_map 數(shù)組。

7.1 轉(zhuǎn)換步驟(回顧與補(bǔ)充)

根據(jù)設(shè)備樹(shù)節(jié)點(diǎn)名查找已有的 rockchip_pin_group

pinctrl_name_to_group(info, np->name) 返回之前在解析 pinctrl 控制器時(shí)構(gòu)建的 group 對(duì)象。

分配 pinctrl_map 數(shù)組

數(shù)量 = 1(復(fù)用映射) + group->npins(每個(gè)引腳一個(gè)配置映射)。

創(chuàng)建復(fù)用映射

new_map[0].type = PIN_MAP_TYPE_MUX_GROUP
new_map[0].data.mux.function = parent->name(即 function 節(jié)點(diǎn)名,如 “i2c3”)
new_map[0].data.mux.group = np->name(即 group 節(jié)點(diǎn)名,如 “i2c3m0_xfer”)

創(chuàng)建配置映射

對(duì) group 中的每個(gè)引腳,創(chuàng)建一個(gè) PIN_MAP_TYPE_CONFIGS_PIN 映射,group_or_pin 設(shè)置為引腳名,configs 指向預(yù)先解析好的配置數(shù)組。

new_map++;
for (i = 0; i < grp->npins; i++) {
    new_map[i].type = PIN_MAP_TYPE_CONFIGS_PIN;
    new_map[i].data.configs.group_or_pin = pin_get_name(pctldev, grp->pins[i]);
    new_map[i].data.configs.configs = grp->data[i].configs;
    new_map[i].data.configs.num_configs = grp->data[i].nconfigs;
}

這些映射最終會(huì)被 pinctrl_select_state 使用,依次調(diào)用 set_muxpin_config_set 完成硬件配置。

8. 調(diào)試實(shí)踐:利用 debugfs 驗(yàn)證驅(qū)動(dòng)行為

在第二部分最后,我們展示如何利用 debugfs 驗(yàn)證 Rockchip pinctrl 驅(qū)動(dòng)的配置是否生效。

查看所有引腳組

cat /sys/kernel/debug/pinctrl/1000000.pinctrl/pingroups

輸出示例:

registered pin groups:
group: i2c3m0-xfer
pin 6 (GPIO_6)
pin 7 (GPIO_7)

查看復(fù)用功能

cat /sys/kernel/debug/pinctrl/1000000.pinctrl/pinmux-functions

輸出:

function: i2c3, groups = [ i2c3m0-xfer i2c3m1-xfer ]
function: uart0, groups = [ uart0-xfer ]

檢查某個(gè)設(shè)備的引腳狀態(tài)

cat /sys/kernel/debug/pinctrl/pinctrl-handles

會(huì)顯示類似:

device: fe5c0000.i2c
  state: default
    type: MUX_GROUP controller 1000000.pinctrl group: i2c3m0-xfer function: i2c3
    type: CONFIGS_GROUP controller 1000000.pinctrl group i2c3m0-xfer 0x00000000 0x00020009

實(shí)時(shí)修改引腳配置(用于測(cè)試)

可以通過(guò) debugfs 直接寫(xiě)入配置,例如:

echo "pin 6 pull-up" > /sys/kernel/debug/pinctrl/1000000.pinctrl/pinconf-pins

(需要驅(qū)動(dòng)支持 debugfs 命令,Rockchip 驅(qū)動(dòng)實(shí)現(xiàn)了該接口)

9. 總結(jié)

第二部分詳細(xì)剖析了 Rockchip pinctrl 驅(qū)動(dòng)的內(nèi)部實(shí)現(xiàn),涵蓋:

  • 驅(qū)動(dòng)私有數(shù)據(jù)結(jié)構(gòu)與 SoC 差異化描述
  • 初始化的三個(gè)關(guān)鍵步驟:獲取 regmap、解析設(shè)備樹(shù)、注冊(cè) pinctrl 控制器
  • 引腳組(groups)和功能(functions)的構(gòu)建過(guò)程
  • 復(fù)用操作(set_mux)如何操作 IOMUX 寄存器
  • 配置操作(pin_config_set)如何設(shè)置上下拉、驅(qū)動(dòng)強(qiáng)度等電氣特性
  • 設(shè)備樹(shù)節(jié)點(diǎn)到 Pinctrl 映射的完整轉(zhuǎn)換(dt_node_to_map
  • 調(diào)試方法:利用 debugfs 檢查實(shí)際配置

理解這些細(xì)節(jié),你就能夠:

  • 為新的 Rockchip SoC 移植或修改 pinctrl 驅(qū)動(dòng)
  • 排查引腳配置不生效的問(wèn)題(例如檢查 rockchip,pins 格式是否正確,寄存器偏移是否匹配)
  • 在自定義硬件上正確添加新的引腳組和功能

第三部分:電源管理、GPIO交互與高級(jí)調(diào)試

1. 引言

在前兩部分中,我們學(xué)習(xí)了 Pinctrl 子系統(tǒng)的通用框架(第一部分)以及 Rockchip 平臺(tái)驅(qū)動(dòng)的具體實(shí)現(xiàn)(第二部分)。第三部分將聚焦于 運(yùn)行時(shí)動(dòng)態(tài)管理 的高級(jí)主題,包括:

  • 電源管理(PM)集成:如何在系統(tǒng)休眠/喚醒時(shí)自動(dòng)或手動(dòng)切換引腳狀態(tài)以降低功耗
  • 與 GPIO 子系統(tǒng)的交互:pinctrl 如何與 GPIO 框架協(xié)同工作,確保引腳在 GPIO 模式下的正確配置
  • Hog 機(jī)制:控制器自身對(duì)引腳的默認(rèn)配置
  • Setting 應(yīng)用詳解add_setting、pinmux_enable_setting 等內(nèi)部函數(shù)的完整流程
  • 高級(jí)調(diào)試技巧:利用 debugfs 排查引腳沖突和配置錯(cuò)誤

通過(guò)學(xué)習(xí)本部分,你將掌握如何編寫(xiě)支持電源管理的設(shè)備驅(qū)動(dòng),理解 pinctrl 與 GPIO 的協(xié)同原理,并能獨(dú)立診斷引腳配置問(wèn)題。

2. 電源管理中的引腳狀態(tài)切換

2.1 設(shè)備樹(shù)中定義睡眠狀態(tài)

在客戶端設(shè)備樹(shù)節(jié)點(diǎn)中,可以為設(shè)備定義多個(gè) pinctrl 狀態(tài),常見(jiàn)的包括:

  • default:正常運(yùn)行狀態(tài)
  • sleep:系統(tǒng) suspend 時(shí)使用的低功耗狀態(tài)
  • idle:運(yùn)行時(shí) idle 狀態(tài)(可選)
&uart0 {
    pinctrl-names = "default", "sleep";
    pinctrl-0 = <&uart0_xfer>;
    pinctrl-1 = <&uart0_sleep>;
    status = "okay";
};

在 sleep 狀態(tài)中,通常會(huì)將引腳切換為 GPIO 功能并設(shè)置為輸入或上拉,以降低漏電流。

2.2 內(nèi)核自動(dòng)管理:pinctrl_pm_*接口

Pinctrl 子系統(tǒng)提供了兩個(gè)輔助函數(shù),供設(shè)備驅(qū)動(dòng)在 suspend/resume 時(shí)自動(dòng)切換狀態(tài):

int pinctrl_pm_select_default_state(struct device *dev);
int pinctrl_pm_select_sleep_state(struct device *dev);
int pinctrl_pm_select_idle_state(struct device *dev);

這些函數(shù)內(nèi)部會(huì)調(diào)用 pinctrl_select_state,前提是設(shè)備已經(jīng)在 probe 階段通過(guò) pinctrl_bind_pins 綁定好了句柄。

典型用法(在設(shè)備驅(qū)動(dòng)中):

static int my_drv_suspend(struct device *dev)
{
    return pinctrl_pm_select_sleep_state(dev);
}

static int my_drv_resume(struct device *dev)
{
    return pinctrl_pm_select_default_state(dev);
}

static const struct dev_pm_ops my_drv_pm_ops = {
    .suspend = my_drv_suspend,
    .resume = my_drv_resume,
};

2.3 內(nèi)核自動(dòng)處理的時(shí)機(jī)

實(shí)際上,對(duì)于掛載在標(biāo)準(zhǔn)總線(如 platform、i2c、spi)上的設(shè)備,如果驅(qū)動(dòng)沒(méi)有提供 suspend/resume 回調(diào),內(nèi)核不會(huì)自動(dòng)調(diào)用 pinctrl_pm_* 函數(shù)。因此,驅(qū)動(dòng)開(kāi)發(fā)者需要顯式地在 suspend/resume 回調(diào)中調(diào)用這些函數(shù)。

但有一種例外:當(dāng)整個(gè)系統(tǒng)進(jìn)入 suspend 時(shí),pinctrl 控制器驅(qū)動(dòng)本身會(huì)收到 PM 事件,并可能對(duì)所有已注冊(cè)的設(shè)備嘗試切換狀態(tài)?并非如此——狀態(tài)切換是按設(shè)備進(jìn)行的,每個(gè)設(shè)備的引腳狀態(tài)需要各自管理。

正確的理解pinctrl_bind_pins 只是在 probe 階段為設(shè)備找到了 defaultinit 狀態(tài)并激活了其中一個(gè)。sleepidle 狀態(tài)雖然被查找并保存在 dev->pins->sleep_state 中,但不會(huì)自動(dòng)應(yīng)用,需要驅(qū)動(dòng)在 suspend 時(shí)主動(dòng)調(diào)用 pinctrl_select_state。

2.4 狀態(tài)切換時(shí)的內(nèi)部操作

當(dāng)調(diào)用 pinctrl_select_state(p, new_state) 時(shí),內(nèi)核執(zhí)行以下步驟(簡(jiǎn)化):

int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state)
{
    struct pinctrl_setting *setting, *setting2;
    struct pinctrl_state *old_state = p->state;
    
    if (p->state) {
        // 1. 禁用舊狀態(tài)中的所有復(fù)用設(shè)置
        list_for_each_entry(setting, &p->state->settings, node) {
            if (setting->type == PIN_MAP_TYPE_MUX_GROUP)
                pinmux_disable_setting(setting);
        }
    }
    
    p->state = NULL;
    
    // 2. 啟用新?tīng)顟B(tài)中的所有設(shè)置
    list_for_each_entry(setting, &state->settings, node) {
        switch (setting->type) {
        case PIN_MAP_TYPE_MUX_GROUP:
            ret = pinmux_enable_setting(setting);
            break;
        case PIN_MAP_TYPE_CONFIGS_PIN:
        case PIN_MAP_TYPE_CONFIGS_GROUP:
            ret = pinconf_apply_setting(setting);
            break;
        }
    }
    
    p->state = state;
    return 0;
}

注意:對(duì)于復(fù)用設(shè)置,禁用舊狀態(tài)時(shí)會(huì)調(diào)用 pinmux_disable_setting,這通常會(huì)將該引腳組的功能設(shè)置為 GPIO(安全默認(rèn)值),避免功能沖突。

3. Pinctrl 與 GPIO 子系統(tǒng)的交互

3.1 GPIO 與 Pinctrl 的關(guān)系

在許多 SoC 中,GPIO 控制器是 pinctrl 硬件的一部分:同一個(gè)物理引腳既可以作為通用 GPIO,也可以作為外設(shè)功能引腳。Pinctrl 驅(qū)動(dòng)通常也負(fù)責(zé) GPIO 控制器的注冊(cè),但 GPIO 子系統(tǒng)和 pinctrl 子系統(tǒng)是獨(dú)立的框架,它們通過(guò)以下方式協(xié)作:

  • GPIO 請(qǐng)求時(shí),pinctrl 確保引腳已被配置為 GPIO 模式
  • GPIO 釋放時(shí),pinctrl 可以將其恢復(fù)為默認(rèn)功能
  • GPIO 方向設(shè)置時(shí),可能需要同時(shí)配置引腳的上拉/下拉

3.2gpio-ranges屬性

設(shè)備樹(shù)中的 gpio-ranges 屬性建立了 GPIO 控制器的 GPIO 號(hào)與 pinctrl 引腳號(hào)之間的映射關(guān)系。例如:

gpio0: gpio@fdd60000 {
    compatible = "rockchip,gpio-bank";
    reg = <0x0 0xfdd60000 0x0 0x100>;
    gpio-controller;
    #gpio-cells = <2>;
    gpio-ranges = <&pinctrl 0 0 32>;
};

該屬性表示:gpio0 控制器的 GPIO 0~31 對(duì)應(yīng) pinctrl 控制器的引腳 0~31。

內(nèi)核通過(guò) gpiochip_add_pin_rangegpiochip_add_pingroup_range 建立映射,這樣 GPIO 子系統(tǒng)在請(qǐng)求引腳時(shí),可以找到對(duì)應(yīng)的 pinctrl 設(shè)備。

3.3 Pinctrl 驅(qū)動(dòng)提供的 GPIO 相關(guān)回調(diào)

pinmux_ops 中包含三個(gè) GPIO 相關(guān)的回調(diào):

struct pinmux_ops {
    int (*gpio_request_enable)(struct pinctrl_dev *pctldev,
                               struct pinctrl_gpio_range *range,
                               unsigned offset);
    void (*gpio_disable_free)(struct pinctrl_dev *pctldev,
                              struct pinctrl_gpio_range *range,
                              unsigned offset);
    int (*gpio_set_direction)(struct pinctrl_dev *pctldev,
                              struct pinctrl_gpio_range *range,
                              unsigned offset, bool input);
};
  • gpio_request_enable:當(dāng)一個(gè) GPIO 被請(qǐng)求(gpio_request)時(shí),GPIO 子系統(tǒng)會(huì)調(diào)用該回調(diào),使 pinctrl 驅(qū)動(dòng)將對(duì)應(yīng)的引腳切換為 GPIO 功能。
  • gpio_disable_free:當(dāng) GPIO 被釋放時(shí)調(diào)用,可將引腳恢復(fù)為默認(rèn)狀態(tài)。
  • gpio_set_direction:當(dāng) GPIO 方向改變(gpio_direction_input/output)時(shí)調(diào)用,用于配置引腳的上下拉等。

Rockchip 驅(qū)動(dòng)實(shí)現(xiàn)了這些回調(diào):

static const struct pinmux_ops rockchip_pmx_ops = {
    // ...
    .gpio_request_enable = rockchip_gpio_request_enable,
    .gpio_disable_free = rockchip_gpio_disable_free,
    .gpio_set_direction = rockchip_gpio_set_direction,
};

rockchip_gpio_request_enable 中,會(huì)調(diào)用 rockchip_pinctrl_set_mux 將引腳復(fù)用值設(shè)置為 GPIO(即 mux = 0)。

3.4 GPIO 請(qǐng)求的完整流程

4. Hog 機(jī)制:控制器自身的引腳配置

有時(shí)候,某些引腳在系統(tǒng)啟動(dòng)后始終作為 GPIO 或特定功能使用,不歸屬于任何具體的客戶端設(shè)備。Pinctrl 允許在控制器節(jié)點(diǎn)中直接定義“hog”引腳組,并在控制器 probe 時(shí)自動(dòng)應(yīng)用這些配置。

4.1 設(shè)備樹(shù)中的 Hog 節(jié)點(diǎn)

在 pinctrl 控制器節(jié)點(diǎn)下,可以創(chuàng)建沒(méi)有 pinctrl-* 屬性的子節(jié)點(diǎn),并在其中定義 rockchip,pins,這些節(jié)點(diǎn)會(huì)被視為 hog 組。

&pinctrl {
    hog_pins: hog-pins {
        rockchip,pins = <0 RK_PA0 RK_FUNC_GPIO &pcfg_pull_up>,
                        <0 RK_PA1 RK_FUNC_GPIO &pcfg_pull_up>;
    };
};

內(nèi)核在注冊(cè) pinctrl 控制器時(shí),會(huì)遍歷所有子節(jié)點(diǎn),對(duì)于沒(méi)有 pinctrl-0 屬性的節(jié)點(diǎn)(即非客戶端引用),會(huì)調(diào)用 pinctrl_hog 將其作為默認(rèn)狀態(tài)應(yīng)用。

4.2 內(nèi)核實(shí)現(xiàn)

pinctrl_register 最后會(huì)調(diào)用 pinctrl_enable,該函數(shù)內(nèi)部會(huì)執(zhí)行:

int pinctrl_enable(struct pinctrl_dev *pctldev)
{
    // ...
    if (pctldev->desc->pctlops->dt_node_to_map) {
        // 遍歷控制器節(jié)點(diǎn)下的所有子節(jié)點(diǎn),找出 hog 節(jié)點(diǎn)
        pinctrl_hog_map(pctldev, np);
    }
    // ...
}

pinctrl_hog_map 為每個(gè) hog 節(jié)點(diǎn)創(chuàng)建一個(gè)虛擬的“設(shè)備”(控制器自身),并調(diào)用 dt_node_to_map 生成映射,然后添加到控制器的 hog 狀態(tài)中,最后激活該狀態(tài)。

5. Setting 應(yīng)用詳解(add_setting與pinmux_enable_setting)

5.1add_setting完整分析

create_pinctrl 函數(shù)中,對(duì)每個(gè) pinctrl_map 調(diào)用 add_setting,將其轉(zhuǎn)換為 pinctrl_setting 并添加到對(duì)應(yīng)狀態(tài)的鏈表中。

static int add_setting(struct pinctrl *p, struct pinctrl_dev *pctldev,
                       const struct pinctrl_map *map)
{
    struct pinctrl_state *state;
    struct pinctrl_setting *setting;
    
    // 根據(jù) map->name 查找或創(chuàng)建狀態(tài)
    state = find_state(p, map->name);
    if (!state)
        state = create_state(p, map->name);
    
    // 分配 setting 結(jié)構(gòu)體
    setting = kzalloc(sizeof(*setting), GFP_KERNEL);
    setting->type = map->type;
    setting->pctldev = pctldev;
    setting->dev_name = map->dev_name;
    
    switch (map->type) {
    case PIN_MAP_TYPE_MUX_GROUP:
        setting->data.mux.group = map->data.mux.group;   // group 選擇器
        setting->data.mux.func = map->data.mux.function; // function 選擇器
        break;
    case PIN_MAP_TYPE_CONFIGS_PIN:
        setting->data.configs.group_or_pin = map->data.configs.group_or_pin;
        setting->data.configs.configs = map->data.configs.configs;
        setting->data.configs.num_configs = map->data.configs.num_configs;
        break;
    case PIN_MAP_TYPE_CONFIGS_GROUP:
        // 類似
        break;
    default:
        break;
    }
    
    list_add_tail(&setting->node, &state->settings);
    return 0;
}

注意:map->data.mux.groupmap->data.mux.function 在映射階段是字符串名稱,但在 add_setting 中并未轉(zhuǎn)換為選擇器。

真正的轉(zhuǎn)換發(fā)生在 pinctrl_select_state 調(diào)用 pinmux_enable_setting 時(shí),通過(guò) pinctrl_get_group_selectorpinctrl_get_function_selector 動(dòng)態(tài)查找。

5.2pinmux_enable_setting實(shí)現(xiàn)

int pinmux_enable_setting(struct pinctrl_setting *setting)
{
    struct pinctrl_dev *pctldev = setting->pctldev;
    const struct pinmux_ops *ops = pctldev->desc->pmxops;
    unsigned group = 0, func = 0;
    int ret;
    
    // 將字符串名稱轉(zhuǎn)換為選擇器
    group = pinctrl_get_group_selector(pctldev, setting->data.mux.group);
    func = pinctrl_get_function_selector(pctldev, setting->data.mux.func);
    
    // 調(diào)用驅(qū)動(dòng)的 set_mux
    ret = ops->set_mux(pctldev, func, group);
    return ret;
}

類似地,pinconf_apply_setting 會(huì)調(diào)用 pin_config_group_setpin_config_set。

6. 高級(jí)調(diào)試技巧

6.1 查看引腳當(dāng)前的配置值

除了之前提到的文件,/sys/kernel/debug/pinctrl/<controller>/pinconf-pins 可以查看每個(gè)引腳的當(dāng)前配置(需要驅(qū)動(dòng)實(shí)現(xiàn) pin_config_dbg_show)。

Rockchip 驅(qū)動(dòng)實(shí)現(xiàn)了該接口,輸出類似:

pin 0 (GPIO_0): bias-pull-up, drive-strength = 12 mA
pin 1 (GPIO_1): bias-disable, drive-strength = 6 mA

6.2 動(dòng)態(tài)修改引腳配置(測(cè)試用)

通過(guò) debugfs 可以臨時(shí)修改某個(gè)引腳的配置,無(wú)需重新編譯內(nèi)核:

echo "pin 0 bias-pull-up drive-strength=12" > /sys/kernel/debug/pinctrl/1000000.pinctrl/pinconf-pins

這需要驅(qū)動(dòng)實(shí)現(xiàn) pin_config_dbg_parse_modify 回調(diào),Rockchip 驅(qū)動(dòng)未實(shí)現(xiàn)該功能,但其他平臺(tái)可能有。

6.3 檢查引腳占用沖突

當(dāng)出現(xiàn)“pin already requested”錯(cuò)誤時(shí),使用以下命令查找沖突源:

cat /sys/kernel/debug/pinctrl/pinctrl-handles | grep -A 10 "pin 6"

也可以直接查看某個(gè)控制器的引腳占用情況(如果驅(qū)動(dòng)實(shí)現(xiàn)了 pin_dbg_show):

cat /sys/kernel/debug/pinctrl/1000000.pinctrl/pins

輸出會(huì)顯示每個(gè)引腳是否被占用,以及被哪個(gè)設(shè)備占用。

6.4 模擬狀態(tài)切換

手動(dòng)觸發(fā)設(shè)備的狀態(tài)切換,驗(yàn)證電源管理邏輯:

# 先找到設(shè)備在 debugfs 中的句柄
cat /sys/kernel/debug/pinctrl/pinctrl-handles | grep "device:"
# 假設(shè)設(shè)備是 78b6000.i2c

# 無(wú)法直接通過(guò)命令切換,但可以查看當(dāng)前狀態(tài)
cat /sys/kernel/debug/pinctrl/pinctrl-handles

更好的方法是在驅(qū)動(dòng)中增加 sysfs 節(jié)點(diǎn)來(lái)觸發(fā)狀態(tài)切換。

7. 總結(jié)

第三部分涵蓋了 Pinctrl 子系統(tǒng)的運(yùn)行時(shí)管理核心:

  • 電源管理:通過(guò) pinctrl_pm_select_* 函數(shù)在 suspend/resume 中切換引腳狀態(tài),降低功耗。驅(qū)動(dòng)需要主動(dòng)調(diào)用這些函數(shù)。
  • GPIO 交互gpio-ranges 建立映射,gpio_request_enable 等回調(diào)確保引腳在 GPIO 請(qǐng)求時(shí)正確配置。
  • Hog 機(jī)制:控制器自身對(duì)引腳的默認(rèn)配置,無(wú)需客戶端設(shè)備。
  • 內(nèi)部流程add_setting 創(chuàng)建設(shè)置,pinmux_enable_setting 在激活時(shí)解析名稱并調(diào)用驅(qū)動(dòng)回調(diào)。
  • 調(diào)試:利用 debugfs 查看引腳占用、配置值和狀態(tài)切換。

.2 動(dòng)態(tài)修改引腳配置(測(cè)試用)

通過(guò) debugfs 可以臨時(shí)修改某個(gè)引腳的配置,無(wú)需重新編譯內(nèi)核:

echo "pin 0 bias-pull-up drive-strength=12" > /sys/kernel/debug/pinctrl/1000000.pinctrl/pinconf-pins

這需要驅(qū)動(dòng)實(shí)現(xiàn) pin_config_dbg_parse_modify 回調(diào),Rockchip 驅(qū)動(dòng)未實(shí)現(xiàn)該功能,但其他平臺(tái)可能有。

6.3 檢查引腳占用沖突

當(dāng)出現(xiàn)“pin already requested”錯(cuò)誤時(shí),使用以下命令查找沖突源:

cat /sys/kernel/debug/pinctrl/pinctrl-handles | grep -A 10 "pin 6"

也可以直接查看某個(gè)控制器的引腳占用情況(如果驅(qū)動(dòng)實(shí)現(xiàn)了 pin_dbg_show):

cat /sys/kernel/debug/pinctrl/1000000.pinctrl/pins

輸出會(huì)顯示每個(gè)引腳是否被占用,以及被哪個(gè)設(shè)備占用。

6.4 模擬狀態(tài)切換

手動(dòng)觸發(fā)設(shè)備的狀態(tài)切換,驗(yàn)證電源管理邏輯:

# 先找到設(shè)備在 debugfs 中的句柄
cat /sys/kernel/debug/pinctrl/pinctrl-handles | grep "device:"
# 假設(shè)設(shè)備是 78b6000.i2c

# 無(wú)法直接通過(guò)命令切換,但可以查看當(dāng)前狀態(tài)
cat /sys/kernel/debug/pinctrl/pinctrl-handles

更好的方法是在驅(qū)動(dòng)中增加 sysfs 節(jié)點(diǎn)來(lái)觸發(fā)狀態(tài)切換。

7. 總結(jié)

第三部分涵蓋了 Pinctrl 子系統(tǒng)的運(yùn)行時(shí)管理核心:

  • 電源管理:通過(guò) pinctrl_pm_select_* 函數(shù)在 suspend/resume 中切換引腳狀態(tài),降低功耗。驅(qū)動(dòng)需要主動(dòng)調(diào)用這些函數(shù)。
  • GPIO 交互gpio-ranges 建立映射,gpio_request_enable 等回調(diào)確保引腳在 GPIO 請(qǐng)求時(shí)正確配置。
  • Hog 機(jī)制:控制器自身對(duì)引腳的默認(rèn)配置,無(wú)需客戶端設(shè)備。
  • 內(nèi)部流程add_setting 創(chuàng)建設(shè)置,pinmux_enable_setting 在激活時(shí)解析名稱并調(diào)用驅(qū)動(dòng)回調(diào)。
  • 調(diào)試:利用 debugfs 查看引腳占用、配置值和狀態(tài)切換。

最后

掌握這些內(nèi)容后,你不僅可以正確編寫(xiě)使用 pinctrl 的設(shè)備驅(qū)動(dòng),還能夠深入調(diào)試引腳相關(guān)的硬件問(wèn)題。Pinctrl 子系統(tǒng)的設(shè)計(jì)體現(xiàn)了 Linux 內(nèi)核“分離機(jī)制與策略”的哲學(xué),是學(xué)習(xí)內(nèi)核子系統(tǒng)的優(yōu)秀范例。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Linux內(nèi)核設(shè)備驅(qū)動(dòng)之系統(tǒng)調(diào)用筆記整理

    Linux內(nèi)核設(shè)備驅(qū)動(dòng)之系統(tǒng)調(diào)用筆記整理

    今天小編就為大家分享一篇關(guān)于Linux內(nèi)核設(shè)備驅(qū)動(dòng)之系統(tǒng)調(diào)用筆記整理,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2018-12-12
  • Ubuntu系統(tǒng)下擴(kuò)展LVM根目錄的方法

    Ubuntu系統(tǒng)下擴(kuò)展LVM根目錄的方法

    這篇文章主要給大家介紹了關(guān)于Ubuntu系統(tǒng)下擴(kuò)展LVM根目錄的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-05-05
  • Linux Pinctrl子系統(tǒng)使用及說(shuō)明

    Linux Pinctrl子系統(tǒng)使用及說(shuō)明

    這篇文章主要介紹了Linux Pinctrl子系統(tǒng)使用及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2026-04-04
  • CentOS虛擬機(jī)開(kāi)機(jī)出現(xiàn)問(wèn)題及解決過(guò)程

    CentOS虛擬機(jī)開(kāi)機(jī)出現(xiàn)問(wèn)題及解決過(guò)程

    文章介紹了CentOS虛擬機(jī)開(kāi)機(jī)出現(xiàn)問(wèn)題的解決方法,通過(guò)輸入命令xfs_repair -v -L /dev/dm-0,強(qiáng)制xfs_repair將日志歸零,解決了無(wú)法掛載/sysroot的問(wèn)題
    2025-11-11
  • Nginx虛擬主機(jī)多server_name的順序問(wèn)題

    Nginx虛擬主機(jī)多server_name的順序問(wèn)題

    今天在配置Nginx + PHP + MediaWiki中,發(fā)現(xiàn)一個(gè)問(wèn)題:MediaWiki所在的Nginx虛擬主機(jī)綁定了多個(gè)域名,但是不管通過(guò)什么域名訪問(wèn)MediaWiki首頁(yè),都會(huì)被跳轉(zhuǎn)到其中的一個(gè)域名上。
    2008-12-12
  • ubuntu開(kāi)啟22端口的實(shí)現(xiàn)

    ubuntu開(kāi)啟22端口的實(shí)現(xiàn)

    這篇文章主要介紹了ubuntu開(kāi)啟22端口的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • Linux環(huán)境下完整搭建GitLab私有代碼倉(cāng)庫(kù)的詳細(xì)流程

    Linux環(huán)境下完整搭建GitLab私有代碼倉(cāng)庫(kù)的詳細(xì)流程

    在現(xiàn)代軟件開(kāi)發(fā)中,代碼版本控制是團(tuán)隊(duì)協(xié)作的基石,GitLab 作為一款功能強(qiáng)大、開(kāi)源免費(fèi)的 DevOps 平臺(tái),無(wú)疑是私有代碼倉(cāng)庫(kù)的最佳選擇之一,本文將帶你從零開(kāi)始,在 Linux 環(huán)境下完整搭建 GitLab 私有代碼倉(cāng)庫(kù),需要的朋友可以參考下
    2026-04-04
  • Apache2.4.2編譯安裝2個(gè)常見(jiàn)錯(cuò)誤和解決方法

    Apache2.4.2編譯安裝2個(gè)常見(jiàn)錯(cuò)誤和解決方法

    這篇文章主要介紹了Apache2.4.2編譯安裝2個(gè)常見(jiàn)錯(cuò)誤和解決方法,需要的朋友可以參考下
    2014-06-06
  • Ubuntu16.04根目錄磁盤(pán)擴(kuò)容實(shí)現(xiàn)方式

    Ubuntu16.04根目錄磁盤(pán)擴(kuò)容實(shí)現(xiàn)方式

    本文詳細(xì)介紹了如何在Ubuntu 16.04系統(tǒng)中使用gparted工具進(jìn)行磁盤(pán)擴(kuò)容和分區(qū)調(diào)整,包括安裝gparted、啟動(dòng)工具、調(diào)整分區(qū)大小以及啟用交換空間等步驟
    2026-01-01
  • linux搭建FastDFS文件服務(wù)器的實(shí)現(xiàn)步驟

    linux搭建FastDFS文件服務(wù)器的實(shí)現(xiàn)步驟

    本文主要介紹在linux服務(wù)器如何搭建FastDFS文件服務(wù)器。文中通過(guò)圖文示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08

最新評(píng)論

通山县| 宕昌县| 浦江县| 漳浦县| 潜山县| 连南| 柯坪县| 佛教| 吴江市| 泰来县| 沭阳县| 同仁县| 茂名市| 淮北市| 峨边| 晴隆县| 彭泽县| 开远市| 孟津县| 绥德县| 宁城县| 会昌县| 资溪县| 鄱阳县| 东港市| 平湖市| 张北县| 湘阴县| 香格里拉县| 固始县| 北碚区| 泉州市| 宁津县| 澄迈县| 南溪县| 庄浪县| 固始县| 科尔| 泸溪县| 乌拉特中旗| 英山县|