平台: MSM8X39
OS: Android4.4
Kernel: 3.10.28

GPIO SW:

Overview:

在之前的msm8926平台以及以前的平台 ,GPIO 作为一个整体存在,也就是说是一个GPIO子系统,不区分是控制什么功能。而在未来的msm8939/msm8994平台,GPIO模块引起了kernel支持的pinctrl系统。

出现pinctrl系统的原因应该是开发者想把GPIO里的内部功能给区分开来。
从硬件上来说,原有GPIO模块的功能可分为三大类:
1. 和IO端口本身的功能设定有关,称作pin controller. 通过设定它可以实现:
a. 引脚功能配置。 如是普通GPIO还是特殊功能pin。
b. 引脚特性配置。 如pull up/down, driver-strength设定。
2. 如果被配置成特殊功能如I2C, 那么pin就被连接到I2C controller。 如果被配置成 GPIO, 那么被连接到GPIO controller。在此情况下,可以配置GPIO controller:
a. 配置GPIO方向。
b. 如果是输出,可以配置高低电平。
c. 如果是输入,可获取GPIO电平状态。
3. 如果GPIO具有中断功能,那就可以被连接到interrupt controller block. 在此情况下可以配置:
a. 中断使能
b. 触发方式

因此,从SW层面上来说,GPIO模块就被划分成了pinctrl subsystem, gpio subsystem以及interrupt subsystem.另外,pinctrl subsystem 的存在也是为了将一些common的内容给抽象出来。

按照上述的理论,在我们脑中的框图应该是这样子:


GPIO和其他的复用功能并列存在,然后通过multiplexer来选择其中一个,pinctrl HW block单独控制pull up/down以及driver-strength.
事实上,在看过code之后,你会发现并非你想得那么理想,真实框图是这样:

也就是说GPIO的控制要经过pinctrl模块。具体的接口后面再描述。
基于此原因,gpio/pinctrl的初始化也是同时进行的了,gpio subsystem有兴趣可自行研究,后面描述此模块时只说到注册为止。

Pinctrl subsystem:

Overview:

再细分pinctrl主要完成的功能:
1. 管理系统中所有pins,后面会讲述系统如何初始化。
2. 管理pin的复用。
3. 配置pin的driver-strength , pull up/down.

由于pin相关的初始化定义/配置/状态都从 board-xxx-gpiomux.c中挪到Device
Tree,所以在看下面章节内容的前提是你知道device tree的语法。

device tree:

控制gpio的DT文件是msm8939-pinctrl.dtsi, 主要包含三部分内容:
1. Pinctrl 自身配置。
2. 各个controller设置。
3. 各个controller下面的pin设置。

Pinctrl配置:

Compatible用于和driver匹配。 Reg和interrupts就是之前.c文件中的
IORESOURCE_MEM和 IORESOURCE_IRQ 这两个资源。 表示memory是起始地址范围以及IRQ号。

各个controller设置:
Msm8939上有三个相关controller:
主要定义了pin的 type, nums, cells, gpio controller还定义了interrupt相关的内容。
 GPIO controller:


 SDC controller:

 QDSD controller: (trace)

Pin配置:
相关的配置是: gpio number, 用的是哪个function, active/sleep状态及对应的Pull up/down, drive-strength.

初始化:

可以想到,pinctrl subsystem 的一个主要任务就是将前面的msm8939-pinctrl.dtsi里的内容全部解析出来,以备后面使用。
另外一部分内容就是,初始化一些pinctrl接口供其他driver调用(系统其实是通过GPIO接口来设置pin)。
之前说过,由于GPIO通过pinctrl调用实现,所以这里也会提到gpio的枚举以及初始化相关接口。
我们就带着这些猜测直接看source code吧!

Note: 一般描述source code基本上会讲一些关键结构,然后再跟source code, 鄙人觉得有利也有弊,弊处是: 当第一次看到这些描述时,听得让人云里雾里。
所以本次想反其道而行之,先看source code,之后再回过来看下这些关键结构,就会让你觉得just so so…

根据设备匹配模型以及DT的compatible property 找到对应使用的文件入口是pinctrl-msm-tlmm-v4.c。
msm_tlmm_v4_probe():

[msm8939-pinctrl.dtsi]tlmm_pinmux: pinctrl@1000000 {compatible = "qcom,msm-tlmm-v4";reg = <0x1000000 0x300000>;interrupts = <0 208 0>;
…
};

