1
完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
linux下的pintcrl和gpio子系统就类似于ST的“BSP库”,但是linux的pinctrl和gpio系统实现的功能和过程要远远比STM32的“BSP库”复杂。linux下引入pincrtl和gpio子系统,大大释放了驱动工程师的工作量,特别是引入“设备树”之后,使用一个外设时,对于pin引脚的初始化和管理,只需通过设备树描述即可,然后由pin子系统管理;对于gpio则由gpio子系统管理。
因此,与CPU引脚“关联”的设备驱动,最终都会调用pincrtl和gpio子系统。二者是设备驱动的基础,这二者也是一个设备驱动。 2. pinctrl子系统 2.1 pinctrl子系统功能 CPU的gpio引脚除了的方向、速度、上下拉、驱动能力等基本的电气特性外,一般会包括复用功能,即该引脚既可以作为普通gpio,还可能复位为i2c引脚、uart引脚等。如果采用直接配置寄存器的方式进行驱动开发,会非常繁琐,每更改一个功能就得重新翻阅手册配一遍寄存器,另一方面还可能存在“冲突”问题,比如该引脚已被复用为i2c在使用,但被驱动工程师忽略了,再去使用该gpio时会导致未知预期的问题。引入pintctrl子系统就可以解决诸如此类问题,结合设备树的使用,只需把pin信息在设备树描述清楚,即由pinctrl子系统介入管理。 pinctrl对于pin管理功能: 2.2 pin设备树描述 #源自rk3399-firefly-port.dtsi &pinctrl { buttons { pwrbtn: pwrbtn { rockchip,pins = <0 5 RK_FUNC_GPIO &pcfg_pull_up>; }; }; leds { led_power: led-power { rockchip,pins = <2 27 RK_FUNC_GPIO &pcfg_pull_none>; }; led_user: led-user { rockchip,pins = <0 13 RK_FUNC_GPIO &pcfg_pull_none>; }; }; .......... #源自rk3399.dtsi i2c3 { i2c3_xfer: i2c3-xfer { rockchip,pins = <4 17 RK_FUNC_1 &pcfg_pull_none>, #复用为i2c属性 <4 16 RK_FUNC_1 &pcfg_pull_none>; }; i2c3_gpio: i2c3_gpio { rockchip,pins = <4 17 RK_FUNC_GPIO &pcfg_pull_none>,#复用为普通gpio属性 <4 16 RK_FUNC_GPIO &pcfg_pull_none>; }; }; 以上设备树摘自部分rk3399的设备树文件:
#源自rk3399.dtsi i2c3: i2c@ff130000 { compatible = "rockchip,rk3399-i2c"; reg = <0x0 0xff130000 0x0 0x1000>; clocks = <&cru SCLK_I2C3>, <&cru PCLK_I2C3>; clock-names = "i2c", "pclk"; interrupts = pinctrl-names = "default"; #默认功能是i2c pinctrl-0 = <&i2c3_xfer>; #使用i2c复用功能的pin属性 #address-cells = <1>; #size-cells = <0>; status = "disabled"; };
2.3 pin驱动 pin驱动一般芯片原厂已经提供,rk3399 pin驱动位于源码位于“kernel/drivers/pinctrl/pinctrl-rockship.c”中。“pinctrl-rockchip.c”支持了瑞芯微常用的CPU,如rk3288、rk3399、px30等,使用哪一款CPU,与linux其他驱动一样,可以通过pinctrl节点的设备树进行自动选择匹配。 rockchip pinctrl驱动匹配表 static const struct of_device_id rockchip_pinctrl_dt_match[] = { ........ { .compatible = "rockchip,rk3328-pinctrl", .data = &rk3328_pin_ctrl }, { .compatible = "rockchip,rk3366-pinctrl", .data = &rk3366_pin_ctrl }, { .compatible = "rockchip,rk3368-pinctrl", .data = &rk3368_pin_ctrl }, { .compatible = "rockchip,rk3399-pinctrl", .data = &rk3399_pin_ctrl }, {}, }; static struct platform_driver rockchip_pinctrl_driver = { .probe = rockchip_pinctrl_probe, .driver = { .name = "rockchip-pinctrl", .pm = &rockchip_pinctrl_dev_pm_ops, .of_match_table = rockchip_pinctrl_dt_match, }, }; rk3399 pinctrl 节点设备树描述 pinctrl: pinctrl { compatible = "rockchip,rk3399-pinctrl"; rockchip,grf = <&grf>; rockchip,pmu = <&pmugrf>; #address-cells = <2>; #size-cells = <2>; ranges; ......... 3. gpio子系统 3.1 gpio子系统功能 pinctrl子系统主要是管理pin的电气属性和复用功能,而gpio子系统则是管理gpio的申请释放、控制输入输出、io中断等功能。gpio子系统屏蔽了gpio相关寄存器的配置过程,换而提供了常用的接口函数给驱动工程师使用,方便gpio相关的驱动开发。 常见与gpio相关的驱动:
3.2 gpio子系统常用函数 gpio子系统对于驱动层的API位于“/kernel/include/linux/gpio.h”中。 【1】检查gpio是否可用 int gpio_is_valid(int number); [tr]参数/含义/含义[/tr]
【2】申请使用一个gpio 使用一个gpio前,必须向内核申请该gpio。 int gpio_request(unsigned gpio, const char *label) [tr]参数/含义[/tr]
【3】释放已申请gpio 如果不使用该gpio,则需要释放,否则其他模块申请不到该gpio序号。 int gpio_free(unsigned gpio) [tr]参数/含义[/tr]
【4】设置gpio输入模式 int gpio_direction_input(unsigned gpio) [tr]参数含义[/tr]
【5】设置gpio输出模式 void gpio_set_value(unsigned gpio, int value) [tr]参数含义[/tr]
【6】读取 gpio状态 int gpio_get_value(unsigned int gpio) [tr]参数含义[/tr]
【7】设置 gpio状态 void gpio_set_value(unsigned int gpio, int value) [tr]参数含义[/tr]
【8】中断号映射 int gpio_to_irq(unsigned gpio) [tr]参数含义[/tr]
4. 应用 利用pinctrl和gpio子系统实现一个io翻转控制LED。firefly-rk3399板子上有两个LED,分别是power led和user led。linux自带的“gpio-leds”驱动是在linux led框架的基础上实现的,我们使用user led的gpio口,不使用led框架,直接使用gpio子系统实现io输出状态控制和读取。 4.1 修改设备树 【1】首先屏蔽rk3399原有的“led user”设备树,否则驱动会冲突。 leds { compatible = "gpio-leds"; ...... /*user { * label = "firefly:yellow:user"; * linux,default-trigger = "ir-user-click"; * gpios = <&gpio0 13 GPIO_ACTIVE_HIGH>; * pinctrl-names = "default"; * pinctrl-0 = <&led_user>; * default-state = "off"; *}; */ }; 【2】添加本次实现的驱动设备树。 在“kernel/arch/arm64/boot/dts/rockchip/rk3399-firefly.dtsi” gpiopin{ compatible = "gpiopin"; /*驱动兼容属性*/ gpios = <&gpio0 13 GPIO_ACTIVE_HIGH>;/* gpio描述,提供给pinctrl子系统使用*/ pinctrl-names = "default"; /*pin脚默认状态*/ pinctrl-0 = <&led_user>; /*直接使用rk原命名的pin脚设备树,也可以单独重新命名*/ default-state = "off"; /*默认gpio输出0*/ }; #源自rk3399-firefly-port.dtsi &pinctrl { leds { led_power: led-power { rockchip,pins = <2 27 RK_FUNC_GPIO &pcfg_pull_none>; }; led_user: led-user { rockchip,pins = <0 13 RK_FUNC_GPIO &pcfg_pull_none>;/*pin脚为普通gpio模式*/ }; }; 修改完设备树,可以编译内核,更新板子boot区域(内核和设备树文件)。 4.2 驱动源码 设备驱动则是套用linux的platform驱动框架了,platform驱动框架结合设备树一起使用,通过设备节点和驱动匹配,然后注册驱动。另外,通过gpio子系统,控制一个gpio是非常简易的事情了,就类似使用STM32的标准库控制gpio。 4.2.1 platform驱动匹配和注册 static struct of_device_id of_gp0_ids[] = { {.compatible = "gpiopin"}, /* 与节点设备树“compatible ”属性一致 */ { } }; static struct platform_driver gp0_driver = { .driver = { .owner = THIS_MODULE, .name = DEV_NAME, .of_match_table = of_match_ptr(of_gp0_ids), }, .probe = gp0_probe, .remove = gp0_remove, }; module_platform_driver(gp0_driver); /* platform 驱动注册和注销 */ |
|
|
|
4.2.2 probe
probe探测函数,驱动和设备树匹配时,会调用该函数,该函数完成gpio配置及字符驱动的创建过程。
static int gp0_probe(struct platform_device *pdev) { struct device *dev; int ret = -1; dev_t id = 0; //gp0.nd = of_find_node_by_path("/gpiopin"); gp0.nd = pdev->dev.of_node; if(gp0.nd == NULL) { printk("get node faileedn"); return -1; } gp0.gpio = of_get_named_gpio(gp0.nd, "gpios", 0);/* 获取gpio序号,"gpios"为设备树的描述信息 */ if(gp0.gpio < 0) { printk("get gpio failedn"); return -1; } ret = gpio_request(gp0.gpio, "gp0"); /* 申请GPIO */ if(ret < 0) { printk("gpio request failedn"); return ret; } ret = gpio_direction_output(gp0.gpio, 0); /* output,low default */ if(ret<0) { printk("gpio set failedn"); gpio_free(gp0.gpio); return ret; } /*创建字符驱动过程*/ ...... } 4.2.3 read / write read/write函数主要调用gpio子系统函数读取或者设置gpio状态。 static ssize_t gp0_read(struct file *pfile, char __user *buf, size_t size, loff_t *offset) { int ret = 0; struct gpiopin_dev *p; char level = 0; p = pfile->private_data; level = gpio_get_value(p->gpio); ret = copy_to_user(buf, &level, sizeof(level)); return ret; } static ssize_t gp0_write(struct file *pfile, const char __user *buf, size_t size, loff_t *offset) { int ret = 0; struct gpiopin_dev *p; char level = 0; p = pfile->private_data; ret = copy_from_user(&level, buf, size); gpio_set_value(p->gpio, level); return ret; } 4.3 Makefile ifeq ($(KERNELRELEASE),) KERNELDIR = /opt/rk3399/linux-sdk/linux-sdk/kernel PWD := $(shell pwd) modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules clean: rm -rf *.o *.ko .mod.o *.mod.c *.symvers else obj-m := gpiopin.o endif 4.4 运行 4.4.1 加载驱动 执行Makefile把驱动程编译为模块文件,生成gpiopin.ko文件,把文件通过NFS、U盘等传输至板子,系统起来后手动insmod到内核。驱动加载成功,会在“/dev”目录上生成设备文件。如图,“gp0”就是生成的驱动文件。 4.4.2 测试应用程序,执行“aarch64-linux-gnu-gcc gpiopin_app.c -o gpiopin_app”,生成可以执行文件“gpiopin_app”,把文件通过NFS、U盘等传输至板子“/home”目录,然后需更改文件属性,增加可执行属性。 int main(int argc, int **argv) { int fd; uint8_t w_level = 1, r_level = 0; fd = open("/dev/gp0", O_RDWR); if(-1 == fd) { perror("open gpiopin failedn"); return -1; } printf("open gpiopin driver succn"); for(;;) { write(fd, &w_level, 1); read(fd, &r_level, 1); printf("pin = %dn", r_level); w_level = !w_level; sleep(1); } close(fd); return 0; } 5.源码 6. 参考 本文主要目的是理清pinctrl子系统和gpio子系统的使用,关于pinctrl和gpio内部的原理实现可以参考以下分析文章。pinctrl子系统和gpio子系统的驱动属于原厂“BSP”范畴,芯片原厂已经实现,如果是驱动工程师只需熟悉其接口(当然能够深入理解其实现原理和框架是最好的)即可进行驱动开发工作。 |
|
|
|
你正在撰写答案
如果你是对答案或其他答案精选点评或询问,请使用“评论”功能。
基于米尔瑞芯微RK3576核心板/开发板的人脸疲劳检测应用方案
498 浏览 0 评论
768 浏览 1 评论
667 浏览 1 评论
1893 浏览 1 评论
3138 浏览 1 评论
小黑屋| 手机版| Archiver| 德赢Vwin官网 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-21 18:20 , Processed in 0.691591 second(s), Total 72, Slave 56 queries .
Powered by 德赢Vwin官网 网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
德赢Vwin官网 观察
版权所有 © 湖南华秋数字科技有限公司
德赢Vwin官网 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号