Linux驅動學習day20(pinctrl子系統驅動大全)

一、Pinctrl作用

Pinctrl(Pin Controller):控制引腳

引腳的枚舉與命名、引腳復用、引腳配置。Pinctrl驅動一般由芯片原廠的BSP工程師來寫,一般驅動工程師只需要在設備樹中指明使用哪個引腳,復用為哪個功能、配置為哪些狀態。

二、Pin Controller重要結構體

由于面向對象的思想,在Linux系統中使用pinctrl_desc和?pinctrl_dev描述pinController。

左邊是controller(抽象出pinctrl_dev結構體)部分,右邊是設備(抽象出device結構體,該結構體含有.pinctrl會和左邊pinctrl_dev掛鉤)部分。

2.1 pinctrl_dev結構體?

我們并不需要自己構建pinctrl_dev,只需要描述它,提供一個pinctrl_desc結構體使用pinctrl_register(函數原型如下)可以構建。

struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc,struct device *dev, void *driver_data)
{struct pinctrl_dev *pctldev;int error;pctldev = pinctrl_init_controller(pctldesc, dev, driver_data);if (IS_ERR(pctldev))return pctldev;error = pinctrl_enable(pctldev);if (error)return ERR_PTR(error);return pctldev;}

當設備樹中的pinctrl節點compatible屬性和驅動中的compatible匹配上之后 ,會調用驅動程序的probe函數。在probe函數中,會分配設置pinctrl_desc結構體,并且使用這個結構體來注冊pinctrl_dev。使用pinctrl_desc中的pins 和 npins來描述一個引腳,使用pctlops結構體來描述一組引腳。

在pctlops結構體中必須要構造下面這些函數:

2.2?client(dev_pin_info)

設備樹上的設備節點不一定都會被構建成dev下的設備,比如I2C的設備節點,會被構建成I2C_client,但是不論構建成哪一個,結構體中都會含有dev結構體。每個device結構體里面都有一個?dev_pin_info結構體,用來保存設備的pinctrl信息。內核已經給出了幾個默認的狀態,每種狀態對應的信息會被保存到對應的pinctrl_state結構體中,也可以自己指定,但是自己指定的狀態會被保存到pinctrl指針的list鏈表中。

在pinctrl這個大節點下面會有很多節點,節點會對應某個狀態,在pinctrl的驅動程序中會使用pinctrl_ops的dt_node_to_map函數,這個函數會把每個子節點轉換成一系列的pinctrl_map,pinctrl_map又會被轉換成一系列的pinctrl_setting,這些pinctrl_setting會被存放在某一個pinctrl.state.settings鏈表里面。

三、設備樹解析流程

在rk3568.dtsi文件中存在pinctrl這個節點,該節點會被轉化成一個dev,當dev的compatible屬性和驅動程序中的?compatible匹配,驅動程序中的probe會被調用,我們根據相關的關系可以找到rk3568的pinctrl的驅動代碼。

dd@dd-NP3020M3:~/RK3568/SDK/linux/rk3568_linux_sdk/kernel/drivers$ grep "rockchip,rk3568-pinctrl" -nr
Binary file pinctrl/pinctrl-rockchip.o matches
pinctrl/pinctrl-rockchip.c:4120:	{ .compatible = "rockchip,rk3568-pinctrl",

作用1. 描述獲得引腳,解析設備樹

1. 單個引腳

可以在開發板上查看

/sys/kernel/debug/pinctrl/pinctrl-rockchip-pinctrl

關于rk3568,下圖是其打印信息

2. 引腳組信息

cat /sys/kernel/debug/pinctrl/pinctrl-rockchip-pinctrl/pingroups

?3. 引腳組功能

3.2 client節點的pinctrl構造過程

3.2.1 設備樹轉換成pinctrl_map?

設備節點和驅動匹配之后調用really_probe,really_probe給每個dev設置綁定pins,并將設備樹信息轉化成pinctrl_map。(pinctrl_bind_pins() 是在 really_probe()且在 probe 之前調用的,這就是內核的 pinctrl 綁定機制。)借用chatgpt回答:

__device_attach_driver()└─ really_probe(dev, drv)└─ dev->driver = drv;└─ pinctrl_bind_pins(dev);  ← 設置 pins└─ drv->probe(dev);         ← 調用你的驅動 probe

四、編寫虛擬的pinctrl驅動程序

首先先來回顧一下pinctrl子系統的三大左右:引腳枚舉與命名、引腳復用、引腳配置。

pinctrl驅動的核心是分配設置構造一個pinctrl_desc結構體

?需要做的事情1、創建設備樹pinctrller節點,編寫驅動程序 2、創建client節點,編寫驅動程序

4.1 設備樹節點?

/{virtual_pincontroller {compatible = "xupt,virtual_pinctrl";i2cgrp{pins{functions = "i2c","i2c";groups = "pin0" , "pin1";configs = <0x11224455 0x8746446>;}}}
}virtual_i2c{compatible = "xupt,virtual_i2c";pinctrl_name = "default";pinctrl_0 = <&i2cgrp>;
}

4.2?virtual_pinctrl_client_drv.c

#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/slab.h>
#include <linux/regmap.h>static const struct of_device_id virtual_client_of_match[] = {{ .compatible = "xupt,virtual_i2c", },{ },
};static int virtual_client_probe(struct platform_device *pdev)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}
static int virtual_client_remove(struct platform_device *pdev)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}static struct platform_driver virtual_client_driver = {.probe		= virtual_client_probe,.remove		= virtual_client_remove,.driver		= {.name	= "xupt,virtual_i2c",.of_match_table = of_match_ptr(virtual_client_of_match),}
};/* 1. 入口函數 */
static int __init virtual_client_init(void)
{	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);/* 1.1 注冊一個platform_driver */return platform_driver_register(&virtual_client_driver);
}/* 2. 出口函數 */
static void __exit virtual_client_exit(void)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);/* 2.1 反注冊platform_driver */platform_driver_unregister(&virtual_client_driver);
}module_init(virtual_client_init);
module_exit(virtual_client_exit);MODULE_LICENSE("GPL");