[pinctrl-msm-tlmm-v4.c]

static struct platform_driver msm_tlmm_v4_drv = {.probe      = msm_tlmm_v4_probe,.driver = {/* 通过此元素来匹配*/.name   = "msm-tlmmv4-pinctrl",.owner  = THIS_MODULE,.of_match_table = of_match_ptr(msm_tlmm_v4_dt_match),},
};struct msm_tlmm_pintype tlmm_v4_pintypes = {.num_entries = ARRAY_SIZE(tlmm_v4_pininfo),.pintype_info = tlmm_v4_pininfo,
};static const struct of_device_id msm_tlmm_v4_dt_match[] = {{ .compatible = "qcom,msm-tlmm-v4",.data = &tlmm_v4_pintypes, },{},
};

匹配成功调用后msm_tlmm_v4_probe():

static int msm_tlmm_v4_probe(struct platform_device *pdev)
{const struct of_device_id *match;const struct msm_tlmm_pintype *pinfo;struct msm_tlmm_desc *tlmm_desc;int irq, ret;struct resource *res;struct device_node *node = pdev->dev.of_node;/*取出msm_tlmm_v4_dt_match,主要是里面的data。 */match = of_match_node(msm_tlmm_v4_dt_match, node);if (IS_ERR(match))return PTR_ERR(match);pinfo = match->data;tlmm_desc = devm_kzalloc(&pdev->dev, sizeof(*tlmm_desc), GFP_KERNEL);if (!tlmm_desc) {dev_err(&pdev->dev, "Alloction failed for tlmm desc\n");return -ENOMEM;}/*获取IORESOURCE_MEM ,也就是dtsi中的interrupts = <0 208 0>; */res = platform_get_resource(pdev, IORESOURCE_MEM, 0);if (!res) {dev_err(&pdev->dev, "cannot find IO resource\n");return -ENOENT;}tlmm_desc->base = devm_ioremap(&pdev->dev, res->start,resource_size(res));if (IS_ERR(tlmm_desc->base))return PTR_ERR(tlmm_desc->base);tlmm_desc->irq = -EINVAL;
/*获取IORESOURCE_IRQ ,也就是dtsi中的reg = <0x1000000 0x300000>;*/res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);if (res) {irq = res->start;ret = devm_request_irq(&pdev->dev, irq, msm_tlmm_v4_handle_irq,IRQF_TRIGGER_HIGH,dev_name(&pdev->dev),tlmm_desc);if (ret) {dev_err(&pdev->dev, "register for irq failed\n");return ret;}tlmm_desc->irq = irq;}/*和dtsi中的一致,有三个controller info, gpio/sdc/qdsd.
pintype_info 就是下面的tlmm_v4_pininfo。
这里获取了三个controller 信息以及数量。 */tlmm_desc->pintypes = pinfo->pintype_info;tlmm_desc->num_pintypes = pinfo->num_entries;/*pinctrl probe滴干活!*/return msm_pinctrl_probe(pdev, tlmm_desc);
}
static struct msm_pintype_info tlmm_v4_pininfo[] = {{.prg_cfg = msm_tlmm_v4_gp_cfg,.prg_func = msm_tlmm_v4_gp_fn,.set_reg_base = msm_tlmm_v4_gp_set_reg_base,.reg_base = NULL,.prop_name = "qcom,pin-type-gp",.name = "gp",.gc = {.label        = "msm_tlmm_v4_gpio",.direction_input  = msm_tlmm_v4_gp_dir_in,.direction_output = msm_tlmm_v4_gp_dir_out,.get              = msm_tlmm_v4_gp_get,.set              = msm_tlmm_v4_gp_set,.to_irq           = msm_tlmm_v4_gp_to_irq,},.init_irq = msm_tlmm_v4_gp_irq_init,.irq_chip = &msm_tlmm_v4_gp_irq,},{.prg_cfg = msm_tlmm_v4_sdc_cfg,.set_reg_base = msm_tlmm_v4_sdc_set_reg_base,.reg_base = NULL,.prop_name = "qcom,pin-type-sdc",.name = "sdc",},{.prg_cfg = msm_tlmm_v4_qdsd_cfg,.set_reg_base = msm_tlmm_v4_qdsd_set_reg_base,.reg_base = NULL,.prop_name = "qcom,pin-type-qdsd",.name = "qdsd",}
};

msm_pinctrl_probe():
[pinctrl-msm.c]

