pinctrl子系统之二

以炬芯的方案来分析一下pinctrl子系统的代码

kernel/drivers/pinctrl/owl/pinctrl-s700.c中

pinctrl驱动是注册为平台驱动的。

platform_driver_register(&s700_pinctrl_driver); //注册平台设备驱动
//直接去看probe函数
static int s700_pinctrl_probe(struct platform_device *pdev)
{
	pr_info("[OWL] pinctrl s700 probe\n");
	return owl_pinctrl_probe(pdev, &s700_pinctrl_info); //重点是s700_pinctrl_info结构
}

//s700_pinctrl_info结构中封装了炬芯IC关于pin的所有基本信息,包括function了,group了等等。
static struct owl_pinctrl_soc_info s700_pinctrl_info = {
	.gpio_ranges = s700_gpio_ranges,
	.gpio_num_ranges = ARRAY_SIZE(s700_gpio_ranges),
	.padinfo = s700_pad_tab,
	.pins = (const struct pinctrl_pin_desc *)s700_pads,
	.npins = ARRAY_SIZE(s700_pads),
	.functions = s700_functions,
	.nfunctions = ARRAY_SIZE(s700_functions),
	.groups = s700_groups,
	.ngroups = ARRAY_SIZE(s700_groups),
	/*.owl_gpio_pad_data = &s700_gpio_pad_data,*/


};

接着到真正的probe函数,先看一下owl_pinctrl_desc结构,这个是贯穿pinctrl功能的一个结构,里面主要封装了此芯片pinctrl相关的操作函数,这些函数都是芯片厂商来实现的。

static struct pinctrl_desc owl_pinctrl_desc = {
	.name = NULL,
	.pins = NULL,
	.npins = 0,
	.pctlops = &owl_pctlops_ops, //全局相关的控制函数
	.pmxops = &owl_pmxops_ops,   //复用引脚相关的操作函数
	.confops = &owl_confops_ops, //用来配置引脚的特性如:上拉下拉
	.owner = THIS_MODULE,
};

int owl_pinctrl_probe(struct platform_device *pdev,
		struct owl_pinctrl_soc_info *info)
{
	struct resource *res;
	struct owl_pinctrl *apctl;
	int i;

	pr_info("[OWL] pinctrl initialization\n");

	if (!info || !info->pins || !info->npins) {
		dev_err(&pdev->dev, "wrong pinctrl info\n");
		return -EINVAL;
	}

	owl_pinctrl_desc.name = dev_name(&pdev->dev); //对owl_pinctrl_desc继续做一些初始化工作
	owl_pinctrl_desc.pins = info->pins;
	owl_pinctrl_desc.npins = info->npins;

	/* Create state holders etc for this driver */
	apctl = devm_kzalloc(&pdev->dev, sizeof(*apctl), GFP_KERNEL);
	if (!apctl)
		return -ENOMEM;

	apctl->info = info;
	apctl->dev = &pdev->dev;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!res)
		return -ENODEV;

	apctl->membase = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(apctl->membase))
		return PTR_ERR(apctl->membase);

	/* enable GPIO/MFP clock */
	apctl->clk = devm_clk_get(&pdev->dev, NULL);
	if (IS_ERR(apctl->clk)) {
		dev_err(&pdev->dev, "no clock defined\n");
		return -ENODEV;
	}
	clk_prepare_enable(apctl->clk);

	dev_info(&pdev->dev, "nfunctions %d, ngroups %d\n",
		info->nfunctions, info->ngroups);

	apctl->pctl = pinctrl_register(&owl_pinctrl_desc, &pdev->dev, apctl); //将owl_pinctrl_desc注册进内核中。
	if (!apctl->pctl) {
		dev_err(&pdev->dev, "could not register Actions SOC pinmux driver\n");
		return -EINVAL;
	}

	/* We will handle a range of GPIO pins */
	for (i = 0; i < info->gpio_num_ranges; i++)
		pinctrl_add_gpio_range(apctl->pctl, &info->gpio_ranges[i]);

	platform_set_drvdata(pdev, apctl);

	dev_dbg(&pdev->dev, "initialized Actions SOC pin control driver\n");

	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;

	pctldev = kzalloc(sizeof(*pctldev), GFP_KERNEL);

	/* 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);

	/* Register all the pins */
	dev_dbg(dev, "try to register %d pins ...\n",  pctldesc->npins);
	ret = pinctrl_register_pins(pctldev, pctldesc->pins, pctldesc->npins);//把每一个pin都注册到系统中

	mutex_lock(&pinctrldev_list_mutex);
	list_add_tail(&pctldev->node, &pinctrldev_list);
	mutex_unlock(&pinctrldev_list_mutex);

	pctldev->p = pinctrl_get(pctldev->dev); //pinctrl_get调用create_pinctrl来获取pinctrl句柄

	if (!IS_ERR(pctldev->p)) {
		pctldev->hog_default =
			pinctrl_lookup_state(pctldev->p, PINCTRL_STATE_DEFAULT);//查找pin的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))  //将pin设置为default状态
				dev_err(dev,
					"failed to select default state\n");
		}

		pctldev->hog_sleep =
			pinctrl_lookup_state(pctldev->p,
						    PINCTRL_STATE_SLEEP); //查找sleep状态。
	}

	pinctrl_init_device_debugfs(pctldev);

	return pctldev;
}

pinctrl_get获取pin mapping databae的过程大致如下:

(1)pinctrl_get函数调用create_pinctrl来创建pinctrl句柄

(2)create_pinctrl函数调用pinctrl_dt_to_map来解析dts当前设备节点中的命名为pinctrl-N的属性信息。