? 4.3?virtual_pinctrl_drv.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/bitops.h>
#include <linux/gpio.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_device.h>
#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/clk.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#include <linux/rockchip/cpu.h>
#include <dt-bindings/pinctrl/rockchip.h>#include "core.h"static struct pinctrl_dev *g_pctldev;static const struct pinctrl_pin_desc pins[] = {{ 0 ,  "pin0" , NULL},{ 1 ,  "pin1" , NULL},{ 2 ,  "pin2" , NULL},{ 3 ,  "pin3" , NULL},
};static unsigned long g_configs[4];struct virtual_function_desc{const char *func_name;const char **groups;int num_group;
};static const char *func0_pins[] = {"pin0" , "pin1" , "pin2" , "pin3"};
static const char *func1_pins[] = {"pin0" , "pin1"};
static const char *func2_pins[] = {"pin2" , "pin3"};static struct virtual_function_desc g_func_des[] = {{"gpio" , func0_pins , 4},{"i2c" , func1_pins , 2},{"uart" , func2_pins , 2},
};static int virtual_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
{return pctldev->desc->npins;
}static const char *virtual_pinctrl_get_group_name(struct pinctrl_dev *pctldev,unsigned selector)
{return pctldev->desc->pins[selector].name;
}static int virtual_pinctrl_get_group_pins(struct pinctrl_dev *pctldev,unsigned selector, const unsigned **pins,unsigned *npins)
{if (selector >= pctldev->desc->npins)return -EINVAL;*pins = &pctldev->desc->pins[selector].number;*npins = 1;return 0;
}/*	
i2cgrp{pins{functions = "i2c","i2c";groups = "pin0" , "pin1";driver-open-drain;}}one pin ==> two pinctrl_map (one for mux , one for config)*/static int virtual_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev,struct device_node *np,struct pinctrl_map **map, unsigned *num_maps)
{/* alloc pinctrl_map and num_pin */int num_pins = 1;int i;const char *pins;const char *functions;unsigned int config;struct pinctrl_map *new_map;unsigned long *configs;while(1){if(of_property_read_string_index(np, "groups", num_pins, &pins) == 0)num_pins++;else {break;}}new_map = kmalloc( sizeof(*new_map) *num_pins* 2, GFP_KERNEL);*map = new_map;*num_maps = num_pins * 2;for(i = 0 ; i < num_pins; i++){/* get pin / function / config  (from decice tree)*/of_property_read_string_index(np, "functions", i, &pins);of_property_read_string_index(np, "functions", i, &functions);of_property_read_u32_index(np, "configs", i, &config);configs = kmalloc(sizeof(*configs) , GFP_KERNEL);/* save pinctrl_map */new_map[i*2].type = PIN_MAP_TYPE_MUX_GROUP;new_map[i*2].data.mux.function = functions;new_map[i*2].data.mux.group = pins;new_map[i*2 + 1].type = PIN_MAP_TYPE_CONFIGS_PIN;new_map[i*2 + 1].data.configs.group_or_pin = pins;configs[i] = config;new_map[i*2 + 1].data.configs.configs = configs;new_map[i*2 + 1].data.configs.num_configs = 1;}return 0;
}static void virtual_pinctrl_dt_free_map(struct pinctrl_dev *pctldev,struct pinctrl_map *map, unsigned num_maps)
{while(num_maps--){if(map->type == PIN_MAP_TYPE_MUX_GROUP){kfree(map->data.configs.configs);}kfree(map);map++;}
}static const struct pinctrl_ops virtual_pctrl_ops = {.get_groups_count	= virtual_pinctrl_get_groups_count,.get_group_name		= virtual_pinctrl_get_group_name,.get_group_pins		= virtual_pinctrl_get_group_pins,.dt_node_to_map		= virtual_pinctrl_dt_node_to_map,.dt_free_map		= virtual_pinctrl_dt_free_map,
};static int virtual_pinctrl_pmx_get_funcs_count(struct pinctrl_dev *pctldev)
{return ARRAY_SIZE(g_func_des);
}static const char *virtual_pinctrl_pmx_get_func_name(struct pinctrl_dev *pctldev,unsigned selector)
{return g_func_des[selector].func_name;
}static int virtual_pinctrl_pmx_get_groups(struct pinctrl_dev *pctldev,unsigned selector, const char * const **groups,unsigned * const num_groups)
{*groups = g_func_des[selector].groups;*num_groups = g_func_des[selector].num_group;return 0;
}static int virtual_pinctrl_pmx_set(struct pinctrl_dev *pctldev, unsigned selector,unsigned group)
{printk("virtual_pinctrl_pmx_set %s as %s\n" , pctldev->desc->pins[group].name , g_func_des[selector].func_name);return 0;
}/* 實現引腳復用功能,獲取功能數量、功能名、功能包含組,及設置復用。 */
static const struct pinmux_ops virtual_pinctrl_pmx_ops = {.get_functions_count	= virtual_pinctrl_pmx_get_funcs_count,.get_function_name	= virtual_pinctrl_pmx_get_func_name,.get_function_groups	= virtual_pinctrl_pmx_get_groups,.set_mux		= virtual_pinctrl_pmx_set,
};static int virtual_pinctrl_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin,unsigned long *config)
{*config = g_configs[pin];return 0;
}static int virtual_pinctrl_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,unsigned long *configs, unsigned num_configs)
{if(num_configs != 1)return -EINVAL;g_configs[pin] = *configs;printk("config %s as 0x%lx\n" , pctldev->desc->pins[pin].name , *configs);return 0;
}/* 實現引腳配置功能,保存引腳配置狀態并打印配置日志。 */
static const struct pinconf_ops virtual_pinctrl_pinconf_ops = {.pin_config_get			= virtual_pinctrl_pinconf_get,.pin_config_set			= virtual_pinctrl_pinconf_set,.is_generic			= true,
};static int virtual_pinctrl_probe(struct platform_device *pdev)
{/* alloc pinctrl_desc  */struct pinctrl_desc  * desc;desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);/* setting */desc->pins = pins;desc->npins = ARRAY_SIZE(pins);desc->owner = THIS_MODULE;desc->name = "virtual-pinctrl";desc->pctlops = &virtual_pctrl_ops;desc->pmxops = &virtual_pinctrl_pmx_ops;desc->confops = &virtual_pinctrl_pinconf_ops;g_pctldev = devm_pinctrl_register(&pdev->dev, desc, NULL);return 0;
}static const struct of_device_id virtual_pinctrl_dt_match[] = {{ .compatible = "xupt,virtual_pinctrl", .data = NULL },{},
};static struct platform_driver virtual_pinctrl_driver = {.probe		= virtual_pinctrl_probe,.driver = {.name	= "vitrual-pinctrl",.of_match_table = virtual_pinctrl_dt_match,},
};static int __init virtual_pinctrl_drv_init(void)
{return platform_driver_register(&virtual_pinctrl_driver);
}static void __exit virtual_pinctrl_drv_exit(void)
{platform_driver_unregister(&virtual_pinctrl_driver);
}module_init(virtual_pinctrl_drv_init);
module_exit(virtual_pinctrl_drv_exit);
MODULE_LICENSE("GPL");