int msm_pinctrl_probe(struct platform_device *pdev,struct msm_tlmm_desc *tlmm_info)
{struct msm_pinctrl_dd *dd;struct device *dev = &pdev->dev;int ret;/*分配struct msm_pinctrl_dd */dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL);if (!dd) {dev_err(dev, "Alloction failed for driver data\n");return -ENOMEM;}dd->dev = dev;/*tlmm_info的信息再传递给dd.*/dd->msm_pintype = tlmm_info->pintypes;dd->base = tlmm_info->base;dd->irq = tlmm_info->irq;dd->num_pintypes = tlmm_info->num_pintypes;/*这里去解析DT里的内容了。下面会详细分析。*/ret = msm_pinctrl_get_drvdata(dd, pdev);if (ret) {dev_err(&pdev->dev, "driver data not available\n");return ret;}/*注册gpio chip,跟进去就是gpio subsystem的范畴了。*/msm_register_gpiochip(dd);/*注册pintrl */ret = msm_register_pinctrl(dd);if (ret) {msm_pinctrl_cleanup_dd(dd);return ret;}/*注册irq chip*/msm_register_irqchip(dd);platform_set_drvdata(pdev, dd);return 0;
}

msm_pinctrl_get_drvdata():

static int msm_pinctrl_get_drvdata(struct msm_pinctrl_dd *dd,struct platform_device *pdev)
{int ret;struct device_node *node = pdev->dev.of_node;/*解析各个controller信息。*/ret = msm_pinctrl_dt_parse_pintype(node, dd);if (ret)goto out;/*获取各个pins的信息*/ret = msm_pinctrl_dt_parse_pins(node, dd);if (ret)msm_pinctrl_cleanup_dd(dd);
out:return ret;
}

msm_pinctrl_dt_parse_pintype():

static int msm_pinctrl_dt_parse_pintype(struct device_node *dev_node,struct msm_pinctrl_dd *dd)
{struct device_node *pt_node;struct msm_pindesc *msm_pindesc;struct msm_pintype_info *pintype, *pinfo;void __iomem **ptype_base;u32 num_pins, pinfo_entries, curr_pins;int i, ret;uint total_pins = 0;pinfo = dd->msm_pintype;pinfo_entries = dd->num_pintypes;curr_pins = 0;/*一次遍历解析每个controller.*/for_each_child_of_node(dev_node, pt_node) {for (i = 0; i < pinfo_entries; i++) {pintype = &pinfo[i];/* Check if node is pintype node *//*只有prop_name的值存在才能继续下一步。
可以看到pintype的值其实是和DT中的值是一致的,
三个都有:qcom,pin-type-gp , qcom,pin-type-sdc ,
qcom,pin-type-qdsd */if (!of_find_property(pt_node, pintype->prop_name,NULL))continue;/*取出DT 中的controller node.*/of_node_get(pt_node);pintype->node = pt_node;/* determine number of pins of given pin type *//*获取当前controller能控制的pin number.*/ret = of_property_read_u32(pt_node, "qcom,num-pins",&num_pins);if (ret) {dev_err(dd->dev, "num pins not specified\n");return ret;}/* determine pin number range for given pin type *//*将pin number更新到pintype中。*/pintype->num_pins = num_pins;/*可以看出controller的end和下个controller的start的pin value是紧挨着的。*/pintype->pin_start = curr_pins;pintype->pin_end = curr_pins + num_pins;ptype_base = &pintype->reg_base;/*调用各个controller的set_reg_base, 例如gpio controller对应的是
msm_tlmm_v4_gp_set_reg_base(),将dd->base的值给pintype->reg_base.
前面有:dd->base = tlmm_info->base; */pintype->set_reg_base(ptype_base, dd->base);total_pins += num_pins;curr_pins += num_pins;}}/*又分配一个struct, 它保存了什么信息呢?
你看它分配了total_pins个! 说明肯定用来保存
所有pin的信息了. 可以带着疑问往下看!*/dd->msm_pindesc = devm_kzalloc(dd->dev,sizeof(struct msm_pindesc) *total_pins, GFP_KERNEL);if (!dd->msm_pindesc) {dev_err(dd->dev, "Unable to allocate msm pindesc");goto alloc_fail;}/*所有controller的pin number之和。*/dd->num_pins = total_pins;msm_pindesc = dd->msm_pindesc;/** Populate pin descriptor based on each pin type present in Device* tree and supported by the driver*//*又遍历一遍controller,看它这次需要什么内容.*/for (i = 0; i < pinfo_entries; i++) {pintype = &pinfo[i];/* If entry not in device tree, skip */if (!pintype->node)continue;/*关键的是这一句,看来需要跟进去看看!*/msm_populate_pindesc(pintype, msm_pindesc);}return 0;
alloc_fail:for (i = 0; i < pinfo_entries; i++) {pintype = &pinfo[i];if (pintype->node)of_node_put(pintype->node);}return -ENOMEM;
}

