一、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);
学习笔记,有错的话望指教,感激不尽!