iomuxc、pinctrl子系统、gpio子系统(学习总结)

news2024/11/14 18:04:57

iomuxc、pinctrl子系统、gpio子系统三者的关系

  1. 相互依赖:IOMUXC、pinctrl子系统和gpio子系统在功能上相互依赖。IOMUXC提供了引脚复用和电气属性的配置能力,pinctrl子系统负责从设备树中获取这些配置信息并完成初始化,而gpio子系统则在引脚被配置为GPIO功能时提供具体的操作接口。

  2. 分层结构:从系统架构的角度来看,这三者构成了一个分层的结构。IOMUXC位于最底层,提供硬件级别的引脚复用和电气属性配置;pinctrl子系统位于中间层,负责软件层面的引脚配置和初始化;gpio子系统则位于最上层,为开发者提供易于使用的GPIO操作接口。

  3. 协同工作:在实际应用中,这三者协同工作以实现引脚的有效管理和控制。例如,在配置一个LED灯的GPIO引脚时,首先需要通过IOMUXC将该引脚复用为GPIO功能,然后通过pinctrl子系统从设备树中获取该引脚的配置信息并完成初始化,最后通过gpio子系统提供的API接口来设置引脚的方向和输出值以控制LED灯的亮灭。

IOMUXC

一、IOMUXC的基本概念

  • 概念引入:为了推出功能丰富的核心板,SOC厂商引入了引脚复用-IOMUXC架构。这种架构通过输入输出多路复用器(Input/Output Multiplexer),使得每个引脚最多可复用好几种功能,每个功能又可以出现在不同的引脚上。
  • 核心功能:IOMUXC的核心功能是IO口复用,即利用有限的IO口资源,通过动态切换,满足多个内部外设的IO需求。

二、IOMUXC的架构与组成

  • 基本单元:IOMUX由一些基本的iomux_cell单元组成,每个基本IOMUX单元只处理一个pad复用信号。
  • 子块组成:IOMUXC通常由两个子块组成:IOMUXC_REGISTERS(包含所有IOMUXC寄存器)和IOMUXC_LOGIC(包含所有IOMUXC组合逻辑,如IP接口控制、地址解码器、可观察复用控制)。
  • 寄存器分类:在IOMUXC控制器中,寄存器主要分为三大类:IOMUXC_SW_PAD_CTRL_X(管脚控制寄存器)、IOMUXC_SW_MUX_CTL_PAD_X(输出路由寄存器)、IOMUXC_X_SELECT_INPUT(输入路由寄存器)。

三、IOMUXC的配置与使用

  • 配置方式:IOMUXC的配置通常涉及对控制器中的寄存器进行参数设置。这些参数包括控制寄存器的偏移地址、MUX控制寄存器的偏移地址、MUX模式、输入路由寄存器的偏移地址以及Daisy Chain模式等。
  • Linux内核配置:在Linux内核中,SOC厂商会把各功能引脚的配置参数值写到相应的宏定义头文件中(如xxx-pinfunc.h),用户可以根据芯片使用手册中的说明,在设备树(dtb)中添加或修改引脚定义。
  • 功能切换:IOMUXC只起了一个功能切换的作用,引脚配置完成后,还需要配置具体功能的控制器里寄存器参数,这样才能共同完成引脚的功能确定。

四、IOMUXC的应用场景

  • GPIO配置:在GPIO配置中,IOMUXC用于选择引脚的功能模式,如将某个引脚配置为GPIO输入或输出模式。
  • 外设接口配置:对于其他外设接口(如UART、SPI、I2C等),IOMUXC同样用于选择引脚的功能模式,以满足外设的IO需求。

五、总结

        IOMUXC是SOC设计中实现引脚复用的关键组件,它通过动态切换引脚功能,提高了IO资源的利用率,满足了不同外设和功能的IO需求。在配置和使用IOMUXC时,需要参考芯片使用手册和设备树文档,确保引脚功能的正确设置。

Pinctrl       

一、Pinctrl子系统的作用

        Pinctrl子系统的主要作用是管理和配置处理器上的引脚资源。在嵌入式系统中,引脚是处理器与外部世界进行通信和交互的接口。不同的处理器和硬件平台可能具有不同的引脚数量和功能,因此需要一种统一的机制来管理和配置这些引脚资源,以确保系统的正常运行。

