1. 程式人生 > >gpio子系統和pinctrl子系統(下)

gpio子系統和pinctrl子系統(下)

轉載地址:http://www.cnblogs.com/rongpmcu/p/7662756.html

情景分析

打算從兩個角度來情景分析,先從bsp驅動工程師的角度,然後是驅動工程師的角度,下面以三星s3c6410 Pinctrl-samsung.c為例看看pinctrl輸入引數的初始化過程(最開始的zynq平臺的pin配置貌似是通過bitstreams來的,核心層沒看到有關配置pin的程式碼,不過最新的zynq程式碼里加入了pinctrl,但我手上的恰好的較早其的zynq程式碼,所以這裡以三星的程式碼為例子),不過這裡貼的程式碼有點多(儘量將無關的程式碼刪掉),耐心的看吧^_^

bsp驅動工程師的角度

static int samsung_pinctrl_probe(struct platform_device *pdev)
{
    ...
... ... //解析pinctrl資訊,後面分析 ctrl = samsung_pinctrl_get_soc_data(drvdata, pdev); drvdata->ctrl = ctrl; drvdata->dev = dev; ... ... ... //向gpio子系統註冊(三星有用gpio子系統) ret = samsung_gpiolib_register(pdev, drvdata); if (ret) return ret; //向pinctrl子系統註冊 ret = samsung_pinctrl_register(pdev, drvdata); if
(ret) { samsung_gpiolib_unregister(pdev, drvdata); return ret; } ... ... ... return 0; }

先貼下6410 pinctrl裝置樹資訊(arch/arm/boot/dts/s3c64xx.dtsi):

