Linux下GPIO驱动详解文章
嵌入式Linux下GPIO驱动程序的开发及应用

第28卷第4期增刊 2007年4月仪 器 仪 表 学 报Chinese Jour nal of Scientif ic InstrumentVol.28No.4Apr.2007 嵌入式L inux 下GPIO 驱动程序的开发及应用3何 泉,贺玉梅(北京化工大学信息科学与技术学院 北京 100029)摘 要:嵌入式Linux 是一种适用于嵌入式系统的源码开放的占先式实时多任务操作系统,是目前操作系统领域中的一个热点,其重点与难点是驱动程序的开发。
开发嵌人式Linux 下的设备驱动程序,可以更好地利用新硬件特性,提高系统访问硬件的效率,改善整个应用系统的性能。
驱动程序修改非常方便,使应用系统非常灵活。
本文简要论述了基于A TM E L 公司嵌入式ARM 处理器芯片的嵌入式Linux 的GP IO 驱动程序的开发原理及流程。
关键词:嵌入式Linux ;ARM ;驱动程序;设备文件;GPIOInvest igat ion an d a pplicat ion of GP IO dr iver in t he embedded L inuxHe Quan ,He YuMei(School of I nf orma tion Science and Tec hnology BU CT ,Beij ing 100029,China )Abstract :Embedded Linu x ,w hich i s a full y real 2time kernel and applicable to embedded syst ems ,has bec o me a hot s 2po t in t he do main of op erati ng system at present.It s out line and difficult y is to investigat e drivers.Developi ng device dri vers o n embedded Lin ux can help using t he new devices ,and imp rovi ng t he e fficiency of access to t he new devices and t he p erformance cap abilit y.As drivers can be changed easil y ,t he system is very convenient and flexi ble.Thi s p a 2p er simpl y point s o ut t he element s and flow of t he GPIO driver in t he embedded Linux based o n t he A RM proces sor of A TMEL system.Key words :embedded Li nux ;A RM ;driver ;device file ;GPIO 3基金项目国家自然科学基金(6)、北京化工大学青年教师自然科学研究基金(QN 58)资助项目1 引 言随着半导体技术的飞速发展,嵌入式产品已经广泛应用于军事、消费电子、网络通信、工业控制等各个领域,这是嵌入式系统发展的必然趋势。
GPIO驱动原理

GPIO设备驱动原理
在Linux系统下,字符设备和块设备的主要区别是:在对字符设备发出读/写请求时,实际的硬件I/O操作就紧接着发生了。块设备则不然,它利用一块系统内存作缓冲区,当用户进程对设备请求能满足用户的要求,就返回请求的数据;如果不能,就调用请求函数来进行实际的I/O操作。块设备是主要针对磁盘等慢速译成模块以供动态加载。由于嵌入式Linux支持静态编译和动态加载两种模式,如果考虑到精简内核的需要,这里可以使用动态加载的方法来实现驱动的装载。
设备驱动程序必须向Linux核心或者它所在的子系统提供一个标准的接口。例如,USB驱动程序向Linux核心提供了一个设备文件I/O接口,GPIO设备驱动程序向GPIO子系统提供了GPIO设备接口,接着向核心提供了文件I/O和缓冲区的接口。
linux内核的gpiolib详解