二、Pinctrl子系统的组成

Pinctrl子系统主要由以下几个部分组成:

  1. Pinctrl Core:Pinctrl Core是Pinctrl子系统的核心部分,负责管理和维护系统上的引脚和Pinctrl配置,提供通用的Pinctrl接口供设备驱动程序使用,并管理设备驱动程序之间的引脚冲突和资源共享。

  2. Pinctrl驱动程序:Pinctrl驱动程序是一个用于管理芯片引脚的Linux内核驱动程序。它通过实现Pinctrl Core提供的接口来向Pinctrl Core注册自己,并提供芯片引脚的配置和控制功能。

  3. Pinctrl映射器:Pinctrl映射器是一个将Pinctrl配置映射到具体芯片引脚的模块。它负责解析Pinctrl配置,映射到具体的引脚,并将映射结果传递给Pinctrl驱动程序。Pinctrl映射器可以根据不同的芯片架构和芯片引脚布局进行定制。

三、Pinctrl子系统的工作原理

Pinctrl子系统的工作原理可以分为以下几个阶段:

  1. 解析阶段:在系统启动时,Pinctrl子系统会解析设备树(Device Tree)中的引脚配置信息。

  2. 配置阶段:解析完成后,Pinctrl子系统会根据解析得到的信息配置引脚资源。它会根据配置模式设置引脚的功能,并根据电气特性配置引脚的输入/输出电路。如果需要,Pinctrl子系统还会配置引脚的中断信息,以便处理外部事件。

  3. 应用阶段:配置完成后,Pinctrl子系统会将配置应用于处理器上的引脚。这意味着处理器上的引脚将按照配置信息进行工作。此时,其他驱动程序或应用程序可以通过Pinctrl子系统提供的接口来访问和操作这些引脚。

四、Pinctrl子系统的功能

Pinctrl子系统提供了丰富的功能,以满足不同的应用场景需求:

  1. 引脚复用:允许多个引脚通过复用的方式连接到芯片上的不同功能模块,从而提高了引脚的利用率。

  2. 引脚配置:支持对引脚进行详细的配置,包括设置引脚的电气特性(如上拉、下拉、开漏等)、驱动能力、速度等。

  3. 引脚组配置:支持将多个引脚组合成一个逻辑上的引脚组,以便进行批量配置和操作,提高了配置效率。

  4. 中断管理:支持引脚中断的配置和管理,使得开发者可以方便地处理外部事件。

五、imx6ull举例

以参数值为例进行分析:

MX6UL_PAD_UART1_RTS_B__GPIO1_IO19       0x17059 /* SD1 CD */

参数分为前后的两部分 MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059

首先是 MX6UL_PAD_UART1_RTS_B__GPIO1_IO19  此参数是一个宏定义如下对应了5个数据

#define MX6UL_PAD_UART1_RTS_B__GPIO1_IO19            0x0090 0x031C 0x0000 0x5 0x0
参数值的宏定义对应了5个数据值对应关系如下表 :

说明数据值
mux_reg复用寄存器的地址或偏移量0x0090
conf_reg配置寄存器的地址或偏移量0x031C
input_reg输入寄存器的地址或偏移量0x0000
mux_mode复用模式0x5
input_val输入值0x0

iomuxc的 reg 属 性 可 知 IOMUXC 外 设 寄 存 器 起 始 地 址 为 0x020e0000 。

1.mux_reg :0x0090

UART1_RTS_Bpinmux_reg寄存器地址就是 :0x020e0000+0x0090= 0x020e0090。

2.conf_reg :0x031C

UART1_RTS_Bpinconf_reg寄存器地址就是 :0x020e0000+0x031c= 0x020e031c

3.input_reg :0x0000

input_reg 寄存器偏移地址,有些外设有 input_reg 寄存器,有 input_reg 寄存器的外设需要配置 input_reg 寄存器。

UART1_RTS_Bpininput_reg寄存器没有。

4.mux_mode:0x5

表示UART1_RTS_B复用为gpio1_io19 

5.input_val :0x0

