关于设备树里面pinctrl的属性不能生效的问题

Linux下gpio(旧API)和gpiod(新API)子系统主要负责配置GPIO的输入/输出方向,读取输入的电平,和设置输出的电平。
pinctrl子系统主要负责设置gpio其他方面的东西,比如配置复用功能(alternate function),配置上下拉电阻,推挽输出或开漏输出,配置输出速度等等。

本人阅读了正点原子imx6ull开发板手册“【正点原子】阿尔法Linux开发板(A盘)-基础资料/09、文档教程(非常重要)/【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.6.pdf”里面的“第四十五章 pinctrl 和 gpio 子系统实验”,实验的设备树文件中有如下语句:

pinctrl_led: ledgrp {
  fsl,pins = <
    MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10B0 /* LED0 */
  >;
}

笔者将0x10b0修改为0x1031后,运行程序,发现SW_PAD_GPIO1_IO03寄存器(0x020e02f4)的值读出来后并不是0x1031,设备树里面有关pinctrl的内容全部都没有生效!!

后来笔者研究了linux内核相关源码发现,某个节点要使用pinctrl-names和pinctrl-0属性,这个节点就必须要有compatible属性,而且编写的驱动程序必须要绑定到这个compatible属性,例如将struct platform_driver结构体的driver.of_match_table[0].compatible的值设置为和设备树compatible属性相同的字符串。
(请注意是driver.of_match_table[X].compatible成员和设备树的compatible属性匹配,不是driver.name成员)
正点原子实验里面只有led_init函数,没有用到platform_driver框架,也没有probe函数,所以pinctrl-names和pinctrl-0属性无法生效!这算是正点原子教程里面非常大的一个失误!


测试用的设备树文件arch/arm/boot/dts/myboard.dts:

#include "imx6ull-14x14-emmc-4.3-800x480-c.dts"

/ {
	alphaled {
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "myleddevice";
		status = "okay";
		reg = <0x020c406c 0x04 // CCM_CCGR1
			   0x020e0068 0x04 // SW_MUX_GPIO1_IO03
			   0x020e02f4 0x04 // SW_PAD_GPIO1_IO03
			   0x0209c000 0x04 // GPIO1_DR
			   0x0209c004 0x04 // GPIO1_GDIR
		>;
		hello = "hello world";
		
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_mytest>;
		gpios = <&gpio1 3 GPIO_ACTIVE_LOW
		         &gpio1 18 GPIO_ACTIVE_LOW>;
	};
	
	/delete-node/leds;
	/delete-node/gpio_keys@0;
};

&iomuxc {
	imx6ul-evk {
		/delete-node/gpio-leds;
		/delete-node/gpio-keys;
	
		pinctrl_mytest: testgrp {
			fsl,pins = <
				MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x1031
			>;
		};
	};
};

&i2c1 {
	ap3216c@1e {
		compatible = "myap3216c";
	};
};

设备树里面用/delete-node语法把正点原子原有设备树里面的pinctrl属性和leds灯节点全部删掉了。
设备树文件编写好之后,make dtbs编译设备树源码,生成arch/arm/boot/dts/myboard.dtb文件,重启开发板,在uboot中加载这个dtb设备树。

sudo mount /dev/mmcblk0p2 /mnt/sdlinux -t vfat
sudo cp /home/oct1158/learn_drivers/kernel/arch/arm/boot/dts/myboard.dtb /mnt/sdlinux
sudo umount /mnt/sdlinux
sudo reboot

setenv mmcroot "/dev/mmcblk0p4 rootwait rw"
setenv loadfdt "fatload mmc 0:2 ${fdt_addr} myboard.dtb"
boot

测试用的linux驱动:

#include <asm/io.h> // readl()
#include <linux/module.h>
#include <linux/of_address.h> // of_iomap
#include <linux/platform_device.h>

struct led5_data
{
	int num;
	char str[100];
};