linux内核的gpiolib详解#include <linux/init.h> // __init __exit#include <linux/module.h> // module_init module_exit#include <mach/regs-gpio.h>#include <mach/gpio-bank.h>#include <asm/io.h> //writel#include <mach/gpio.h>#include <linux/leds.h>#include <asm/string.h>#define X210_LED_OFF 1U#define X210_LED_ON 0Ustruct led_classdev cdev1;struct led_classdev cdev2;struct led_classdev cdev3;void s5pv210_led1_set(struct led_classdev *led_cdev,enum led_brightness brightness);void s5pv210_led2_set(struct led_classdev *led_cdev,enum led_brightness brightness);void s5pv210_led3_set(struct led_classdev *led_cdev,enum led_brightness brightness);static struct gpio x210_led_gpio[] ={{ S5PV210_GPJ0(3), GPIOF_OUT_INIT_HIGH, "LED1" }, /* default to OFF */{ S5PV210_GPJ0(4), GPIOF_OUT_INIT_HIGH, "LED2" }, /* default to OFF */{ S5PV210_GPJ0(5), GPIOF_OUT_INIT_HIGH, "LED3" } /* default to OFF */};void s5pv210_led1_set(struct led_classdev *led_cdev,enum led_brightness brightness){printk(KERN_INFO "s5pv210_led1_set successful %d\n",brightness);if(brightness == LED_OFF){gpio_set_value(x210_led_gpio[0].gpio,X210_LED_OFF);}else{gpio_set_value(x210_led_gpio[0].gpio,X210_LED_ON);}}void s5pv210_led2_set(struct led_classdev *led_cdev,enum led_brightness brightness){printk(KERN_INFO "s5pv210_led2_set successful %d\n",brightness);if(brightness == LED_OFF){gpio_set_value(x210_led_gpio[1].gpio,X210_LED_OFF);}else{gpio_set_value(x210_led_gpio[1].gpio,X210_LED_ON);}}void s5pv210_led3_set(struct led_classdev *led_cdev,enum led_brightness brightness){printk(KERN_INFO "s5pv210_led3_set successful %d\n",brightness);if(brightness == LED_OFF){gpio_set_value(x210_led_gpio[2].gpio,X210_LED_OFF);}else{gpio_set_value(x210_led_gpio[2].gpio,X210_LED_ON);}}static int __init s5pv210_led_init(void){int ret = -1;printk(KERN_INFO "s5pv210_led_init successful \n");cdev1.brightness_set = s5pv210_led1_set; = "led1";ret = led_classdev_register(NULL, &cdev1);if (ret < 0){printk(KERN_WARNING "led_classdev_register fail \n");goto reg_err1;}cdev2.brightness_set = s5pv210_led2_set; = "led2";ret = led_classdev_register(NULL, &cdev2);if (ret < 0){printk(KERN_WARNING "led_classdev_register fail \n");goto reg_err2;}cdev3.brightness_set = s5pv210_led3_set; = "led3";ret = led_classdev_register(NULL, &cdev3);if (ret < 0){printk(KERN_WARNING "led_classdev_register fail \n");goto reg_err3;}ret = gpio_request_array(x210_led_gpio, ARRAY_SIZE(x210_led_gpio));if (ret){goto gpio_err;}return0;gpio_err:led_classdev_unregister(&cdev3);reg_err3:led_classdev_unregister(&cdev2);reg_err2:led_classdev_unregister(&cdev1);reg_err1:return ret;}static void __exit s5pv210_led_exit(void){printk(KERN_INFO "s5pv210_led_exit successful \n");gpio_free_array(x210_led_gpio, ARRAY_SIZE(x210_led_gpio));led_classdev_unregister(&cdev1);led_classdev_unregister(&cdev2);led_classdev_unregister(&cdev3);}module_init(s5pv210_led_init);module_exit(s5pv210_led_exit);// MODULE_xxx这种宏作⽤是⽤来添加模块描述信息MODULE_LICENSE("GPL"); // 描述模块的许可证MODULE_AUTHOR("musk"); // 描述模块的作者MODULE_DESCRIPTION("x210 LED driver"); // 描述模块的介绍信息MODULE_ALIAS("led_driver"); // 描述模块的别名信息View Code⼀. 什么是gpiolib1.1. linux中从2.6.35以后就开始有gpiolib库了,gpiolib的作⽤是对所有的gpio实⾏统⼀管理,因为驱动在⼯作的时候,会出现好⼏个驱动共同使⽤同⼀个gpio的情况;这会造成混乱。
linux内核gpio用法