aliases {                                                                   
    i2c0 = &i2c0;                                                           
    pinctrl0 = &pinctrl0
; }; pinctrl0: [email protected] { compatible = "samsung,s3c64xx-pinctrl"; reg = <0x7f008000 0x1000>; interrupt-parent = <&vic1>; interrupts = <21>; pctrl_int_map: pinctrl-interrupt-map { interrupt-map = <0 &vic0 0>, <1 &vic0 1>, <2 &vic1 0>, <3 &vic1 1>; #address-cells = <0>; #size-cells = <0>; #interrupt-cells = <1>; }; wakeup-interrupt-controller { compatible = "samsung,s3c64xx-wakeup-eint"; interrupts = <0>, <1>, <2>, <3>; interrupt-parent = <&pctrl_int_map>; }; };

下面邊看程式碼邊對照上面的裝置樹描述,看看解析過程:

static struct samsung_pin_ctrl *samsung_pinctrl_get_soc_data(
                struct samsung_pinctrl_drv_data *d,
                struct platform_device *pdev)
{
    int id;
    const struct of_device_id *match;
    struct device_node *node = pdev->dev.of_node;
    struct device_node *np;
    struct samsung_pin_ctrl *ctrl;
    struct samsung_pin_bank *bank;
    int i;

    //獲取pinctrl的alias id,其實就是上面的pinctrl0了
    id = of_alias_get_id(node, "pinctrl");
    if (id < 0) {
        dev_err(&pdev->dev, "failed to get alias id\n");
        return NULL;
    }
    //獲取該節點對應的match
    match = of_match_node(samsung_pinctrl_dt_match, node);
    //通過id找到對應的pinctrl,因為三星的有些soc是存在多個pinctrl的,
    //也就是說pinctrl0,pinctrl1等等同時存在,這裡就是獲取第id個,對於6410,就一個
    //struct samsung_pin_ctrl s3c64xx_pin_ctrl[] = {
    //    {
    //        /* pin-controller instance 1 data */
    //        .pin_banks    = s3c64xx_pin_banks0,
    //        .nr_banks = ARRAY_SIZE(s3c64xx_pin_banks0),
    //        .eint_gpio_init = s3c64xx_eint_gpio_init,
    //        .eint_wkup_init = s3c64xx_eint_eint0_init,
    //        .label        = "S3C64xx-GPIO",
    //    },
    //};
    對於exynos5420,就存在多個啦:
    //struct samsung_pin_ctrl exynos5420_pin_ctrl[] = {
    //    {
    //        /* pin-controller instance 0 data */
    //        .pin_banks    = exynos5420_pin_banks0,
    //        .nr_banks = ARRAY_SIZE(exynos5420_pin_banks0),
    //        .geint_con    = EXYNOS_GPIO_ECON_OFFSET,
    //        .geint_mask   = EXYNOS_GPIO_EMASK_OFFSET,
    //        .geint_pend   = EXYNOS_GPIO_EPEND_OFFSET,
    //        .weint_con    = EXYNOS_WKUP_ECON_OFFSET,
    //        .weint_mask   = EXYNOS_WKUP_EMASK_OFFSET,
    //        .weint_pend   = EXYNOS_WKUP_EPEND_OFFSET,
    //        .svc      = EXYNOS_SVC_OFFSET,
    //        .eint_gpio_init = exynos_eint_gpio_init,
    //        .eint_wkup_init = exynos_eint_wkup_init,
    //        .label        = "exynos5420-gpio-ctrl0",
    //    }, {
    //        /* pin-controller instance 1 data */
    //        .pin_banks    = exynos5420_pin_banks1,
    //        .nr_banks = ARRAY_SIZE(exynos5420_pin_banks1),
    //        .geint_con    = EXYNOS_GPIO_ECON_OFFSET,
    //       .geint_mask    = EXYNOS_GPIO_EMASK_OFFSET,
    //        .geint_pend   = EXYNOS_GPIO_EPEND_OFFSET,
    //        .svc      = EXYNOS_SVC_OFFSET,
    //        .eint_gpio_init = exynos_eint_gpio_init,
    //        .label        = "exynos5420-gpio-ctrl1",
    //    },
    //    ...
    //    ...
    //    ...
    //};
    ctrl = (struct samsung_pin_ctrl *)match->data + id;
    //提取pin ctrl裡的banks資訊,這裡就是ARRAY_SIZE(s3c64xx_pin_banks0)
    bank = ctrl->pin_banks;
    //遍歷每一個bank,填充相應的資訊
    for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
        spin_lock_init(&bank->slock);
        bank->drvdata = d;
        //設定bank的pin base
        bank->pin_base = ctrl->nr_pins;
        //更新ctrl->nr_pins,即該pin ctrl的pin數量,在後面的註冊時會用到該成員
        ctrl->nr_pins += bank->nr_pins;
    }

    //遍歷該節點的每一個子節點,上面的s3c64xx.dtsi檔案末尾有一個
    //#include "s3c64xx-pinctrl.dtsi" 語句,s3c64xx-pinctrl.dtsi裡
    //的資訊是對當前節點pinctrl0的補充,內容如下:
    //&pinctrl0 {                                                                     
    ///*                                                                          
    // * Pin banks                                                                
    // */                                                                         
    //
    //gpa: gpa {                                                                  
    //    gpio-controller;                                                        
    //    #gpio-cells = <2>;                                                      
    //    interrupt-controller;                                                   
    //    #interrupt-cells = <2>;                                                 
    //};                                                                          
    //
    //gpb: gpb {                                                                  
    //    gpio-controller;                                                        
    //    #gpio-cells = <2>;                                                      
    //    interrupt-controller;                                                   
    //    #interrupt-cells = <2>;                                                 
    //};                                                                          

    //gpc: gpc {                                                                  
    //    gpio-controller;                                                        
    //    #gpio-cells = <2>;                                                      
    //    interrupt-controller;                                                   
    //    #interrupt-cells = <2>;                                                 
    //};          
    //...
    //...
    //...
    //hsi_bus: hsi-bus {                                                          
    //    samsung,pins = "gpk-0", "gpk-1", "gpk-2", "gpk-3",                      
    //            "gpk-4", "gpk-5", "gpk-6", "gpk-7";                             
    //    samsung,pin-function = <3>;                                             
    //    samsung,pin-pud = <PIN_PULL_NONE>;                                      
    //};     
    //}
    //這裡就是處理這些子節點
    for_each_child_of_node(node, np) {
        //如果該子節點沒有gpio-controller屬性,跳過處理,這裡處理的是bank
        //只和gpio有關,所以跳過不關心的
        if (!of_find_property(np, "gpio-controller", NULL))
            continue;
        bank = ctrl->pin_banks;
        for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
            if (!strcmp(bank->name, np->name)) {
                //將bank對應到它自己的裝置節點
                bank->of_node = np;
                break;
            }
        }
    }

    ctrl->base = pin_base;
    pin_base += ctrl->nr_pins;

    return ctrl;
}

