1.pinctrl & gpio 子系统
如果我们当前有个系统,里面有很多的gpio操作,每一个GPIO都需要定义多个寄存器,那么就需要看手册写很多很多的寄存器,所以厂家在设备树的pinctrl子系统里面已经把gpio都给定义好了,并且节点的属性为gpio controller,同时又是interrupt controller,以rk3288为例,因为我只有这个..。。。。
(/linux-4.4.154/arch/arm/boot/dts/rk3288.dtsi)
pinctrl: pinctrl {
compatible = "rockchip,rk3288-pinctrl";
rockchip,grf = <&grf>;
rockchip,pmu = <&pmu>;
#address-cells = <2>;
#size-cells = <2>;
ranges;
gpio0: gpio0@ff750000 {
compatible = "rockchip,gpio-bank";
reg = <0x0 0xff750000 0x0 0x100>;
interrupts = <GIC_SPI 81 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru PCLK_GPIO0>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
};
这里可以看到 在pinctrl节点下,有gpio0: gpio0@ff750000,是gpio0的节点,里面包含了gpio0的内存地址,内存长度,时钟来源,并且表明是个gpio-controller。那么我们就可以通过pinctrl子系统,选择使用哪个管脚,并且配置管脚的电气属性,这里要注意#gpio-cells = <2>,这里表示gpio属性需要用2个cell来表示。
2.gpio属性的格式
names-gpios = <phandle gpio-specifier>
names | 用户自己起的名字,如 led-gpios |
phandle | gpio 控制器节点的phandle,如&gpio5 |
gpio-specifier | 由gpio controller的#gpio-cells指定的gpio属性 |
那么我们来看官方例子。
gpio1: gpio1 {
gpio-controller
#gpio-cells = <2>;
};
gpio2: gpio2 {
gpio-controller
#gpio-cells = <1>;
};
[...]
enable-gpios = <&gpio2 2>;
data-gpios = <&gpio1 12 0>,
<&gpio1 13 0>,
<&gpio1 14 0>,
<&gpio1 15 0>;
这里看到gpio1的gpio controller的 #gpio-cells = <2>,gpio2的cells为1,那么在下面定义的gpio中,使用的phandle为gpio1时,需要用两个32位数来描述这个gpio的管脚,和状态。那么再看个例子。
node {
enable-gpios = <&qe_pio_e 18 GPIO_ACTIVE_HIGH>;
};
enable-gpios | “enable” 为gpio的名字 |
&qe_pio_e | "qe_pio_e" gpio controller的引用,表示哪个GPIO组 |
18 | "qe_pio_e" 这个GPIO组的第18个管脚 |
GPIO_ACTIVE_HIGH | 表示这个IO是高电平有效,这个后面在代码设置GPIO输出逻辑的时候会说 |
如果我们要使用pinctrl,那么还需要指定pinctrl的name,和pinctrl的state.
3.pinctrl格式
pinctrl-names = "default","sleep";
pinctrl-0 = <&state_0_node_a>;
pinctrl-1 = <&state_1_node_a>;
pinctrl-names | pinctrl 节点的名字,也是状态 第一个名字“default”对应pinctrl-0, 第二个名字“sleep”对应pinctrl-1。 |
pinctrl-0 | pinctrl-0的节点 &state_0_node_a |
pinctrl-1 | pinctrl-1的节点 &state_1_node_a(如果pinctrl-names只有一个状态,那么这个pinctrl-1就不存在了) |
再来看一下pinctrl节点里面的pin的状态:
rockchip,pins = <GROUP PINS FUNCTION PIN_STATE>;
rockchip,pins | pin的名字,rockchip是使用RK必须要这样写 |
GROUP | 使用的GPIO组 |
PINS | 对应的GPIO组里面哪个引脚 |
FUNCTION | 复用的引脚功能,需要看GRF寄存器中,对应引脚的功能配置,如下图。 |
PIN_STATE | 引脚的默认状态 |
FUNCTION的配置看如下的寄存器
例:完整的DTS pinctrl例子如下:
/{
alon_dts: alon-dts {
compatible = "for_test_dts"; //用来匹配plaform Drvier的名字
led-gpios = <&gpio8 2 GPIO_ACTIVE_LOW>; //gpio8_A2管脚,低电平有效
pinctrl-names = "default"; //pinctrl的name
pinctrl-0 = <&led_test>; //pinctrl-0的引用节点为led_test
};
&pinctrl {
led_test: led-test {
rockchip,pins = <8 2 RK_FUNC_GPIO &pcfg_output_high>; //gpio8 A2管脚,复用功能为GPIO,默认输出高电平
};
};
4.gpio子系统常用的函数
函数 | 功能 |
gpiod_get_index | 设备树gpio有多组值时,通过INDEX获取GPIO |
gpiod_get | 设备树中gpio属性只有一组值时,获取GPIO |
gpiod_get_direction | 获取gpio的配置状态,输入或输出 |
gpiod_direction_input | 将GPIO配置为输入 |
gpiod_direction_output | 将GPIO配置为输出 |
gpiod_get_value | 获取GPIO的逻辑值 |
gpiod_set_value | 设置gpio的逻辑值 |
gpiod_put | 释放gpio资源,是在gpiod_get和gpiod_get_index中申请的 |
4.1 gpiod_get_index
struct gpio_desc *__must_check gpiod_get_index(struct device *dev,
const char *con_id,
unsigned int idx,
enum gpiod_flags flags);
功能 | 设备树gpio有多组值时,通过INDEX获取GPIO |
*dev | node的dev结构体指针 |
*con_id | gpio名字,如gpio属性为led-gpios,那么该值为"led" |
idx | 多组gpio值中的哪一组 |
flags | 默认输出的电平 |
返回值 | GPIO结构体指针 |
4.2 gpiod_get
struct gpio_desc *__must_check gpiod_get(struct device *dev,
const char *con_id,
enum gpiod_flags flags);
功能 | 设备树gpio只有一组值时,获取gpio |
*dev | node的dev结构体指针 |
*con_id | gpio名字,如gpio属性为led-gpios,那么该值为"led" |
flags | 默认输出的电平 |
返回值 | GPIO结构体指针 |
4.3 gpiod_get_direction
int gpiod_get_direction(struct gpio_desc *desc);
功能 | 获取gpio的配置状态,输入或输出 |
*desc | GPIO结构体指针 |
返回值 | GPIOF_DIR_IN or GPIOF_DIR_OUT |
4.4 gpiod_direction_input
int gpiod_direction_input(struct gpio_desc *desc);
功能 | 将gpio配置为输入 |
*desc | GPIO结构体指针 |
返回值 | 0成功,其他值有错误 |
4.5 gpiod_direction_output
int gpiod_direction_output(struct gpio_desc *desc, int value);
功能 | 将gpio配置为输出 |
*desc | GPIO结构体指针 |
value | 默认输出电平 |
返回值 | 0成功,其他值有错误 |
4.6 gpiod_get_value
int gpiod_get_value(const struct gpio_desc *desc);
功能 | 获取GPIO的逻辑值 |
*desc | GPIO结构体指针 |
返回值 | 返回GPIO的逻辑值 |
4.7 gpiod_set_value
static inline void gpiod_set_value(struct gpio_desc *desc, int value)
4.8 gpiod_put
功能 | 设置GPIO的逻辑值 |
*desc | GPIO结构体指针 |
value | 设置的逻辑值 |
返回值 | 无 |
void gpiod_put(struct gpio_desc *desc);
功能 | 释放GPIO资源 |
*desc | GPIO结构体指针 |
返回值 | 无 |
注意:驱动里面调用gpiod_set_value这个函数对管脚的电平状态进行设置时,参数为1,意味有效,0为无效。
如果此时led-gpios = <&gpio8 2 GPIO_ACTIVE_LOW>;
gpiod_set_value(1):管脚输出低电平,因为低电平有效
gpiod_set_value(0):管脚输出高电平
伪代码如下:
int led_probe(struct platform_device *pdev){
int ret;
printk(KERN_ERR "dts match driver!!\n");
gpio_res = gpiod_get_index(&pdev->dev,"led",0,GPIOD_OUT_LOW); //获取名为led的gpio
if(IS_ERR(gpio_res))
{
printk(KERN_ERR "gpiod_get_index is error!!\n");
return (PTR_ERR(gpio_res));
}
ret = gpiod_direction_output(gpio_res,0); //设置GPIO为输出
if(ret < 0)
{
printk(KERN_ERR "gpio_direction_output is error!!\n");
return -1;
}
printk(KERN_ERR "get gpio is ok!!\n");
return 0;
}
ssize_t charDev_write (struct file *file, const char __user *buf, size_t size, loff_t *ppos){
int cmdbuf;
int ret;
ret = copy_from_user(&cmdbuf, buf, 1);
if(ret < 0)
{
printk("access data from user failed!\n");
}
gpiod_set_value(gpio_res,cmdbuf);
return 0;
}
5.没有pinctrl与gpio子系统的操作
由于设备树的引入,完全可以将之前的platform_device里面的resource写在设备树里面,然后通过驱动去查找节点,获取节点属性值,进而实现操作IO,由于Driver不是platform总线的驱动,所以不需要compatible属性去匹配驱动。因为不是这个笔记的重点,只记录个伪代码,防止忘了。
DTS文件:
/{
alon_dts: alon-dts {
CLK-reg = <0xFF760000+0x198 4>;
DR-reg = <0xFF7F0000+0x000 4>;
DDR-reg = <0xFF770000+0x080 4>;
IOMUX-reg = <0xFF7F0000+0x004 4>;
};
Driver:
struct device_node *node;
u32 CLK_REG[2];
u32 DR_REG[2];
u32 DDR_REG[2];
u32 IOMUX_REG[2];
static volatile unsigned int *clk_vir_addr = NULL;
static volatile unsigned int *dr_vir_addr = NULL;
static volatile unsigned int *ddr_vir_addr = NULL;
static volatile unsigned int *iomux_vir_addr = NULL;
int charDev_init(void)
{
int ret;
node = of_find_node_by_name("alon_dts");
ret = of_property_read_u32_array(node ,"CLK-reg",CLK_REG,2);
ret = of_property_read_u32_array(node ,"DR-reg",DR_REG,2);
ret = of_property_read_u32_array(node ,"DDR-reg",DDR_REG,2);
ret = of_property_read_u32_array(node ,"IOMUX-reg",IOMUX_REG,2);
clk_vir_addr = ioremap(CLK_REG[0],CLK_REG[1]);
dr_vir_addr = ioremap(DR_REG[0],DR_REG[1]);
ddr_vir_addr = ioremap(DDR_REG[0],DDR_REG[1]);
iomux_vir_addr = ioremap(IOMUX_REG[0],IOMUX_REG[1]);
*clk_vir_addr = *******;
*ddr_vir_addr = *******;
*iomux_vir_addr = *******;
}
ssize_t charDev_write (struct file *file, const char __user *buf, size_t size, loff_t *ppos){
*ddr_vir_addr = *******;//点灯
return 0;
}