自学linux驱动从入门到放弃(十一)Pinctrl与GPIO子系统

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
phandlegpio 控制器节点的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-0pinctrl-0的节点 &state_0_node_a
pinctrl-1pinctrl-1的节点 &state_1_node_a(如果pinctrl-names只有一个状态,那么这个pinctrl-1就不存在了)

再来看一下pinctrl节点里面的pin的状态:

rockchip,pins = <GROUP PINS FUNCTION PIN_STATE>;
rockchip,pinspin的名字,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
*devnode的dev结构体指针
*con_idgpio名字,如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
*devnode的dev结构体指针
*con_idgpio名字,如gpio属性为led-gpios,那么该值为"led"
flags默认输出的电平
返回值GPIO结构体指针

4.3 gpiod_get_direction

int gpiod_get_direction(struct gpio_desc *desc);
功能获取gpio的配置状态,输入或输出
*descGPIO结构体指针
返回值GPIOF_DIR_IN or GPIOF_DIR_OUT

4.4 gpiod_direction_input

int gpiod_direction_input(struct gpio_desc *desc);
功能将gpio配置为输入
*descGPIO结构体指针
返回值0成功,其他值有错误

4.5 gpiod_direction_output

int gpiod_direction_output(struct gpio_desc *desc, int value);
功能将gpio配置为输出
*descGPIO结构体指针
value默认输出电平
返回值0成功,其他值有错误

4.6 gpiod_get_value

int gpiod_get_value(const struct gpio_desc *desc);
功能获取GPIO的逻辑值
*descGPIO结构体指针
返回值返回GPIO的逻辑值

4.7 gpiod_set_value

static inline void gpiod_set_value(struct gpio_desc *desc, int value)


4.8 gpiod_put

功能设置GPIO的逻辑值
*descGPIO结构体指针
value设置的逻辑值
返回值
void gpiod_put(struct gpio_desc *desc);
功能释放GPIO资源
*descGPIO结构体指针
返回值

注意:驱动里面调用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;
}
  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值