【Linux驱动】pinctrl子系统的概念与使用

一、pinctrl子系统相关概念

1.1 什么是pinctrl子系统?

在这里插入图片描述
如上图所示,在芯片内部通常存在一个管理引脚功能复用的模块IOMUX:

  • 如果我们想让pinApinB用于SPI功能,需要设置IOMUX,配置这两个引脚连接到SPI模块。
  • 如果我们想让pinApinB用于GPIO功能,需要设置IOMUX,配置这两个引脚连接到GPIO模块。

📌 此处的GPIO模块与SPIIICUART等为并列关系,它与pinctrl子系统的关系是,使用pinctrl子系统将pinApinB等复用至GPIO模块,然后才能将pinApinB用于GPIO功能。

除了将引脚复用为某种功能外,有时候还需要配置引脚的电气属性,如上拉、下拉、开漏等等。

目前,市场上有很多的芯片厂商,不同厂商生产的芯片在引脚功能上都有差异,此外,一个芯片动辄上百个引脚,如果我们每次更换芯片都需要一个一个的去配置引脚,那需要耗费太多的精力。虽然各个厂商的芯片引脚功能都有差异,但在使用时也有着诸多的共性,如上面说到的功能复用、特性配置等。linux内核中基于这些共性,提供了一套代码来管理这些引脚,这套代码就是pinctrl subsystem,主要实现以下功能:

  • 枚举所有可以控制的pin并做唯一标识;
  • pin进行功能复用;
  • 配置这些引脚的电气属性,如上拉、下拉、开漏等等。

1.3 pinctrl子系统的重要概念

1.3.1 pin controller device 和 client device

pinctrl subsystem涉及到了两个对象:

  • pin controller device:其主要目的是提供服务,可以用来复用引脚,配置引脚;
  • client device:作为客户,使用pin controller提供的服务,声明自己需要使用哪些引脚的哪些功能以及怎么去配置它们,因此只需要在设备节点中描述与引脚相关的信息。

在设备树中,以上两个对象被定义成了两个设备节点,如下所示。

/* client device */
device{
	pinctrl-names = "default","sleep"		/* 1.该设备有default和sleep两种状态 */
	pinctrl-0 = <state_0_node_a>;			/* 2.第0个状态名字是default,对应引脚在pinctrl-0里定义 */
	pinctrl-1 = <state_1_node_a>;			/* 3.第1个状态名字是sleep,对应引脚在pinctrl-1里定义 */
};

/* pin controller device */
pincontroller{
	state_0_node_a{
		function = "uart0";					/* 1.复用为哪些功能 */
		groups = "u0rxtx","uortscts";		/* 2. 用到哪些引脚 */
	};
	state_1_node_a{		
		groups = "u0rxtx","uortscts";
		output-high;						/* 3.配置成什么状态 */
	};
};

1.3.2 pin state

如上1.3.1所示,在一个client device中,有多个状态,第一个状态名为default,其对应的引脚定义在pinctrl-0中;第二个状态名为sleep,其对应的引脚定义在pinctrl-1中。

1.3.3 pin group

以功能为依据,用于同一功能的多个引脚为一个pin group。当然,一个设备可以用到多组引脚,比如A1A2两组引脚,A1组复用为F1功能,A2组复用为F2功能。

1.3.4 pin bank

pin bank可以理解为同一个GPIO控制器下的一组GPIO端口,以s3c2440为例,分为了9 个GPIO控制器,如下:

GPIO控制器GPIO端口名称GPIO端口数量
GPIOAGPA0~GPA2425
GPIOBGPB0~GPB1011
GPIOCGPC0~GPC1516
GPIODGPD0~GPD1516
GPIOEGPE0~GPE1516
GPIOFGPF0~GPF78
GPIOGGPG0~GPG1516
GPIOGGPG0~GPG1516
GPIOHGPH0~GPH1011
GPIOJGPJ0~GPJ1213

二、pinctrl子系统的使用

2.1 在设备树文件中定义pin controller device节点和client device节点

  1. 查找原理图,确定设备使用了哪些引脚。
  2. 生成pin controller的设备树信息——功能复用和特性配置。由于pincontroller格式不统一,生成pincontroller设备树信息有3种方法:
    • 使用图形化工具;
    • 看设备树文档或参考设备树的例子;
    • 阅读驱动代码。
  3. 生成client device的设备树信息,比如在I2C设备节点里定义"pinctrl-name",“pinctrl-0”,“pinctrl-1”。

2.2 在client driver中使用pinctrl子系统

方法1:linux内核自动调用pinctrl子系统

pinctrl子系统对于驱动工程师来说是透明的,我们在编写client driver时基本上不需要管,当设备切换状态时(对应设备树中的pinctrl-names的状态),对应的pinctrl子节点就会被调用,当系统休眠时,也会去设置该设备sleep状态对应的引脚,不需要我们自己去调用代码。

那么,linux内核是如何调用pinctrl子系统呢?在linux系统初始化时,由如下操作:

  • 注册pin controller device
  • 将设备节点转换为platform_device,或者其它结构体(比如i2c_client、spi_device);

在将设备节点转换为platform_device后,platform_device会被注册进linux内核中,在注册的过程中,pinctrl子系统被调用。

uart0驱动为例,其设备节点:

uart0: serial@50000000 {
    compatible = "samsung,s3c2410-uart";
    reg = <0x50000000 0x4000>;
    interrupts = <1 28 0 4>, <1 28 1 4>;
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&uart0_data>;
};

serial@50000000设备节点会被转换为platform_device,并被注册到Linux内核中,以便与其它驱动程序进行匹配和绑定。其中,platform_device注册使用函数为platform_device_register,函数调用流程如下:

platform_device_register
    platform_device_add
        device_add
            bus_probe_device;
                device_initial_probe
                    __device_attach
                        bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver); // 对于plarform_bus_type下的每一个driver, 调用__device_attach_driver
                        
__device_attach_driver
    driver_match_device(drv, dev);
        drv->bus->match(dev, drv)// 调用platform_bus_type.match
            driver_probe_device(drv, dev)
                really_probe(dev, drv)                
                    ret = pinctrl_bind_pins(dev); // 绑定设备使用的pinctrl,获取一个pinctrl_dev handle
                        pinctrl_get(dev);
                            create_pinctrl
                    pinctrl_init_done(dev);//配置设备使用的pin
                    drv->probe //执行client driver中的probe函数    

其中,函数pinctrl_bind_pins的内部实现如下:

int pinctrl_bind_pins(struct device *dev)
{
    /*1.先get*/
    dev->pins->p = devm_pinctrl_get(dev);

    /*2.获取各种state下的pin*/
    dev->pins->default_state = pinctrl_lookup_state(dev->pins->p, PINCTRL_STATE_DEFAULT); /*default的必须要有,否则直接退出*/
    dev->pins->init_state = pinctrl_lookup_state(dev->pins->p, PINCTRL_STATE_INIT);
    /*3.选中一个state进行设置*/
    if (IS_ERR(dev->pins->init_state)) {
        /*这里对clent的gpio设备树节点进行解析,并对硬件进行了设置*/
        ret = pinctrl_select_state(dev->pins->p, dev->pins->default_state); /*如果有init state,就设置为init state,否则设置为default state, Qcom没有定义sleep state*/
    } else {
        ret = pinctrl_select_state(dev->pins->p, dev->pins->init_state);
    }

#ifdef CONFIG_PM //R上也定义了这个值
    dev->pins->sleep_state = pinctrl_lookup_state(dev->pins->p, PINCTRL_STATE_SLEEP);
    dev->pins->idle_state = pinctrl_lookup_state(dev->pins->p, PINCTRL_STATE_IDLE);
#endif

    return 0;
}

方法2:使用pinctrl子系统向client driver提供的API

上面说到pinctrl子系统对驱动工程师来说是透明的,我们驱动中基本不用管,但是当我们非要自己在驱动中调用pinctrl子系统时,可进行如下操作:

  1. 解析设备树,获取pin controller节点。
struct pinctrl *devm_pinctrl_get(struct device *dev)
  1. pin controller节点的中,获取各种state的gpio配置。
struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p, const char *name)  
  1. 将上面获取的指定state设置到硬件中。
int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state)

三、总结

  1. pin controller节点在pinctrl子系统的驱动中是没有被解析的,是在设备驱动(client driver)中使用的时候才会去解析设备树,进行实际的硬件配置。若是只是设备树中定义了pin controllerclient device,但是没有client driver使用,则不会什么影响。
  2. 一般是驱动自身,在suspend回调中设置为sleep stateresume时设置为avtive state。平台设备驱动在probe()时会自动设置为default state,其它状态驱动在probe()内可以由驱动工程师手动进行设置。

四、参考文章

linux设备树—pinctrl子系统
pinctrl(1)——pinctrl子系统的使用
pinctrl子系统介绍

  • 26
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
pinctrl 子系统Linux 内核中的一个子系统,用于管理和控制 SoC(System-on-a-Chip)上的引脚。在编写 pinctrl 子系统驱动时,需要完成以下几个步骤: 1. 定义 pinctrl 子系统设备树节点 在设备树中定义 pinctrl 子系统节点,包括引脚组、引脚和功能等信息。这些信息将在驱动程序中使用。 2. 注册 pinctrl 子系统驱动程序中调用 pinctrl_register() 函数注册 pinctrl 子系统。此时,内核会根据设备树节点中的信息创建 pinctrl 子系统的实例,并将其加入到内核中的全局列表中。 3. 实现 pinctrl 子系统驱动程序 实现 pinctrl 子系统驱动程序,包括 pinmux 和 pinconf 两个部分。 pinmux 部分负责选择引脚组中的某个引脚,并将其配置为特定的模式(如输入、输出等)。 pinconf 部分负责配置引脚的其他属性,如电气特性、驱动能力等。 4. 注册 pinctrl 子系统驱动程序 在驱动程序中调用 pinctrl_register_mappings() 函数注册 pinctrl 子系统驱动程序。这将使驱动程序与 pinctrl 子系统建立连接,并允许驱动程序向子系统发送命令。 5. 使用 pinctrl 子系统驱动程序中使用 pinctrl 子系统,包括选择引脚组和引脚,配置引脚的模式和属性等。这些操作通过调用 pinctrl_select_state() 和 pinctrl_set_state() 等函数完成。 以上是编写 pinctrl 子系统驱动程序的基本步骤。需要注意的是,具体实现可能会因为硬件平台和需求的不同而有所不同。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值