填充完必要的資訊,就開始註冊了,先看pinctrl的註冊吧!注意,傳入的引數drvdata是已經經過前面的解析填入了很多資訊的

static int samsung_pinctrl_register(struct platform_device *pdev,
                    struct samsung_pinctrl_drv_data *drvdata)
{
    struct pinctrl_desc *ctrldesc = &drvdata->pctl;
    struct pinctrl_pin_desc *pindesc, *pdesc;
    struct samsung_pin_bank *pin_bank;
    char *pin_names;
    int pin, bank, ret;

    //初始化pinctrl_desc,register的時候要用
    ctrldesc->name = "samsung-pinctrl";
    ctrldesc->owner = THIS_MODULE;
    //這個ops是必須要的,裡面的幾個函式前面也都用到了,主要有
    //get_groups_count、dt_node_to_map、get_group_pins
    ctrldesc->pctlops = &samsung_pctrl_ops;
    //這個是pinctrl chip driver根據自己平臺的特性,可選的支援的
    //主要有request、get_functions_count、get_function_groups、
    //enable,和gpio相關的還有額外幾個gpio_request_enable、gpio_disable_free、gpio_set_direction
    ctrldesc->pmxops = &samsung_pinmux_ops;
    //這個是pinctrl chip driver根據自己平臺的特性,可選的支援的
    //主要有pin_config_get、pin_config_set、pin_config_group_get、pin_config_group_set
    ctrldesc->confops = &samsung_pinconf_ops;

    //下面這部分也是pinctrl chip driver根據自己平臺的特性必須填充的,用於表示該pinctrl chip
    //所有的pin資訊
    pindesc = devm_kzalloc(&pdev->dev, sizeof(*pindesc) *
            drvdata->ctrl->nr_pins, GFP_KERNEL);
    if (!pindesc) {
        dev_err(&pdev->dev, "mem alloc for pin descriptors failed\n");
        return -ENOMEM;
    }
    ctrldesc->pins = pindesc;
    ctrldesc->npins = drvdata->ctrl->nr_pins;//該成員就是samsung_pin_ctrl填充的

    //填充pin號
    /* dynamically populate the pin number and pin name for pindesc */
    for (pin = 0, pdesc = pindesc; pin < ctrldesc->npins; pin++, pdesc++)
        pdesc->number = pin + drvdata->ctrl->base;//該成員也是由samsung_pin_ctrl填充的

    //分配空間,用於填充pin名字
    /*
     * allocate space for storing the dynamically generated names for all
     * the pins which belong to this pin-controller.
     */
    pin_names = devm_kzalloc(&pdev->dev, sizeof(char) * PIN_NAME_LENGTH *
                    drvdata->ctrl->nr_pins, GFP_KERNEL);
    if (!pin_names) {
        dev_err(&pdev->dev, "mem alloc for pin names failed\n");
        return -ENOMEM;
    }

    /* for each pin, the name of the pin is pin-bank name + pin number */
    for (bank = 0; bank < drvdata->ctrl->nr_banks; bank++) {
        pin_bank = &drvdata->ctrl->pin_banks[bank];
        for (pin = 0; pin < pin_bank->nr_pins; pin++) {
            //填充pin的名字,注意這裡的格式,裝置樹裡的命名就得按照該格式,即bank名字+pin號
            sprintf(pin_names, "%s-%d", pin_bank->name, pin);
            pdesc = pindesc + pin_bank->pin_base + pin;
            pdesc->name = pin_names;
            pin_names += PIN_NAME_LENGTH;
        }
    }
    
    //到現在,離註冊需要的條件就剩function和group的填充了,其實它們不是pinctrl子系統要求的,
    //但是回撥函式的實現依賴這些,因此需要解析裝置樹資訊來填充它們,後面會詳細分析該函式
    ret = samsung_pinctrl_parse_dt(pdev, drvdata);
    if (ret)
        return ret;
    