msm_populate_pindesc():

static void msm_populate_pindesc(struct msm_pintype_info *pinfo,struct msm_pindesc *msm_pindesc)
{int i;struct msm_pindesc *pindesc;for (i = 0; i < pinfo->num_pins; i++) {/*根据pin_start来获取,前面知道,pin_start是按照pin number的数量计算的,
并且controller之前连续。*/pindesc = &msm_pindesc[i + pinfo->pin_start];/*保存自己所属的pinfo,也就是对应的controller.*/pindesc->pin_info = pinfo;/*保存controller的name,并且后面后面-x来表示当前Pin的index, 注意这里的
i不一定正好是GPIO对应的number号。
到这里pindesc的用处验证了我们之前的猜测。*/snprintf(pindesc->name, sizeof(pindesc->name),"%s-%d", pinfo->name, i);}
}

到这里,各个controller的信息,如base, pin number这些已经有了。
现在应该需要知道各个pin group内部的信息了吧!?
返回上一层到msm_pinctrl_dt_parse_pins().
msm_pinctrl_dt_parse_pins():

static int msm_pinctrl_dt_parse_pins(struct device_node *dev_node,struct msm_pinctrl_dd *dd)
{struct device *dev;struct device_node *pgrp_np;struct msm_pin_grps *pin_grps, *curr_grp;struct msm_pmx_funcs *pmx_funcs, *curr_func;char *func_name;const char *grp_name;int ret, i, grp_index = 0, func_index = 0;uint pin = 0, *pins, num_grps = 0, num_pins = 0, len = 0;uint num_funcs = 0;u32 func = 0;dev = dd->dev;/*遍历msm8939-pinctrl.dtsi中的各个node.*/for_each_child_of_node(dev_node, pgrp_np) {/*如果有qcom,pins定义,才表明是一个pin group.*/if (!of_find_property(pgrp_np, "qcom,pins", NULL))continue;/*有功能复用的话就加1,像sdc controller就没有这个property.*/if (of_find_property(pgrp_np, "qcom,pin-func", NULL))num_funcs++;num_grps++;}/*根据num_grps来分配,所以此struct应该是存pin group的信息了.*/pin_grps = (struct msm_pin_grps *)devm_kzalloc(dd->dev,sizeof(*pin_grps) * num_grps,GFP_KERNEL);if (!pin_grps) {dev_err(dev, "Failed to allocate grp desc\n");return -ENOMEM;}/*根据num_funs分配,所以应该是保存功能复现相关信息了。*/pmx_funcs = (struct msm_pmx_funcs *)devm_kzalloc(dd->dev,sizeof(*pmx_funcs) * num_funcs,GFP_KERNEL);if (!pmx_funcs) {dev_err(dev, "Failed to allocate grp desc\n");return -ENOMEM;}/** Iterate over all child nodes, and for nodes containing pin lists* populate corresponding pin group, and if provided, corresponding* function*//*遍历所有node*/for_each_child_of_node(dev_node, pgrp_np) {/*和之前一样,只遍历pin group node.*/if (!of_find_property(pgrp_np, "qcom,pins", NULL))continue;/*取出一个新的struct.*/curr_grp = pin_grps + grp_index;/* Get group name from label*//*获取pin group对应的label name.*/ret = of_property_read_string(pgrp_np, "label", &grp_name);if (ret) {dev_err(dev, "Unable to allocate group name\n");return ret;}/*此值表示当前pin group要用到几个pin管脚。*/ret = of_property_read_u32(pgrp_np, "qcom,num-grp-pins",&num_pins);if (ret) {dev_err(dev, "pin count not specified for groups %s\n",grp_name);return ret;}/*我去,又分配了一个struct, 有num_pins,看来准备存
Pin group 中若干个 pin的信息了.*/pins = devm_kzalloc(dd->dev, sizeof(unsigned int) * num_pins,GFP_KERNEL);if (!pins) {dev_err(dev, "Unable to allocte pins for %s\n",grp_name);return -ENOMEM;}/*遍历当前controller所有的pins.*/for (i = 0; i < num_pins; i++) {/*当前猜测它是读取pin group里的内容,带着疑问一会儿
分析此函数是不是我们想的那样?*/ret = msm_of_get_pin(pgrp_np, i, dd, &pin);if (ret) {dev_err(dev, "Pin grp %s does not have pins\n",grp_name);return ret;}pins[i] = pin;}/*每个pin group中对应的pin信息保存起来。*/curr_grp->pins = pins;curr_grp->num_pins = num_pins;curr_grp->name = grp_name;grp_index++;/* Check if func specified *//*如果没有复用功能,那就没必要执行下面的code了。*/if (!of_find_property(pgrp_np, "qcom,pin-func", NULL))continue;curr_func = pmx_funcs + func_index;len = strlen(grp_name) + strlen("-func") + 1;func_name = devm_kzalloc(dev, len, GFP_KERNEL);if (!func_name) {dev_err(dev, "Cannot allocate func name for grp %s",grp_name);return -ENOMEM;}snprintf(func_name, len, "%s%s", grp_name, "-func");/*以grp_name – func的形式保存复用名字*/curr_func->name = func_name;curr_func->gps = devm_kzalloc(dev, sizeof(char *), GFP_KERNEL);if (!curr_func->gps) {dev_err(dev, "failed to alloc memory for group list ");return -ENOMEM;}/*读取具体复用值,可从gpio复用列表中查询值对应的具体功能。*/of_property_read_u32(pgrp_np, "qcom,pin-func", &func);curr_grp->func = func;curr_func->gps[0] = grp_name;curr_func->num_grps = 1;func_index++;}/*把前面得到的信息都放到dd中管理。*/dd->pin_grps = pin_grps;dd->num_grps = num_grps;dd->pmx_funcs = pmx_funcs;dd->num_funcs = num_funcs;return 0;
}