input_val:就是写入 input_reg, 寄存器的值为0

        然后数据值 0x17059 就是 conf_reg 寄存器值。此值由用户自行设置,通 过此值来设置一个 IO 的上 / 下拉、驱动能力和速度等。在这里就相当于设置寄存器IOMUXC_SW_PAD_CTL_PAD_UART1_RTS_B 的值为 0x17059 。

向设备树中的节点添加pinCtrl节点例子如下

pinctrl_test: testgrp {
    fsl,pins = <
    MX6UL_PAD_GPIO1_IO00__GPIO1_IO00   config //onfig 是具体设置值
    ..........
    >;
};
        设备树是通过属性来保存信息的,因此需要添加一个属性,属性名字一定要为“fsl,pins ”,
因为对于 I.MX 系列 SOC 而言, pinctrl 驱动程序是通过读取“ fsl,pins ”属性值来获取 PIN 的配
置信息.

GPIO

        GPIO子系统是Linux内核中用于统一和便捷地访问GPIO(通用输入输出)引脚的一个子系统。它提供了一组API函数,使得驱动程序和用户空间程序能够方便地设置GPIO的方向、读取或写入GPIO的值,以及处理GPIO中断等。以下是对GPIO子系统的详细介绍,包括相关的API函数和OF(设备树)函数。

一、GPIO子系统的作用

        GPIO子系统的主要作用是方便驱动开发者使用GPIO引脚。通过GPIO子系统,开发者可以在设备树中添加GPIO相关的信息,然后在驱动程序中使用GPIO子系统提供的API函数来操作GPIO引脚,如设置GPIO的方向、读取或写入GPIO的值等。这样,开发者就无需直接操作硬件寄存器,从而简化了驱动程序的开发过程。

二、GPIO子系统的架构

Linux的GPIO子系统驱动框架主要由以下几个部分组成:

  1. GPIO控制器驱动程序:这是与硬件相关的代码,用于处理GPIO控制器与系统总线之间的通信。该部分代码通常由芯片厂商提供,以与特定的GPIO硬件交互。

  2. 平台驱动程序:平台驱动程序用于与硬件平台交互,识别GPIO硬件并将其与相关的GPIO控制器驱动程序关联起来。平台驱动程序的主要任务是向系统注册GPIO控制器设备,与GPIO控制器驱动程序进行绑定,并为其分配资源。

  3. GPIO字符设备驱动程序:该驱动程序提供了用户空间API,使应用程序能够使用GPIO。用户可以通过/sys/class/gpio目录下的文件系统接口,通过读取和写入GPIO寄存器来控制GPIO。

三、GPIO子系统的API函数

GPIO子系统提供了一系列API函数,用于操作GPIO引脚。以下是一些常用的API函数及其功能:

  1. gpio_request(unsigned gpio, const char *label):用于申请一个GPIO引脚。在使用一个GPIO引脚之前,必须先通过此函数进行申请。

  2. gpio_free(unsigned gpio):用于释放之前申请的GPIO引脚。当不再需要使用某个GPIO引脚时,应调用此函数进行释放。

  3. gpio_direction_input(unsigned gpio):用于设置GPIO引脚为输入模式。

  4. gpio_direction_output(unsigned gpio, int value):用于设置GPIO引脚为输出模式,并可选地设置初始输出值。

  5. gpio_get_value(unsigned gpio):用于获取GPIO引脚的值(0或1)。

  6. gpio_set_value(unsigned gpio, int value):用于设置GPIO引脚的值(0或1)。

四、GPIO子系统的OF函数

在Linux内核中,设备树(Device Tree)是一种用于描述硬件设备的数据结构。GPIO子系统提供了一系列OF(设备树)函数,允许驱动程序从设备树中获取GPIO引脚的信息。以下是一些常用的OF函数及其功能:

  1. of_gpio_named_count(struct device_node *np, const char *propname):用于统计设备树中某个属性里面定义了几个GPIO信息。

  2. of_gpio_count(struct device_node *np):与of_gpio_named_count类似,但专门用于统计“gpios”属性的GPIO数量。

  3. of_get_named_gpio(struct device_node *np, const char *propname, int index):用于从设备树中获取指定属性的GPIO编号。该函数是驱动开发中常用的,因为它允许驱动程序根据设备树中的配置信息动态地获取GPIO编号。

