Linux--字符设备开发之pinctrl、gpio子系统

一、pinctrl
pinctrl 即为Pin Control 意思就是用来控制引脚的。

传统Pin的配置方式:操作寄存器

①IO复用 eg:IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03
②pin配置电气属性 eg:IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03
这种方式需要用到地址映射(请看LED驱动开发—寄存器版),对寄存器进行读写操作,比较麻烦,而且也不清楚pin是否之前被占用导致冲突。

pinctrl子系统应运而生
主要功能:
①获取设备树中的pin信息
②通过pin信息进行设置IO复用、配置pin电气特性
pinctrl子系统的驱动程序在drivers/pinctrl中,具有多种芯片的文件夹
在这里插入图片描述
这里面的有些复杂,暂时不用看,知道怎么通过API调用使用即可。
我们需要清楚的在设备树中,以本人移植(也是直接在复制的.dts文件基础上修改)的dts文件:
imx6ull-alientek-emmc.dts 它会引用头文件imx6ull.dtsi,主节点都在这个头文件中。
/ soc ->aips1: aips-bus@02000000->iomuxc
iomuxc节点就是我们要找的

在这里插入图片描述
可看到这里内容很少,其实主要实现在imx6ull-alientek-emmc.dts ,它会对iomuxc追加节点:
在这里插入图片描述
这就是我们在设备树中要找的pinctrl对相关pin的配置操作了。以uart1为例

pinctrl_uart1: uart1grp {
	fsl,pins = <
		MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 0x1b0b1 //pin -- TX
		MX6UL_PAD_UART1_RX_DATA__UART1_DCE_RX 0x1b0b1 //pin -- RX
	>;
};

格式都一样,就不追究了,入乡随俗,根据功能进行仿写即可,关键说一下
MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 这是什么?看着像宏定义,直接搜索;
在arch/arm/boot/dts/imx6ul_pinfunc.h中找到了:
在这里插入图片描述
就是宏定义,这一串字符分两部分:
MX6UL_PAD_UART1_TX_DATA(代表pin)
UART1_DCE_TX(代表复用)
后面的几个数值代表什么?

//依次为
 * <
 * mux_reg    //复用寄存器偏移地址
 * conf_reg   //pin 电器属性配置寄存器
 * input_reg  //这个并不是所有pin都有的需要看手册
 * mux_mode   //复用模式对应的值
 * input_val  //input寄存器对应的值
 * >

宏定义已经说明他是哪一个寄存器了,只需要根据名字去找IOMUXC_SW_MUX_CTL_PAD…
IOMUXC_SW_PAD_CTL_PAD…
IOMUXC_…_…_SELECT_INPUT这些寄存器查看即可,其实这些都不需要我们修改,都配置好了
下面就根据MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 的五个值证明一下:
1)mux_reg 0x0084
复用寄存器的偏移地址
在这里插入图片描述
2)conf_reg 0x0310
pin寄存器的偏移地址
在这里插入图片描述

3)input_reg 0x0000
uart没有 input寄存器,默认为0x0000
4)mux_mode 0x0
复用功能配置,寄存器为 IOMUXC_SW_MUX_CTL_PAD_UART1_TX_DATA,0x0即复用为uart1
在这里插入图片描述
5)input_val 0x0
因为没有input寄存器,所以值默认为0x0。
这五个值的来源都说清楚了,你是否看出,还有一个关键的配置并没有出现,即:电气特性,在这五个数中只给了寄存器的地址,却没有配置值。
其实啊?这个值一开始我们就见到了
在这里插入图片描述
该数值是对下面32位寄存器每一位对应的功能需求配置得来的
在这里插入图片描述
这还没完,创建完pinctrl …{},还需要被使用:

&uart1 {
	pinctrl-names = "default";
	pinctrl-0 = **<&pinctrl_uart1>**;//在这里被调用 其格式我们入乡随俗仿写即可
	status = "okay";
};

可以看到它会在uart1追加的节点中被调用,
一个完整的设备节点
这就是一个完整的设备节点。当我们创建节点的时候,最好也写全了。举例添加自己的节点和pinctrl

//在imx6ull-alientek-emmc.dts的根节点下添加:
	gpio_led{
		#address-cells = <1>;
        #size-cells = <1>;
		compatible = "product,gpio_led"; //内容随意
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_gpioled>;
		led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;//说明用的是gpio1_03引脚,低电平有效
		status = "okey";
	};
//在在imx6ull-alientek-emmc.dts/&iomuxc {/imx6u-evk{ 下添加
pinctrl_gpioled: ledgrp{
	fsl,pins=<
		MX6UL_PAD_GPIO1_IO03__GPIO1_IO03  0x10b0
	>;
};

补充一下:
#address-cells = <1>; 表示寄存器的地址大小所占字长为1(32位)
#size-cells = <1>; 表示地址长度所占字节为1(32位)

二、gpio子系统
前面所说的pinctrl是用来配置PIN(pad)的复用与电气特性。如果IO复用为了gpio,那么就需要用到gpio子系统了。
gpio子系统会提供一系列API函数,比如用于配置gpio输入输出,读取/设置gpio的值等。方便驱动开发者使用gpio。同样具体的gpio子系统的实现驱动就不深究了,暂时会用即可。
API及功能:

1)首先,获取到GPIO所处的设备节点,比如of_find_node_by_path。
2)获取GPIO的编号,of_get_named_gpio函数,返回值就是GPIO编号。
3)请求(申请)此编号的GPIO,gpio_request
4)设置GPIO,输入/输出,gpio_direction_input或gpio_direction_output。
5)如果是输入、那么通过gpio_get_value函数读取GPIO值,如果是输出,通过gpio_set_value设置GPIO的值。

