1、STM32MP1的pinctrl子系统驱动
pinctrl子系统源码目录为drivers/pinctrl,一个PIN最好只能被一个外设使用。
“stm32mp151.dtsi”中有一个“pin-controller节点标签”叫pinctrl
pinctrl: pin-controller@50002000 {
#address-cells = <1>;
/*定义子节点的reg和ranges的addres长度为32个位*/
#size-cells = <1>;
/*定义子节点的reg和ranges的length长度为32个位*/
compatible = "st,stm32mp157-pinctrl";
/*compatible属性用于将设备和驱动绑定起来*/
ranges = <0 0x50002000 0xa400>;
/*子节点寄存器起始地址为0*/
/*父节点寄存器起始地址为0x50002000*/
/*寄存器最大偏移地址为0xa400*/
interrupt-parent = <&exti>;
st,syscfg = <&exti 0x60 0xff>;
hwlocks = <&hsem 0 1>;
pins-are-numbered;
gpioa: gpio@50002000 { /*子节点gpio起始地址为0x50002000*/
gpio-controller;
#gpio-cells = <2>;
/*定义描述使用一个gpio口需要提供2个指定的参数*/
interrupt-controller;
#interrupt-cells = <2>;
reg = <0x0 0x400>;
/*reg属性的值:当前节点寄存器起始地址为0,长度为0x400*/
clocks = <&rcc GPIOA>;
st,bank-name = "GPIOA";
status = "disabled";
};
gpiob: gpio@50003000 {
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
/*表明引用这个中断控制器需要2个cell*/
/*一个是使用哪个中断,另外一个是中断触发类型*/
reg = <0x1000 0x400>;
/*reg属性的值:当前节点寄存器起始地址为0x1000,长度为0x400*/
clocks = <&rcc GPIOB>;
st,bank-name = "GPIOB";
status = "disabled";
};
gpioc: gpio@50004000 {
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
reg = <0x2000 0x400>;
/*reg属性的值:当前节点寄存器起始地址为0x2000,长度为0x400*/
clocks = <&rcc GPIOC>;
st,bank-name = "GPIOC";
status = "disabled";
};
gpiod: gpio@50005000 {
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
reg = <0x3000 0x400>;
/*reg属性的值:当前节点寄存器起始地址为0x3000,长度为0x400*/
clocks = <&rcc GPIOD>;
st,bank-name = "GPIOD";
status = "disabled";
};
gpioe: gpio@50006000 {
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
reg = <0x4000 0x400>;
/*reg属性的值:当前节点寄存器起始地址为0x4000,长度为0x400*/
clocks = <&rcc GPIOE>;
st,bank-name = "GPIOE";
status = "disabled";
};
gpiof: gpio@50007000 {
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
reg = <0x5000 0x400>;
/*reg属性的值:当前节点寄存器起始地址为0x5000,长度为0x400*/
clocks = <&rcc GPIOF>;
st,bank-name = "GPIOF";
status = "disabled";
};
gpiog: gpio@50008000 {
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
reg = <0x6000 0x400>;
/*reg属性的值:当前节点寄存器起始地址为0x6000,长度为0x400*/
clocks = <&rcc GPIOG>;
st,bank-name = "GPIOG";
status = "disabled";
};
gpioh: gpio@50009000 {
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
reg = <0x7000 0x400>;
/*reg属性的值:当前节点寄存器起始地址为0x7000,长度为0x400*/
clocks = <&rcc GPIOH>;
st,bank-name = "GPIOH";
status = "disabled";
};
gpioi: gpio@5000a000 {
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
reg = <0x8000 0x400>;
/*reg属性的值:当前节点寄存器起始地址为0x8000,长度为0x400*/
clocks = <&rcc GPIOI>;
st,bank-name = "GPIOI";
status = "disabled";
};
gpioj: gpio@5000b000 {
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
reg = <0x9000 0x400>;
/*reg属性的值:当前节点寄存器起始地址为0x9000,长度为0x400*/
clocks = <&rcc GPIOJ>;
st,bank-name = "GPIOJ";
status = "disabled";
};
gpiok: gpio@5000c000 {
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
reg = <0xa000 0x400>;
/*reg属性的值:当前节点寄存器起始地址为0xa000,长度为0x400*/
clocks = <&rcc GPIOK>;
st,bank-name = "GPIOK";
status = "disabled";
};
};
pinctrl_z: pin-controller-z@54004000 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "st,stm32mp157-z-pinctrl";
ranges = <0 0x54004000 0x400>;
/*子节点寄存器起始地址为0*/
/*父节点寄存器起始地址为0x50004000*/
/*寄存器最大偏移地址为0x400*/
pins-are-numbered;
interrupt-parent = <&exti>;
st,syscfg = <&exti 0x60 0xff>;
hwlocks = <&hsem 0 1>;
gpioz: gpio@54004000 {
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
reg = <0 0x400>;
/*reg属性的值:当前节点寄存器起始地址为0,长度为0x400*/
clocks = <&scmi0_clk CK_SCMI0_GPIOZ>;
st,bank-name = "GPIOZ";
st,bank-ioport = <11>;
status = "disabled";
};
};
stm32mp15-pinctrl.dtsi中有一个pinctrl节点
&pinctrl { /*&pinctrl为节点名称*/
adc1_in6_pins_a: adc1-in6 { /*adc1-in6为设备名*/
pins {
pinmux = <STM32_PINMUX('F', 12, ANALOG)>;
/*GPIOF12用做AD输入口*/
};
};
m_can1_pins_a: m-can1-0 {
pins1 {
pinmux = <STM32_PINMUX('H', 13, AF9)>; /* GPIOH13用作CAN1_TX */
slew-rate = <1>;
drive-push-pull;/*推挽输出*/
bias-disable;/*禁止使用内部偏置电压*/
};
pins2 {
pinmux = <STM32_PINMUX('I', 9, AF9)>; /*GPIOI9用作CAN1_RX */
bias-disable;/*禁止使用内部偏置电压*/
};
};
uart4_pins_a: uart4-0 {
pins1 {
pinmux = <STM32_PINMUX('G', 11, AF6)>; /*GPIOG11用作UART4_TX */
bias-disable;/*禁止使用内部偏置电压*/
drive-push-pull;/*推挽输出*/
slew-rate = <0>;
};
pins2 {
pinmux = <STM32_PINMUX('B', 2, AF8)>; /*GPIOB2用作UART4_RX */
bias-disable;/*禁止使用内部偏置电压*/
};
};
uart4_pins_b: uart4-1 {
pins1 {
pinmux = <STM32_PINMUX('D', 1, AF8)>; /* GPIOD1用作UART4_TX */
bias-disable; /*禁止使用内部偏置电压*/
drive-push-pull; /*推挽输出*/
slew-rate = <0>;
};
pins2 {
pinmux = <STM32_PINMUX('B', 2, AF8)>; /* GPIOB2用作UART4_RX */
bias-disable;/*禁止使用内部偏置电压*/
};
};
pwm1_pins_a: pwm1-0 {
pins {
pinmux = <STM32_PINMUX('E', 9, AF1)>, /* GPIOE9用作TIM1_CH1 */
<STM32_PINMUX('E', 11, AF1)>, /*GPIOE11用作TIM1_CH2 */
<STM32_PINMUX('E', 14, AF1)>; /* GPIOE14用作TIM1_CH4 */
bias-pull-down;/*内部下拉*/
drive-push-pull;/*推挽输出*/
slew-rate = <0>;
};
};
};
2、gpio子系统提供的常用API函数:
需要包含头文件#include <linux/gpio.h>
1)、申请“gpio编号”
int gpio_request(unsigned gpio, const char *label)
gpio:要申请的“gpio编号”
Iabel:给这个gpio引脚设置个名字为label所指向的字符串
返回值:0,申请“gpio编号”成功;其他值,申请“gpio编号”失败;
注意:GPIOA有16个引脚,因此GA0的“gpio编号”为0,GA15的“gpio编号”为15;GPIOB有16个引脚,因此GB0的“gpio编号”为16,GB15的“gpio编号”为31;GPIOC有16个引脚,因此GC0的“gpio编号”为32,GC15的“gpio编号”为47;等等以此类推;
2)、释放“gpio编号”
void gpio_free(unsigned gpio)
gpio:要释放的“gpio编号”
struct MyGpioLED_dev{
dev_t devid; /*声明32位变量devid用来给保存设备号 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
struct cdev cdev; /*字符设备结构变量cdev */
struct class *class; /* 类 */
struct device *device;/*设备*/
struct device_node *nd;/* 设备节点 */
int led_gpio; /* led所使用的GPIO编号 */
};
struct MyGpioLED_dev strMyGpioLED;
int ret;
strMyGpioLED.nd = of_find_node_by_path("/gpio_led");
//path="/gpio_led,使用“全路径的节点名“在“stm32mp157d-atk.dts“中查找节点“gpio_led”
//返回值:返回找到的节点,如果为NULL,表示查找失败。
if(strMyGpioLED.nd == NULL) {
printk("gpio_led node not find!\r\n");
return -EINVAL;
}
strMyGpioLED.led_gpio = of_get_named_gpio(strMyGpioLED.nd, "led-gpio", 0);
//在gpio_led节点中,led-gpio = <&gpioi 0 GPIO_ACTIVE_LOW>
//np=strMyGpioLED.nd,指定的“设备节点”
//propname="led-gpio",给定要读取的属性名字
//Index=0,给定的GPIO索引为0
//返回值:正值,获取到的GPIO编号;负值,失败。
if(strMyGpioLED.led_gpio < 0) {
printk("can't get led-gpio");
return -EINVAL;
}
printk("led-gpio num = %d\r\n", strMyGpioLED.led_gpio);
//打印结果为:“led-gpio num = 128“
//因为GPIO编号是从0开始的,GPIOI端口的序号是8,每个端口有16个IO口,因此GPIOI0的编号为8*16=12
ret = gpio_request(strMyGpioLED.led_gpio, "LED-GPIO");
//gpio=strMyGpioLED.led_gpio,指定要申请的“gpio编号”
//Iabel="LED-GPIO",给这个gpio引脚设置个名字为"LED-GPIO"
//返回值:0,申请“gpio编号”成功;其他值,申请“gpio编号”失败;
if (ret) {
printk(KERN_ERR "strMyGpioLED: Failed to request led-gpio\n");
return ret;
}
gpio_free(strMyGpioLED.led_gpio);//释放“gpio编号”
3)、设置“某个GPIO为输入口”
int gpio_direction_input(unsigned gpio)
gpio:要设置为输入的“gpio编号”;
返回值:0,设置“某个GPIO为输入口”成功;负值,设置“某个GPIO为输入口”失败。
4)、设置“某个GPIO为输出口”
int gpio_direction_output(unsigned gpio, int value)
gpio:要设置为输出的“gpio编号”;
Value:GPIO 默认输出值;
返回值:0,设置“某个GPIO为输出口”成功;负值,设置“某个GPIO为输出口”失败。
5)、读取某个gpio口的值
#define gpio_get_value __gpio_get_value
int __gpio_get_value(unsigned gpio)
gpio:要获取的“gpio编号”;
返回值:非负值,返回“gpio口的值”;负值,获取“gpio口的值”失败。
6)、设置gpio口的值为value
#define gpio_set_value __gpio_set_value
void __gpio_set_value(unsigned gpio, int value)
gpio:要设置的“gpio编号”;
Value:要设置的值;
举例:
struct MyGpioLED_dev{
dev_t devid; /*声明32位变量devid用来给保存设备号 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
struct cdev cdev; /*字符设备结构变量cdev */
struct class *class; /* 类 */
struct device *device;/*设备*/
struct device_node *nd;/* 设备节点 */
int led_gpio; /* led所使用的GPIO编号 */
};
struct MyGpioLED_dev strMyGpioLED;
int ret;
strMyGpioLED.nd = of_find_node_by_path("/gpio_led");
//path="/gpio_led,使用“全路径的节点名“在“stm32mp157d-atk.dts“中查找节点“gpio_led”
//返回值:返回找到的节点,如果为NULL,表示查找失败。
if(strMyGpioLED.nd == NULL) {
printk("gpio_led node not find!\r\n");
return -EINVAL;
}
strMyGpioLED.led_gpio = of_get_named_gpio(strMyGpioLED.nd, "led-gpio", 0);
//在gpio_led节点中,led-gpio = <&gpioi 0 GPIO_ACTIVE_LOW>
//np=strMyGpioLED.nd,指定的“设备节点”
//propname="led-gpio",给定要读取的属性名字
//Index=0,给定的GPIO索引为0
//返回值:正值,获取到的GPIO编号;负值,失败。
if(strMyGpioLED.led_gpio < 0) {
printk("can't get led-gpio");
return -EINVAL;
}
printk("led-gpio num = %d\r\n", strMyGpioLED.led_gpio);
//打印结果为:“led-gpio num = 128“
//因为GPIO编号是从0开始的,GPIOI端口的序号是8,每个端口有16个IO口,因此GPIOI0的编号为8*16=12
gpio_set_value(strMyGpioLED->led_gpio, 0); /* 打开LED灯 */
3、与gpio相关的of函数
需要包含头文件#include <linux/of_gpio.h>
1)、根据给定的“设备节点”和“GPIO属性”,统计GPIO的数量
int of_gpio_named_count(struct device_node *np, const char *propname)
np:指定的“设备节点”。
propname:要统计的GPIO属性。
返回值:正值,统计到的GPIO数量;负值,失败。
2)、根据给定的“设备节点”,统计具有“gpios属性”的GPIO的数量
int of_gpio_count(struct device_node *np)
np:指定的“设备节点”。
返回值:正值,返回具有“gpios属性”的GPIO的数量;负值,失败。
3)、根据给定的“设备节点”,属性名和GPIO索引,读取GPIO编号
int of_get_named_gpio(struct device_node *np, const char *propname, int index)
np:指定的“设备节点”。
propname:包含要获取GPIO信息的属性名;
Index:GPIO索引,因为一个属性里面可能包含多个GPIO,此参数指定要获取哪个GPIO的编号,如果只有一个GPIO信息的话此参数为0;
返回值:正值,获取到的GPIO编号;负值,失败。
struct MyGpioLED_dev{
dev_t devid; /*声明32位变量devid用来给保存设备号 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
struct cdev cdev; /*字符设备结构变量cdev */
struct class *class; /* 类 */
struct device *device;/*设备*/
struct device_node *nd;/* 设备节点 */
int led_gpio; /* led所使用的GPIO编号 */
};
struct MyGpioLED_dev strMyGpioLED;
int ret;
strMyGpioLED.nd = of_find_node_by_path("/gpio_led");
//path="/gpio_led,使用“全路径的节点名“在“stm32mp157d-atk.dts“中查找节点“gpio_led”
//返回值:返回找到的节点,如果为NULL,表示查找失败。
if(strMyGpioLED.nd == NULL) {
printk("gpio_led node not find!\r\n");
return -EINVAL;
}
strMyGpioLED.led_gpio = of_get_named_gpio(strMyGpioLED.nd, "led-gpio", 0);
//在gpio_led节点中,led-gpio = <&gpioi 0 GPIO_ACTIVE_LOW>
//np=strMyGpioLED.nd,指定的“设备节点”
//propname="led-gpio",给定要读取的属性名字
//Index=0,给定的GPIO索引为0
//返回值:正值,获取到的GPIO编号;负值,失败。
if(strMyGpioLED.led_gpio < 0) {
printk("can't get led-gpio");
return -EINVAL;
}
printk("led-gpio num = %d\r\n", strMyGpioLED.led_gpio);
//打印结果为:“led-gpio num = 128“
//因为GPIO编号是从0开始的,GPIOI端口的序号是8,每个端口有16个IO口,因此GPIOI0的编号为8*16=128