五、举例

GPIO节点基本结构

在设备树中,一个典型的GPIO节点可能包含以下几个属性:

  1. gpio-controller:指定GPIO控制器的名称或引用。这通常是一个phandle,指向设备树中定义GPIO控制器的节点。

  2. #gpio-cells:表示每个GPIO引脚描述所占用的单元数。这通常与GPIO控制器的硬件设计相关。

  3. gpio-range:定义GPIO引脚的范围,包括起始引脚号和引脚数量。例如,<&gpio0 0 16>表示从GPIO控制器gpio0的第0个引脚开始,共有16个引脚。

  4. interrupt-parent:指定中断控制器的名称或引用,如果GPIO引脚用于中断的话。

  5. interrupts:描述GPIO引脚的中断配置,包括中断号、触发方式和中断处理函数等。

  6. status:表示GPIO节点的状态,通常是"okay"表示节点有效。

&gpio0 {  
    /* GPIO控制器引用,假设gpio0是在设备树的其他部分定义的GPIO控制器节点 */  
    gpio-controller; /* 声明这是一个GPIO控制器节点 */  
    #gpio-cells = <2>; /* 每个GPIO引脚描述占用2个单元 */  
  
    example_device_gpios: example_gpio@0 {  
        /* 定义一个名为example_gpio的GPIO子节点,起始地址为0 */  
        reg = <0>; /* 指定GPIO引脚的起始地址,这里为0 */  
        #gpio-cells = <1>; /* 在这个子节点中,每个GPIO描述占用1个单元(通常对于单个引脚来说是这样) */  
        gpio-range = <&gpio0 0 1>; /* 从gpio0控制器的第0个引脚开始,使用1个引脚 */  
          
        /* 如果需要,还可以添加中断相关属性 */  
        // interrupt-parent = <&intc>; /* 指定中断控制器 */  
        // interrupts = <0 1>; /* 中断号和触发方式(例如,0表示中断号,1表示边沿触发) */  
  
        status = "okay"; /* 节点状态为有效 */  
    };  
};  
  
/* 注意:上述示例中的具体值(如#gpio-cells、gpio-range等)会根据实际的硬件和GPIO控制器设计而有所不同。 */  
/* 另外,设备树中的节点和属性命名也应遵循具体的硬件和驱动程序的要求。 */

解释

  • &gpio0:这是对设备树中其他地方定义的GPIO控制器节点的引用。
  • gpio-controller:声明这个节点是一个GPIO控制器节点。
  • #gpio-cells:指定每个GPIO描述所需的单元数。这取决于GPIO控制器的硬件设计。
  • example_device_gpios: example_gpio@0:定义了一个名为example_gpio的子节点,用于描述特定设备的GPIO配置。@0表示这个节点的起始地址(或索引)是0。
  • reg:指定了GPIO引脚的起始地址。在某些情况下,这个属性可能不是必需的,因为gpio-range已经提供了足够的信息。
  • #gpio-cells(在子节点中):在这个特定的子节点中,每个GPIO描述所需的单元数。对于单个引脚来说,这通常是1。
  • gpio-range:定义了从哪个GPIO控制器的哪个引脚开始使用,以及使用多少个引脚。
  • interrupt-parentinterrupts(可选):如果GPIO引脚用于中断,则需要指定中断控制器和中断配置。
  • status:表示节点的状态。通常是"okay"表示节点有效且应该被内核包含。

请注意,上述模板是一个简化的示例,实际设备树中的GPIO节点可能会根据具体的硬件和驱动程序要求包含更多的属性和子节点。在编写设备树时,应仔细参考硬件手册和驱动程序文档以确保正确的配置。

CODE-LED

