资讯详情

关于设备树里面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相同属性的字符串。 (请注意是成员和设备树compatible属性匹配,不成员) 正点原子实验只有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, "!\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=0xx\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 '/hom/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的设置将无法生效!

标签: 1040电阻

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台