    //一切準備好後,就註冊了
    drvdata->pctl_dev = pinctrl_register(ctrldesc, &pdev->dev, drvdata);
    if (!drvdata->pctl_dev) {
        dev_err(&pdev->dev, "could not register pinctrl driver\n");
        return -EINVAL;
    }

    //
    for (bank = 0; bank < drvdata->ctrl->nr_banks; ++bank) {
        pin_bank = &drvdata->ctrl->pin_banks[bank];
        pin_bank->grange.name = pin_bank->name;
        pin_bank->grange.id = bank;
        pin_bank->grange.pin_base = pin_bank->pin_base;
        pin_bank->grange.base = pin_bank->gpio_chip.base;
        pin_bank->grange.npins = pin_bank->gpio_chip.ngpio;
        pin_bank->grange.gc = &pin_bank->gpio_chip;
        pinctrl_add_gpio_range(drvdata->pctl_dev, &pin_bank->grange);
    }

    return 0;
}

samsung_pinctrl_parse_dt分析:

static int samsung_pinctrl_parse_dt(struct platform_device *pdev,
                    struct samsung_pinctrl_drv_data *drvdata)
{
    ...
    //獲取pinctrl裝置的子節點數量,前面已經講過有哪些子節點了,不再重複
    grp_cnt = of_get_child_count(dev_np);
    if (!grp_cnt)
        return -EINVAL;
    
    //根據獲取的數量,分配空間,每個配置節點對應於一個group(pin的集合)
    groups = devm_kzalloc(dev, grp_cnt * sizeof(*groups), GFP_KERNEL);
    if (!groups) {
        dev_err(dev, "failed allocate memory for ping group list\n");
        return -EINVAL;
    }
    grp = groups;

    //根據獲取的數量,分配空間,每個配置節點對應的功能
    functions = devm_kzalloc(dev, grp_cnt * sizeof(*functions), GFP_KERNEL);
    if (!functions) {
        dev_err(dev, "failed to allocate memory for function list\n");
        return -EINVAL;
    }
    func = functions;

    //遍歷每一個子節點,一個個處理
    /*
     * Iterate over all the child nodes of the pin controller node
     * and create pin groups and pin function lists.
     */
    for_each_child_of_node(dev_np, cfg_np) {
        u32 function;
        //檢查samsung,pins屬性
        if (!of_find_property(cfg_np, "samsung,pins", NULL))
            continue;

        //將samsung,pins屬性裡面指定的名字列表轉換為pin號列表
        //,這裡面會用到前面samsung_pinctrl_get_soc_data填充的資訊來匹配
        ret = samsung_pinctrl_parse_dt_pins(pdev, cfg_np,
                    &drvdata->pctl, &pin_list, &npins);
        if (ret)
            return ret;

        //下面就是構成一個pin group了,注意pin組的名字
        //,是配置節點名+GROUP_SUFFIX,GROUP_SUFFIX為-grp
        /* derive pin group name from the node name */
        gname = devm_kzalloc(dev, strlen(cfg_np->name) + GSUFFIX_LEN,
                    GFP_KERNEL);
        if (!gname) {
            dev_err(dev, "failed to alloc memory for group name\n");
            return -ENOMEM;
        }
        sprintf(gname, "%s%s", cfg_np->name, GROUP_SUFFIX);

        grp->name = gname;
        grp->pins = pin_list;
        grp->num_pins = npins;
        of_property_read_u32(cfg_np, "samsung,pin-function", &function);
        grp->func = function;
        grp++;

        if (!of_find_property(cfg_np, "samsung,pin-function", NULL))
            continue;

        //如果存在samsung,pin-function屬性,那麼構建一個功能名
        //,功能名組合方式是配置節點名+FUNCTION_SUFFIX,FUNCTION_SUFFIX為-mux
        /* derive function name from the node name */
        fname = devm_kzalloc(dev, strlen(cfg_np->name) + FSUFFIX_LEN,
                    GFP_KERNEL);
        if (!fname) {
            dev_err(dev, "failed to alloc memory for func name\n");
            return -ENOMEM;
        }
        sprintf(fname, "%s%s", cfg_np->name, FUNCTION_SUFFIX);

        func->name = fname;
        func->groups = devm_kzalloc(dev, sizeof(char *), GFP_KERNEL);
        if (!func->groups) {
            dev_err(dev, "failed to alloc memory for group list "
                    "in pin function");
            return -ENOMEM;
        }
        func->groups[0] = gname;
        func->num_groups = 1;
        func++;
        func_idx++;
    }
    