/*************************************************************************
#    > File Name: pinctrl_gpio.c
#    > Author: HENG-W
#    > Mail: 
#    > Created Time: 2024年09月04日 星期三 18时36分27秒
#    > Describe: pinctrl_gpio子系统实验练习
#*************************************************************************/
/*头文件*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_address.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/mach/map.h>
/*
1.首先在 imx6ull-zealginat-emmc.dts 文件,
在根节点“/”下创建如下内容
	zealginat-pinctrl-gpio-led {
		compatible = "zealginat-pinctrl-gpio-led";
		status = "okay";
		pinctrl-0 = <&zealginat_zealgaint_pinctrl_led>;
		pinctrl-names = "pinctrl-gpio-led";
		pinctrl-gpio-led-pin = <&gpio1 3 GPIO_ACTIVE_LOW>;
	};

2. iomuxc 节点的 imx6ul-evk 子节点下创建一个名为“zealginat_zealgaint_pinctrl_led”的子节点
    zealginat_zealgaint_pinctrl_led: ledgrp {
        fsl,pins = <
            MX6UL_PAD_GPIO1_IO03__GPIO1_IO03	0x10B0
        >;
    };
3.检查 PIN 是否被其他外设使用!!!
    检查 PIN 是否被其他外设使用!!!
    检查 PIN 是否被其他外设使用!!!
    检查 PIN 是否被其他外设使用!!!
    查找如下两个参数是否有重复
    MX6UL_PAD_GPIO1_IO03__GPIO1_IO03
    <&gpio1 3 GPIO_ACTIVE_LOW>;
*/
/*定义设备数量、设备名、开灯关灯宏定义*/
#define GPIO_DEV_COUNT 1
#define GPIO_DEV_NAME "pinctrl_gpio"
#define LED_ON  1
#define LED_OFF 0

/*定义pinctrl_gpio设备结构体*/
struct pinctrl_gpio_dev {
    dev_t devid;             /* 设备号*/
    int major;              /* 主设备号*/
    int minor;              /* 次设备号*/
    struct cdev cdev;       /* cdev结构体*/
    struct device *device;  /* 设备结构体指针*/
    struct class  *class;   /* 类结构体指针*/   
    struct device_node *node;   /* 设备树节点指针*/
    int led_gpio_num;       /* led gpio编号*/
    int led_status;         /* led状态*/
};
/*声明并初始化pinctrl_gpio_dev结构体*/  
static struct pinctrl_gpio_dev pinctrl_gpio_led;
/*打开设备*/
static int pinctrl_gpio_open(struct inode *inode, struct file *file)
{
    /*设置私有数据*/
    file->private_data = &pinctrl_gpio_led;
    printk("pinctrl_gpio_open\n");
    return 0;
}
/*向设备写数据*/
static ssize_t pinctrl_gpio_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
    int retvale = 0;
    unsigned char databuf[1];
    unsigned char ledstatus;
    struct pinctrl_gpio_dev *dev = file->private_data; /* 获取私有数据*/
    /*4、从用户空间拷贝数据*/
    retvale = copy_from_user(databuf, buf, count);
    if (retvale< 0) {
        printk("copy_from_user failed\n");
        return -EFAULT;
    }
    ledstatus = databuf[0]; /* 获取用户输入 状态值*/
    if (ledstatus == LED_ON) {
        gpio_set_value(dev->led_gpio_num, LED_ON);
        dev->led_status = LED_ON;
    } else if (ledstatus == LED_OFF) {
        gpio_set_value(dev->led_gpio_num, LED_OFF);
        dev->led_status = LED_OFF;
    } else {
        printk("input error\n");
    }
    return 0;
}
/*关闭设备*/
static int pinctrl_gpio_release(struct inode *inode, struct file *file)
{
    printk("pinctrl_gpio_release\n");
    return 0;
}

/*设备操作函数结构体*/
static const struct file_operations pinctrl_gpio_fops = {
   .owner = THIS_MODULE,
   .open = pinctrl_gpio_open,
   .write = pinctrl_gpio_write,
   .release = pinctrl_gpio_release,
};