linux内核gpio用法Linux内核对GPIO的使用是非常广泛的,本文将会通过几个步骤来解释如何在Linux内核中使用GPIO,包括GPIO的基本知识、配置GPIO、读取GPIO值、设置GPIO值和释放GPIO资源。
一、GPIO的基本知识GPIO(General Purpose Input/Output)是一种通用的输入/输出接口,它可以与各种设备进行连接,比如传感器、开关、LED等。
在Linux内核中,GPIO 被抽象为一个特殊的设备,可以通过相应的驱动程序进行读写操作。
每个GPIO引脚都被赋予了一个唯一的数字编号,这个编号称为GPIO号。
在不同的硬件平台上,GPIO号可能不同,因此在使用GPIO之前,需要先了解具体的GPIO号对应关系。
二、配置GPIO在使用GPIO之前,需要先配置GPIO的功能和方向,可以通过以下步骤来实现。
步骤1:加载GPIO驱动程序在Linux内核中,GPIO驱动程序以模块的形式存在,首先需要加载相应的GPIO 驱动程序。
可以使用如下命令加载GPIO驱动程序:modprobe gpio步骤2:导出GPIO引脚在Linux内核中,GPIO引脚默认是不可用的,需要先导出GPIO引脚,才能使用。
可以使用如下命令导出GPIO引脚:echo GPIO号> /sys/class/gpio/export其中,GPIO号为需要导出的GPIO引脚的编号。
步骤3:配置GPIO方向GPIO引脚有输入和输出两种方向,需要根据实际需求选择。
可以使用如下命令配置GPIO方向:echo in/out > /sys/class/gpio/gpioGPIO号/direction其中,in表示输入方向,out表示输出方向。
三、读取GPIO值配置好GPIO方向后,就可以读取GPIO引脚的值了。
可以使用如下命令读取GPIO值:cat /sys/class/gpio/gpioGPIO号/value其中,GPIO号为需要读取值的GPIO引脚的编号。
基于rk3568的linux驱动开发——gpio知识点 -回复

基于rk3568的linux驱动开发——gpio知识点-回复基于rk3568的Linux驱动开发——GPIO知识点GPIO(General Purpose Input/Output)是通用输入输出的意思,是嵌入式系统中的常用功能。
在rk3568芯片上,GPIO用于实现与外部设备的通信和控制,比如控制LED灯、键盘、电机等。
本文将介绍rk3568芯片上的GPIO控制器、GPIO驱动的开发以及GPIO 在Linux系统中的应用。
一、GPIO控制器在rk3568芯片中,GPIO控制器是用来控制GPIO端口的硬件模块。
每个GPIO控制器可以管理多个GPIO端口,每个GPIO端口可以被配置为输入或输出。
GPIO控制器通常包含寄存器用于配置和控制GPIO端口的功能,比如方向、电平等。
二、GPIO驱动的开发GPIO驱动是用于控制和管理GPIO功能的软件模块。
在Linux内核中,GPIO驱动通过sysfs接口暴露给用户空间,以便用户可以通过文件系统访问和控制GPIO端口。
以下是GPIO驱动的开发过程:1. 确定GPIO控制器和GPIO端口:首先需要确定要使用的GPIO控制器和GPIO端口。
在rk3568芯片手册中可以找到相应的信息。
2. 创建GPIO设备:在Linux内核中,GPIO驱动是通过GPIO子系统来管理的。
首先需要在设备树中添加GPIO设备描述,并分配一个唯一的GPIO号码。
3. 注册GPIO设备:在驱动的初始化函数中,需要调用相应的函数注册GPIO设备,以便系统能够识别和管理该设备。
4. 设置GPIO模式和方向:通过调用GPIO控制器的寄存器,可以设置GPIO端口的模式和方向。
例如,可以将GPIO端口配置为输入模式或输出模式。
5. 读取和写入GPIO值:读取GPIO值可以通过读取GPIO控制器的寄存器来实现,写入GPIO值可以通过写入GPIO控制器的寄存器来实现。
例如,可以将GPIO端口的电平设置为高或低。
基于rk3568的linux驱动开发——gpio知识点