    //儲存下解析的資料資訊
    drvdata->pin_groups = groups;
    drvdata->nr_groups = grp_cnt;
    drvdata->pmx_functions = functions;
    drvdata->nr_functions = func_idx;

    return 0;
}

下面通過分析各個ops,來進一步理解下上面幾個函式所起的作用:

static const struct pinctrl_ops samsung_pctrl_ops = {
    .get_groups_count   = samsung_get_group_count,
    .get_group_name     = samsung_get_group_name,
    .get_group_pins     = samsung_get_group_pins,
    .dt_node_to_map     = samsung_dt_node_to_map,
    .dt_free_map        = samsung_dt_free_map,
};
static const struct pinmux_ops samsung_pinmux_ops = {
    .get_functions_count    = samsung_get_functions_count,
    .get_function_name  = samsung_pinmux_get_fname,
    .get_function_groups    = samsung_pinmux_get_groups,
    .enable         = samsung_pinmux_enable,
    .disable        = samsung_pinmux_disable,
    //由pinmux_gpio_direction間接呼叫,最開始應該是gpio子系統
    //的gpio_pin_direction_input、gpio_pin_direction_output觸發
    .gpio_set_direction = samsung_pinmux_gpio_set_direction,
};
static const struct pinconf_ops samsung_pinconf_ops = {
    .pin_config_get     = samsung_pinconf_get,
    .pin_config_set     = samsung_pinconf_set,
    .pin_config_group_get   = samsung_pinconf_group_get,
    .pin_config_group_set   = samsung_pinconf_group_set,
};

從上面一路分析下路來,我們應該知道dt_node_to_map是最先呼叫的,其次是get_functions_countget_function_nameget_function_groupsget_groups_countget_group_nameget_group_pinsrequest(三星pinmux_ops沒有實現它)、enablepin_config_setpin_config_group_set所以我打算就按這個順序進行分析。

呼叫dt_node_to_map的時候,從前文應該很清楚了吧,就是在某一個裝置(pinctrl本身也算是一個裝置,不過從前文貼出來的pinctrl0裡,我沒發現有pinctrl-xxx的屬性,也就是說不需要對它做任何pin ctrl)用pinctrl_get請求解析自己裝置樹資訊的時候,說的更準確點的話,就是解析該裝置裡某一個狀態的某一個配置(一個狀態可能需要多個配置來完成)的時候。下面用某一個子裝置的裝置樹資訊為例子,對應檔案s3c6410-smdk6410.dts

#define PIN_PULL_NONE   0  

&uart0 {                                                                        
    pinctrl-names = "default";                                                  
    pinctrl-0 = <&uart0_data>, <&uart0_fctl>;                                   
    status = "okay";                                                            
};
uart0_data: uart0-data {                                                    
    samsung,pins = "gpa-0", "gpa-1";                                        
    samsung,pin-function = <2>;                                             
    samsung,pin-pud = <PIN_PULL_NONE>;                                      
};       
uart0_fctl: uart0-fctl {                                                    
    samsung,pins = "gpa-2", "gpa-3";                                        
    samsung,pin-function = <2>;                                             
    samsung,pin-pud = <PIN_PULL_NONE>;                                      
};

//下面部分是uart0的其他資訊,和本文關心的pinctrl無關,之所以也列出來,只是不想讓讀者對這部分有誤解
uart0: [email protected] {                                                
    compatible = "samsung,s3c6400-uart";                                
    reg = <0x7f005000 0x100>;                                           
    interrupt-parent = <&vic1>;                                         
    interrupts = <5>;                                                   
    clock-names = "uart", "clk_uart_baud2",                             
            "clk_uart_baud3";                                           
    clocks = <&clocks PCLK_UART0>, <&clocks PCLK_UART0>,                
            <&clocks SCLK_UART>;                                        
    status = "disabled";                                                
};  