static int led5_probe(struct platform_device *pdev)
{
	struct led5_data *data;
	u32 regval;
	void *__iomem sw_pad;
	
	pr_err("%s(0x%p);\n", __FUNCTION__, pdev);
	data = devm_kzalloc(&pdev->dev, sizeof(struct led5_data), GFP_KERNEL);
	if (data == NULL)
	{
		pr_err("devm_kzalloc() failed!\n");
		return -ENOMEM;
	}
	strlcpy(data->str, "hello world", sizeof(data->str));
	strlcat(data->str, "!!!", sizeof(data->str));
	data->num = strlen(data->str);
	platform_set_drvdata(pdev, data);
	
	sw_pad = of_iomap(pdev->dev.of_node, 2);
	if (sw_pad == NULL)
	{
		pr_err("of_iomap() failed!\n");
		return -ENODEV;
	}
	regval = readl(sw_pad);
	pr_err("sw_pad=0x%04x\n", regval);
	iounmap(sw_pad);
	return 0;
}

static int led5_remove(struct platform_device *pdev)
{
	struct led5_data *data = platform_get_drvdata(pdev);
	
	pr_err("%s(0x%p);\n", __FUNCTION__, pdev);
	pr_err("num=%d, str=%s\n", data->num, data->str);
	return 0;
}

static const struct of_device_id of_led5_match[] = {
	{.compatible = "myleddevice"},
	{}
};

MODULE_DEVICE_TABLE(of, of_led5_match);

static struct platform_driver led5_driver = {
	.driver = {
		.name = "myled5",
		.of_match_table = of_led5_match
	},
	.probe = led5_probe,
	.remove = led5_remove
};

module_platform_driver(led5_driver);

MODULE_AUTHOR("Oct1158");
MODULE_LICENSE("GPL");

程序运行结果:
(备注:开发板安装的是uboot2016.03+linux4.1.15内核+ubuntu18.04根文件系统)

oct1158@alientek:~/learn_drivers/led$ ls
Makefile        led.o       led3.ko     led4.mod.o  led_noop.sh
Module.symvers  led2.c      led3.mod.c  led4.o      led_off.sh
key_test.sh     led2.ko     led3.mod.o  led5.c      led_on.sh
led.c           led2.mod.c  led3.o      led5.ko     led_test.sh
led.ko          led2.mod.o  led4.c      led5.mod.c  modules.order
led.mod.c       led2.o      led4.ko     led5.mod.o  stop_blinking.sh
led.mod.o       led3.c      led4.mod.c  led5.o
oct1158@alientek:~/learn_drivers/led$ make
make -C /home/oct1158/learn_drivers/kernel M=/home/oct1158/learn_drivers/led EXTRA_CFLAGS=-fno-pic modules
make[1]: Entering directory '/home/oct1158/learn_drivers/kernel'
  CC [M]  /home/oct1158/learn_drivers/led/led5.o
  Building modules, stage 2.
  MODPOST 5 modules
  CC      /home/oct1158/learn_drivers/led/led5.mod.o
  LD [M]  /home/oct1158/learn_drivers/led/led5.ko
make[1]: Leaving directory '/home/oct1158/learn_drivers/kernel'
oct1158@alientek:~/learn_drivers/led$ sudo insmod led5.ko
[sudo] password for oct1158:
[ 1036.945408] led5_probe(0x940ffc00);
[ 1036.948949] sw_pad=0x1031
oct1158@alientek:~/learn_drivers/led$ sudo rmmod led5.ko
[ 1040.441553] led5_remove(0x940ffc00);
[ 1040.445343] num=14, str=hello world!!!
oct1158@alientek:~/learn_drivers/led$

通过实验可以发现,platform_driver_register后,我们去访问0x020e02f4寄存器,读出来的值确实是0x1031。说明pinctrl的设置成功生效了。


pinctrl要点:

(1)在根节点下任选一个节点,设置好compatible属性,pinctrl-names属性以及pinctrl-0属性。
(2)在&iomuxc的imx6ul-evk节点下新建一个节点,通过fsl,pins属性配置GPIO引脚。pinctrl-0属性要引用这个节点。
(3)编写的linux驱动必须要用到platform_driver,通过of_match_table与设备树中的compatible属性匹配。如果匹配不了,pinctrl的设置将无法生效!

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

巨大八爪鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值