五、總結

這個子系統主要還是分配設置注冊?pinctrl_desc? 結構體,里面需要提供將設備樹信息轉換成所需要的引腳信息,復用信息,引腳配置等函數,全部加起來就是一大塊內容,涉及超級多的結構體和函數,設計這個的人一定是個大聰明,能把所有的鏈接到一塊去。太厲害了。

對于這幾天每個子系統都是很龐大的一塊知識,聽的云里霧里的。 太難了。

六、問題

pinctrl是在加載pinctrl驅動時設置的引腳,還是在加載模塊時設置的引腳復用呢?還是說會
設置成倆次呢?是在加載使用這些引腳的設備驅動時設置的引腳復用。

__device_attach_driver()└─ really_probe(dev, drv)└─ dev->driver = drv;└─ pinctrl_bind_pins(dev);  ← 設置 pins└─ drv->probe(dev);         ← 調用你的驅動 probe

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/90430.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/90430.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/90430.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Debiased All-in-one Image Restoration with Task Uncertainty Regularization

Abstract 一體化圖像恢復是一項基礎的底層視覺任務&#xff0c;在現實世界中有重要應用。主要挑戰在于在單個模型中處理多種退化情況。雖然當前方法主要利用任務先驗信息來指導恢復模型&#xff0c;但它們通常采用統一的多任務學習&#xff0c;忽略了不同退化任務在模型優化中的…