基于rk3568的linux驱动开发——gpio知识点基于rk3568的Linux驱动开发——GPIO知识点一、引言GPIO(General Purpose Input/Output)通用输入/输出,是现代计算机系统中的一种常用接口,它可以根据需要配置为输入或输出。
通过GPIO 接口,我们可以与各种外设进行通信,如LED灯、按键、传感器等。
在基于Linux系统的嵌入式设备上开发驱动程序时,熟悉GPIO的使用是非常重要的一环。
本文将以RK3568芯片为例,详细介绍GPIO的相关知识点和在Linux驱动开发中的应用。
二、GPIO概述GPIO是系统中的一个基本的硬件资源,它可以通过软件的方式对其进行配置和控制。
在嵌入式设备中,通常将一部分GPIO引脚连接到外部可编程电路,以实现与外部设备的交互。
在Linux中,GPIO是以字符设备的形式存在,对应的设备驱动为"gpiolib"。
三、GPIO的驱动开发流程1. 导入头文件在驱动程序中,首先需要导入与GPIO相关的头文件。
对于基于RK3568芯片的开发,需要导入头文件"gpiolib.h"。
2. 分配GPIO资源在驱动程序中,需要使用到GPIO资源,如GPIO所在的GPIO Bank和GPIO Index等。
在RK3568芯片中,GPIO资源的分配是通过设备树(Device Tree)来进行的。
在设备树文件中,可以定义GPIO Bank和GPIO Index等信息,以及对应的GPIO方向(输入或输出)、电平(高电平或低电平)等属性。
在驱动程序中,可以通过设备树接口(Device Tree API)来获取这些GPIO资源。
3. GPIO的配置与控制在驱动程序中,首先要进行GPIO的初始化与配置。
可以通过函数"gpiod_get()"来打开指定的GPIO,并判断其是否有效。
如果成功打开GPIO,则可以使用函数"gpiod_direction_output()"或"gpiod_direction_input()"来设置GPIO的方向,分别作为输出或输入。
gpio子系统和pinctrl子系统(一)

gpio⼦系统和pinctrl⼦系统(⼀)前⾔ 随着内核的发展,linux驱动框架在不断的变化。
很早很早以前,出现了gpio⼦系统,后来⼜出现了pinctrl⼦系统。
在⽹上很难看到⼀篇讲解这类⼦系统的⽂章。
就拿gpio操作来说吧,很多时候都是简单的调⽤gpio⼦系统提供的api,然后根据sdk说明⽂档写明的gpio号传参数,⾄于⾥⾯的⼯作过程对于驱动⼯程师⽽⾔就像个⿊盒⼦。
当我们⾃⼰设计的板⼦和demo板有很⼤变动时,问题就出现了。
⾸先遇到的是怎么配置pin(是基于设备树还是不基于设备树,基于设备树的话,怎么修改设备树关于pinctrl部分的内容,⾥⾯各个字段什么意思,怎么改),然后是在哪⾥配置pin(内核部分有哪些需要相应修改,还是不需要⼀点修改呢),接着就是怎么调试等等。
我想只有清楚了尽量多的gpio⼦系统和pinctrl⼦系统细节,才会更快更好的完成这些⼯作。
有些平台的实现没有使⽤内核提供的pinctrl⼦系统,⽽是继续采⽤在内核提供pinctrl⼦系统前⾃⼰实现的那套机制来pinmux操作,如omap,有些平台则基于pinctrl⼦系统来实现pinmux、pinconf的控制。
本⽂以gpio⼦系统为⼊⼝慢慢深⼊,最后分析pinctrl⼦系统。
如果有错误的地⽅,欢迎⼤家直接指出gpio⼦系统 gpio⼦系统帮助我们管理整个系统gpio的使⽤情况,同时通过sys⽂件系统导出了调试信息和应⽤层控制接⼝。
它内部实现主要提供了两类接⼝,⼀类给bsp⼯程师,⽤于注册gpio chip(也就是所谓的gpio控制器驱动),另⼀部分给驱动⼯程师使⽤,为驱动⼯程师屏蔽了不同gpio chip之间的区别,驱动⼯程师调⽤的api的最终操作流程会导向gpio对应的gpio chip的控制代码,也就是bsp的代码。
gpio⼦系统核⼼实现分析gpio⼦系统的内容在drivers/gpio⽂件夹下,主要⽂件有:devres.cgpiolib.cgpiolib-of.cgpiolib-acpi.cgpio-xxx.cdevres.c是针对gpio api增加的devres机制的⽀持,devres机制讲解请参考,gpiolib.c是gpio⼦系统的核⼼实现,gpiolib-of.c是对设备树的⽀持,gpiolib-acpi.c和acpi相关,不分析(acpi还未深⼊了解^_^),最后情景分析的时候,会找⼀个平台的gpio-xxx.c来分析。
linuxSPI驱动——gpio模拟spi驱动(三)

