GPIO子系统与Pinctrl子系统的交互

一、使用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
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

习惯就好zz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值