嵌入式Linux开发|pinctrl子系统

概况

众所周知,每颗SoC都会有很多的pin,除了具有特殊作用的,比如电源、地等pin,其他的pin一般都会**“身兼数职”**,比如,一个pin既可以当做GPIO来使用,也可以用作UART的TX,又或者是SPI的MOSI;而且,这些pin往往具有不同的能力,比如,上拉、下拉、不同的驱动能力等等。

pinctrl子系统就是用来完成上述功能而设计的,pinctrl对下管理SoC所有的pin,对上提供配置SoC所有pin的接口。通常,再将linux系统移植到一款新SoC上时,其中一项工作,就是将所有pin注册到pinctrl子系统中,并且为配置这些pin提供相应接口。

pinctrl子系统Linux内核的位置,由于本篇文章,主要介绍pinctrl的基本配置、使用方式,如果你想更多的了解pinctrl,请参考最为权威的内核文档,这是内核5.18中关于pinctrl子系统的介绍可以参考我之前写过的文章

作用

pinctrl子系统作为嵌入式Linux的重要组成部分,其一般具有如下几项作用:

  1. 枚举、命名SoC所有的pin;
  2. pin分组管理功能;对于某些接口比如spi或者i2c,其一般需要多个pin配合使用,并且对于同一个外设控制器,其可能会有多组pin的组合。比如,i2c0控制器,其引脚组合可能是{21,22},也可能是{31,32}。pinctrl子系统需要具有管理这些pin组的能力。
  3. 提供pin复用配置;
  4. 提供配置pin特殊能力的接口,比如,上拉、下拉、推挽输出、开漏输出、负载驱动能力配置等等。

pinctrl会从系统全局的角度,管理所有的pin,提供pin互斥访问的服务,即,每个pin同一时间只能被一个consumer使用,并且只能配置成一种功能。

在进行驱动开发时,大到动辄有十多条控制信号线的网卡芯片,小到只有一个GPIO的led或者按键,谨记,第一件事就是配置外设所使用的pin的复用功能和IO特性。 就比如,我们使用一个pin来控制一个LED,我们要做的配置如下:

  1. pin复用为GPIO。
  2. IO为输出模式。
  3. 根据实际硬件电路,确定是否启用上拉、下拉,以及所需要的驱动能力。

架构

在这里插入图片描述
pinctrl子系统架构图表示,其提供两套接口,

  1. 对下,为各种硬件平台提供pins定义、控制方式注册接口。
  2. 对上,为各种驱动提供操作pins的接口,比如,GPIO基本控制接口,GPIO子系统与pinctrl子系统交互接口。

顶层接口

  • 定义pinctrl硬件,实际上pinctrl硬件就是一片内存寄存器,这些寄存器可以配置pins,完成诸如复用(multiplex)、上/下拉配置(bias)、负载能力(load capacitance)、驱动能力等。
  • 定义SoC所有的pin,pads, fingers, balls都是同一个意思,表示某一个pin,每个pinctrl硬件所控制的pins,属于同一个命名空间,范围是0~maxpins,它会略过不存在的pin,所以,这个空间可能是不连续的。

每个pinctrl硬件在初始化的时候,都会向pinctrl framework注册其所涉及的所有pins的描述符。这里以一个PGA(Pin Grid Array)封装的SoC为例,该SoC所有pins的排列方式如下:

		A   B   C   D   E   F   G   H

   8    o   o   o   o   o   o   o   o

   7    o   o   o   o   o   o   o   o

   6    o   o   o   o   o   o   o   o

   5    o   o   o   o   o   o   o   o

   4    o   o   o   o   o   o   o   o

   3    o   o   o   o   o   o   o   o

   2    o   o   o   o   o   o   o   o

   1    o   o   o   o   o   o   o   o

下面的代码枚举了上面SoC中所有的pins,宏PINCTRL_PIN用于定义每个pin的number和名字,number是系统唯一的。
注意:以上面SoC为例,这里pin的定义顺序是从0到63,这个顺序不是唯一的,它依赖于几个方面1)pingctrl硬件寄存器的布局;2)与GPIO numer映射时是否简单;

#include <linux/pinctrl/pinctrl.h>

	const struct pinctrl_pin_desc foo_pins[] = {
    
    
		PINCTRL_PIN(0, "A8"),
		PINCTRL_PIN(1, "B8"),
		PINCTRL_PIN(2, "C8"),
		...
		PINCTRL_PIN(61, "F1"),
		PINCTRL_PIN(62, "G1"),
		PINCTRL_PIN(63, "H1"),
	};

	static struct pinctrl_desc foo_desc = {
    
    
		.name = "foo",
		.pins = foo_pins,
		.npins = ARRAY_SIZE(foo_pins),
		.owner = THIS_MODULE,
	};

	int __init foo_probe(void)
	{
    
    
		int error;

		struct pinctrl_dev *pctl;

		error = pinctrl_register_and_init(&foo_desc, <PARENT>,
						  NULL, &pctl);
		if (error)
			return error;

		return pinctrl_enable(pctl);
	}

GPIO子系统与pinctrl子系统的关系

GPIO子系统控制硬件的操作,最终都是通过pinctrl实现的。但是,这两套系统都有自己的命名空间,比如,GPIO子系统是按照block来划分pin的,类似于blockA、blockB等等,而pinctrl子系统,是按照SoC的pin的序号,顺序定义的,所以,两个系统pin的索引不是一一对应的,这就需要一个map来进行映射。比如,下面的代码:

	struct gpio_chip chip_a;
	struct gpio_chip chip_b;

	static struct pinctrl_gpio_range gpio_range_a = {
    
    
		.name = "chip a",
		.id = 0,
		.base = 32,
		.pin_base = 32,
		.npins = 16,
		.gc = &chip_a;
	};

	static struct pinctrl_gpio_range gpio_range_b = {
    
    
		.name = "chip b",
		.id = 0,
		.base = 48,
		.pin_base = 64,
		.npins = 8,
		.gc = &chip_b;
	};

	{
    
    
		struct pinctrl_dev *pctl;
		...
		pinctrl_add_gpio_range(pctl, &gpio_range_a);
		pinctrl_add_gpio_range(pctl, &gpio_range_b);
	}
  • chip_a和chip_b是GPIO子系统的两个block;
  • pinctrl_gpio_range结构用于定义两个子系统pin的映射关系;
  • base表示某一block中pin的起始序号,pin_base表示其在pinctrl子系统中起始序号,bpins表示block中pin的个数;

下面是就是两个两个子系统pin映射关系:

chip a:
 - GPIO range : [32 .. 47]
 - pin range  : [32 .. 47]
chip b:
 - GPIO range : [48 .. 55]
 - pin range  : [64 .. 71]

注意,上述表示,两个子系统中pin是线性映射的,如果是非线性或随机的呢?请看下面的代码:

static const unsigned range_pins[] = {
    
     14, 1, 22, 17, 10, 8, 6, 2 };

	static struct pinctrl_gpio_range gpio_range = {
    
    
		.name = "chip",
		.id = 0,
		.base = 32,
		.pins = &range_pins,
		.npins = ARRAY_SIZE(range_pins),
		.gc = &chip;
	};

注意,用pins代替了线性映射的pin_base,而range_pins中的pin都是随机的。

**结论:**不管是线性映射还是随机映射,都要注意,对于同一个pin,在两个系统中的number,很有可能是不一样的!

GPIO子系统在控制某一pin时,通过两个系统pins的映射关系,转换为pinctrl中的pin number,从而控制硬件。

DTS配置

示例

猜你喜欢

转载自blog.csdn.net/linux_embedded/article/details/124830867
今日推荐