逆向 qq 音樂 sign,data, 解密 response 返回的 arraybuffer

解密 arraybuffer python requests 請求得到 arraybuffer&#xff0c;轉為 hex 傳遞給 js res_data sign ctx.call("decrypt", response.content.hex())function decrypt(hex) {const bytes new Uint8Array(hex.length / 2);for (let i 0; i < hex.length; i …

PPT處理控件Aspose.Slides教程:在 C# 中將 ODP 轉換為 PPTX

您是否正在尋找可靠的 PowerPoint SDK 來以編程方式開發ODP到PPTX轉換器&#xff1f;本篇博文演示了如何使用 C# 將 ODP 轉換為 PPTX。ODP是一種基于 XML 的演示文稿文件&#xff0c;可能包含圖像、視頻、文本等。但是&#xff0c;將打開的文檔演示文稿轉換為 PowerPoint 格式可…

[746] 使用最小花費爬樓梯

可以從下標0或者1作為起始位置————dp[0] dp[1] 0。一次性可以選擇移動1次或者2次&#xff0c;故當下標>2的時候&#xff0c;到達2有可能是從下標0開始或者下標1開始&#xff0c;cost[0] or cost[1]&#xff1b;到達n&#xff0c;有可能是花費cost[n-1]到達&#xff0c…

樹莓派vsftpd文件傳輸服務器的配置方法

在樹莓派上安裝和配置 vsftpd&#xff08;Very Secure FTP Daemon&#xff09;服務器的步驟如下&#xff1a; 1. 安裝 vsftpd 打開終端&#xff0c;執行以下命令安裝 vsftpd&#xff1a; sudo apt update sudo apt install vsftpd安裝完成后&#xff0c;vsftpd 會自動啟動。可以…

4.服務注冊發現:微服務的神經系統

在微服務架構中,服務之間不再是固定連接,而是高度動態、短暫存在的。如何讓每個服務準確找到彼此,是分布式系統治理的核心問題之一。服務注冊發現機制,正如神經系統之于人體,承擔著連接、協調、感知變化的關鍵角色。 本文將圍繞 Netflix 開源的服務注冊發現組件 Eureka 展…

基于Docker Compose部署Traccar容器與主機MySQL的完整指南

Traccar Docker鏡像內嵌了H2數據庫&#xff0c;該數據庫容量有限&#xff0c;當達到一定容量時&#xff0c;定位數據無法寫入會導致無法定位顯示。為此有必要為Traccar 配置外部數據庫。根據官網文檔和自身經驗我選擇了MySQL。 參考的官方文檔 軟件環境為ubuntu server 24.04版…

paddlehub環境搭建和測試

目錄1.環境搭建1.1 創建conda環境1.2 安裝paddlepaddle和paddlehub1.3 安裝依賴2. 移動端模型部署2.1 安裝移動端模型2.2 測試3. 服務部署3.1 啟動PaddleHub Serving3.2 發送預測請求1.環境搭建 1.1 創建conda環境 conda create --name paddlehub python3.8 conda activate p…

408第三季part2 - 計算機網絡 - ip地址II

理解路由聚合就是從第一個不一樣的往后全置為0題目這里一般來說會到達2個目的地址&#xff0c;但中間有個路由&#xff0c;所以路由聚合一下就行了聚合出來這個然后下一跳就是跳到下一個路由器d前面一樣的不動&#xff0c;不一樣的開始全置為0c再次理解題目這個先匹配169.96.40…

【Unity】MiniGame編輯器小游戲(十一)消消樂【Crush】