到此,msm_pinctrl_get_drvdata()分析完了,即dts解析完毕,下一步应该是注册了,接着看
msm_register_gpiochip():

static void msm_register_gpiochip(struct msm_pinctrl_dd *dd)
{struct gpio_chip *gc;struct msm_pintype_info *pintype, *pinfo;int i, ret = 0;pinfo = dd->msm_pintype;/*遍历controller.*/for (i = 0; i < dd->num_pintypes; i++) {pintype = &pinfo[i];/*gpio 类型才可以!*/if (!msm_pintype_supports_gpio(pintype))continue;/*填充gpio对应的信息已经函数指针,可以想到,后面在调用的时候会跑到这些函数了。*/gc = &pintype->gc;gc->request = msm_pinctrl_request_gpio;gc->free = msm_pinctrl_free_gpio;gc->dev = dd->dev;gc->ngpio = pintype->num_pins;gc->base = -1;/*添加即注册一个gpio chip到系统中。*/ret = gpiochip_add(gc);if (ret) {dev_err(dd->dev, "failed to register gpio chip\n");pinfo->supports_gpio = false;}}
}

/通过DT controller node中的 gpio-controller property来判断。/

static bool msm_pintype_supports_gpio(struct msm_pintype_info *pinfo)
{struct device_node *pt_node;if (!pinfo->node)return false;for_each_child_of_node(pinfo->node, pt_node) {if (of_find_property(pt_node, "gpio-controller", NULL)) {pinfo->gc.of_node = pt_node;pinfo->supports_gpio = true;return true;}}return false;
}

Gpio注册完之后,后面陆续注册pinctrl和irq.

msm_register_pinctrl():

