IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> STM32MP157 GPIO子系统 -> 正文阅读

[嵌入式]STM32MP157 GPIO子系统

GPIO子系统

1.GPIO子系统作用

  • GPIO说明

    芯片有很多引脚,每个引脚的功能很多,可用作GPIO、I2C、UART、SPI等功能,在Pincrt子系统篇,讲解了Pinctrl子系统,我们可以通过Pinctrl子系统对选择引脚的复用功能(如复用为GPIO或I2C等功能)以及配置引脚(上拉、下拉、驱动能力等)。

    当一个引脚被复用为GPIO功能时,我们可以去设置它的方向(Input or Output),读取它的状态,GPIO名为"General Purpose Input/Output",通用目的的输入/输出,GPIO可以是芯片自带的,也可以使用扩展芯片通过I2C、SPI接口来扩展。

  • GPIO子系统作用

    为了方便管理GPIO,linux内核抽象除了GPIO子系统,它既能支持芯片本身的GPIO,也能支持扩展的GPIO,提供统一的、简便的访问接口,实现GPIO基本的输入输出和中断操作。

    在没有使用GPIO子系统时,我们使用一个引脚作为GPIO功能时,要通过寄存器来操作GPIO的引脚,对于不公的SOC,它的代码就不相同。

    引入了GPIO子系统之后,我们就可以通过GPIO子系统来操作GPIO,操作方式如下:

    • 在设备树中指定GPIO引脚
    • 在驱动代码中:使用GPIO子系统提供的标准函数获取GPIO、设置GPIO方向、读取/设置GPIO值,而这段代码对于所有的SOC使用来说都是一样的。

2.GPIO子系统结构和相关数据结构

一般在驱动中使用GPIO,都是调用标准函数来操作GPIO,那标准函数是如何操作GPIO呢,下面是我整理的一个基本的框架图

上述框架图中牵扯到几个重要的结构体:

  • struct gpio_device:用来描述一个gpio设备的状态和属性,如这个gpio设备有多少个pin,以及对每个pin的描述,最重要的struct gpio_chip结构体,它是gpio controller的抽象,用来控制引脚。例如在STM32MP157中的GPIOA就可以被抽象为一个struct gpio_device结构体,用来描述GPIOA的状态及属性,它下面有一个gpio_chip,里边有各种对gpio的操作,就是操作gpio0~gpio15。
  • struct gpio_chip: 内核中对gpio controller的抽象,用来控制gpio device下都有一个gpio_chip,用来操作gpio device下的pin。

gpio controller中会对每一个pin进行编号,例如GPIOA0GPIO15编号015,GPIOB0GPIOB15编号1631等等,假如如我们GPIOA操作,在设备树中的节点就如下:

设备树:
beep-gpio = <&gpioa 0 GPIO_ACTIVE_HIGH>;

如果在驱动中我们要使用gpioa0这个pin,首先我们会使用of_get_named_gpio来解析设备节点,解析的结果就是获取gpio编号,后续对gpio的操作都是基于这个编号来操作。

驱动程序:
int beep_gpio;
beep_gpio=of_get_named_gpio(beep.node,"beep-gpio",0);

例如对于上面的gpioa0进行request操作流程就如下:

  • 第一步:设备树中指定引脚gpioa0:beep-gpio = <&gpioa 0 GPIO_ACTIVE_HIGH>;
  • 第二步:驱动程序中解析设备节点,获取gpioa0编号:beep_gpio=of_get_named_gpio(beep.node,“beep-gpio”,0);
  • 第三步:调用gpiolib库中的标准gpio操作API函数gpio_request():gpio_request(beep_gpio,“BEEP-GPIO”);
    • gpio_request()函数中根据gpioa0编号找到GPIOA对应的gpio device
    • 调用GPIOA对应的gpio device下的gpio chip中的requst()函数来操作gpioa0

其他的gpio操作与上面的过程一致

将GPIO设备的抽象为struct gpio_device结构体:例如STM32MP157中GPIOA就是一个GPIO设备,最终我们可以使用struct gpio_device来描述GPIO的属性和状态