對應的解析程式碼如下,從前文描述應該清楚,期望回撥函式返回該裝置該狀態該配置下的所有設定資訊(可能只存在mux設定,也可能同時存在mux和conf設定),而上面的裝置樹裡的uart0只有一個狀態,default,對應的配置有兩個,一個是uart0_data,一個是uart0_fctl,它們都是對配置節點的引用,配置節點都是pinctrl節點下的子節點,下面看程式碼吧:

static int samsung_dt_node_to_map(struct pinctrl_dev *pctldev,
            struct device_node *np, struct pinctrl_map **maps,
            unsigned *nmaps)
{
...
    //檢查該節點(第一次呼叫應該是uart0_data節點,第二次呼叫應該是uart0_fctl節點啦)
    //含有多少個自己定義的屬性,包括:
    //{ "samsung,pin-pud", PINCFG_TYPE_PUD },
    //{ "samsung,pin-drv", PINCFG_TYPE_DRV },
    //{ "samsung,pin-con-pdn", PINCFG_TYPE_CON_PDN },
    //{ "samsung,pin-pud-pdn", PINCFG_TYPE_PUD_PDN },    
    /* count the number of config options specfied in the node */
    for (idx = 0; idx < ARRAY_SIZE(pcfgs); idx++) {
        if (of_find_property(np, pcfgs[idx].prop_cfg, NULL))
            cfg_cnt++;
    }

    /*
     * Find out the number of map entries to create. All the config options
     * can be accomadated into a single config map entry.
     */
    //如果有,那麼說明需要繼續後面的conf操作
    if (cfg_cnt)
        map_cnt = 1;
    //如果存在samsung,pin-function屬性,那麼不僅要做後面的操作,還需要額外做一些mux操作
    if (of_find_property(np, "samsung,pin-function", NULL))
        map_cnt++;
    if (!map_cnt) {
        dev_err(dev, "node %s does not have either config or function "
                "configurations\n", np->name);
        return -EINVAL;
    }

    //分配空間
    /* Allocate memory for pin-map entries */
    map = kzalloc(sizeof(*map) * map_cnt, GFP_KERNEL);
    if (!map) {
        dev_err(dev, "could not alloc memory for pin-maps\n");
        return -ENOMEM;
    }
    *nmaps = 0;

    //從前面的分析應該清楚了組名的格式,下面就是根據配置節點名構建一個格式,然後到系統
    //裡找對應的資訊
    /*
     * Allocate memory for pin group name. The pin group name is derived
     * from the node name from which these map entries are be created.
     */
    gname = kzalloc(strlen(np->name) + GSUFFIX_LEN, GFP_KERNEL);
    if (!gname) {
        dev_err(dev, "failed to alloc memory for group name\n");
        goto free_map;
    }
    sprintf(gname, "%s%s", np->name, GROUP_SUFFIX);

    /*
     * don't have config options? then skip over to creating function
     * map entries.
     */
    if (!cfg_cnt)
        goto skip_cfgs;

    //根據前面獲取的數量來分配配置節點空間
    /* Allocate memory for config entries */
    cfg = kzalloc(sizeof(*cfg) * cfg_cnt, GFP_KERNEL);
    if (!cfg) {
        dev_err(dev, "failed to alloc memory for configs\n");
        goto free_gname;
    }

    //將已經定義的,屬於自己定義列表裡面的屬性值提取出來,對應於我們這裡,都是PIN_PULL_NONE
    /* Prepare a list of config settings */
    for (idx = 0, cfg_cnt = 0; idx < ARRAY_SIZE(pcfgs); idx++) {
        u32 value;
        if (!of_property_read_u32(np, pcfgs[idx].prop_cfg, &value))
            cfg[cfg_cnt++] =
                PINCFG_PACK(pcfgs[idx].cfg_type, value);
    }

    //建立設定資訊,如設定名字,型別,以及多少個conf操作,每一個conf值
    /* create the config map entry */
    map[*nmaps].data.configs.group_or_pin = gname;
    map[*nmaps].data.configs.configs = cfg;
    map[*nmaps].data.configs.num_configs = cfg_cnt;
    map[*nmaps].type = PIN_MAP_TYPE_CONFIGS_GROUP;
    *nmaps += 1;

skip_cfgs:
    /* create the function map entry */
    if (of_find_property(np, "samsung,pin-function", NULL)) {
        //如果存在samsung,pin-function屬性,說明有mux的需求,處理它
        //這裡是構建功能名,和前面初始化的時候一致
        fname = kzalloc(strlen(np->name) + FSUFFIX_LEN, GFP_KERNEL);
        if (!fname) {
            dev_err(dev, "failed to alloc memory for func name\n");
            goto free_cfg;
        }
        sprintf(fname, "%s%s", np->name, FUNCTION_SUFFIX);

        //填充mux操作需要的資訊,如哪一個裝置,哪一個功能
        map[*nmaps].data.mux.group = gname;
        map[*nmaps].data.mux.function = fname;
        map[*nmaps].type = PIN_MAP_TYPE_MUX_GROUP;
        *nmaps += 1;
    }

    *maps = map;
    return 0;
...
}