static int msm_register_pinctrl(struct msm_pinctrl_dd *dd)
{int i;struct pinctrl_pin_desc *pindesc;struct msm_pintype_info *pinfo, *pintype;struct pinctrl_desc *ctrl_desc = &dd->pctl;/*填充ctrl_desc, 主要是这些函数操作指针,设置的时候会被调用到。*/ctrl_desc->name = "msm-pinctrl";ctrl_desc->owner = THIS_MODULE;ctrl_desc->pmxops = &msm_pmxops;ctrl_desc->confops = &msm_pconfops;ctrl_desc->pctlops = &msm_pctrlops;pindesc = devm_kzalloc(dd->dev, sizeof(*pindesc) * dd->num_pins,GFP_KERNEL);if (!pindesc) {dev_err(dd->dev, "Failed to allocate pinctrl pin desc\n");return -ENOMEM;}/*保存所有pin的index, name信息。*/for (i = 0; i < dd->num_pins; i++) {pindesc[i].number = i;pindesc[i].name = dd->msm_pindesc[i].name;}ctrl_desc->pins = pindesc;ctrl_desc->npins = dd->num_pins;/*注册pinctrl. 后面有时间再分解吧!*/dd->pctl_dev = pinctrl_register(ctrl_desc, dd->dev, dd);if (!dd->pctl_dev) {dev_err(dd->dev, "could not register pinctrl driver\n");return -EINVAL;}/*把各个controller作为一个range来管理。*/pinfo = dd->msm_pintype;for (i = 0; i < dd->num_pintypes; i++) {pintype = &pinfo[i];if (!pintype->supports_gpio)continue;pintype->grange.name = pintype->name;pintype->grange.id = i;pintype->grange.pin_base = pintype->pin_start;pintype->grange.base = pintype->gc.base;pintype->grange.npins = pintype->gc.ngpio;pintype->grange.gc = &pintype->gc;/*添加到gpio range中*/pinctrl_add_gpio_range(dd->pctl_dev, &pintype->grange);}return 0;
}

接口的注册还没叙述,脑细胞死多了,伤不起.
pinctrl_register():

struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc,struct device *dev, void *driver_data)
{struct pinctrl_dev *pctldev;int ret;if (!pctldesc)return NULL;if (!pctldesc->name)return NULL;pctldev = kzalloc(sizeof(*pctldev), GFP_KERNEL);if (pctldev == NULL) {dev_err(dev, "failed to alloc struct pinctrl_dev\n");return NULL;}/* Initialize pin control device struct */pctldev->owner = pctldesc->owner;pctldev->desc = pctldesc;pctldev->driver_data = driver_data;INIT_RADIX_TREE(&pctldev->pin_desc_tree, GFP_KERNEL);INIT_LIST_HEAD(&pctldev->gpio_ranges);pctldev->dev = dev;mutex_init(&pctldev->mutex);/*一堆检查,不管。*//* check core ops for sanity */if (pinctrl_check_ops(pctldev)) {dev_err(dev, "pinctrl ops lacks necessary functions\n");goto out_err;}/* If we're implementing pinmuxing, check the ops for sanity */if (pctldesc->pmxops) {if (pinmux_check_ops(pctldev))goto out_err;}/* If we're implementing pinconfig, check the ops for sanity */if (pctldesc->confops) {if (pinconf_check_ops(pctldev))goto out_err;}/* Register all the pins */dev_dbg(dev, "try to register %d pins ...\n",  pctldesc->npins);/*将之前解析到的所有pins加到系统中去*/ret = pinctrl_register_pins(pctldev, pctldesc->pins, pctldesc->npins);if (ret) {dev_err(dev, "error during pin registration\n");pinctrl_free_pindescs(pctldev, pctldesc->pins,pctldesc->npins);goto out_err;}mutex_lock(&pinctrldev_list_mutex);list_add_tail(&pctldev->node, &pinctrldev_list);mutex_unlock(&pinctrldev_list_mutex);/*这个函数看似简单,其实内藏玄机,有没有发现pin group里的active/sleep
状态我们到现在还没解析到? 这里其实就是解析state的。
不过在此流程里面,只是解析controller对应的state,DT中没用到,
所以应该查找失败会失败。 各个device的state解析会在内部driver init的
时候来解析。*/pctldev->p = pinctrl_get(pctldev->dev);if (!IS_ERR(pctldev->p)) {pctldev->hog_default =pinctrl_lookup_state(pctldev->p, PINCTRL_STATE_DEFAULT);if (IS_ERR(pctldev->hog_default)) {dev_dbg(dev, "failed to lookup the default state\n");} else {if (pinctrl_select_state(pctldev->p,pctldev->hog_default))dev_err(dev,"failed to select default state\n");}pctldev->hog_sleep =pinctrl_lookup_state(pctldev->p,PINCTRL_STATE_SLEEP);if (IS_ERR(pctldev->hog_sleep))dev_dbg(dev, "failed to lookup the sleep state\n");}/*创建debugfs文件。*/pinctrl_init_device_debugfs(pctldev);return pctldev;out_err:mutex_destroy(&pctldev->mutex);kfree(pctldev);return NULL;
}

