pinctrl和GPIO子系统
pinctrl子系统
管理gpio的复用功能
管理gpio的电气特性(上下拉,驱动能力等)
iomuxc: iomuxc@020e0000 {
compatible = "fsl,imx6ul-iomuxc";
……
pinctrl_led{
fsl,pins = <
MX6ULL_PAD_SNVS_TAMPER3__GPIO5_IO03 0x1b0b0
>;
……
};
在arch/arm/boot/dts/imx6ull-pinfunc-snvs.h 里面有宏定义定义
#define MX6ULL_PAD_SNVS_TAMPER3__GPIO5_IO03 0x0014 0x0058 0x0000 0x5 0x0
后面四个值分别代表的意思如下:
0x0014:mux_reg 复用寄存器偏移地址 基地址通过父节点iomuxc找到 可以看出MX6ULL_PAD_SNVS_TAMPER3的mux_reg地址就是020e0000+0x0014 查阅手册就可以看出是正确的
0x0058:conf_reg 配置寄存器偏移地址
0x0000:input_reg 输入寄存器偏移地址 (不是每个io都有,这里就没有所以0x0000无效的值)
0x5 :mux_mode 配置mux_reg寄存器的值,即选择了ALT5 复用为GPIO
0x0 :input_val 配置input_reg的值,这里无效
而pinctrl_led里面的0x1b0b0 就是设置conf_reg寄存器的值,用来设置电器属性(上下拉等等)
所以通过 pinctrl_led描述,就可以确定这个IO功能。
根据iomuxc的属性compatible = "fsl,imx6ul-iomuxc"可以确定其驱动程序在
drivers/pinctrl/freescale/pinctrl-imx6ul.c文件中,这个即是fsl 官方为linux适配的pinctrl子系统的驱动程序
gpio子系统
在arch/arm/boot/dts/imx6ull.dtsi文件中定义各gpio控制器的属性
gpio5: gpio@020ac000 {
// 可以找到fsl官方为imx适配的linux的gpio子系统的源代码驱动文件 drivers/gpio/gpio-mxc.c
compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
reg = <0x020ac000 0x4000>; // GPIO5寄存器的基地址(GPIO5_DR GPIO5_GDIR……)
interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller; // 表示 gpio5节点是个 GPIO控制器。
/* 有两个cells 第一个是GPIO编号,例如&gpio5 3
* 第二个指GPIO的电平,0(GPIO_ACTIVE_HIGH)高电平有效 1(GPIO_ACTIVE_LOW)低电平有效。
*/
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
};
那么我们在下面需要引用到gpio节点的时候如下
leds {
compatible = "xym-led";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_led>;
led0: cpu {
gpios = <&gpio5 3 GPIO_ACTIVE_LOW>; // 正如这句,说明要用GPIO5_03 默认低电平有效
status = "okay";
};
};
有关gpio子系统的API函数接口
//1: 用于申请一个 GPIO管脚,在使用一个GPIO之前一定要申请 label是给GPIO设置个名字 返回0代表成功
int gpio_request(unsigned gpio, const char *label)
//2:释放
void gpio_free(unsigned gpio)
//3: 设置为输入 返回0代表成功
int gpio_direction_input(unsigned gpio)
//4: 设置为输出,默认输出值为value 返回0代表成功 c
int gpio_direction_output(unsigned gpio, int value)
//5: 获取gpio的值,负值失败
#define gpio_get_value __gpio_get_value
int __gpio_get_value(unsigned gpio)
//6: 设置gpio的值, 为value
#define gpio_set_value __gpio_set_value
void __gpio_set_value(unsigned gpio, int value)
与 gpio相关的 OF函数
int of_gpio_named_count(struct device_node *np, const char *propname)
// 获取设备树某个属性里面定义了几个 GPIO信息,要注意的是空的 GPIO信息也会被统计到,比如下面的代码会得到的返回值是 4
// gpios = <0
// &gpio1 1 2
// 0
// &gpio2 3 4>;
int of_gpio_count(struct device_node *np)
// 和of_gpio_named_count函数一样,但是不同的地方在于,此函数统计的是“gpios”这个属
// 性的GPIO数量,而 of_gpio_named_count函数可以统计任意属性的 GPIO信息,
int of_get_named_gpio(struct device_node *np, const char *propname, int index)
// 此函数获取GPIO编号,
// np:设备节点。
// propname:包含要获 取 GPIO信息的属性名。
// index GPIO索引,因为一个属性里面可能包含多个 GPIO,此参数指定要获取哪个 GPIO的编号,如果只有一个 GPIO信息的话此参数为 0。
// 返回值: 正值,获取到的 GPIO编号;负值,失败。
代码分析
参见代码【05.led_pinctrl_gpio】工程;
和上章设备树【04.led_driver_device_tree】唯一不同的就是设备树文件和底层驱动文件的修改如下:
设备树修改的地方,不在采用以前的传入寄存器资源,用户自己配了,这里直接传入gpio子系统需要的设备节点信息
led-gpio = <&gpio5 3 GPIO_ACTIVE_LOW>/****************************************修改的地方****************************************************/ /* leds { compatible = "gpio-leds"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_led>; led0: cpu { label = "cpu"; gpios = <&gpio5 3 GPIO_ACTIVE_LOW>; default-state = "on"; linux,default-trigger = "heartbeat"; }; }; */ xym_led { compatible = "xym-led"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_led>; led-gpio = <&gpio5 3 GPIO_ACTIVE_LOW>; }; /****************************************修改的地方****************************************************/
底层驱动文件【board_fire_imx6ull_pro.c】:这里直接得到led的管脚号_hw_led_gpio,接着就可以通过
gpio_request、gpio_direction_output等GPIO子系统下的接口来操作GPIO。void hw_led_init(struct device_node *nd) { int ret; /* 2、 获取设备树中的gpio属性,得到LED所使用的LED编号 */ _hw_led_gpio = of_get_named_gpio(nd, "led-gpio", 0); if(_hw_led_gpio < 0) { printk("can't get led-gpio"); return ; } printk("led-gpio num = %d\r\n", _hw_led_gpio); gpio_free(_hw_led_gpio); ret = gpio_request(_hw_led_gpio, "led"); if(!ret){ printk("can't req gpio!\r\n"); return; } /* 3、设置GPIO1_IO03为输出,并且输出高电平,默认关闭LED灯 */ ret = gpio_direction_output(_hw_led_gpio, 1); if(ret < 0) { printk("can't set gpio!\r\n"); } c }