struct gpio_device {
	int			id;
	struct device		dev;
	struct cdev		chrdev;
	struct device		*mockdev;
	struct module		*owner;
    /* chip: gpio controller的抽象,用来直接对引脚的控制*/
	struct gpio_chip	*chip;
    /* descs:用来描述gpio的数组,每个gpio_controller下的所有gpio都会在这个数组中进行描述 */
	struct gpio_desc	*descs;
	int			base;/* GPIO编号基值 */
	u16			ngpio;
	const char		*label;
	void			*data;
	struct list_head        list;

#ifdef CONFIG_PINCTRL
	/*
	 * If CONFIG_PINCTRL is enabled, then gpio controllers can optionally
	 * describe the actual pin range which they serve in an SoC. This
	 * information would be used by pinctrl subsystem to configure
	 * corresponding pins for gpio usage.
	 */
	struct list_head pin_ranges;
#endif
};

struct gpio_chip :gpio controller的抽象,就是对gpio最底层的控制,它里边的操作函数就是直接的通过控制寄存器来操作GPIO

struct gpio_chip {
	const char		*label;
	struct gpio_device	*gpiodev;
	struct device		*parent;
	struct module		*owner;

	int			(*request)(struct gpio_chip *chip,
						unsigned offset);
	void			(*free)(struct gpio_chip *chip,
						unsigned offset);
	int			(*get_direction)(struct gpio_chip *chip,
						unsigned offset);
	int			(*direction_input)(struct gpio_chip *chip,
						unsigned offset);
	int			(*direction_output)(struct gpio_chip *chip,
						unsigned offset, int value);
	int			(*get)(struct gpio_chip *chip,
						unsigned offset);
	int			(*get_multiple)(struct gpio_chip *chip,
						unsigned long *mask,
						unsigned long *bits);
	void			(*set)(struct gpio_chip *chip,
						unsigned offset, int value);
	void			(*set_multiple)(struct gpio_chip *chip,
						unsigned long *mask,
						unsigned long *bits);
	int			(*set_config)(struct gpio_chip *chip,
					      unsigned offset,
					      unsigned long config);
	int			(*to_irq)(struct gpio_chip *chip,
						unsigned offset);

	void			(*dbg_show)(struct seq_file *s,
						struct gpio_chip *chip);

	int			(*init_valid_mask)(struct gpio_chip *chip,
						   unsigned long *valid_mask,
						   unsigned int ngpios);

	int			base;
	u16			ngpio;
	const char		*const *names;
	bool			can_sleep;

#if IS_ENABLED(CONFIG_GPIO_GENERIC)
	unsigned long (*read_reg)(void __iomem *reg);
	void (*write_reg)(void __iomem *reg, unsigned long data);
	bool be_bits;
	void __iomem *reg_dat;
	void __iomem *reg_set;
	void __iomem *reg_clr;
	void __iomem *reg_dir_out;
	void __iomem *reg_dir_in;
	bool bgpio_dir_unreadable;
	int bgpio_bits;
	spinlock_t bgpio_lock;
	unsigned long bgpio_data;
	unsigned long bgpio_dir;
#endif /* CONFIG_GPIO_GENERIC */
#ifdef CONFIG_GPIOLIB_IRQCHIP
	struct gpio_irq_chip irq;
#endif /* CONFIG_GPIOLIB_IRQCHIP */

	unsigned long *valid_mask;

#if defined(CONFIG_OF_GPIO)
	struct device_node *of_node;
	unsigned int of_gpio_n_cells;
	int (*of_xlate)(struct gpio_chip *gc,
			const struct of_phandle_args *gpiospec, u32 *flags);
#endif /* CONFIG_OF_GPIO */
};

综合上述代码我们可以看出对GPIO操作最底层的操作就是通过gpio_chip结构体来实现,其他从gpiolib库到调用gpio_chip结构体中的函数这个框架内核的gpio子系统驱动已经帮我们写好了,假如我们自己要写一个gpio controller驱动,我们只需要构建gpio_chip,所以写一个gpio controller驱动就有以下三个步骤:

  • 第一步:分配gpio_chip结构体
  • 第二步:设置gpio_chip结构体
  • 第三步:注册gpio_chip结构体,注册采用gpiochip_add_data()函数,在include/linux/gpio/driver.h文件中定义,注册时会自动创建struct gpio_device结构体。