对主要API进行介绍:
1)of_get_named_gpio函数 (#include <linux/of_gpio.h>)
虽然这个函数并不属于gpio子系统的API,但是要经常用

/**
 * of_get_named_gpio() - Get a GPIO number to use with GPIO API
 * @np:		device node to get GPIO from(设备节点)
 * @propname:	Name of property containing gpio specifier(s)
 * @index:	index of the GPIO
 *
 * Returns GPIO number to use with Linux generic GPIO API, or one of the errno
 * value on the error condition.
 */
static inline int of_get_named_gpio(struct device_node *np,
                                   const char *propname, int index)
{
	return of_get_named_gpio_flags(np, propname, index, NULL);
}

2)gpio_request 函数 (#include <linux/gpio.h>)
用于申请一个GPIO引脚,在使用gpio之前一定要进行申请

/*
*@gpio 要申请的gpio标号,来自1)
*@label gpio设备的名字  例 led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
*@return 0 成功,非零失败
*/
static inline int gpio_request(unsigned gpio, const char *label)
{
	return -ENOSYS;
}

3)gpio_free 函数 (#include <linux/gpio.h>)
用于释放gpio,与gpio_request对应。

//gpio gpio标号
static inline void gpio_free(unsigned gpio)
{
	might_sleep();

	/* GPIO can never have been requested */
	WARN_ON(1);
}

4)gpio_direction_input/gpio_direction_output (#include <linux/gpio.h>)

/*
*@gpio gpio标号
*@return 0 成功,非零失败
*/
static inline int gpio_direction_input(unsigned gpio)
{
	return -ENOSYS;
}
/*
*gpio GPIO 标号
*value 默认输出值
*return 0 成功,非零失败
*/
static inline int gpio_direction_output(unsigned gpio, int value)
{
	return -ENOSYS;
}

5) gpio_get_value/gpio_set_value (#include <linux/gpio.h>)

/*
*用在输入
*gpio gpio标号
*return 0 成功,非零失败
*/
static inline int gpio_get_value(unsigned gpio)
{
	/* GPIO can never have been requested or set as {in,out}put */
	WARN_ON(1);
	return 0;
}
/*
*用在输出
*gpio gpio标号
*value 设置的值
*无返回值
*/
static inline void gpio_set_value(unsigned gpio, int value)
{
	/* GPIO can never have been requested or set as output */
	WARN_ON(1);
}

与gpio相关的of函数 (#include <linux/of_gpio.h>)
1)of_gpio_named_count 函数
用于获取设备树某个属性里面定义了几个 GPIO 信息,要注意的是空的 GPIO 信息也会被统计到

 /* 
 * of_gpio_named_count() - Count GPIOs for a device
 * @np:		device node to count GPIOs for
 * @propname:	property name containing gpio specifier(s)
 * @return : 正值,统计到的 GPIO 数量;负值,失败。
 * Example:
 * gpios = <0
 *          &gpio1 1 2
 *          0
 *          &gpio2 3 4>;
 *
 * The above example defines four GPIOs, two of which are not specified.
 * This function will return '4'
 * */
static inline int of_gpio_named_count(struct device_node *np, const char* propname)
{
	return of_count_phandle_with_args(np, propname, "#gpio-cells");
}

2)of_get_named_gpio 函数

/**
 * of_get_named_gpio() - Get a GPIO number to use with GPIO API
 * @np:		device node to get GPIO from 从gpio得到的设备节点
 * @propname:	Name of property containing gpio specifier(s) gpio说明符的名字
 * @index:	index of the GPIO
 *
 * Returns GPIO number to use with Linux generic GPIO API, or one of the errno
 * value on the error condition.
 */
static inline int of_get_named_gpio(struct device_node *np,
                                   const char *propname, int index)
{
	return of_get_named_gpio_flags(np, propname, index, NULL);
}

以点亮LED具体的使用流程举例:

static  struct gpio_led{
    struct device_node *nd;//设备节点
    int  led_gpio;//led所使用的gpio编号 
}gpioled;
//第一步:获取gpio设备节点
gpioled.nd = of_find_node_by_path("/gpio_led");//注gpio_led对应上面pinctrl的自己添加的设备节点gpio_led
if(gpioled.nd == NULL)
{
    printk("fail_gpio_nd\r\n");
    goto fail_gpio_nd;
}
//第二步:得到led的gpio的标号
gpioled.led_gpio = of_get_named_gpio(gpioled.nd,"led-gpio",0);//led-gpio来自自己添加的led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;  0代表索引只有一个
if(gpioled.led_gpio < 0)
{
    printk("can't get led-gpio");
    goto fail_gpio_nd;
}
//第三步:申请gpio
ret = gpio_request(gpioled.led_gpio,"led-gpio");
if(ret)
{
    printk("led_gpio request fail\r\n");
    ret = -EINVAL;
    goto fail_gpio_nd;
}
//第四步:设置gpio为输出模式
ret = gpio_direction_output(gpioled.led_gpio, 1); //1代表默认高电平,不点亮
if(ret) {
    printk("can't set gpio!\r\n");
    goto fail_set_output;
}
//第五步:设置gpio,低电平,默认点亮
gpio_set_value(gpioled.led_gpio,0);

学习笔记,有错的话望指教,感激不尽!

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值