Pinctrl_get()不展开了,也是一般解析DT的方法,这里给出过程:

还剩一个irq注册没讲。
msm_register_irqchip():

static int msm_register_irqchip(struct msm_pinctrl_dd *dd)
{struct msm_pintype_info *pintype, *pinfo;int i, ret = 0;pinfo = dd->msm_pintype;for (i = 0; i < dd->num_pintypes; i++) {pintype = &pinfo[i];if (!msm_pintype_supports_irq(pintype))continue;/*调用的是gpio controller 的init_irq函数指针,即msm_tlmm_v4_gp_irq_init()*/
ret = pintype->init_irq(dd->irq, pintype, dd->dev);return ret;}return 0;
}

msm_tlmm_v4_gp_irq_init():
这个函数大家应该都能看懂了,不想码字了…

static int msm_tlmm_v4_gp_irq_init(int irq, struct msm_pintype_info *pinfo,struct device *tlmm_dev)
{int num_irqs;struct msm_tlmm_irq_chip *ic = pinfo->irq_chip;if (!ic->domain)return 0;num_irqs = ic->num_irqs;ic->enabled_irqs = devm_kzalloc(tlmm_dev, sizeof(unsigned long)* BITS_TO_LONGS(num_irqs), GFP_KERNEL);if (IS_ERR(ic->enabled_irqs)) {dev_err(tlmm_dev, "Unable to allocate enabled irqs bitmap\n");return PTR_ERR(ic->enabled_irqs);}ic->dual_edge_irqs = devm_kzalloc(tlmm_dev, sizeof(unsigned long)* BITS_TO_LONGS(num_irqs), GFP_KERNEL);if (IS_ERR(ic->dual_edge_irqs)) {dev_err(tlmm_dev, "Unable to allocate dual edge irqs bitmap\n");return PTR_ERR(ic->dual_edge_irqs);}ic->wake_irqs = devm_kzalloc(tlmm_dev, sizeof(unsigned long)* BITS_TO_LONGS(num_irqs), GFP_KERNEL);if (IS_ERR(ic->wake_irqs)) {dev_err(tlmm_dev, "Unable to allocate wake irqs bitmap\n");return PTR_ERR(ic->wake_irqs);}spin_lock_init(&ic->irq_lock);ic->chip_base = pinfo->reg_base;ic->irq = irq;ic->dev = tlmm_dev;ic->num_irqs = pinfo->num_pins;ic->pinfo = pinfo;register_syscore_ops(&msm_tlmm_v4_irq_syscore_ops);return 0;
}

虽然定义了好多的struct,不过分析完code之后,就可以很清楚的知道他们分别是用来干什么的,不像一开始上来云里雾里的。

接口使用:

Active/sleep状态设置:
对应pinctrl,重要而且常用的有如下几个接口:
 devm_pinctrl_get():
获取对应的pinctrl,你可以认为是拿到DT对应node中信息。
 pinctrl_lookup_state():
获取对应pinctrl state,其实就是获取自己在node中配置的actvie/sleep state.
 pinctrl_select_state():
根据active/sleep的信息设置为对应的state。

上面三个函数其实是取代了之前board-xxxx-gpiomux.c的设置active/sleep方法。

GPIO配置:
GPIO对外的接口基本上没变化,差异在内部通过调用pinctrl系统的API实现。
那gpio_direction_input()举例,现在的调用过程变成:

其他接口类似。


参考文档:

80-NL239-47_A_MSM8916_APSS_GPIO_Pin_Control_SW.pdf