3.STM32MP157中GPIO Controller的实现过程

  1. 设备树

    gpio controller在设备树中的描述:它分别在两个文件中进行了描述,arch/arm/boot/dts/stm32mp151.dtsi和

    arch/arm/boot/dts/stm32mp15xxac-pinctrl.dtsi文件,现在我把它合并为一个,如下:

    pinctrl: pin-controller@50002000 {
    			#address-cells = <1>;
    			#size-cells = <1>;
    			compatible = "st,stm32mp157-pinctrl";
    			ranges = <0 0x50002000 0xa400>;
    			interrupt-parent = <&exti>;
    			st,syscfg = <&exti 0x60 0xff>;
    			hwlocks = <&hsem 0 1>;
    			pins-are-numbered;
    
    			gpioa: gpio@50002000 {
    				gpio-controller;
    				#gpio-cells = <2>;
    				interrupt-controller;
    				#interrupt-cells = <2>;
    				reg = <0x0 0x400>;
    				clocks = <&rcc GPIOA>;
    				st,bank-name = "GPIOA";
                    status = "okay";
                    ngpios = <16>;
                    gpio-ranges = <&pinctrl 0 0 16>;
    			};
    
    			gpiob: gpio@50003000 {
    				gpio-controller;
    				#gpio-cells = <2>;
    				interrupt-controller;
    				#interrupt-cells = <2>;
    				reg = <0x1000 0x400>;
    				clocks = <&rcc GPIOB>;
    				st,bank-name = "GPIOB";
              	    status = "okay";
                    ngpios = <16>;
                    gpio-ranges = <&pinctrl 0 16 16>;
    			};
        .........
    };
    

    里边例举了两个节点gpioa和gpiob,剩下的和它们结构一样,里边都有一个gpio-controller属性,表明这个设备节点是一个gpio controller节点,而且里边没有compatible属性,它的父节点是pinctrl: pin-controller,这个是pin controller节点,所以gpio controller驱动是在pinctrl驱动中实现的。它实在pinctrl驱动中进行对gpio_chip结构体的分配、设置和注册的。

    我们再来看它的驱动代码:

    int stm32_pctl_probe(struct platform_device *pdev)
    {
    	struct device_node *np = pdev->dev.of_node;/* 对应的设备树节点 */
    	struct device_node *child; /* 子节点 */
    	const struct of_device_id *match; /* 设备驱动匹配项 */
    	struct device *dev = &pdev->dev;/* 对应的设备 */
    	struct stm32_pinctrl *pctl;     /* STM32 pinctrl */
    	struct pinctrl_pin_desc *pins;  /* pin脚描述 */
    	int i, ret, hwlock_id, banks = 0;
    
        .........
            /* pin controller驱动代码部分 */
        ..........
            
          /* gpio conroller驱动代码部分 */
    	for_each_available_child_of_node(np, child)
    		if (of_property_read_bool(child, "gpio-controller"))
    		/* 对于pincrtl下的每个子节点查找是否存在 gpio-controller属性,统计gpio controller的个数*/
                banks++;
    
    	if (!banks) {
    		dev_err(dev, "at least one GPIO bank is required\n");
    		return -EINVAL;
    	}
        /*struct stm32_pinctrl下的banks 为struct stm32_gpio_bank类型,在STM32MP157中将每个gpio设备抽象为struct stm32_gpio_bank,在struct stm32_gpio_bank下定义了		gpio_chip结构体,所以分配struct stm32_gpio_bank内存相当于也给gpio_chip分配了内存 */
    	pctl->banks = devm_kcalloc(dev, banks, sizeof(*pctl->banks),
    			GFP_KERNEL);
    	if (!pctl->banks)
    		return -ENOMEM;
    
    	i = 0;
    	for_each_available_child_of_node(np, child) {
            /* GPIO设备的时钟配置 */
    		struct stm32_gpio_bank *bank = &pctl->banks[i];
    
    		if (of_property_read_bool(child, "gpio-controller")) {
    			bank->rstc = of_reset_control_get_exclusive(child,
    								    NULL);
    			if (PTR_ERR(bank->rstc) == -EPROBE_DEFER)
    				return -EPROBE_DEFER;
    
    			bank->clk = of_clk_get_by_name(child, NULL);
    			if (IS_ERR(bank->clk)) {
    				if (PTR_ERR(bank->clk) != -EPROBE_DEFER)
    					dev_err(dev,
    						"failed to get clk (%ld)\n",
    						PTR_ERR(bank->clk));
    				return PTR_ERR(bank->clk);
    			}
    			i++;
    		}
    	}
    
    	for_each_available_child_of_node(np, child) {
    		if (of_property_read_bool(child, "gpio-controller")) {
                /* 对每个gpio controller节点进行注册,其中gpio_chip结构体的设置和注册就在这个函数中进行 */
    			ret = stm32_gpiolib_register_bank(pctl, child);
    			if (ret) {
    				of_node_put(child);
    				return ret;
    			}
    
    			pctl->nbanks++;
    		}
    	}
    
    	dev_info(dev, "Pinctrl STM32 initialized\n");
    
    	return 0;
    }
    
    

    stm32_gpiolib_register_bank()函数解析

    static int stm32_gpiolib_register_bank(struct stm32_pinctrl *pctl,
    	struct device_node *np)
    {
    	struct stm32_gpio_bank *bank = &pctl->banks[pctl->nbanks];
    	int bank_ioport_nr;
    	struct pinctrl_gpio_range *range = &bank->range;
    	struct of_phandle_args args;
    	struct device *dev = pctl->dev;
    	struct resource res;
    	int npins = STM32_GPIO_PINS_PER_BANK;
    	int bank_nr, err;
    
    	if (!IS_ERR(bank->rstc))
    		reset_control_deassert(bank->rstc);
    
    	if (of_address_to_resource(np, 0, &res))
    		return -ENODEV;
    
    	bank->base = devm_ioremap_resource(dev, &res);
    	if (IS_ERR(bank->base))
    		return PTR_ERR(bank->base);
    
    	err = clk_prepare(bank->clk);
    	if (err) {
    		dev_err(dev, "failed to prepare clk (%d)\n", err);
    		return err;
    	}
    
        /* gpio_chip赋值,里边时gpio操作函数 
            static const struct gpio_chip stm32_gpio_template = {
            .request		= stm32_gpio_request,
            .free			= stm32_gpio_free,
            .get			= stm32_gpio_get,
            .set			= stm32_gpio_set,
            .direction_input	= stm32_gpio_direction_input,
            .direction_output	= stm32_gpio_direction_output,
            .to_irq			= stm32_gpio_to_irq,
            .get_direction		= stm32_gpio_get_direction,
            .set_config		= gpiochip_generic_config,
    };
        */
    	bank->gpio_chip = stm32_gpio_template;
    
    	of_property_read_string(np, "st,bank-name", &bank->gpio_chip.label);
    
        /* 
        	解析设备树节点中的gpio-ranges属性
        	例如:gpio-ranges = <&pinctrl 1  2  3>;
        	解析结果:args.args[0] = 1  args.args[1] = 2  args.args[2] = 3 
        	我们可以从设备树中看出args[1]的这个值就是该gpio controller中pin 编号的基值
        */
    	if (!of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, 0, &args)) {
    		/*
    		  GPIOA: args.args[1]=0  bank_nr=0  bank->gpio_chip.base=0
    		  GPIOB: args.args[1]=16  bank_nr=1  bank->gpio_chip.base=16
    		  GPIOC: args.args[1]=32  bank_nr=2  bank->gpio_chip.base=32
    		 */
            bank_nr = args.args[1] / STM32_GPIO_PINS_PER_BANK;/* 给bankb编号 */
    		bank->gpio_chip.base = args.args[1];/* pins编号基值 */
    	} else {
    		bank_nr = pctl->nbanks;
    		bank->gpio_chip.base = bank_nr * STM32_GPIO_PINS_PER_BANK;
    		range->name = bank->gpio_chip.label;
    		range->id = bank_nr;
    		range->pin_base = range->id * STM32_GPIO_PINS_PER_BANK;
    		range->base = range->id * STM32_GPIO_PINS_PER_BANK;
    		range->npins = npins;
    		range->gc = &bank->gpio_chip;
    		pinctrl_add_gpio_range(pctl->pctl_dev,
    				       &pctl->banks[bank_nr].range);
    	}
    
    	if (of_property_read_u32(np, "st,bank-ioport", &bank_ioport_nr))
    		bank_ioport_nr = bank_nr;
    
    	bank->gpio_chip.base = bank_nr * STM32_GPIO_PINS_PER_BANK;
    
    	bank->gpio_chip.ngpio = npins;
    	bank->gpio_chip.of_node = np;
    	bank->gpio_chip.parent = dev;
    	bank->bank_nr = bank_nr;
    	bank->bank_ioport_nr = bank_ioport_nr;
    	spin_lock_init(&bank->lock);
    
    	/* create irq hierarchical domain */
    	bank->fwnode = of_node_to_fwnode(np);
    
    	bank->domain = irq_domain_create_hierarchy(pctl->domain, 0,
    					STM32_GPIO_IRQ_LINE, bank->fwnode,
    					&stm32_gpio_domain_ops, bank);
    
    	if (!bank->domain)
    		return -ENODEV;
    
    	/* 注册struct gpio_chip结构体 */
    	err = gpiochip_add_data(&bank->gpio_chip, bank);
    	if (err) {
    		dev_err(dev, "Failed to add gpiochip(%d)!\n", bank_nr);
    		return err;
    	}
    
    	dev_info(dev, "%s bank added\n", bank->gpio_chip.label);
    	return 0;
    }
    

    最后调用gpiochip_add_data()注册gpio_chip结构体,接下来我们分析gpiochip_add_data()函数,在include/linux/gpio/driver.h文件中被宏定义:

    #define gpiochip_add_data(chip, data) gpiochip_add_data_with_key(chip, data, NULL, NULL)
    

    所以最终使用的函数为gpiochip_add_data_with_key();在drivers/gpio/gpiolib.c文件中定义

    int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
    			       struct lock_class_key *lock_key,
    			       struct lock_class_key *request_key)
    {
    	unsigned long	flags;
    	int		ret = 0;
    	unsigned	i;
    	int		base = chip->base;
    	struct gpio_device *gdev;
    
        /*  分配并设置gpio_device结构体*/
    	gdev = kzalloc(sizeof(*gdev), GFP_KERNEL);
    	if (!gdev)
    		return -ENOMEM;
    	gdev->dev.bus = &gpio_bus_type;
        /* gpio device 中的chip指向在上面驱动函数中构造的gpio chip */
    	gdev->chip = chip;
    	chip->gpiodev = gdev;
    	if (chip->parent) {
    		gdev->dev.parent = chip->parent;
    		gdev->dev.of_node = chip->parent->of_node;
    	}
    
    #ifdef CONFIG_OF_GPIO
    	/* If the gpiochip has an assigned OF node this takes precedence */
    	if (chip->of_node)
    		gdev->dev.of_node = chip->of_node;
    	else
    		chip->of_node = gdev->dev.of_node;
    #endif
    
    	gdev->id = ida_simple_get(&gpio_ida, 0, 0, GFP_KERNEL);
    	if (gdev->id < 0) {
    		ret = gdev->id;
    		goto err_free_gdev;
    	}
    	dev_set_name(&gdev->dev, "gpiochip%d", gdev->id);
    	device_initialize(&gdev->dev);
    	dev_set_drvdata(&gdev->dev, gdev);
    	if (chip->parent && chip->parent->driver)
    		gdev->owner = chip->parent->driver->owner;
    	else if (chip->owner)
    		/* TODO: remove chip->owner */
    		gdev->owner = chip->owner;
    	else
    		gdev->owner = THIS_MODULE;
    
        /* 
        分配struct gpio_desc结构体数组,用来描述gpio device下的每个gpio 
                        struct gpio_desc {
                        struct gpio_device	*gdev;
                        unsigned long		flags;
                    /* flag symbols are bit numbers */
                    #define FLAG_REQUESTED	0
                    #define FLAG_IS_OUT	1
                    #define FLAG_EXPORT	2	/* protected by sysfs_lock */
                    #define FLAG_SYSFS	3	/* exported via /sys/class/gpio/control */
                    #define FLAG_ACTIVE_LOW	6	/* value has active low */
                    #define FLAG_OPEN_DRAIN	7	/* Gpio is open drain type */
                    #define FLAG_OPEN_SOURCE 8	/* Gpio is open source type */
                    #define FLAG_USED_AS_IRQ 9	/* GPIO is connected to an IRQ */
                    #define FLAG_IRQ_IS_ENABLED 10	/* GPIO is connected to an enabled IRQ */
                    #define FLAG_IS_HOGGED	11	/* GPIO is hogged */
                    #define FLAG_TRANSITORY 12	/* GPIO may lose value in sleep or reset */
                    #define FLAG_PULL_UP    13	/* GPIO has pull up enabled */
                    #define FLAG_PULL_DOWN  14	/* GPIO has pull down enabled */
                        /* Connection label 
                        const char		*label;
                        Name of the GPIO 
                        const char		*name;
                    };
    */
    	gdev->descs = kcalloc(chip->ngpio, sizeof(gdev->descs[0]), GFP_KERNEL);
    	if (!gdev->descs) {
    		ret = -ENOMEM;
    		goto err_free_ida;
    	}
    
    	if (chip->ngpio == 0) {
    		chip_err(chip, "tried to insert a GPIO chip with zero lines\n");
    		ret = -EINVAL;
    		goto err_free_descs;
    	}
    
    	if (chip->ngpio > FASTPATH_NGPIO)
    		chip_warn(chip, "line cnt %u is greater than fast path cnt %u\n",
    		chip->ngpio, FASTPATH_NGPIO);
    
    	gdev->label = kstrdup_const(chip->label ?: "unknown", GFP_KERNEL);
    	if (!gdev->label) {
    		ret = -ENOMEM;
    		goto err_free_descs;
    	}
    
    	gdev->ngpio = chip->ngpio;
    	gdev->data = data;
    
    	spin_lock_irqsave(&gpio_lock, flags);
    
    	/*
    	 * TODO: this allocates a Linux GPIO number base in the global
    	 * GPIO numberspace for this chip. In the long run we want to
    	 * get *rid* of this numberspace and use only descriptors, but
    	 * it may be a pipe dream. It will not happen before we get rid
    	 * of the sysfs interface anyways.
    	 */
    	if (base < 0) {
    		base = gpiochip_find_base(chip->ngpio);
    		if (base < 0) {
    			ret = base;
    			spin_unlock_irqrestore(&gpio_lock, flags);
    			goto err_free_label;
    		}
    		/*
    		 * TODO: it should not be necessary to reflect the assigned
    		 * base outside of the GPIO subsystem. Go over drivers and
    		 * see if anyone makes use of this, else drop this and assign
    		 * a poison instead.
    		 */
    		chip->base = base;
    	}
    	gdev->base = base;
    
        /* 将gpio device添加到链表中 */
    	ret = gpiodev_add_to_list(gdev);
    	if (ret) {
    		spin_unlock_irqrestore(&gpio_lock, flags);
    		goto err_free_label;
    	}
    
    	spin_unlock_irqrestore(&gpio_lock, flags);
    
    	for (i = 0; i < chip->ngpio; i++)
    		gdev->descs[i].gdev = gdev;
    
    #ifdef CONFIG_PINCTRL
    	INIT_LIST_HEAD(&gdev->pin_ranges);
    #endif
    
    	ret = gpiochip_set_desc_names(chip);
    	if (ret)
    		goto err_remove_from_list;
    
    	ret = gpiochip_alloc_valid_mask(chip);
    	if (ret)
    		goto err_remove_from_list;
    
    	ret = of_gpiochip_add(chip);
    	if (ret)
    		goto err_free_gpiochip_mask;
    
    	ret = gpiochip_init_valid_mask(chip);
    	if (ret)
    		goto err_remove_of_chip;
    
        /*  对每个gpio在gpio device下的descs结构体数组中进行描述 */
    	for (i = 0; i < chip->ngpio; i++) {
    		struct gpio_desc *desc = &gdev->descs[i];
    
    		if (chip->get_direction && gpiochip_line_is_valid(chip, i)) {
    			if (!chip->get_direction(chip, i))
    				set_bit(FLAG_IS_OUT, &desc->flags);
    			else
    				clear_bit(FLAG_IS_OUT, &desc->flags);
    		} else {
    			if (!chip->direction_input)
    				set_bit(FLAG_IS_OUT, &desc->flags);
    			else
    				clear_bit(FLAG_IS_OUT, &desc->flags);
    		}
    	}
    ...............................
    }
    

    STM32MP157的gpio controller驱动程序我们就分析到这里,看上去很复杂,其实也就是实现前面所分析的三步:第一步:分配gpio_chip结构体;第二步:设置gpio_chip结构体;第三步:注册gpio_chip结构体

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-08-05 17:31:05  更:2021-08-05 17:32:22 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 18:39:07-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码