samsung_get_functions_count,它用於獲取功能的總數量drvdata->nr_functions,前面已經分析過初始化這個的過程,所以這裡就不再分析。samsung_pinmux_get_fname從已經初始化的資料結構裡拿出對應索引上的name,name就是由配置節點名+-mux字尾構成。pinctrl_get的過程(pinmux_map_to_setting),會以map->data.mux.function為引數呼叫samsung_pinmux_get_fname獲取該功能對應的索引來初始化setting->data.mux.func,然後在用samsung_pinmux_get_groups獲取的組資訊裡,用前面解析出來的map[*nmaps].data.mux.group作為輸入引數,獲取該組的索引來初始化setting->data.mux.group。最後在pinctrl_select_state的時候,會通過上面的資訊並結合最開始初始化的一些資料結構進行mux和conf操作。pinconf_map_to_setting的操作類似,不再重複。在pinctrl_select_state的時候samsung_pinmux_enablesamsung_pinconf_set有可能會觸發,這裡就不再繼續分析了,但還是貼出程式碼吧!

/* enable a specified pinmux by writing to registers */
static int samsung_pinmux_enable(struct pinctrl_dev *pctldev, unsigned selector,
                    unsigned group)
{
    samsung_pinmux_setup(pctldev, selector, group, true);
    return 0;
}

static void samsung_pinmux_setup(struct pinctrl_dev *pctldev, unsigned selector,
                    unsigned group, bool enable)
{
    struct samsung_pinctrl_drv_data *drvdata;
    const unsigned int *pins;
    struct samsung_pin_bank *bank;
    void __iomem *reg;
    u32 mask, shift, data, pin_offset, cnt;
    unsigned long flags;

    drvdata = pinctrl_dev_get_drvdata(pctldev);
    pins = drvdata->pin_groups[group].pins;

    /*
     * for each pin in the pin group selected, program the correspoding pin
     * pin function number in the config register.
     */
    for (cnt = 0; cnt < drvdata->pin_groups[group].num_pins; cnt++) {
        struct samsung_pin_bank_type *type;

        pin_to_reg_bank(drvdata, pins[cnt] - drvdata->ctrl->base,
                &reg, &pin_offset, &bank);
        type = bank->type;
        mask = (1 << type->fld_width[PINCFG_TYPE_FUNC]) - 1;
        shift = pin_offset * type->fld_width[PINCFG_TYPE_FUNC];
        if (shift >= 32) {
            /* Some banks have two config registers */
            shift -= 32;
            reg += 4;
        }

        spin_lock_irqsave(&bank->slock, flags);

        data = readl(reg + type->reg_offset[PINCFG_TYPE_FUNC]);
        data &= ~(mask << shift);
        if (enable)
            data |= drvdata->pin_groups[group].func << shift;
        writel(data, reg + type->reg_offset[PINCFG_TYPE_FUNC]);

        spin_unlock_irqrestore(&bank->slock, flags);
    }
}
/* set the pin config settings for a specified pin */
static int samsung_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
                unsigned long