(3)pinctrl_dt_to_map搜索当前设备驱动节点所有的pinctrl-%d,并通过其指向的phandle找到定义在pin controller device node中的pinctrl state节点,然后调用dt_to_map_one_config进行解析。

(4)dt_to_map_one_config函数向上搜索pinctrl state节点的父亲节点,并与所有哦注册的pinctrl驱动比对,找到匹配的pinctrl驱动的句柄后,调用pinctrl驱动注册的dt_node_to_map函数来解析pinctrl state节点数据。

(5)主控pinctrl驱动的owl_pinctrl_dt_node_to_map收到pin state节点句柄后,搜索以下属性:“actions,function”、“actions,pull”、“actions,paddrv”,“actions,pins”,“actions,groups”并将解析的数据打包成struct pinctrl_map结构,并回传给调用者。

至此,pinctrl_get就完成了收集并组装pin mapping database工作,并返回了pinctrl的控制句柄。设备驱动可以通过这个句柄来完成对pinctrl的各项操作。


pin controller device 包含一系列pin configuration node作为子节点。这些子节点描述了pinctrl_map中可能用到的所有pin state。

    pinctrl模块对这些pin configuration node定义如下:

1.actions,groups的属性用于表示Mux Group的名字数组。

2.actions,pins的属性用于表示该组pin的名字数组。

3.actions,function的属性表示该组Mux Group使用功能的名字。

4.actions,paddrv的属性表示该组Dirve Group所代表的pin的驱动能力。

5.actions,pull的属性表述该组的pin需要配置的上下拉状态。

6.pin configuration node的名字任意。

pin controller的device node 定义为pinctrl@e01b0040,示例如下:

        pinctrl@e01b0000 { //pinctrl@e01b0000其实就是一个设备节点,这个设备节点的解析是由pinctrl主控驱动完成的,主控驱动在初始化时会调用pinctrl_register函数将自己注册到pinctrl子系统中。
                compatible = "actions,s700-pinctrl";
                reg = <0 0xe01b0000 0 0x1000>;

                pinctrl-names = "default";
                pinctrl-0 = <&state_default>;//pinctrl_register会调用pinctrl_get来获取属于主控驱动的pin mapping database。pinctrl-names为default的节点会首先被初始化。
                clocks = <&clock CLK_GPIO>;  //pinctrl_register在获得pinctrl句柄后,会申请default的pin配置,所以如果有不属于任何其他设备的pinctrl配置,但也想使能,就可以统一放到这里进行初始化。
                clock-names = "mfp"; 

                state_default: pinctrl_default {
                };

                spi0_state_default: spi0_default {//其他子节点,由各个驱动在加载时由统一设备驱动模型在device和driver绑定时调用pinctrl_get时被解析,并将解析完成的pinctrl句柄存放在驱动设备的device中。
                        spi0_mfp {
                                actions,groups = "mfp1_2_0","mfp1_4_3";
                                actions,function = "spi0";
                        };
                        spi0_paddrv {
                                actions,groups = "paddrv1_11_10";
                                actions,paddrv = <2>;/*level 2*/
                        };
                        //spi0_pull {
                                //actions,pins = "P_I2C0_SCLK", "P_I2C0_SDATA";
                                //actions,schmitt  = "P_I2C0_SCLK", "P_I2C0_SDATA";
                                //actions,pull = <0>;
                        //};
                };
                serial0_state_default: serial0_default{
                        serial_0{
                                actions,groups = "mfp2_2_0","mfp3_21_19";
                                actions,function = "uart0";
                        };
                };

                serial1_state_default: serial1_default{ //这些其他的
                        serial_1{
                                actions,groups = "mfp2_13_11";
                                actions,function = "uart1";
                        };
                };

                serial2_state_default: serial2_default{
                        serial_2{
                                actions,groups = "mfp2_23","mfp2_22","uart2_dummy";
                                actions,function = "uart2";
                        };
                };

                serial3_state_default: serial3_default{
                        serial_3{
                                actions,groups = "mfp2_21","mfp2_20","uart3_dummy";
                                actions,function = "uart3";
                        };
                };

统一设备模型在匹配到驱动后,会调用devm_pinctrl_get来建立pinctrl database map。

在具体的驱动设备节点中,通过关键字“pinctrl-N”来引用在pin controller device设备节点中定义的pinctrl state节点。其中“N”表示序号,是pinctrl-names顶一顶 字符串数组下标。

        mmc0: mmc@e0218000 {
                compatible = "actions,s700-mmc";
                reg = <0 0xe0218000 0 0x40>, <0 0xe01b0000 0 0x410>,
                          <0 0xe0168000 0 0x100>;
                interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>;
                clocks = <&clock CLK_SD2>;
                clock-names = "mmc";
                resets = <&reset RESET_SD2>;
                reset-names = "mmc";
                dmas = <&dma DMA_DRQ_SD2>;
                dma-names = "mmc";
                pinctrl-names = "pinctrl_mmc2";
                pinctrl-0 = <&mm2_pinctrl_state>;
                status = "disabled";
        };

mmc@e0218000是定义在root节点下的一级子节点。

在驱动调用platform_driver_register()时由统一设备模型对"compatible"属性进行配置,在匹配到后就调用pinctrl_bind_pins进行绑定,并调用pinctrl_get进行绑定,并调用pinctrl_get接卸“pinctrl-N”引用的pinctrl子节点,并将pinctrl句柄放到platform_device结构的dev结构中。

驱动通过pinctrl驱动提供的pinctrl_get接口从dev结构中获得pinctrl句柄的引用,并调用其他的pinctrl驱动提供的接口对pinctrl进行操作。

驱动使用pinctrl接口代码示例



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值