[笔记分享] [GPIO] MSM8x39 GPIO 软件部分小结相关推荐

  1. [笔记分享] [GPIO] MSM8x39 GPIO 硬件部分小结

    平台: MSM8X39 OS: Android4.4 Kernel: 3.10.28 术语及缩写: TLMM: Top-Level Mode Multiplexer. 此模块提供了一种机制可以让一组G ...

  2. [笔记分享] [Exception] 内核空间异常之流程小结

    平台: MSM8260 OS: Android 2.3.4 Overview 本文主要描述下kernel exception(这里以oops为主线)的flow以及如何对oops进行分析作一个讲解. E ...

  3. MSP430F5529 DriverLib 库函数学习笔记(二)GPIO

    目录 硬知识 一.MSP430单片机端口概述 二.通用IO端口输出特性 三.端口P1和P2 1.输入寄存器PxIN 2.输出寄存器PxOUT 3.方向寄存器PxDIR 4.上拉/下拉电阻使能寄存器Px ...

  4. 笔记3:STM32 ---GPIO的基本知识

    一.简介: GPIO:general purpose input output,通用输入输出端口,简单的来说也就是软件可控制的引脚,可以通过软件控制输出1或者0,STM32的GPIO引脚与外部设备连接 ...

  5. 我的内核学习笔记10:Intel GPIO驱动源码分析

    本文对Intel e3800的GPIO驱动源码进行分析. 一.概述 1.1 内核配置 Intel e3800的GPIO在Linux内核中使用的驱动名为gpio_ich(为了行文方便,将对应的设备称为& ...

  6. STM32学习笔记——基于正点原子例程编码器模式小结

    STM32学习笔记--基于正点原子例程编码器模式小结 最近一段时间学习了,STM32f4的编码器功能,经过自己探索和他人的热心帮助,对于编码器模式有了一定了解.STM32f4单片机提供编码器模式,以便 ...

  7. 笔记分享②:GPS经纬度坐标转为CGCS2000

    在上一期笔记分享①:坐标定义及转换中成功解决将CGCS2000坐标系数据转为WGS1984坐标,可加载至Google Earth或者其他通用地图软件. 本期笔记总结:通过Python批量获取照片经纬度 ...

  8. 我的Linux(ubuntu)自学笔记分享

    ** 我的Linux(ubuntu)自学笔记分享 本人自学Linux有了好长一段时间,从最开始的无从下手.一窍不通到现在已经有很大的进步了.所以分享一下学习方法,第一最好买一本Linux书系统的学一下 ...

  9. CSDN-我的在线学习笔记分享平台

    CSDN-我的在线学习笔记分享平台 1.你来自哪里?来CSDN想收获什么? 4.你用过哪些开源项目让你忍不住推荐给朋友? 5.有什么事情想做很久了?还没去做的原因是什么? 6.你和朋友讨论过的哪些有趣 ...

  10. 【安信可A9G专题②】A9G在微信公众号上的定位功能笔记分享;

    本系列博客学习由 安信可科技 - 官方博客 技术分享,如有疑问请留言或联系邮箱. 1.A9G环境在windows上搭建并编译,串口打印 Hello GPRS 2.A9G在微信公众号上的定位功能笔记分享 ...

最新文章

  1. option:contains后面加变量_什么是配置环境变量,配置以后有什么作用呢?
  2. 【华为云技术分享】Linux内核模块依赖图绘制(2)
  3. PyCharm 2019.3 EAP 7 发布,支持 R 语言插件
  4. n皇后---一维数组解法
  5. windows模拟微信小程序_微信PC端可以打开小程序了!目前仅支持Windows系统
  6. 毕设题目:Matlab数字信号去噪
  7. docker搭建python开发环境_PyCharm使用之利用Docker镜像搭建Python开发环境
  8. 四、云计算-国产-华为-运维、灾备和迁移+HCIE Cloud相关知识点+笔试题库
  9. ATmega128A 串口问题
  10. 社交网络中常用数据集
  11. mysql 导出数据 insert_mysql导出数据和导入数据
  12. 【VBA研究】利用DateAdd函数取上月或上年同期的日期
  13. Object.defineProperty()详解
  14. Win10系统C++调用OpenCV实现网络摄像头录像和抓拍图片
  15. cad哪个版本最好用?思路提供
  16. python try catch
  17. 我的世界服务器搭建原理,我的世界自动化农场的建造与原理 这里有你想知道的...
  18. Python基础从0到1自我学习(3)
  19. Spring Boot、Spring Cloud 自定义配置文件(如何整合配置中心)
  20. Cannot access ‘router‘ before initialization

热门文章

  1. IDEA技巧:如何根据注释生成swagger注解
  2. 硬盘的IDE、AHCI和NVMe模式区别
  3. Mac环境下设置打印机驱动程序
  4. ndk读取assets文件
  5. 程序员的自我修养之数学基础10:超定方程的求解
  6. 如何采用类比法和类推法估算软件项目工作量
  7. 图解排序算法之「冒泡排序」(详细解析)
  8. Android对话框的详细介绍(提示对话框,自定义对话框)
  9. 光耦驱动单向可控硅_超低功耗光电耦合驱动单向可控硅电路,光电耦合器
  10. MacOS 10.15 Catalina:13个问题和修复