/*驱动程序加载函数*/
static int __init pinctrl_gpio_init(void)
{
    int ret;
    /*1、获取设备树节点*/
    pinctrl_gpio_led.node = of_find_node_by_path("/zealginat-pinctrl-gpio-led");
    if (!pinctrl_gpio_led.node) {
        printk("can't find zealginat-pinctrl-gpio-led node\n");
        return -ENOENT;
    }
    /*2、获取led gpio编号*/
    pinctrl_gpio_led.led_gpio_num = of_get_named_gpio(pinctrl_gpio_led.node, "pinctrl-gpio-led-pin", 0);
    if (pinctrl_gpio_led.led_gpio_num < 0) {
        printk("can't get pinctrl-gpio-led-pin\n");
        return -ENOENT;
    } else {
        printk("pinctrl-gpio-led-pin num:%d\n", pinctrl_gpio_led.led_gpio_num);
    }
    /*3、设置GPIO1_IO03为输出模式,输出高电平,默认关闭*/
    ret = gpio_request(pinctrl_gpio_led.led_gpio_num, "pinctrl-gpio-led-pin");
    if (ret < 0) {
        printk("can't request gpio %d\n", pinctrl_gpio_led.led_gpio_num);
        gpio_free(pinctrl_gpio_led.led_gpio_num);
        return -1;
    }
    ret = gpio_direction_output(pinctrl_gpio_led.led_gpio_num, LED_ON);/*设置为输出模式,默认关闭,高电平关闭所以是LED_ON  1 */
    if (ret < 0) {
        printk("can't set direction of gpio %d\n", pinctrl_gpio_led.led_gpio_num);
        gpio_free(pinctrl_gpio_led.led_gpio_num);
        return -1;
    }  
    /*4、申请字符设备号*/
    /*初始化主设备号*/
    pinctrl_gpio_led.major = 0;
    if (pinctrl_gpio_led.major)
    {
        pinctrl_gpio_led.devid = MKDEV(pinctrl_gpio_led.major, 0);
        ret = register_chrdev_region(pinctrl_gpio_led.devid, GPIO_DEV_COUNT, GPIO_DEV_NAME);    /* 申请设备号*/
    }else{
        ret = alloc_chrdev_region(&pinctrl_gpio_led.devid, 0, GPIO_DEV_COUNT, GPIO_DEV_NAME);   /* 申请设备号*/
        pinctrl_gpio_led.major = MAJOR(pinctrl_gpio_led.devid);                                 /* 获取主设备号*/
        pinctrl_gpio_led.minor = MINOR(pinctrl_gpio_led.devid);                                 /* 获取次设备号*/                          
    }
    if (ret<0)
    {
        printk("can't register char device\n");
        goto err_unregister_chrdev;
    }else{
        printk("pinctrl_gpio_led major num:%d, minor num:%d\n", pinctrl_gpio_led.major, pinctrl_gpio_led.minor);
    }
    /*5、创建字符设备*/
    pinctrl_gpio_led.cdev.owner = THIS_MODULE;
    cdev_init(&pinctrl_gpio_led.cdev, &pinctrl_gpio_fops);
    ret = cdev_add(&pinctrl_gpio_led.cdev, pinctrl_gpio_led.devid, GPIO_DEV_COUNT);
    if (ret < 0) {
        printk("can't add cdev\n");
        goto err_destroy_cdev;
    }else {
        printk("cdev add success\n");
    }
    /*6、创建类*/
    pinctrl_gpio_led.class = class_create(THIS_MODULE, GPIO_DEV_NAME);
    if (IS_ERR(pinctrl_gpio_led.class)) {
        printk("can't create class\n");
        goto err_destroy_class;
    } else {
        printk("class create success\n");
    }
    /*7、创建设备*/
    pinctrl_gpio_led.device = device_create(pinctrl_gpio_led.class, NULL, pinctrl_gpio_led.devid, NULL, GPIO_DEV_NAME);
    if (IS_ERR(pinctrl_gpio_led.device)) {
        printk("can't create device\n");
        goto err_device_create;     
    } else {
        printk("device create success\n");
    }

    return 0;
err_device_create:
    class_destroy(pinctrl_gpio_led.class);
    cdev_del(&pinctrl_gpio_led.cdev);
    unregister_chrdev_region(pinctrl_gpio_led.devid, GPIO_DEV_COUNT);
    return 0;
err_destroy_class:
    cdev_del(&pinctrl_gpio_led.cdev);
    unregister_chrdev_region(pinctrl_gpio_led.devid, GPIO_DEV_COUNT);
    return 0;
err_destroy_cdev:
    unregister_chrdev_region(pinctrl_gpio_led.devid, GPIO_DEV_COUNT);
    return 0;
err_unregister_chrdev:
    return 0;
}

