因为热爱所以奋斗

gpio子系统(2)之 gpiod_get() 函数解析过程

介绍

  • Linux版本:4.9.88
  • 芯片类型:IMX6ULL
  • 作者唠嗑:因分析的是gpio子系统的主线,其他不相关的内容已进行了相应的删除,使其主线更清晰。过程和结论都很重要!喜欢的话可以分享,记得附上原链接 ~ 不胜感激0.0
  • 背景提要:在第一章可知,GPIO子系统已从dt解析了所有的GPIO-controller node,根据SOC特定信息,生成和设置了gpio_chip gpio_dev,并按顺序生成pin number,此时便可通过pin number找到所有已定义的pin。
  • pin号计算方法:假设需要使用 gpiox_ioy,则 x*32 + y = pin number。
  • 返回值:返回gpio_desc,此时可通过该结构体,使用该gpio了,如set direction,set value等操作。

1 gpiod_get()

/*
 * 1. 函数适用于只有一个gpio,index为0,即list = <&phandle1 1 2>;
 * 2. 如果想使用phandle2,list = <&phandle1 1 2 &phandle2 3 3>;
 *    则为 gpiod_get_index(dev, con_id, 1, flags);
 */
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) {
        
		/*使用dt*/
		if (IS_ENABLED(CONFIG_OF) && dev->of_node) {
           
            /* 
             * 解析dt,找到gpio-controller,根据解析的引脚号,
             * 找到对应 gpio-controller 的 gpio_desc。 
             */ 
			desc = of_find_gpio(dev, con_id, idx, &lookupflags);            
		} 
	}

    /* 有关"gpio-ranges"属性,暂不分析 */  
	status = gpiod_request(desc, con_id);

	/* 根据传入flag设置方向,flag为null会设为输入 */
	status = gpiod_configure_flags(desc, con_id, lookupflags, flags);

	return desc;
}

1-1 of_find_gpio()

static const char * const gpio_suffixes[] = { "gpios", "gpio" };


struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id,
			       unsigned int idx,
			       enum gpio_lookup_flags *flags)
{
	char prop_name[32]; /* 32 is max size of property name */
	enum of_gpio_flags of_flags;
	struct gpio_desc *desc;
	unsigned int i;

	for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
        
        /* 如 "led-gpios" 或 "led-gpio" 的属性名 */
		if (con_id)
			snprintf(prop_name, sizeof(prop_name), "%s-%s", con_id,
				 gpio_suffixes[i]);
        
        /* 合成,如 "gpios" 或 "gpio" 的属性名 */
		else 
			snprintf(prop_name, sizeof(prop_name), "%s",
				 gpio_suffixes[i]);

        /* 
         * 1. 根据属性名(led-gpios)查找匹配,保存内容在gpio_desc
         * 2. 根据gpiospec的phandle找到chip,即:有gpio-controller的节点。
         * 3. 根据gpiospec的引脚号,找到在chip中的desc数组偏移位置, 
         *    即对应的引脚号的desc
         * 4. 并返回了flags "OF_GPIO_ACTIVE_LOW"
         */
		desc = of_get_named_gpiod_flags(dev->of_node, prop_name, idx,
						&of_flags);

	}
	
    /* 如果是低电平有效,设置flag */
	if (of_flags & OF_GPIO_ACTIVE_LOW)
		*flags |= GPIO_ACTIVE_LOW;

	if (of_flags & OF_GPIO_SINGLE_ENDED) {
		if (of_flags & OF_GPIO_ACTIVE_LOW)
			*flags |= GPIO_OPEN_DRAIN;
		else
			*flags |= GPIO_OPEN_SOURCE;
	}

	return desc;
}

1-1-1 of_get_named_gpiod_flags()

/* 
 * 1. 在user的node(如led),解析dt有关gpio的内容,
 * 2. 寻找要调用的gpio-controller,进而找到gpiochip
 * 3. 通过dt提供的引脚号,找到对应引脚的gpio_desc
 */
struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
		     const char *propname, int index, enum of_gpio_flags *flags)
{
	struct of_phandle_args gpiospec;
	struct gpio_chip *chip;
	struct gpio_desc *desc;
	int ret;

    /*
     * propname:当前node的属性名
     * "#gpio-cells":父node的属性名
     * index:第index个phandle
     * 举例:propname = <&phandle1 1 2 &phandle2 3>;
     *       1. 当#gpio-cells为2,index为0时,
     *       2. gpiospec->arg[0] = 1 | [1] = 2;
     *          gpiospec->args_count = 2;
     *          gpiospec->np = phandle1;
     */  
	ret = of_parse_phandle_with_args(np, propname, "#gpio-cells", index,
					 &gpiospec);

    	
	 /* 根据gpiospec,遍历 gpio_devices,找到匹配的gpiochip */
	chip = of_find_gpiochip_by_xlate(&gpiospec);


	/* 根据dt中,"xx-gpios"属性提供的引脚号,找到对应引脚的gpio_desc */
	desc = of_xlate_and_get_gpiod_flags(chip, &gpiospec, flags);

	return desc;
}

1-2 gpiod_request()

/* 
 * desc:对应引脚号的gpio_desc 
 * lable:"led" 
*/
int gpiod_request(struct gpio_desc *desc, const char *label)
{
	int status = -EPROBE_DEFER;
	struct gpio_device *gdev;

	gdev = desc->gdev;

	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;


	/* 设置label("led-gpios的led").如果为NULL,设置为"?" */
	desc_set_label(desc, label ? : "?");
	status = 0;
  
    /* 没有"gpio-ranges"属性,无作用 */
	if (chip->request) {    
        /* mxc_gpio_request */
        status = chip->request(chip, gpio_chip_hwgpio(desc));
    }
    
    /*获得gpio方向*/
	if (chip->get_direction) {
		gpiod_get_direction(desc);
	}

	return status;
}

1-3 gpiod_configure_flags()

/**
 * gpiod_configure_flags - helper function to configure a given GPIO
 * @desc:	gpio whose value will be assigned
 * @con_id:	function within the GPIO consumer
 * @lflags:	gpio_lookup_flags - returned from of_find_gpio() or
 *		of_get_gpio_hog()
 * @dflags:	gpiod_flags - optional GPIO initialization flags
 *
 * Return 0 on success, -ENOENT if no GPIO has been assigned to the
 * requested function and/or index, or another IS_ERR() code if an error
 * occurred while trying to acquire the GPIO.
 */
static int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
		unsigned long lflags, enum gpiod_flags dflags)
{
	int status;

    /* 
     * 将解析dt的flag,记录在对应引脚的gpio_desc->flags 
     * 用途是实现逻辑电平的统一,无关实际物理电平。
     */
	if (lflags & GPIO_ACTIVE_LOW)
		set_bit(FLAG_ACTIVE_LOW, &desc->flags);
    
	if (lflags & GPIO_OPEN_DRAIN)
		set_bit(FLAG_OPEN_DRAIN, &desc->flags);
    
	if (lflags & GPIO_OPEN_SOURCE)
		set_bit(FLAG_OPEN_SOURCE, &desc->flags);

    
    /*
     * gpio_get解析传入的flag,引脚设置方向
     * GPIOD_FLAGS_BIT_DIR_OUT: 输出
     * others: 输入
     */
	if (dflags & GPIOD_FLAGS_BIT_DIR_OUT)       
		status = gpiod_direction_output(desc,
					      dflags & GPIOD_FLAGS_BIT_DIR_VAL);
	else
		status = gpiod_direction_input(desc);

	return status;
}

总结

  • gpiod_get_index(index)

    • 根据传入参数index,确认要使用“xxx-gpios”的第几个pin。
    • 解析dt属性,获取使用了哪个gpio-controller,使用了哪个引脚号。
    • 找到gpio-controller节点,获取已配置好的gpio_device。
    • 根据引脚号,找到对应挂在gpio_device的gpio_desc,返回给user。
  • gpiod_set_value(),gpiod_direction_output(),gpiod_direction_input() 等

    • 因为已经获得gpio_desc,则反向找到gpio_device的gpiochip。

    • 利用gpiochip提供的中间层函数,操作gpio,如设置电平,设置方向。

  • 在dt的”ngpios”属性,意思是gpio控制器的实际pin数量,即寄存器是32位宽,但只有18位对应了控制引脚,1个gpio-controller控制18个pin,设置"ngpios = <18>,通知driver只有18个pin。

posted on 2022-08-31 17:44  kengk  阅读(3656)  评论(0编辑  收藏  举报

导航