linuxSPI驱动——gpio模拟spi驱动(三)⼀:⾸先在我的平台注册platform_device,保证能让spi-gpio.c能执⾏到probe函数。
1: struct spi_gpio_platform_data {2: unsigned sck;3: unsigned mosi;4: unsigned miso;5:6: u16 num_chipselect;7: };1: //#define NCS GPIO_PB(2) //定义SS所对应的GPIO接⼝编号2: //#define SCLK GPIO_PB(0) //定义SCLK所对应的GPIO接⼝编号3: //#define MOSI GPIO_PB(4) //定义SCLK所对应的GPIO接⼝编号4: //#define MISO GPIO_PB(1)5: static struct spi_gpio_platform_data jz_spi_gpio_data = {6: .sck = GPIO_PB(0), //GPIO_SPI_SCK,7: .mosi = GPIO_PB(4), //GPIO_SPI_MOSI,8: .miso = GPIO_PB(1), //GPIO_SPI_MISO,9: .num_chipselect = 1,10: };11:12: struct platform_device jz_spi_gpio_device = {13: .name = "spi_gpio",14: .id = 0,15: .dev = {16: .platform_data = &jz_spi_gpio_data,17: },18: };注册platform device1: platform_device_register(&jz_spi_gpio_device);⼆:注册platform_driver在spi_gpio.c⾥⾯注册platform driver1: MODULE_ALIAS("platform:" DRIVER_NAME);2:3: static struct platform_driver spi_gpio_driver = {4: = DRIVER_NAME,5: .driver.owner = THIS_MODULE,6: .remove = __exit_p(spi_gpio_remove),7: };8:9: static int __init spi_gpio_init(void)10: {11: return platform_driver_probe(&spi_gpio_driver, spi_gpio_probe);12: }13: module_init(spi_gpio_init);14:15: static void __exit spi_gpio_exit(void)16: {17: platform_driver_unregister(&spi_gpio_driver);18: }19: module_exit(spi_gpio_exit);20:21:22: MODULE_DESCRIPTION("SPI master driver using generic bitbanged GPIO ");23: MODULE_AUTHOR("David Brownell");24: MODULE_LICENSE("GPL");三:具体算法分析1: struct spi_gpio {2: struct spi_bitbang bitbang; /* gpio 模拟spi算法相关的结构 */3: struct spi_gpio_platform_data pdata; /* spi platform data 对应模拟spi的四个gpio编号 */4: struct platform_device *pdev; /* 对应注册的 platform device */5: };1:2: static int __init spi_gpio_probe(struct platform_device *pdev)3: {4: int status;5: struct spi_master *master;6: struct spi_gpio *spi_gpio;7: struct spi_gpio_platform_data *pdata;8: u16 master_flags = 0;9:10: pdata = pdev->dev.platform_data; /* 存放spi的四根gpio */11: #ifdef GENERIC_BITBANG12: if (!pdata || !pdata->num_chipselect)13: return -ENODEV;14: #endif15:16: /* 申请注册四个gpio */17: status = spi_gpio_request(pdata, dev_name(&pdev->dev), &master_flags);18: if (status < 0) {19: return status;20: }21:22: /* alloc a spi master ,master->dev->p->driver_data = &master[1]*/23: master = spi_alloc_master(&pdev->dev, sizeof *spi_gpio);24: if (!master) {25: status = -ENOMEM;26: goto gpio_free;27: }28: /* spi_gpio指向⼀块空间, 即指向mstaer[1]29: pdev->dev->p->driver_data = spi_gpio;30: 初始化spi_gpio31: */32: spi_gpio = spi_master_get_devdata(master);33: platform_set_drvdata(pdev, spi_gpio);34:35: spi_gpio->pdev = pdev;36: if (pdata)37: spi_gpio->pdata = *pdata;38:39: master->flags = master_flags;40: master->bus_num = pdev->id;41: master->num_chipselect = SPI_N_CHIPSEL;42: master->setup = spi_gpio_setup; /* setup ⽐如cs引脚申请 */43: master->cleanup = spi_gpio_cleanup;44: /* spi_gpio->bitbang.master = master */45: spi_gpio->bitbang.master = spi_master_get(master);46: spi_gpio->bitbang.chipselect = spi_gpio_chipselect;47: /* spi_gpio->bitbang.txrx_word 数组函数四个元素指针,分别指向spi四种mode算法函数 */ 48: if ((master_flags & (SPI_MASTER_NO_TX | SPI_MASTER_NO_RX)) == 0) {49: spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_txrx_word_mode0;50: spi_gpio->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_txrx_word_mode1;51: spi_gpio->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_txrx_word_mode2;52: spi_gpio->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_txrx_word_mode3;53: } else {54: spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_spec_txrx_word_mode0;55: spi_gpio->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_spec_txrx_word_mode1;56: spi_gpio->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_spec_txrx_word_mode2;57: spi_gpio->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_spec_txrx_word_mode3;58: }59: /* spi_gpio->bitbang.setup_transfer初始化传输的bits_per_word和speed */60: spi_gpio->bitbang.setup_transfer = spi_bitbang_setup_transfer;61: spi_gpio->bitbang.flags = SPI_CS_HIGH;62: /* spi_gpio->bitbang相关算法接⼝初始化 */63: status = spi_bitbang_start(&spi_gpio->bitbang);64: if (status < 0) {65: spi_master_put(spi_gpio->bitbang.master);66: gpio_free:67: if (SPI_MISO_GPIO != SPI_GPIO_NO_MISO)68: gpio_free(SPI_MISO_GPIO);69: if (SPI_MOSI_GPIO != SPI_GPIO_NO_MOSI)70: gpio_free(SPI_MOSI_GPIO);71: gpio_free(SPI_SCK_GPIO);72: spi_master_put(master);73: }74:75: return status;76: }四:总之最终让spi_gpi0整个对象存放了整个gpio模拟spi的算法结构;⽽pdev->dev->p->driver_data = spi_gpio; platform device和 platform driver两者match结果是:root@CarRadio:/# ls /sys/bus/platform/devices/spi_gpio.0/driver modalias power spi0.0 spi_master subsystem ueventroot@CarRadio:/# ls /sys/bus/platform/devices/spi_gpio.0/driver/spi_gpio.0 uevent。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
/Linux/2011-09/43084.htm打算跟着友善之臂的《mini2440 Linux移植开发指南》见/Linux/2011-06/37904.htm 来做个LED驱动,虽然LED的原理简单得不能再简单了,但是要把kernel中针对于s3c24**的GPIO的一些数据结构,还有函数搞清楚也不是那么轻松的事,所以本文主要简单地说明下LED驱动中的相关数据结构以及函数/宏的定义,并对驱动加以验证***************************************************************************注意:在/arch/arm/mach-s3c2410/include/mach/gpio-fns.h源代码中有如下说明:16/* These functions are in the to-be-removed category and it is strongly17 * encouraged not to use these in new code. They will be marked deprecated18 * very soon.19 *20 * Most of the functionality can be either replaced by the gpiocfg calls21 * for the s3c platform or by the generic GPIOlib API.22 *23 * As of 2.6.35-rc, these will be removed, with the few drivers using them24 * either replaced or given a wrapper until the calls can be removed.25*/该头文件包括:static inline void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int cfg)该函数直接使用linux/arch/arm/plat-s3c/gpio-config.c中的int s3c_gpio_cfgpin(unsigned int pin, unsigned int config)即可***************************************************************************首先看一下设备初始化程序:85 /*86 * 设备初始化87 */88 static int __init dev_init(void)89 {90 int ret;91 int i;92 for (i = 0; i < 4; i++) {93 //设置LED 对应的端口寄存器为输出(OUTPUT)94 if (s3c_gpio_cfgpin(led_table[i], led_cfg_table[i])<0)printk(KERN_INFO "config pin %d failed", i);95 printk(KERN_INFO "config pin %d failed", i);95 //设置LED 对应的端口寄存器为低电平输出,在模块加载> 结束后,四个LED 应该是全部都是发光96 状态97 s3c2410_gpio_setpin(led_table[i], 0);98 }99 ret = misc_register(&misc); //注册设备100 printk (DEVICE_NAME"/tinitialized/n"); //打印初始化信息101 return ret;102 }可以看到,这里涉及到两个函数,分别是s3c2410_gpio_cfgpin,s3c2410_gpio_setpin,这两个函数分别对四个LED进行配置,从函数名来看,cfgpin对引脚寄存器状态进行配置,而setpin 应该是对寄存器数据值进行配置,我们在分析函数之前先弄清楚传入的参数到底是什么。
led_table[i]28 //LED 对应的GPIO 端口列表29 static unsigned long led_table [] = {30 S3C2410_GPB(5),31 S3C2410_GPB(6),32 S3C2410_GPB(7),33 S3C2410_GPB(8),34 };这里S3C2410_GPB宏定义在mach/gpio-nrs.h中/* GPIO bank sizes */#define S3C2410_GPIO_A_NR (32)#define S3C2410_GPIO_B_NR (32)#define S3C2410_GPIO_C_NR (32)#define S3C2410_GPIO_D_NR (32)#define S3C2410_GPIO_E_NR (32)#define S3C2410_GPIO_F_NR (32)#define S3C2410_GPIO_G_NR (32)#define S3C2410_GPIO_H_NR (32)#define S3C2410_GPIO_J_NR (32) /* technically 16. */#define S3C2410_GPIO_K_NR (32) /* technically 16. */#define S3C2410_GPIO_L_NR (32) /* technically 15. */#define S3C2410_GPIO_M_NR (32) /* technically 2. */#if CONFIG_S3C_GPIO_SPACE != 0#error CONFIG_S3C_GPIO_SPACE cannot be zero at the moment#endif#define S3C2410_GPIO_NEXT(__gpio) /((__gpio##_START) + (__gpio##_NR) + CONFIG_S3C_GPIO_SPACE + 0)//这里的CONFIG_S3C_GPIO_SPAC是内核配置选项,在.config中可以找到,我的配置为:CONFIG_S3C_GPIO_SPACE = 0enum s3c_gpio_number {S3C2410_GPIO_A_START = 0,S3C2410_GPIO_B_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_A),S3C2410_GPIO_C_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_B),S3C2410_GPIO_D_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_C),S3C2410_GPIO_E_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_D),S3C2410_GPIO_F_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_E),S3C2410_GPIO_G_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_F),S3C2410_GPIO_H_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_G),S3C2410_GPIO_J_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_H),S3C2410_GPIO_K_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_J),S3C2410_GPIO_L_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_K),S3C2410_GPIO_M_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_L),};#define S3C2410_GPB(_nr) (S3C2410_GPIO_B_START + (_nr))因此,以S3C2410_GPB(5)为例,其宏展开为:S3C2410_GPIO_NEXT(S3C2410_GPIO_A) +5 =>(S3C2410_GPIO_A_START + S3C2410_GPIO_A_NR + CONFIG_S3C_GPIO_SPACE + 0) + 5 =>很显然,S3C2410_GPB(5)就是从GPA的首地址+GPA个数+GPB的offset就是当前GPB的IO偏移量,即0+32+5=37, 同理S3C2410_GPB(0) 相当于3230 S3C2410_GPB(5) 相当于3731 S3C2410_GPB(6) 相当于3832 S3C2410_GPB(7) 相当于3933 S3C2410_GPB(8) 相当于40***************************************************************************led_cfg_table[i]36 //LED 对应端口将要输出的状态列表37 static unsigned int led_cfg_table [] = {38 S3C2410_GPIO_OUTPUT,39 S3C2410_GPIO_OUTPUT,40 S3C2410_GPIO_OUTPUT,41 S3C2410_GPIO_OUTPUT,42 };S3C2410_GPIO_OUTPUT定义在mach/regs-gpio.h#define S3C2410_GPIO_LEA VE (0xFFFFFFFF) // 最后两位是设置,11表示RESERVE#define S3C2410_GPIO_INPUT (0xFFFFFFF0) /* not available on A */ // 最后两位是设置,00表示INPUT#define S3C2410_GPIO_OUTPUT (0xFFFFFFF1) // 最后两位是设置,01表示OUTPUT#define S3C2410_GPIO_IRQ (0xFFFFFFF2) /* not available for all */#define S3C2410_GPIO_SFN2 (0xFFFFFFF2) /* bank A => addr/cs/nand */#define S3C2410_GPIO_SFN3 (0xFFFFFFF3) /* not available on A */***************************************************************************根据前面的分析,s3c2410传入了当前GPIO的偏移地址,以及OUTPUT状态现在我们深入前面的两个函数:定义在linux/arch/arm/plat-s3c/gpio-config.cint s3c_gpio_cfgpin(unsigned int pin, unsigned int config){struct s3c_gpio_chip *chip = s3c_gpiolib_getchip(pin); //得到对应GPIO结构体首指针,里面包含了该GPIO的各种参数unsigned long flags;int offset;int ret;if (!chip)return -EINVAL; // 没找到的话,返回invalidoffset = pin - chip->chip.base; // 否则offset等于该GPIO引脚相对于GPX(0)的偏移量,每个偏移1s3c_gpio_lock(chip, flags); // 自旋锁锁住该GPIO,通过chip指针指向lock,看下面的define和图ret = s3c_gpio_do_setcfg(chip, offset, config); //设置该GPIO状态寄存器的数值为config s3c_gpio_unlock(chip, flags); // 解锁// 自旋锁操作/* locking wrappers to deal with multiple access to the same gpio bank *///#define s3c_gpio_lock(_oc, _fl) spin_lock_irqsave(&(_oc)->lock, _fl)//#define s3c_gpio_unlock(_oc, _fl) spin_unlock_irqrestore(&(_oc)->lock, _fl)//s3c_gpio_do_setcfg操作static inline int s3c_gpio_do_setcfg(struct s3c_gpio_chip *chip,unsigned int off, unsigned int config){return (chip->config->set_config)(chip, off, config);}//这里的set_config是一个函数指针,由后面的分析知道,如果针对GPA,该函数指针指向s3c_gpio_setcfg_s3c24xx_a , 如果针对GPX应该是指向s3c_gpio_setcfg_s3c24xx——但发现,如果是其他GPX,根本没有定义set_config!!! (这个问题已经解决,见后文s3c24xx_gpiolib_init函数,事实上,其余的config的确指向s3c_gpio_do_setcfg函数)struct s3c_gpio_cfg s3c24xx_gpiocfg_default = {.set_config = s3c_gpio_setcfg_s3c24xx,.get_config = s3c_gpio_getcfg_s3c24xx,};int s3c_gpio_setcfg_s3c24xx_a(struct s3c_gpio_chip *chip, unsigned int off, unsigned int cfg) {void __iomem *reg = chip->base; // GPXCON的物理基地址unsigned int shift = off; // 每个GPA对应一位u32 con;if (s3c_gpio_is_cfg_special(cfg)) { //OUTPUT状态是否为(0xfffffffX),是,返回1 cfg &= 0xf; // cfg = 0xX/* Map output to 0, and SFN2 to 1 */ 本实验不会运行到这cfg -= 1;if (cfg > 1)return -EINV AL;cfg <<= shift;}con = __raw_readl(reg); // 先读出该GPXCON的值,32位con &= ~(0x1 << shift); //con |= cfg; //__raw_writel(con, reg); // 将新值写入GPXCONPS:#define __raw_writeb(v,a) (__chk_io_ptr(a), *(volatile unsigned char __force *)(a) = (v)) #define __raw_writew(v,a) (__chk_io_ptr(a), *(volatile unsigned short __force *)(a) = (v)) #define __raw_writel(v,a) (__chk_io_ptr(a), *(volatile unsigned int __force *)(a) = (v))#define __raw_readb(a) (__chk_io_ptr(a), *(volatile unsigned char __force *)(a))#define __raw_readw(a) (__chk_io_ptr(a), *(volatile unsigned short __force *)(a))#define __raw_readl(a) (__chk_io_ptr(a), *(volatile unsigned int __force *)(a))return 0;}如果针对GPX情况int s3c_gpio_setcfg_s3c24xx(struct s3c_gpio_chip *chip,unsigned int off, unsigned int cfg){void __iomem *reg = chip->base;unsigned int shift = off * 2; // 每个GPX对应2位u32 con;if (s3c_gpio_is_cfg_special(cfg)) {cfg &= 0xf;if (cfg > 3)return -EINV AL;cfg <<= shift; // 将cfg的0,1两位左移offset}con = __raw_readl(reg); // 读对应的GPXCON值con &= ~(0x3 << shift); // 将GPXCON(pin)的两bits请0con |= cfg; // 设置config值__raw_writel(con, reg); // 写入新的GPXCONreturn 0;}return ret;} // end s3c_gpio_cfgpin这里涉及到了一个重要的数据结构,s3c_gpio_chip,此数据结构比较复杂,我贴出这个数据结构的结构图:、这个重要的数据结构中可以记录每个GPIO所需要的所有数据,后面会遇到的s3c24xx_gpios[]结构体就是该结构体的集合,描述了芯片中所有的GPIO端口,之后我们需要时时回头看看这个结构。