一篇帶給你Pinctrl子系統(tǒng)的深入分析
博主假設(shè)大家已經(jīng)看過下面這篇文章:
本文就直接深入分析 pinctrl 子系統(tǒng)。
使用 pinctrl 和 gpio 子系統(tǒng)進(jìn)行 GPIO 驅(qū)動開發(fā),是嵌入式驅(qū)動工程師的基本操作,但大部分驅(qū)動工程師只會用子系統(tǒng)提供的 API 接口,不會對其底層實(shí)現(xiàn)進(jìn)行分析,本文對其底層實(shí)現(xiàn)進(jìn)行分析,文末有參考文章鏈接。
在此框架中,(1)、(2)、(3)由半導(dǎo)體廠商搞定,半導(dǎo)體廠商會利用 Linux 提供的框架,根據(jù)自己的芯片,客制化代碼。普通的驅(qū)動工程師就是調(diào)用 API 即可,就算是寫驅(qū)動了。
本文著重講解(2),所以由圖我們知道,pinctrl 子系統(tǒng)和 gpio 子系統(tǒng)都依賴于(2)的驅(qū)動。
工具
分析源碼可以在線查看 Linux 內(nèi)核源碼,在線網(wǎng)址跳轉(zhuǎn)函數(shù)和查找結(jié)構(gòu)體也很方便:
https://elixir.bootlin.com/linux/latest/source
對嵌入式工程師來講,較少談及設(shè)計(jì)模式、重構(gòu)等,因?yàn)槲覀兪芟抻趦蓚€(gè)方面:硬件、系統(tǒng)(Linux、Android)。很多東西操作系統(tǒng)都規(guī)定好了,留給我們重新設(shè)計(jì)的很少,我們不做設(shè)計(jì)題,我們做填空題。
在 Linux 內(nèi)核源碼中,pinctrl 子系統(tǒng)的代碼大都在 kernel/drivers/pinctrl/...,不同平臺有不同的文件夾。gpio 子系統(tǒng)的代碼大都在 kernel/drivers/gpio/...目錄下。
博主買了正點(diǎn)原子 imx6ull 開發(fā)板,所以就以此板為例程,進(jìn)行分析。
一、主要結(jié)構(gòu)體
pinctrl 子系統(tǒng)主要結(jié)構(gòu)體關(guān)系:
pinctrl_dev 是 pinctrl 子系統(tǒng)的根源結(jié)構(gòu)體,它主要包含三條路:
1、pinctrl_desc:這里包含了pinctrl 子系統(tǒng)三個(gè)最重要的結(jié)構(gòu)體,有三個(gè)操作函數(shù)集,pinctrl_ops 包含了對 PIN 的操作函數(shù)集,pinmux_ops 包含了對 PIN 的復(fù)用函數(shù)集,pinconf_ops 包含了對 PIN 的配置函數(shù),大家可以在自己平臺中點(diǎn)進(jìn)去看看自己平臺實(shí)現(xiàn)了哪個(gè)函數(shù),如何實(shí)現(xiàn)的。
2、pinctrl 結(jié)構(gòu)體:這里包含了 PIN 控制器所控制 PIN 的狀態(tài) state,state 里面包含了 setting,這個(gè) setting 就是在設(shè)備樹中對PIN的設(shè)置,大家點(diǎn)進(jìn)去看相關(guān)數(shù)據(jù)結(jié)構(gòu)就可以看到自己在設(shè)備樹中用到的字符串。
3、gpio 相關(guān)的結(jié)構(gòu)體,我們說過 pinctrl 子系統(tǒng)和 gpio 子系統(tǒng)是耦合的,我們從結(jié)構(gòu)體就可以看得出來,它包含了最重要的結(jié)構(gòu)體 gpio_chip。
我們已經(jīng)闡述了pinctrl 子系統(tǒng)主要的數(shù)據(jù)結(jié)構(gòu),后面講述函數(shù)調(diào)用關(guān)系。
二、函數(shù)調(diào)用邏輯
在文件 drivers/pinctrl/freescale/pinctrl-imx6ul.c 中有如下內(nèi)容:
驅(qū)動的入口是 arch_initcall 中聲明的函數(shù),類似于我們經(jīng)常寫的 module_init,只是 arch_initcall 會先調(diào)用,具體我在這邊寫過:
我們從下往上看,可以看到最后調(diào)用的是 imx6ul_pinctrl_probe 函數(shù),和普通驅(qū)動的 probe 函數(shù)一樣,也就是說,pinctrl 子系統(tǒng)的驅(qū)動也是一個(gè)標(biāo)準(zhǔn)的 platform 驅(qū)動框架,分為:驅(qū)動、設(shè)備、總線。platform 虛擬總線會按照 of_device_id 結(jié)構(gòu)體中的 compatible 屬性去匹配 pinctrl 驅(qū)動和設(shè)備。
在不同芯片廠商的 probe 函數(shù)中,都是對上面所描述數(shù)據(jù)結(jié)構(gòu)的初始化、填充、調(diào)用,釋放。
我們看 292 行,是匹配結(jié)構(gòu)體,根據(jù) compatible 和設(shè)備樹的compatible 字段進(jìn)行匹配,匹配成功執(zhí)行這個(gè) probe 函數(shù)。每個(gè)平臺 probe 函數(shù)都有區(qū)別,就不詳講了。
我們看 292 行的 of_device_id 定義的數(shù)組,最后一個(gè)是空元素,這是必須的,原因是 platform 本身的 match 函數(shù)中需要判斷是否達(dá)到末尾,of_device_id 定義的 compatible 經(jīng)常不止一個(gè),系統(tǒng)需要知道是否匹配到最后一個(gè)元素,這一點(diǎn)一定要注意。
pinctrl_register
probe 函數(shù)后面的調(diào)用中,最重要的是調(diào)用 pinctrl_register 函數(shù),此函數(shù)用于向 Linux 內(nèi)核注冊一個(gè) PIN 控制器,,此函數(shù)原型如下:
- struct pinctrl_dev *pinctrl_register(
- struct pinctrl_desc *pctldesc,
- struct device *dev,
- void *driver_data)
參數(shù) pctldesc 非常重要,因?yàn)榇藚?shù)就是要注冊的 PIN 控制器,PIN 控制器用于配置 SOC的 PIN 復(fù)用功能和電氣特性。參數(shù) pctldesc 是 pinctrl_desc 結(jié)構(gòu)體類型指針,pinctrl_desc 結(jié)構(gòu)體如下所示:
- struct pinctrl_desc {
- const char *name;
- struct pinctrl_pin_desc const *pins;
- unsigned int npins;
- const struct pinctrl_ops *pctlops;
- const struct pinmux_ops *pmxops;
- const struct pinconf_ops *confops;
- struct module *owner;
- #ifdef CONFIG_GENERIC_PINCONF
- unsigned int num_custom_params;
- const struct pinconf_generic_params *custom_params;
- const struct pin_config_item *custom_conf_items;
- #endif
- };
這三個(gè) “_ops” 結(jié)構(gòu)體指針非常重要!因?yàn)檫@三個(gè)結(jié)構(gòu)體就是 PIN 控制器的“工具”,這三個(gè)結(jié)構(gòu)體里面包含了很多操作函數(shù),通過這些操作函數(shù)就可以完成對某一個(gè) PIN 的配置。pinctrl_desc 結(jié)構(gòu)體需要由用戶提供,結(jié)構(gòu)體里面的成員變量也是用戶提供的。但是這個(gè)用戶并不是我們這些使用芯片的程序員,而是半導(dǎo)體廠商,半導(dǎo)體廠商發(fā)布的 Linux 內(nèi)核源碼中已經(jīng)把這些工作做完了。
下面是參考文章,寫的比博主更深入,尤其第一個(gè)鏈接,大家可以查看。
參考文章:
http://www.wowotech.net/sort/gpio_subsystem
https://www.freesion.com/article/89301077969/
https://blog.csdn.net/u012830148/article/details/80609337
《正點(diǎn)原子 imx6u Linux 驅(qū)動開發(fā)指南》