更新日期:2025年7月9日。 項目源碼:獲取項目源碼 索引 消消樂【Crush】一、游戲最終效果二、玩法簡介三、正式開始1.定義游戲窗口類2.規劃游戲窗口、視口區域3.方塊 Block①.定義方塊類②.生成方塊所有類型③.生成消消樂棋盤④.繪制收集欄⑤.繪制方塊陣列4.查看方塊擋住的其他…

RK3588 Android SDK 實戰全解析 —— 架構、原理與開發關鍵點

&#x1f4d6; 推薦閱讀&#xff1a;《Yocto項目實戰教程:高效定制嵌入式Linux系統》 &#x1f3a5; 更多學習視頻請關注 B 站&#xff1a;嵌入式Jerry RK3588 Android SDK 實戰全解析 —— 架構、原理與開發關鍵點 作者&#xff1a;嵌入式 Jerry 一、前言 隨著 AIoT、工業智…

從救火到賦能:運維的職責演進與云原生時代的未來圖景

引言:刻板印象的瓦解 提起"運維工程師",許多人腦海中可能仍會浮現這樣的畫面:深夜里守著閃爍的監控屏幕、手忙腳亂地重啟服務器、在布滿網線的機房里穿梭…這曾是運維工作的真實片段,但絕非全貌,更非未來。 在云計算、DevOps、SRE理念和云原生技術棧的沖擊下,…

UDP的socket編程

socket接口int socket(int domain, int type, int protocol);參數說明??參數說明domain協議族&#xff08;地址族&#xff09;&#xff0c;如 AF_INET&#xff08;IPv4&#xff09;、AF_INET6&#xff08;IPv6&#xff09;type套接字類型&#xff0c;UDP 使用 SOCK_DGRAM&…

基于SD-WAN的管件制造數字化產線系統集成方案

1. 背景與目標隨著制造業向智能化、數字化方向轉型&#xff0c;傳統產線面臨著數據割裂、協同效率低下等問題。管件制造作為典型場景&#xff0c;涉及多環節的設計、制造與質檢流程&#xff0c;亟需一套高效的系統集成方案&#xff0c;保障全流程數據貫通與實時協同。本方案基于…

學習open62541 --- [79] 在docker中運行open62541工程

docker是非常流行的容器技術&#xff0c;解決了部署環境不一致的問題&#xff0c;open62541的工程也可以在docker容器中運行&#xff0c;本文講述如何把open62541工程放到docker容器中運行。 本文使用WSL ubuntu 22.04作為宿主環境&#xff0c;其它linux也是一樣。一 拉取debia…

Spring Boot微服務中集成gRPC實踐經驗分享

Spring Boot微服務中集成gRPC實踐經驗分享 一、業務場景描述 在某電商系統中&#xff0c;推薦服務、庫存服務、訂單服務等微服務需要高效、雙向流式通信&#xff0c;RESTHTTP已無法滿足低延遲、高并發和嚴格類型安全的需求。為此&#xff0c;我們選擇在Spring Boot微服務中集成…

springboot項目編寫測試類,亂碼問題解決

?MockMvc 的默認行為? MockMvc ?默認使用 ISO-8859-1 解碼響應&#xff0c;而服務端實際返回 UTF-8 編碼數據 。 Postman 無亂碼是因瀏覽器自動識別編碼&#xff0c;但 MockMvc 需顯式配置。 ?過濾器失效場景? Spring 的 CharacterEncodingFilter ?默認只對 POST 請求生效…

打破傳統,開啟 AR 智慧課堂?

在教育領域&#xff0c;AR 智慧課堂宛如一場及時雨&#xff0c;為傳統教育模式帶來了革命性的變革&#xff0c;讓學習變得更加生動有趣、高效互動。通過 AR 技術&#xff0c;抽象的知識瞬間變得鮮活起來&#xff0c;學生們可以在虛擬與現實交織的世界中&#xff0c;探索歷史的長…

熱烈祝賀 Flink 2.0 存算分離入選 VLDB 2025

VLDB 2025 論文熱烈祝賀 Apache Flink 2.0 的重磅研究成果《Disaggregated State Management in Apache Flink 2.0 》被數據庫領域頂級會議 VLDB 2025 正式接收&#xff01;這項工作由 Apache Flink 社區 聯合 阿里巴巴實時計算 Flink 團隊 以及多位學術界研究人員共同完成&…

蓄電池能量管理matlab的simulink仿真

蓄電池能量管理matlab的simulink仿真模型 AlternatorTableData.mat , 7395 Battery_Management_Lib.mdl , 577258 Readme.txt , 1293 license.txt , 1551 ssc_battery_management.mdl , 221248 ssc_lead_acid_battery_50Ah_ini.m , 1760 ssc_lead_acid_battery_80Ah_ini.m , 1…