一、使用GPIO前应该设置Pinctrl
假设使用这个虚拟的GPIO Controller的pin A来控制LED:
要使用Pin A来控制LED,需要通过Pinctrl子系统把它设置为GPIO功能,然后才能设置为输出引脚、设置输出值
所以在设备树文件里,应该添加Pinctrl的内容:
virtual_pincontroller {
compatible = "100ask,virtual_pinctrl";
myled_pin: myled_pin { //在pincontroller中增加节点
functions = "gpio";
groups = "pin0";
configs = <0x11223344>;
};
};
gpio_virt: virtual_gpiocontroller {
compatible = "100ask,virtual_gpio";
gpio-controller;
#gpio-cells = <2>;
ngpios = <4>;
};
myled {
compatible = "100ask,leddrv";
led-gpios = <&gpio_virt 0 GPIO_ACTIVE_LOW>;
pinctrl-names = "default"; //设置pinctrl的状态
pinctrl-0 = <&myled_pin>; //设置pinctrl的设置来设置gpio
};
二、GPIO和Pinctrl的映射关系
2.1 示例
比如:在实际的物理设备中,有2个GPIO控制器,每个GPIO分辨控制4个引脚。那么在Pincontroller中
- pinctrl_desc的pins总共有8个引脚,在Pinctrl的内部编号依次为0~7
- 每个GPIO Controller有2个gpio_device来表示不同的GPIO控制器
- GPIO0内部引脚编号为0 ~ 3,在GPIO子系统中全局编号为100 ~ 103
- GPIO1内部引脚编号为0 ~ 3,在GPIO子系统中全局编号为104 ~ 107
- 假设我们要使用Pin1-1,应该这样做:
- 根据GPIO1的内部编号1,找到Pinctrl子系统中的编号5
- 使用Pinctrl的函数,把第5个引脚配置为GPIO功能
2.2 数据结构
struct gpio_pin_range {
struct list_head node;
struct pinctrl_dev *pctldev;
struct pinctrl_gpio_range range;
};
struct pinctrl_gpio_range {
struct list_head node;
const char *name;
unsigned int id;
unsigned int base; // GPIO子系统中的引脚编号
unsigned int pin_base; // Pinctrl设备中的引设备编号
unsigned const *pins;
unsigned int npins; // 引脚个数
struct gpio_chip *gc;
};
三、GPIO调用Pinctrl的过程
GPIO子系统中的request函数,用来申请某个GPIO引脚。
它会导致Pinctrl子系统中的这2个函数之一被调用:
pmxops->gpio_request_enable
pmxops->request
struct gpio_desc *__must_check gpiod_get(struct device *dev, const char *con_id,
enum gpiod_flags flags)
{
return gpiod_get_index(dev, con_id, 0, flags);
}
struct gpio_desc *__must_check gpiod_get_index(struct device *dev,
const char *con_id,
unsigned int idx,
enum gpiod_flags flags)
{
struct gpio_desc *desc = NULL;
int status;
enum gpio_lookup_flags lookupflags = 0;
if (dev) {
/* Using device tree? */
if (IS_ENABLED(CONFIG_OF) && dev->of_node) {
//...
desc = of_find_gpio(dev, con_id, idx, &lookupflags);
} else if (ACPI_COMPANION(dev)) {
//...
}
}
if (!desc || desc == ERR_PTR(-ENOENT)) {
dev_dbg(dev, "using lookup tables for GPIO lookup\n");
desc = gpiod_find(dev, con_id, idx, &lookupflags);
}
//...
status = gpiod_request(desc, con_id);
//....
status = gpiod_configure_flags(desc, con_id, lookupflags, flags);
//....
return desc;
}
int gpiod_request(struct gpio_desc *desc, const char *label)
{
int status = -EPROBE_DEFER;
struct gpio_device *gdev;
//....
if (try_module_get(gdev->owner)) {
status = __gpiod_request(desc, label);
//....
}
//...
return status;
}
static int __gpiod_request(struct gpio_desc *desc, const char *label)
{
struct gpio_chip *chip = desc->gdev->chip;
int status;
unsigned long flags;
//....
if (chip->request) { //在chip->request中
/* chip->request may sleep */
spin_unlock_irqrestore(&gpio_lock, flags);
status = chip->request(chip, gpio_chip_hwgpio(desc));
spin_lock_irqsave(&gpio_lock, flags);
if (status < 0) {
desc_set_label(desc, NULL);
clear_bit(FLAG_REQUESTED, &desc->flags);
goto done;
}
}
if (chip->get_direction) {
/* chip->get_direction may sleep */
spin_unlock_irqrestore(&gpio_lock, flags);
gpiod_get_direction(desc);
spin_lock_irqsave(&gpio_lock, flags);
}
done:
spin_unlock_irqrestore(&gpio_lock, flags);
return status;
}
在driver/gpio/gpio-mxc.c
文件中的函数static int mxc_gpio_probe
中,初始化了request函数
static int mxc_gpio_probe(struct platform_device *pdev)
{
//...
port->gc.request = mxc_gpio_request;
port->gc.free = mxc_gpio_free;
port->gc.parent = &pdev->dev;
port->gc.to_irq = mxc_gpio_to_irq;
port->gc.base = (pdev->id < 0) ? of_alias_get_id(np, "gpio") * 32 :
pdev->id * 32;
//...
}
//drivers/gpio/gpio-mxc.c
static int mxc_gpio_request(struct gpio_chip *chip, unsigned offset)
{
struct mxc_gpio_port *port = gpiochip_get_data(chip);
//...
if (port->gpio_ranges) {
ret = gpiochip_generic_request(chip, offset);
//...
}
ret = pm_runtime_get_sync(chip->parent);
return ret < 0 ? ret : 0;
}
int gpiochip_generic_request(struct gpio_chip *chip, unsigned offset)
{
return pinctrl_request_gpio(chip->gpiodev->base + offset); //从全局编号中的基地址+偏移地址
}
int pinctrl_request_gpio(unsigned gpio)
{
ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range); //获取pinctrl_gpio_range
//....
pin = gpio_to_pin(range, gpio); //使用range转换成pin controller的序号
ret = pinmux_request_gpio(pctldev, range, pin, gpio);
//...
}
int pinmux_request_gpio(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
unsigned pin, unsigned gpio)
{
//...
ret = pin_request(pctldev, pin, owner, range); //继续调用pin_request
//...
}
static int pin_request(struct pinctrl_dev *pctldev,
int pin, const char *owner,
struct pinctrl_gpio_range *gpio_range)
{
struct pin_desc *desc;
const struct pinmux_ops *ops = pctldev->desc->pmxops;
int status = -EINVAL;
desc = pin_desc_get(pctldev, pin);
//....
if (gpio_range) {
//...似乎设置了owner
desc->gpio_owner = owner;
} else {
//...
}
/* Let each pin increase references to this module */
if (!try_module_get(pctldev->owner)) { //让每个引脚增加对该模块的引用
//...
}
/*
* If there is no kind of request function for the pin we just assume
* we got it by default and proceed.
*/
if (gpio_range && ops->gpio_request_enable) //如果提供了函数就调用gpio_request_enable
/* This requests and enables a single GPIO pin */
status = ops->gpio_request_enable(pctldev, gpio_range, pin);
else if (ops->request) //如果提供了request函数,就调用request函数,配置为GPIO功能
status = ops->request(pctldev, pin);
else
status = 0;
//...
return status;
}
所以当我们使用gpiod_get函数去调用gpio的时候,其实隐含了调用request的函数调用。
它会把GPIO子系统中的序号,转换成pinctroller中的序号,然后配置成GPIO模式。
四、设置驱动时需要做什么
如果不想再使用GPIO引脚时,在设备书中设置Pinctrl信息。
如果想要让GPIO和Pinctrl之间建立联系,需要做些事情。
4.1 表明GPIO和Pinctrl间的联系
在GPIO设备树中使用gpio-ranges
来描述它们之间的联系:
-GPIO系统中有引脚号
- Pinctrl子系统中也有自己的引脚号
- 2个号码要建立映射关系
- 在GPIO设备书中使用如下代码建立映射关系
// 当前GPIO控制器的0号引脚, 对应pinctrlA中的128号引脚, 数量为12
gpio-ranges = <&pinctrlA 0 128 12>;
4.2 解析这些联系
在GPIO驱动程序中,解析跟Pinctrl之间的联系:处理gpio-ranges
- 这不需要自己写代码
- 注册gpio_chip
时会自动调节
int gpiochip_add_data(struct gpio_chip *chip, void *data)
status = of_gpiochip_add(chip);
status = of_gpiochip_add_pin_range(chip);
of_gpiochip_add_pin_range
for (;; index++) {
ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3,
index, &pinspec);
pctldev = of_pinctrl_get(pinspec.np); // 根据gpio-ranges的第1个参数找到pctldev
// 增加映射关系
/* npins != 0: linear range */
ret = gpiochip_add_pin_range(chip,
pinctrl_dev_get_devname(pctldev),
pinspec.args[0],
pinspec.args[1],
pinspec.args[2]);
4.3 编程
- 在GPIO驱动程序中,提供gpio_chip->request
- 在Pinctrl驱动程序中,提供pmxops->gpio_request_enable或pmxops->request