/*驱动程序卸载载函数*/
static void __exit pinctrl_gpio_exit(void)
{
    gpio_free(pinctrl_gpio_led.led_gpio_num);
    device_destroy(pinctrl_gpio_led.class, pinctrl_gpio_led.devid);
    class_destroy(pinctrl_gpio_led.class);
    cdev_del(&pinctrl_gpio_led.cdev);
    unregister_chrdev_region(pinctrl_gpio_led.devid, GPIO_DEV_COUNT);
    printk("pinctrl_gpio_led exit\n");
}

module_init(pinctrl_gpio_init);
module_exit(pinctrl_gpio_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("HENG-W");
MODULE_DESCRIPTION("pinctrl_gpio driver for i.MX6ull");
MODULE_VERSION("1.0");

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2107165.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

华三防火墙第-安全策略02

一 安全策略的图解 安全策略是一种根据报文的属性信息对报文进行精细化转发控制的智能安全防护措施。它 融合了多维度精确报文识别、深度报文检测、安全动作执行、智能策略分析、应用风险调 优等多种安全防护功能,为网络的安全性提供全方位保障。 安全策略运行原理 安全策略对…

【代码解读】LLGC

对象创建&#xff1a; model LLGC(description.size(1), label.max().item()1, args.drop_out, args.use_bias).to(device)模型使用&#xff1a; output model(train_features)LLGC&#xff1a; # Lorentzian MODEL class LLGC(nn.Module):def __init__(self, nfeat, ncla…

家政保洁|基于SSM+vue的智能家政保洁预约系统(源码+数据库+文档)

智能家政保洁预约系统 基于SSMvue的智能家政保洁预约系统 一、前言 二、系统设计 三、系统功能设计 系统功能实现 后台模块实现 管理员功能实现 家政人员功能实现 用户功能实现 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获…

多维动态规划-面试高频!-最长公共子序列和最长公共子串、回文串-c++实现和详解

1143. 最长公共子序列 中等 给定两个字符串 text1 和 text2&#xff0c;返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 &#xff0c;返回 0 。 一个字符串的 子序列 是指这样一个新的字符串&#xff1a;它是由原字符串在不改变字符的相对顺序的情况下删…

UE驻网失败问题(三)

这个问题是lab问题&#xff0c;现象如下&#xff1a; 期望UE注册在SA网络下&#xff0c;咋一看没有5G MIB/SIB1打印&#xff0c;好像是没搜到5G小区&#xff0c;而实际上并不是这样。 在查看搜网过程时会发现如下log打印&#xff1a; [I nr5g_rrc_acq.c 3544] RRC ACQ: Band 41…

透彻!驯服大型语言模型(LLMs)的五种方法,及具体方法选择思路

01 引言 随着时间的发展&#xff0c;大型语言模型不再停留在演示阶段而是逐步面向生产系统的应用&#xff0c;随着人们期望的不断增加&#xff0c;目标也发生了巨大的变化。在短短的几个月的时间里&#xff0c;人们对大模型的认识已经从对其zero-shot能力感到惊讶&#xff0c…

ElasticSearch-Ingest Pipeline Painless Script

Ingest Node & Pipeline & Processor Ingest NodePipeline & Processor内置的 Processors创建 pipeline使用 pipeline 更新数据借助 update_by_query 更新已存在的文档Ingest Node VS Logstash Painless Ingest Node & Pipeline & Processor 应用场景&…

坚持与等待的区别!看了当年高考状元如今的现状,我才明白所谓名校的真相——早读(逆天打工人爬取热门微信文章解读)

快 机会来了 引言Python 代码第一篇 洞见 看了当年高考状元如今的现状&#xff0c;我才明白所谓名校的真相第二篇 股市 之 空窗期结尾 &#xff08;不是 你改名 怎么改群名字&#xff01; 这下每个人都知道王妈妈单身了&#xff09; 引言 昨天忘记写了 真的很抱歉 说下借口哈…

Invicti-Professional-V24.8.1

前言 Invicti 专业 Web 应用程序安全扫描器 自动、极其准确且易于使用的 Web 应用程序安全扫描程序&#xff0c;可自动查找网站、Web 应用程序和 Web 服务中的安全漏洞。 Invicti Professional Edition 是一款商业 Web 应用程序安全扫描器。它旨在自动查找和修复 Web 应用程…

VScode:快捷键和技巧

格式化文档 搜索文件名

助贷CRM系统:为金融中介行业打造全新营销管理模式

助贷CRM&#xff08;客户关系管理&#xff09;系统是针对金融中介行业&#xff0c;特别是从事贷款助贷业务的机构设计的一套综合管理系统。该系统旨在通过数字化、智能化的手段&#xff0c;优化金融中介机构的营销、销售、客户管理及服务流程&#xff0c;提升运营效率&#xff…

构建高效在线拍卖系统:SpringBoot实践

MySQL数据库 数据库是系统开发过程中不可或缺的一部分。 在WEB应用方面&#xff0c;MySQL AB开发了一个具有很大优势的MySQL关系数据库管理系统。 MySQL可以将数据存储在不同的表中&#xff0c;这非常灵活&#xff0c;并且还可以提高系统在实际应用中的速度。 数据库访问最常用…

创客匠人8月总结|所有赋能都是服务,都是为了帮客户拿结果

“九层之台&#xff0c;起于累土&#xff1b;千里之行&#xff0c;始于足下。”每一份伟大的成就都源自不懈的积累与坚定的步伐。 作为“知识变现整体解决方案服务商”&#xff0c;我们始终站在时代的前沿&#xff0c;致力于为每一位知识IP搭建桥梁&#xff0c;直通知识变现之…

软件工程造价师习题练习 24

1.关于功能点方法&#xff0c;以下描述不正确的是( ) A. 可用于项目范围管理 B. 可用于澄清需求 C. 反映软件功能规模的大小 D. 与软件开发成本高度相关 功能点方法是一种用于软件规模估算的方法&#xff0c;它主要用于衡量软件的功能规模大小。功能点分析不直接与软件开发…

杀毒软件火绒下载地址

杀毒软件火绒下载地址

【深度学习实战】使用深度学习模型可视化工具——Netron在线可视化深度学习神经网络

一直以来&#xff0c;对于深度学习领域的开发者&#xff0c;可视化模型都是非常迫切的需求&#xff0c;今天主要介绍一款可视化工具——Netron Netron有三种使用方式&#xff1a;在线、本地安装、pip安装 今天在这里只介绍在线使用这种方式。 Netron有个官方的网站&#xff1…

SOC 阵列:创新算力的未来之路

一、SOC阵列的概念与发展历程 SOC 阵列是由多个特定功能集成电路组合在一个芯片上的系统或产品&#xff0c;包含硬件系统及嵌入式软件。从传统集成电路到 SOC 经历多个阶段&#xff0c;初期电路由分立元件组成&#xff0c;后集成到单芯片集成电路中&#xff0c;其发展遵循摩尔…

『功能项目』协程生成怪物模型【25】

打开上一篇24AssetBundle上传加载u3d模型的项目&#xff0c; 本章要做的事情是在项目运行14秒后生成一个怪物鲸鱼&#xff0c;并且怪物鲸鱼会根据路径点自动巡航 在资源商店免费下载怪物模型 重命名为MonsterWhale 创建一个空物体 重命名为Path 在子级下创建小球Sphere作为巡…

STM32CubeIMX修改库文件代码相关问题

有时候会遇到需要需要修改库文件的需求&#xff0c;比如下面两种情况&#xff1a; 1、库文件有问题 2、库文件需要修改以适配当前需求 修改库文件的目的是&#xff0c;当下次生成程序的时候直接生成修改后的&#xff0c;无需在修改库文件。 直接将修改替换至默认文件&#xff…

软件安全测试有多重要?第三方软件测试公司如何进行安全测试?

在当今信息化迅速发展的社会&#xff0c;软件的安全性越来越受到重视。近年来&#xff0c;我国的网络安全形势日趋复杂&#xff0c;各类网络攻击层出不穷&#xff0c;软件泄露、数据丢失等事件屡见不鲜。为了保障软件项目的安全性&#xff0c;安全测试必不可少。 软件安全测试…