網(wǎng)站推廣預(yù)期達(dá)到的目標(biāo)泰安網(wǎng)絡(luò)推廣培訓(xùn)
往期內(nèi)容
本專欄往期內(nèi)容:
- Pinctrl子系統(tǒng)和其主要結(jié)構(gòu)體引入
- Pinctrl子系統(tǒng)pinctrl_desc結(jié)構(gòu)體進(jìn)一步介紹
- Pinctrl子系統(tǒng)中client端設(shè)備樹相關(guān)數(shù)據(jù)結(jié)構(gòu)介紹和解析
- inctrl子系統(tǒng)中Pincontroller構(gòu)造過程驅(qū)動(dòng)分析:imx_pinctrl_soc_info結(jié)構(gòu)體
input子系統(tǒng)專欄:
- 專欄地址:input子系統(tǒng)
- input角度:I2C觸摸屏驅(qū)動(dòng)分析和編寫一個(gè)簡(jiǎn)單的I2C驅(qū)動(dòng)程序
– 末片,有往期內(nèi)容觀看順序I2C子系統(tǒng)專欄:
- 專欄地址:IIC子系統(tǒng)
- 具體芯片的IIC控制器驅(qū)動(dòng)程序分析:i2c-imx.c-CSDN博客
– 末篇,有往期內(nèi)容觀看順序總線和設(shè)備樹專欄:
- 專欄地址:總線和設(shè)備樹
- 設(shè)備樹與 Linux 內(nèi)核設(shè)備驅(qū)動(dòng)模型的整合-CSDN博客
– 末篇,有往期內(nèi)容觀看順序
前言
本節(jié)就不提供相關(guān)源碼文件了,各個(gè)代碼塊處都有標(biāo)明該函數(shù)的文件路徑,需要的可以自行去查看。
主要講解作為使用者來說去使用pinctrl,其相關(guān)驅(qū)動(dòng)程序是如何去進(jìn)行獲取pinctrl信息,對(duì)其進(jìn)行解析將引腳轉(zhuǎn)為map,在轉(zhuǎn)為setting存儲(chǔ)起來,去進(jìn)行使用,也就是其如何去配置、復(fù)用引腳的。
1.回顧client的數(shù)據(jù)結(jié)構(gòu)
看之前的文章:Pinctrl子系統(tǒng)中client端設(shè)備樹相關(guān)數(shù)據(jù)結(jié)構(gòu)介紹和解析
右側(cè)是Pinctrl節(jié)點(diǎn),左側(cè)是client端節(jié)點(diǎn)。Pinctrl節(jié)點(diǎn)中設(shè)置了要使用的引腳的信息以及復(fù)用的功能,client節(jié)點(diǎn)則是對(duì)要使用的引腳進(jìn)行引用,其實(shí)就是模塊的設(shè)備節(jié)點(diǎn)。
Pin Controller:有自己的驅(qū)動(dòng)程序
virtual_pincontroller {compatible = "100ask,virtual_pinctrl";myled_pin: myled_pin {functions = "gpio";groups = "pin0";configs = <0x11223344>;};
};GPIO Controller:有自己的驅(qū)動(dòng)程序
gpio_virt: virtual_gpiocontroller {compatible = "100ask,virtual_gpio";gpio-controller;#gpio-cells = <2>;ngpios = <4>;
};Client:有自己的驅(qū)動(dòng)程序
myled {compatible = "100ask,leddrv";led-gpios = <&gpio_virt 0 GPIO_ACTIVE_LOW>;pinctrl-names = "default";pinctrl-0 = <&myled_pin>;
};另一種寫法(正確寫法):讓GPIO和Pinctrl之間建立聯(lián)系
/ {pinctrl_virt: virtual_pincontroller {compatible = "100ask,virtual_pinctrl";myled_pin: myled_pin {functions = "gpio";groups = "pin0";configs = <0x11223344>;};i2cgrp: i2cgrp {functions = "i2c", "i2c";groups = "pin0", "pin1";configs = <0x11223344 0x55667788>;};};gpio_virt: virtual_gpiocontroller {compatible = "100ask,virtual_gpio";gpio-controller;#gpio-cells = <2>;ngpios = <4>;gpio-ranges = <&pinctrl_virt 0 0 4>; //GPIO控制器的第0號(hào)引腳對(duì)應(yīng)pinctrl_virt的第0號(hào)引腳(也就是對(duì)應(yīng)myled_pin),數(shù)量為4};myled {compatible = "100ask,leddrv";led-gpios = <&gpio_virt 2 GPIO_ACTIVE_LOW>;};
};
2.client的pinctrl構(gòu)造過程
相同state的描述引腳的設(shè)備樹節(jié)點(diǎn)如何轉(zhuǎn)換為pinctrl_map,這些pinctrl_map又如何轉(zhuǎn)換為pinctrl_setting存放在pinctrl_state結(jié)構(gòu)體中的settings鏈表中
2.1 總圖
比較長(zhǎng),看下圖就行了。
really_probe pinctrl_bind_pinsdev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL);dev->pins->p = devm_pinctrl_get(dev); create_pinctrl(dev);ret = pinctrl_dt_to_map(p);for_each_maps(maps_node, i, map) {ret = add_setting(p, map);}dev->pins->default_state = pinctrl_lookup_state(dev->pins->p,PINCTRL_STATE_DEFAULT);
2.2 解析
如果想深入了解各個(gè)代碼的含義,可以繼續(xù)看:
really_probe //\Linux-4.9.88\drivers\base\dd.c//* If using pinctrl, bind pins now before probing */pinctrl_bind_pins //\Linux-4.9.88\drivers\base\dd.cdev->pins->p = devm_pinctrl_get(dev); //直接從該函數(shù)進(jìn)入看p = pinctrl_get(dev);//D\Linux-4.9.88\drivers\pinctrl\core.c
pinctrl_get內(nèi)部實(shí)現(xiàn)如下,根據(jù)其注釋可以知道,去獲取到pinctrl
的句柄,但是在第一次的時(shí)候肯定是沒有的,所以最后會(huì)去調(diào)用create_pinctrl
函數(shù)去創(chuàng)建pinctrl
\Linux-4.9.88\drivers\pinctrl\core.c/*** pinctrl_get() - retrieves the pinctrl handle for a device* @dev: the device to obtain the handle for*/
struct pinctrl *pinctrl_get(struct device *dev)
{struct pinctrl *p;if (WARN_ON(!dev))return ERR_PTR(-EINVAL);/** See if somebody else (such as the device core) has already* obtained a handle to the pinctrl for this device. In that case,* return another pointer to it.*/p = find_pinctrl(dev);if (p != NULL) {dev_dbg(dev, "obtain a copy of previously claimed pinctrl\n");kref_get(&p->users);return p;}return create_pinctrl(dev);
}
進(jìn)入create_pinctrl
函數(shù)看看:
\Linux-4.9.88\drivers\pinctrl\core.c
/static struct pinctrl *create_pinctrl(struct device *dev)
{struct pinctrl *p; // 指向創(chuàng)建的引腳控制結(jié)構(gòu)體const char *devname; // 設(shè)備的名稱struct pinctrl_maps *maps_node; // 引腳映射節(jié)點(diǎn)指針int i; // 循環(huán)計(jì)數(shù)器struct pinctrl_map const *map; // 當(dāng)前的引腳映射int ret; // 存儲(chǔ)返回值,用于錯(cuò)誤檢查/* * 為每個(gè)映射創(chuàng)建狀態(tài)存儲(chǔ)結(jié)構(gòu)體 pinctrl* 消費(fèi)者可以通過 pinctrl_get() 請(qǐng)求該引腳控制句柄*/p = kzalloc(sizeof(*p), GFP_KERNEL);if (p == NULL) {dev_err(dev, "failed to alloc struct pinctrl\n");return ERR_PTR(-ENOMEM);}p->dev = dev; // 關(guān)聯(lián)設(shè)備指針INIT_LIST_HEAD(&p->states); // 初始化引腳控制的狀態(tài)列表INIT_LIST_HEAD(&p->dt_maps); // 初始化設(shè)備樹的引腳映射列表// 將設(shè)備樹映射到引腳控制結(jié)構(gòu)體中ret = pinctrl_dt_to_map(p);if (ret < 0) { // 檢查映射是否成功kfree(p); // 釋放已分配的內(nèi)存return ERR_PTR(ret); // 返回錯(cuò)誤指針}devname = dev_name(dev); // 獲取設(shè)備名稱mutex_lock(&pinctrl_maps_mutex); // 加鎖以保護(hù)全局映射/* 遍歷所有引腳控制映射以找到合適的映射 */for_each_maps(maps_node, i, map) {/* 檢查映射是否屬于當(dāng)前設(shè)備 */if (strcmp(map->dev_name, devname))continue;// 將映射添加到引腳控制結(jié)構(gòu)體中ret = add_setting(p, map);/** 此時(shí)添加設(shè)置可能會(huì):* - 延遲:如果引腳控制設(shè)備尚未可用* - 失敗:如果設(shè)置是 hog 類型并且引腳控制設(shè)備尚不可用* 如果返回的錯(cuò)誤不是 -EPROBE_DEFER,則積累錯(cuò)誤* 以檢查是否有 -EPROBE_DEFER 的情況,因?yàn)槟鞘亲钤愀獾那闆r。*/if (ret == -EPROBE_DEFER) {pinctrl_free(p, false); // 釋放 pinctrl 結(jié)構(gòu)體mutex_unlock(&pinctrl_maps_mutex); // 解鎖return ERR_PTR(ret); // 返回延遲錯(cuò)誤指針}}mutex_unlock(&pinctrl_maps_mutex); // 解鎖映射互斥量if (ret < 0) {/* 如果發(fā)生延遲以外的其他錯(cuò)誤,返回錯(cuò)誤 */pinctrl_free(p, false);return ERR_PTR(ret);}kref_init(&p->users); // 初始化引用計(jì)數(shù)/* 將 pinctrl 句柄添加到全局列表中 */mutex_lock(&pinctrl_list_mutex);list_add_tail(&p->node, &pinctrl_list);mutex_unlock(&pinctrl_list_mutex);return p; // 返回創(chuàng)建的引腳控制結(jié)構(gòu)體
}
其中最主要的就是ret = pinctrl_dt_to_map(p);
將設(shè)備樹節(jié)點(diǎn)轉(zhuǎn)為mapping,ret = add_setting(p, map);
將mapping轉(zhuǎn)為setting并添加進(jìn)pinctrl
結(jié)構(gòu)體中。這個(gè)在之前對(duì)client端的相關(guān)結(jié)構(gòu)體介紹的時(shí)候也有講解過,下面來看看代碼中是如何實(shí)現(xiàn)的:
2.2.1 轉(zhuǎn)mapping
\Linux-4.9.88\drivers\pinctrl\devicetree.c
int pinctrl_dt_to_map(struct pinctrl *p)
{struct device_node *np = p->dev->of_node; // 獲取設(shè)備樹節(jié)點(diǎn)int state, ret;char *propname;struct property *prop;const char *statename;const __be32 *list;int size, config;phandle phandle;struct device_node *np_config;/* CONFIG_OF 啟用時(shí),p->dev 可能沒有從 DT 中實(shí)例化 */if (!np) {if (of_have_populated_dt())dev_dbg(p->dev, "no of_node; not parsing pinctrl DT\n");return 0;}// 斷言引腳控制設(shè)置ret = dt_gpio_assert_pinctrl(p);if (ret) {dev_dbg(p->dev, "failed to assert pinctrl setting: %d\n", ret);return ret;}// 獲取節(jié)點(diǎn)引用of_node_get(np);/* 遍歷每個(gè)狀態(tài) ID */for (state = 0; ; state++) {// 獲取當(dāng)前狀態(tài)的引腳控制屬性名,如 "pinctrl-0", "pinctrl-1" 等propname = kasprintf(GFP_KERNEL, "pinctrl-%d", state);prop = of_find_property(np, propname, &size); // 查找屬性kfree(propname);if (!prop) {// 若狀態(tài)為 0 但找不到屬性,說明沒有定義if (state == 0) {of_node_put(np);return -ENODEV;}break; // 沒有更多狀態(tài)}list = prop->value; // 獲取屬性值列表size /= sizeof(*list); // 計(jì)算列表中的項(xiàng)數(shù)// 從 "pinctrl-names" 中讀取狀態(tài)名ret = of_property_read_string_index(np, "pinctrl-names", state, &statename);/** 如果沒有在 `pinctrl-names` 中找到狀態(tài)名,將狀態(tài)名設(shè)置為 `pinctrl-*`* 屬性名中的 ID 值。例如,"pinctrl-0" 中的 `0` 可以作為狀態(tài)名*/if (ret < 0) {// 跳過 "pinctrl-" 的 8 個(gè)字符,使用屬性名中的 IDstatename = prop->name + 8;}// 遍歷每個(gè)引用的引腳配置節(jié)點(diǎn)for (config = 0; config < size; config++) {phandle = be32_to_cpup(list++); // 獲取 phandle 值// 查找引腳配置節(jié)點(diǎn) ---- 下圖中標(biāo)注 1np_config = of_find_node_by_phandle(phandle);if (!np_config) {dev_err(p->dev, "prop %s index %i invalid phandle\n",prop->name, config);ret = -EINVAL;goto err;}// 解析引腳配置節(jié)點(diǎn),并創(chuàng)建對(duì)應(yīng)的映射 --- 下圖中標(biāo)注 2ret = dt_to_map_one_config(p, statename, np_config);of_node_put(np_config);if (ret < 0)goto err;}// 如果 DT 中沒有項(xiàng),生成一個(gè)空狀態(tài)表項(xiàng)if (!size) {ret = dt_remember_dummy_state(p, statename);if (ret < 0)goto err;}}return 0;err:pinctrl_dt_free_maps(p); // 如果有錯(cuò)誤,釋放分配的映射return ret;
}
來看看ret = dt_to_map_one_config(p, statename, np_config);
是如何解析設(shè)備樹節(jié)點(diǎn)中的引腳,轉(zhuǎn)為map。
dt_to_map_one_config
函數(shù)用于解析一個(gè)設(shè)備樹中的引腳配置節(jié)點(diǎn) (np_config
),并將其轉(zhuǎn)換為內(nèi)核 pinctrl
映射表。這個(gè)過程涉及查找引腳控制器設(shè)備 (pinctrl_dev
),并調(diào)用設(shè)備特定的解析函數(shù)來生成映射表。 其中最重要的就是調(diào)用到了pinctrl_desc->pinctrl_ops->dt_node_to_map,這個(gè)在之前的pincontroller的數(shù)據(jù)結(jié)構(gòu)講解中有提過(Pinctrl子系統(tǒng)pinctrl_desc結(jié)構(gòu)體進(jìn)一步介紹)。接下來看代碼:
\Linux-4.9.88\Linux-4.9.88\drivers\pinctrl\devicetree.cstatic int dt_to_map_one_config(struct pinctrl *p, const char *statename,struct device_node *np_config)
{struct device_node *np_pctldev;struct pinctrl_dev *pctldev;const struct pinctrl_ops *ops;int ret;struct pinctrl_map *map;unsigned num_maps;/* 查找包含 np_config 的引腳控制器節(jié)點(diǎn) */np_pctldev = of_node_get(np_config); // 獲取配置節(jié)點(diǎn)的引用for (;;) {np_pctldev = of_get_next_parent(np_pctldev); // 獲取上級(jí)節(jié)點(diǎn)if (!np_pctldev || of_node_is_root(np_pctldev)) {dev_info(p->dev, "could not find pctldev for node %s, deferring probe\n",np_config->full_name);of_node_put(np_pctldev); // 釋放節(jié)點(diǎn)引用/* 如果未找到引腳控制器,假設(shè)稍后會(huì)出現(xiàn) */return -EPROBE_DEFER;}pctldev = get_pinctrl_dev_from_of_node(np_pctldev); // 獲取引腳控制器設(shè)備if (pctldev)break; // 找到引腳控制器設(shè)備,退出循環(huán)/* 不要延遲對(duì) hog 配置的探測(cè)(避免循環(huán)依賴) */if (np_pctldev == p->dev->of_node) {of_node_put(np_pctldev);return -ENODEV;}}of_node_put(np_pctldev); // 釋放父節(jié)點(diǎn)的引用/** 調(diào)用引腳控制器驅(qū)動(dòng)解析設(shè)備樹節(jié)點(diǎn),并生成映射表項(xiàng)*/ops = pctldev->desc->pctlops; // 獲取引腳控制器的操作函數(shù)if (!ops->dt_node_to_map) {dev_err(p->dev, "pctldev %s doesn't support DT\n",dev_name(pctldev->dev));return -ENODEV;}ret = ops->dt_node_to_map(pctldev, np_config, &map, &num_maps); // 解析配置if (ret < 0)return ret;/* 將映射表項(xiàng)保存以備后用 */return dt_remember_or_free_map(p, statename, pctldev, map, num_maps);
}
- 查找引腳控制器設(shè)備:函數(shù)首先查找與
np_config
對(duì)應(yīng)的引腳控制器節(jié)點(diǎn)。通過向上遍歷父節(jié)點(diǎn),找到第一個(gè)包含配置的pinctrl_dev
,這是引腳控制器的核心設(shè)備。如果找不到合適的引腳控制器,返回-EPROBE_DEFER
表示稍后再次嘗試探測(cè)。 - 避免循環(huán)依賴:在遍歷父節(jié)點(diǎn)時(shí),如果發(fā)現(xiàn)父節(jié)點(diǎn)是當(dāng)前設(shè)備自身(hog 配置),則返回
-ENODEV
以避免循環(huán)依賴。 - 解析設(shè)備樹節(jié)點(diǎn):找到引腳控制器設(shè)備后,通過
pctlops->dt_node_to_map
調(diào)用設(shè)備特定的函數(shù)來解析np_config
節(jié)點(diǎn)并生成映射表項(xiàng)。 - 存儲(chǔ)映射表項(xiàng):最后,通過
dt_remember_or_free_map
函數(shù)將生成的映射表項(xiàng)存儲(chǔ)到pinctrl
結(jié)構(gòu)體中,以便后續(xù)使用。
ret = ops->dt_node_to_map(pctldev, np_config, &map, &num_maps);
調(diào)用的是哪些map函數(shù)呢????這就得去Pincontroller的驅(qū)動(dòng)程序中去看了,因?yàn)檫@個(gè)函數(shù)是pinctrl_desc->pinctrl_ops的,是屬于Pincontroller的:\Linux-4.9.88\drivers\pinctrl\freescale\pinctrl-imx.c📎pinctrl-imx.c
static const struct pinctrl_ops imx_pctrl_ops = {.get_groups_count = imx_get_groups_count,.get_group_name = imx_get_group_name,.get_group_pins = imx_get_group_pins,.pin_dbg_show = imx_pin_dbg_show,.dt_node_to_map = imx_dt_node_to_map, //就是這個(gè)函數(shù).dt_free_map = imx_dt_free_map,
};
static int imx_dt_node_to_map(struct pinctrl_dev *pctldev,struct device_node *np,struct pinctrl_map **map, unsigned *num_maps)
{struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);const struct imx_pinctrl_soc_info *info = ipctl->info;const struct imx_pin_group *grp;struct pinctrl_map *new_map;struct device_node *parent;int map_num = 1;int i, j;/** 首先找到該節(jié)點(diǎn)的引腳組,并檢查是否需要為引腳創(chuàng)建配置映射*/grp = imx_pinctrl_find_group_by_name(info, np->name);if (!grp) {dev_err(info->dev, "unable to find group for node %s\n",np->name);return -EINVAL;}// 確定所需的映射數(shù)量if (info->flags & IMX8_USE_SCU) {map_num += grp->npins;} else {for (i = 0; i < grp->npins; i++) {if (!(grp->pins[i].pin_conf.pin_memmap.config &IMX_NO_PAD_CTL))map_num++;}}// 分配內(nèi)存存儲(chǔ)新映射new_map = kmalloc(sizeof(struct pinctrl_map) * map_num, GFP_KERNEL);if (!new_map)return -ENOMEM;*map = new_map;*num_maps = map_num;/* 創(chuàng)建復(fù)用映射 */parent = of_get_parent(np);if (!parent) {kfree(new_map);return -EINVAL;}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;of_node_put(parent);/* 創(chuàng)建配置映射 */new_map++;for (i = j = 0; i < grp->npins; i++) {if (info->flags & IMX8_USE_SCU) {new_map[j].type = PIN_MAP_TYPE_CONFIGS_PIN;new_map[j].data.configs.group_or_pin =pin_get_name(pctldev, grp->pins[i].pin);new_map[j].data.configs.configs =(unsigned long *)&grp->pins[i].pin_conf.pin_scu.mux;new_map[j].data.configs.num_configs = 2;j++;} else if (!(grp->pins[i].pin_conf.pin_memmap.config & IMX_NO_PAD_CTL)) {new_map[j].type = PIN_MAP_TYPE_CONFIGS_PIN;new_map[j].data.configs.group_or_pin =pin_get_name(pctldev, grp->pins[i].pin);new_map[j].data.configs.configs =&grp->pins[i].pin_conf.pin_memmap.config;new_map[j].data.configs.num_configs = 1;j++;}}dev_dbg(pctldev->dev, "maps: function %s group %s num %d\n",(*map)->data.mux.function, (*map)->data.mux.group, map_num);return 0;
}
imx_dt_node_to_map
函數(shù)用于將指定設(shè)備樹節(jié)點(diǎn) (np
) 轉(zhuǎn)換為 pinctrl_map
映射表,并將其存儲(chǔ)在 map
指針中,供引腳控制子系統(tǒng)使用。此函數(shù)特定于 i.MX 系列硬件平臺(tái),通過設(shè)備樹節(jié)點(diǎn)的信息生成引腳配置和復(fù)用映射。
-
獲取引腳組:
- 使用
imx_pinctrl_find_group_by_name
函數(shù),根據(jù)節(jié)點(diǎn)名稱 (np->name
) 查找對(duì)應(yīng)的引腳組 (grp
)。
- 使用
-
計(jì)算映射數(shù)量:
- 如果
info->flags
標(biāo)記中設(shè)置了IMX8_USE_SCU
,則每個(gè)引腳都需要一個(gè)配置映射,因此總映射數(shù)為1 + grp->npins
。 - 否則,遍歷每個(gè)引腳,僅為需要配置的引腳增加映射數(shù)。
- 如果
-
內(nèi)存分配:
- 使用
kmalloc
為映射數(shù)組分配內(nèi)存,并初始化map
和num_maps
指針。 - 如果內(nèi)存分配失敗,函數(shù)返回
-ENOMEM
錯(cuò)誤。
- 使用
-
創(chuàng)建復(fù)用映射:
- 獲取父節(jié)點(diǎn),檢查其名稱并為其創(chuàng)建復(fù)用 (
MUX
) 映射。 - 將父節(jié)點(diǎn)的
name
作為function
,當(dāng)前節(jié)點(diǎn)的name
作為group
。
- 獲取父節(jié)點(diǎn),檢查其名稱并為其創(chuàng)建復(fù)用 (
-
創(chuàng)建配置映射:
- 遍歷引腳組中的每個(gè)引腳,若符合條件,則創(chuàng)建配置映射。
- 使用
pin_get_name
函數(shù)獲取引腳名稱,并設(shè)置相應(yīng)的配置。 IMX8_USE_SCU
標(biāo)志影響配置內(nèi)容;如果未設(shè)置此標(biāo)志,則僅為未設(shè)置IMX_NO_PAD_CTL
的引腳創(chuàng)建配置。
-
調(diào)試信息:
- 使用
dev_dbg
輸出映射的function
、group
和映射數(shù)量 (map_num
) 以供調(diào)試。
- 使用
2.2.2 mapping轉(zhuǎn)setting
那么解析獲取到mapping后,就要去轉(zhuǎn)化為setting,回到create_pinctrl
函數(shù):
\Linux-4.9.88\drivers\pinctrl\core.c
/static struct pinctrl *create_pinctrl(struct device *dev)
{struct pinctrl *p; // 指向創(chuàng)建的引腳控制結(jié)構(gòu)體const char *devname; // 設(shè)備的名稱struct pinctrl_maps *maps_node; // 引腳映射節(jié)點(diǎn)指針int i; // 循環(huán)計(jì)數(shù)器struct pinctrl_map const *map; // 當(dāng)前的引腳映射int ret; // 存儲(chǔ)返回值,用于錯(cuò)誤檢查//...............// 將設(shè)備樹映射到引腳控制結(jié)構(gòu)體中ret = pinctrl_dt_to_map(p);devname = dev_name(dev); // 獲取設(shè)備名稱mutex_lock(&pinctrl_maps_mutex); // 加鎖以保護(hù)全局映射/* 遍歷所有引腳控制映射以找到合適的映射 */for_each_maps(maps_node, i, map) {/* 檢查映射是否屬于當(dāng)前設(shè)備 */if (strcmp(map->dev_name, devname))continue;// 將映射添加到引腳控制結(jié)構(gòu)體中ret = add_setting(p, map);}//...............}
直接省略掉其它內(nèi)容,對(duì)于pinctrl_dt_to_map
在上文講到過,那么接下來就是add_setting
函數(shù),把每一個(gè)pinctrl_map轉(zhuǎn)換為pinctrl_setting:
static int add_setting(struct pinctrl *p, struct pinctrl_map const *map)
{struct pinctrl_state *state;struct pinctrl_setting *setting;int ret;// 查找指定狀態(tài),如果不存在則創(chuàng)建一個(gè)新的狀態(tài)state = find_state(p, map->name);if (!state)state = create_state(p, map->name);if (IS_ERR(state))return PTR_ERR(state);// 如果映射類型是 PIN_MAP_TYPE_DUMMY_STATE,則無需添加設(shè)置,直接返回if (map->type == PIN_MAP_TYPE_DUMMY_STATE)return 0;// 為 pinctrl_setting 結(jié)構(gòu)體分配內(nèi)存,用于存儲(chǔ)此設(shè)置的信息setting = kzalloc(sizeof(*setting), GFP_KERNEL);if (setting == NULL) {dev_err(p->dev,"failed to alloc struct pinctrl_setting\n");return -ENOMEM;}// 設(shè)置類型,表示這是一個(gè)復(fù)用組或引腳配置setting->type = map->type;// 獲取映射中指定的 pinctrl_dev(引腳控制設(shè)備)setting->pctldev = get_pinctrl_dev_from_devname(map->ctrl_dev_name);if (setting->pctldev == NULL) {// 如果設(shè)備名稱無法解析,釋放內(nèi)存并判斷是否需要延遲加載驅(qū)動(dòng)kfree(setting);// 對(duì)于 hog 引腳,不允許延遲加載,防止循環(huán)依賴if (!strcmp(map->ctrl_dev_name, map->dev_name))return -ENODEV;// 設(shè)備驅(qū)動(dòng)還未加載,輸出提示信息并返回 -EPROBE_DEFER 表示延遲加載dev_info(p->dev, "unknown pinctrl device %s in map entry, deferring probe",map->ctrl_dev_name);return -EPROBE_DEFER;}// 設(shè)置設(shè)備名稱setting->dev_name = map->dev_name;// 根據(jù)映射類型,將映射轉(zhuǎn)換為具體的設(shè)置內(nèi)容switch (map->type) {case PIN_MAP_TYPE_MUX_GROUP:// 如果是引腳復(fù)用組,調(diào)用 pinmux_map_to_setting 進(jìn)行設(shè)置轉(zhuǎn)換ret = pinmux_map_to_setting(map, setting);break;case PIN_MAP_TYPE_CONFIGS_PIN:case PIN_MAP_TYPE_CONFIGS_GROUP:// 如果是引腳或組的配置映射,調(diào)用 pinconf_map_to_setting 進(jìn)行設(shè)置轉(zhuǎn)換ret = pinconf_map_to_setting(map, setting);break;default:// 其他類型無效,返回錯(cuò)誤ret = -EINVAL;break;}if (ret < 0) {// 如果設(shè)置轉(zhuǎn)換失敗,釋放分配的內(nèi)存并返回錯(cuò)誤碼kfree(setting);return ret;}// 將新的設(shè)置添加到狀態(tài)的設(shè)置列表末尾list_add_tail(&setting->node, &state->settings);return 0;
}
主要就是pinmux_map_to_setting
和pinconf_map_to_setting
函數(shù), 根據(jù)映射類型(復(fù)用組、引腳配置等),調(diào)用相應(yīng)的函數(shù) (pinmux_map_to_setting
或 pinconf_map_to_setting
) 將映射轉(zhuǎn)換為具體的引腳設(shè)置。 pinmux和pinconf在Pinccontroller結(jié)構(gòu)體的講解中也講過
先來看pinmux_map_to_setting
函數(shù), 將一個(gè) pinctrl_map
(引腳控制映射)條目中的復(fù)用功能映射轉(zhuǎn)換為一個(gè)具體的 pinctrl_setting
設(shè)置,用于配置指定設(shè)備的引腳復(fù)用功能。
- 設(shè)備和操作集獲取:獲取
pinctrl_dev
(引腳控制設(shè)備)和pinmux_ops
操作集,用于查詢功能對(duì)應(yīng)的引腳組并設(shè)置引腳復(fù)用。 - 功能選擇器索引轉(zhuǎn)換:將
map->data.mux.function
中指定的功能名稱轉(zhuǎn)換為功能選擇器索引,并存儲(chǔ)在setting->data.mux.func
中。 - 功能支持組查詢:通過
pmxops->get_function_groups
函數(shù)獲取該功能支持的引腳組列表和數(shù)量,確保該功能可以選擇特定的引腳組。 - 引腳組驗(yàn)證:如果映射條目中指定了具體的引腳組名稱,則檢查該組是否在功能的支持列表中;如果未指定,則使用默認(rèn)的第一個(gè)組。
- 組選擇器索引轉(zhuǎn)換:將引腳組名稱轉(zhuǎn)換為組選擇器索引,并存儲(chǔ)在
setting->data.mux.group
中。 - 返回設(shè)置:將轉(zhuǎn)換后的功能和組選擇器索引存入
pinctrl_setting
,用于后續(xù)的引腳控制配置操作。如果過程中發(fā)生錯(cuò)誤,則返回相應(yīng)的錯(cuò)誤碼。
\Linux-4.9.88\drivers\pinctrl\pinmux.c
int pinmux_map_to_setting(struct pinctrl_map const *map,struct pinctrl_setting *setting)
{// 獲取 pinctrl_dev 設(shè)備和 pinmux_ops 操作集,用于執(zhí)行引腳復(fù)用操作struct pinctrl_dev *pctldev = setting->pctldev;const struct pinmux_ops *pmxops = pctldev->desc->pmxops;char const * const *groups; // 存儲(chǔ)功能所支持的組名unsigned num_groups; // 功能所支持的組的數(shù)量int ret;const char *group;// 如果設(shè)備不支持引腳復(fù)用操作,返回錯(cuò)誤if (!pmxops) {dev_err(pctldev->dev, "does not support mux function\n");return -EINVAL;}// 將功能名稱轉(zhuǎn)換為功能選擇器索引ret = pinmux_func_name_to_selector(pctldev, map->data.mux.function);if (ret < 0) {dev_err(pctldev->dev, "invalid function %s in map table\n",map->data.mux.function);return ret;}setting->data.mux.func = ret; // 設(shè)置功能選擇器// 查詢?cè)摴δ軐?duì)應(yīng)的引腳組,獲取組的列表和組數(shù)ret = pmxops->get_function_groups(pctldev, setting->data.mux.func,&groups, &num_groups);if (ret < 0) {dev_err(pctldev->dev, "can't query groups for function %s\n",map->data.mux.function);return ret;}// 如果功能不支持任何引腳組,返回錯(cuò)誤if (!num_groups) {dev_err(pctldev->dev,"function %s can't be selected on any group\n",map->data.mux.function);return -EINVAL;}// 如果映射指定了特定的引腳組,驗(yàn)證該組是否存在于支持的組列表中if (map->data.mux.group) {group = map->data.mux.group;ret = match_string(groups, num_groups, group);if (ret < 0) {dev_err(pctldev->dev,"invalid group \"%s\" for function \"%s\"\n",group, map->data.mux.function);return ret;}} else {// 如果未指定引腳組,則使用默認(rèn)的第一個(gè)組group = groups[0];}// 將組名轉(zhuǎn)換為組選擇器索引ret = pinctrl_get_group_selector(pctldev, group);if (ret < 0) {dev_err(pctldev->dev, "invalid group %s in map table\n",map->data.mux.group);return ret;}setting->data.mux.group = ret; // 設(shè)置組選擇器return 0; // 成功返回 0
}
再來看看pinconf_map_to_setting
函數(shù), 將 pinctrl_map
(引腳控制映射)中的引腳或引腳組的配置映射轉(zhuǎn)換為 pinctrl_setting
設(shè)置,用于對(duì)指定設(shè)備的引腳或引腳組進(jìn)行特定的配置。
-
設(shè)備獲取:從
pinctrl_setting
中獲取pinctrl_dev
設(shè)備,用于引腳或引腳組的配置映射。 -
配置類型判斷:根據(jù)
setting->type
判斷配置類型。- 單引腳配置 (PIN_MAP_TYPE_CONFIGS_PIN):根據(jù)引腳名稱獲取引腳索引,并存儲(chǔ)在
setting->data.configs.group_or_pin
字段中。 - 引腳組配置 (PIN_MAP_TYPE_CONFIGS_GROUP):根據(jù)引腳組名稱獲取引腳組選擇器索引,并存儲(chǔ)在
setting->data.configs.group_or_pin
字段中。
- 單引腳配置 (PIN_MAP_TYPE_CONFIGS_PIN):根據(jù)引腳名稱獲取引腳索引,并存儲(chǔ)在
-
配置項(xiàng)存儲(chǔ):將
map
中的配置數(shù)量和配置數(shù)組存儲(chǔ)到setting
中,以便后續(xù)的配置應(yīng)用。 -
返回狀態(tài):如果成功執(zhí)行映射和存儲(chǔ),則返回 0;如果引腳或組未找到,或類型無效,則返回相應(yīng)的錯(cuò)誤碼。
int pinconf_map_to_setting(struct pinctrl_map const *map,struct pinctrl_setting *setting)
{// 獲取與當(dāng)前設(shè)置相關(guān)聯(lián)的 pinctrl_dev 設(shè)備struct pinctrl_dev *pctldev = setting->pctldev;int pin;// 根據(jù)配置類型執(zhí)行相應(yīng)的操作switch (setting->type) {case PIN_MAP_TYPE_CONFIGS_PIN: // 單個(gè)引腳配置// 根據(jù)引腳名稱獲取引腳索引pin = pin_get_from_name(pctldev, map->data.configs.group_or_pin);if (pin < 0) { // 如果獲取失敗,打印錯(cuò)誤信息并返回錯(cuò)誤碼dev_err(pctldev->dev, "could not map pin config for \"%s\"",map->data.configs.group_or_pin);return pin;}// 將引腳索引存儲(chǔ)在設(shè)置的 group_or_pin 字段中setting->data.configs.group_or_pin = pin;break;case PIN_MAP_TYPE_CONFIGS_GROUP: // 引腳組配置// 根據(jù)引腳組名稱獲取引腳組選擇器索引pin = pinctrl_get_group_selector(pctldev, map->data.configs.group_or_pin);if (pin < 0) { // 如果獲取失敗,打印錯(cuò)誤信息并返回錯(cuò)誤碼dev_err(pctldev->dev, "could not map group config for \"%s\"",map->data.configs.group_or_pin);return pin;}// 將組選擇器索引存儲(chǔ)在設(shè)置的 group_or_pin 字段中setting->data.configs.group_or_pin = pin;break;default:// 如果配置類型無效,返回 -EINVAL 錯(cuò)誤碼return -EINVAL;}// 設(shè)置配置項(xiàng)的數(shù)量和配置數(shù)組setting->data.configs.num_configs = map->data.configs.num_configs;setting->data.configs.configs = map->data.configs.configs;return 0; // 成功返回 0
}
3.切換state情景分析
這一部分看下圖簡(jiǎn)單了解一下就行了。
really_probepinctrl_bind_pinspinctrl_select_state/* Apply all the settings for the new state */list_for_each_entry(setting, &state->settings, node) {switch (setting->type) {case PIN_MAP_TYPE_MUX_GROUP:ret = pinmux_enable_setting(setting);ret = ops->set_mux(...);break;case PIN_MAP_TYPE_CONFIGS_PIN:case PIN_MAP_TYPE_CONFIGS_GROUP:ret = pinconf_apply_setting(setting);ret = ops->pin_config_group_set(...);break;default:ret = -EINVAL;break;}