PINCTRL 子系统
这个文档概述了Linux的Pin control子系统
这个子系统处理以下事情:
- 枚举和命名Pins
- Pins、pads、fingers等的复用,详情请见下文
- Pins、pads、finger等的电气配置,例如上下拉、开漏等
Top-level interface
===================
- PIN CONTROLLER的定义:
Pin controller是一个硬件,通常是一组用来控制pins的寄存器。
它们可以用来为一个pins或一组pins配置复用、偏置、下上拉等。
- PIN的定义
PINS就是pads、fingers等一切你想要控制的input/output line。PINS用一个从0-maxpins的整数表示。
这个number space(0-maxpins)取决于PIN CONTROLLER,所以在一个系统里可能有多个number space。
这个pin space不一定是连续的,它们中间可能有一些pin是不存在的。
当一个PIN CONTROLLER被实例化之后,它将注册一个descriptor(描述符)到pin control 框架中,
这个PIN CONTROLLER描述符包含一组pin描述符,这些pin描述符由这个pin controller处理。
下面是一个芯片的例子:
想要注册一个pin controller并且命名这个芯片上的所有pin,我们可以在驱动中这样写:
#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);
}
想要启用pinctrl子系统、PINMUX和PINCONF的subgroups以及选定的驱动,
你需要在你的芯片的Kconfig条目中选择他们,因为他们通常是与你的芯片紧密联系的。
可以看看arch/arm/mach-u300/Kconfig作为例子。
pins name通常和上面描述的有所不同,你可以在芯片的datasheet中找到它们。
注意,pinctrl core中的pinctrl.h提供一个叫做PINCTRL_PIN()的宏用于创建pinctrl_pin_desc结构体。
你可以看到我用0~63枚举了从A8到H1的所有引脚。在实际使用中,你必须考虑这些数字所代表的pin,
使它们可以与你所用的芯片的寄存器布局相符合,否则代码会变得很复杂。
你还需要考虑offset是否和pin controller所控制的GPIO ranges符合。
Pin groups
==========
一些控制器需要处理一组pins,所以pin controller提供一个用于枚举和检索一组pin的机制。
以一组用于SPI的pins{0,8,16,24},和一组用于I2C的pin{24,25}作为例子。
这两组pin可以在pin controller中通过实例化一个 pinctrl_ops来表示:
#include <linux/pinctrl/pinctrl.h>
struct foo_group {
const char *name;
const unsigned int *pins;
const unsigned num_pins;
};
static const unsigned int spi0_pins[] = { 0, 8, 16, 24 };
static const unsigned int i2c0_pins[] = { 24, 25 };
static const struct foo_group foo_groups[] = {
{
.name = "spi0_grp",
.pins = spi0_pins,
.num_pins = ARRAY_SIZE(spi0_pins),
},
{
.name = "i2c0_grp",
.pins = i2c0_pins,
.num_pins = ARRAY_SIZE(i2c0_pins),
},
};
static int foo_get_groups_count(struct pinctrl_dev *pctldev)
{
return ARRAY_SIZE(foo_groups);
}
static const char *foo_get_group_name(struct pinctrl_dev *pctldev,
unsigned selector)
{
return foo_groups[selector].name;
}
static int foo_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector,
const unsigned **pins,
unsigned *num_pins)
{
*pins = (unsigned *) foo_groups[selector].pins;
*num_pins = foo_groups[selector].num_pins;
return 0;
}
static struct pinctrl_ops foo_pctrl_ops = {
.get_groups_count = foo_get_groups_count,
.get_group_name = foo_get_group_name,
.get_group_pins = foo_get_group_pins,
};
static struct pinctrl_desc foo_desc = {
...
.pctlops = &foo_pctrl_ops,
};
Pin controller 子系统可以通过调用. get_groups_count()函数来获取group的数量,然后pin controller就可以通过调用foo_get_group_name()和foo_get_group_pins()来获取相应group的名字和其包含的pins。实际group的数据结构取决于驱动,这仅仅是个简单的例子。
Pin configuration
=================
Pin可以通过多种方式进行配置,大部分是作为inputs和outputs时的电气特性。举个例子,你想要配置某个pin为高阻抗,或是你想要用将一个pin配置为上/下拉。
可以通过添加条目到映射表来进行配置pin configuration,详情请见以下Board/machine configuration的部分。
配置参数的格式和含义,是由pin controller驱动定义的。例如PLATFORM_X_PULL_UP
Pin configuration驱动在pin controller中实现了设置引脚配置的回调函数:
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinconf.h>
#include "platform_x_pindefs.h"
static int foo_pin_config_get(struct pinctrl_dev *pctldev,
unsigned offset,
unsigned long *config)
{
struct my_conftype conf;
... Find setting for pin @ offset ...
*config = (unsigned long) conf;
}
static int foo_pin_config_set(struct pinctrl_dev *pctldev,
unsigned offset,
unsigned long config)
{
struct my_conftype *conf = (struct my_conftype *) config;
switch (conf) {
case PLATFORM_X_PULL_UP:
...
}
}
}
static int foo_pin_config_group_get (struct pinctrl_dev *pctldev,
unsigned selector,
unsigned long *config)
{
...
}
static int foo_pin_config_group_set (struct pinctrl_dev *pctldev,
unsigned selector,
unsigned long config)
{
...
}
static struct pinconf_ops foo_pconf_ops = {
.pin_config_get = foo_pin_config_get,
.pin_config_set = foo_pin_config_set,
.pin_config_group_get = foo_pin_config_group_get,
.pin_config_group_set = foo_pin_config_group_set,
};
/* Pin config operations are handled by some pin controller */
static struct pinctrl_desc foo_desc = {
...
.confops = &foo_pconf_ops,
};
因为有些芯片有配置一整组pin的特殊逻辑,则它们可以通过特殊的回调函数来配置一整组pin。
对于其不想处理的组,pin_config_group_set()函数可以返回错误-EAGAIN。或者其只想做些组级的处理,
然后迭代处理所有的pin,这种情况下每个pin可以通过单独调用pin_config_set()进行配置。
与GPIO subsystem的协同作用
===================================
GPIO驱动可能需要在同一个pin上执行多种操作,并且这个pin已经在pin controller中注册。
首先最重要的,这两个子系统