以下内容源于朱有鹏嵌入式课程的学习整理,如有侵权请告知删除。
一、前言
在实际情况中,很多硬件都要用到GPIO,因此GPIO会复用;如果同一个GPIO被2个驱动同时控制就会出现bug;因此内核提供了gpiolib来统一管理系统中所有GPIO。某个驱动需要用到GPIO时,需要申请,被许可后才可以使用,使用完后释放,其他驱动才能使用该GPIO。
在LED驱动框架的分析文章开头,写到“系统资源的管控属于驱动框架的一部分”。gpiolib体系也属于驱动框架的一部分。
二、gpiolib体系的3条主线
第1条主线:gpiolib的建立过程。
第2条主线:gpiolib的使用方法,即申请、使用、释放。
第3条主线:gpiolib的架构,即涉及哪些目录的哪些文件。
三、第一条主线:gpiolib的建立过程
s5pv210_gpiolib_init()函数是gpiolib初始化的函数。
其定义在x210_kernel\arch\arm\mach-s5pv210\gpiolib.c文件中。
static void __init smdkc110_map_io(void) {s5p_init_io(NULL, 0, S5P_VA_CHIPID);s3c24xx_init_clocks(24000000);s5pv210_gpiolib_init();//这里s3c24xx_init_uarts(smdkc110_uartcfgs, ARRAY_SIZE(smdkc110_uartcfgs));s5p_reserve_bootmem(smdkc110_media_devs, ARRAY_SIZE(smdkc110_media_devs)); #ifdef CONFIG_MTD_ONENANDs5pc110_device_onenand.name = "s5pc110-onenand"; #endif #ifdef CONFIG_MTD_NANDs3c_device_nand.name = "s5pv210-nand"; #endifs5p_device_rtc.name = "smdkc110-rtc"; }
__init int s5pv210_gpiolib_init(void) {struct s3c_gpio_chip *chip = s5pv210_gpio_4bit;//结构体数组int nr_chips = ARRAY_SIZE(s5pv210_gpio_4bit);//GPIO端口数目int i = 0;for (i = 0; i < nr_chips; i++, chip++) {if (chip->config == NULL)chip->config = &gpio_cfg;if (chip->base == NULL)chip->base = S5PV210_BANK_BASE(i);}samsung_gpiolib_add_4bit_chips(s5pv210_gpio_4bit, nr_chips);return 0; }
1、结构体struct s3c_gpio_chip的分析
该结构体定义在\x210_kernel\arch\arm\plat-samsung\include\plat\gpio-core.h文件中。它是一个GPIO端口的抽象。
端口和IO口是两个概念。S5PV210的IO口约有160个,这些IO口被分成N个端口,每个端口中又包含了M个IO口。比如GPA0就是一个端口,里面包含8个IO口,一般记作GPA0_0(或GPA0.0),GPA0_1(或GPA0.1),…,GPA0_7(或GPA0.7)。
内核中为每个GPIO都分配了一个编号,编号是一个数字(比如有160个IO时,编号就可以从1到160连续分布),编号可以让程序很方便的去识别每一个GPIO。
struct s3c_gpio_chip {struct gpio_chip chip;struct s3c_gpio_cfg *config;struct s3c_gpio_pm *pm;void __iomem *base;//端口的基地址int eint_offset;spinlock_t lock; #ifdef CONFIG_PMu32 pm_save[7]; #endif };
该结构体包括着结构体struct gpio_chip,其内容如下:
struct gpio_chip {const char *label;//端口的名字struct device *dev;struct module *owner;int (*request)(struct gpio_chip *chip,unsigned offset);void (*free)(struct gpio_chip *chip,unsigned offset);int (*direction_input)(struct gpio_chip *chip,unsigned offset);int (*get)(struct gpio_chip *chip,unsigned offset);int (*direction_output)(struct gpio_chip *chip,unsigned offset, int value);int (*set_debounce)(struct gpio_chip *chip,unsigned offset, unsigned debounce);void (*set)(struct gpio_chip *chip,unsigned offset, int value);int (*to_irq)(struct gpio_chip *chip,unsigned offset);void (*dbg_show)(struct seq_file *s,struct gpio_chip *chip);int base;///u16 ngpio;const char *const *names;unsigned can_sleep:1;unsigned exported:1; };
2、结构体数组s5pv210_gpio_4bit
该数组定义在x210_kernel\arch\arm\mach-s5pv210\gpiolib.c文件中。
该数组的成员都是struct s3c_gpio_chip结构体类型的变量,在定义数组的同时,都填充了每个成员的chip这个元素。
结构体数组s5pv210_gpio_4bit包含了当前系统中所有的IO端口的信息,比如端口的名字、端口中所有GPIO的编号、端口操作寄存器组的虚拟地址基地址、端口中IO口的数量、端口上下拉等模式的配置函数、端口中的IO口换算其对应的中断号的函数。
static struct s3c_gpio_chip s5pv210_gpio_4bit[] = {{.chip = {.base = S5PV210_GPA0(0), //当前端口的基础编号.ngpio = S5PV210_GPIO_A0_NR,//当前端口拥有的IO口数量.label = "GPA0", //当前端口的名字.to_irq = s5p_gpiolib_gpioint_to_irq,//当前端口中IO口换成对应中断号的方法},}, {.chip = {.base = S5PV210_GPA1(0),.ngpio = S5PV210_GPIO_A1_NR,.label = "GPA1",.to_irq = s5p_gpiolib_gpioint_to_irq,},}, {.chip = {.base = S5PV210_GPB(0),.ngpio = S5PV210_GPIO_B_NR,.label = "GPB",.to_irq = s5p_gpiolib_gpioint_to_irq,},}, //……省略部分代码 }
[root@xjh class]# ls backlight i2c-adapter misc regulator scsi_host //忽略部分 bdi i2c-dev mmc_host rfkill sound block ieee80211 mtd rtc spi_master firmware input net s3c_bc switch gpio lcd power_supply scsi_device timed_output graphics leds ppp scsi_disk tty hidraw mem pvr scsi_generic vc [root@xjh class]# cd gpio/ [root@xjh gpio]# ls export gpiochip164 gpiochip235 gpiochip316 gpiochip396 gpiochip56 gpiochip0 gpiochip172 gpiochip244 gpiochip325 gpiochip40 gpiochip62 gpiochip104 gpiochip181 gpiochip253 gpiochip334 gpiochip405 gpiochip71 gpiochip112 gpiochip188 gpiochip262 gpiochip343 gpiochip414 gpiochip80 gpiochip120 gpiochip197 gpiochip271 gpiochip35 gpiochip423 gpiochip89 gpiochip128 gpiochip206 gpiochip280 gpiochip351 gpiochip431 gpiochip9 gpiochip137 gpiochip212 gpiochip289 gpiochip360 gpiochip438 gpiochip96 gpiochip14 gpiochip221 gpiochip29 gpiochip369 gpiochip447 unexport gpiochip146 gpiochip226 gpiochip298 gpiochip378 gpiochip456 gpiochip155 gpiochip23 gpiochip307 gpiochip387 gpiochip47 [root@xjh gpio]# cd gpiochip0 [root@xjh gpiochip0]# ls base label ngpio power subsystem uevent [root@xjh gpiochip0]# cat base 0 [root@xjh gpiochip0]# cat label GPA0 [root@xjh gpiochip0]# cat ngpio 8 [root@xjh gpiochip0]# cd ../gpiochip14 [root@xjh gpiochip14]# ls base label ngpio power subsystem uevent [root@xjh gpiochip14]# cat base 14 [root@xjh gpiochip14]# cat label GPB [root@xjh gpiochip14]# cat ngpio 8 [root@xjh gpiochip14]# cd ../gpiochip35 [root@xjh gpiochip35]# ls base label ngpio power subsystem uevent [root@xjh gpiochip35]# cat base 35 [root@xjh gpiochip35]# cat label GPD0 [root@xjh gpiochip35]# cat ngpio 4 [root@xjh gpiochip35]#
另外,注意一下诸如S5PV210_GPA0()这样的宏。这个宏的返回值是GPA0端口的某一个IO口的基础编号值,传参是这个IO口在GPA0端口中的局部编号。
/* S5PV210 GPIO number definitions */ #define S5PV210_GPA0(_nr) (S5PV210_GPIO_A0_START + (_nr)) #define S5PV210_GPA1(_nr) (S5PV210_GPIO_A1_START + (_nr)) #define S5PV210_GPB(_nr) (S5PV210_GPIO_B_START + (_nr)) #define S5PV210_GPC0(_nr) (S5PV210_GPIO_C0_START + (_nr)) #define S5PV210_GPC1(_nr) (S5PV210_GPIO_C1_START + (_nr)) ……
#define S5PV210_GPIO_NEXT(__gpio) \((__gpio##_START) + (__gpio##_NR) + CONFIG_S3C_GPIO_SPACE + 1)enum s5p_gpio_number {S5PV210_GPIO_A0_START = 0,S5PV210_GPIO_A1_START = S5PV210_GPIO_NEXT(S5PV210_GPIO_A0),S5PV210_GPIO_B_START = S5PV210_GPIO_NEXT(S5PV210_GPIO_A1),S5PV210_GPIO_C0_START = S5PV210_GPIO_NEXT(S5PV210_GPIO_B),S5PV210_GPIO_C1_START = S5PV210_GPIO_NEXT(S5PV210_GPIO_C0),S5PV210_GPIO_D0_START = S5PV210_GPIO_NEXT(S5PV210_GPIO_C1),S5PV210_GPIO_D1_START = S5PV210_GPIO_NEXT(S5PV210_GPIO_D0), …… }
3、samsung_gpiolib_add_4bit_chips()函数
该函数的函数名中为什么有个“4bit”?查询原理图得知如下。三星的CPU中2440的CON寄存器是2bit对应一个IO口,而6410和210以及之后的系列中CON寄存器是4bit对应1个IO口。所以gpiolib在操作2440和210的CON寄存器时是不同的。
此函数用来注册gpiolib,接收的参数是结构体数组s5pv210_gpio_4bit、该数组元素的个数。
samsung_gpiolib_add_4bit_chips(s5pv210_gpio_4bit, nr_chips);
函数调用关系如下:
|…………samsung_gpiolib_add_4bit_chips()函数
|………………samsung_gpiolib_add_4bit()函数
|……………………samsung_gpiolib_4bit_input函数
|……………………samsung_gpiolib_4bit_output函数
|………………s3c_gpiolib_add()函数
void __init samsung_gpiolib_add_4bit_chips(struct s3c_gpio_chip *chip,int nr_chips) {for (; nr_chips > 0; nr_chips--, chip++) {samsung_gpiolib_add_4bit(chip);s3c_gpiolib_add(chip);} }
查阅代码可知,samsung_gpiolib_add_4bit()函数内部没有做gpiolib的注册工作,而是在做填充,填充的是每一个GPIO被设置成输入模式/输出模式的操作方法。
static int samsung_gpiolib_4bit_input(struct gpio_chip *chip,unsigned int offset) {struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip);void __iomem *base = ourchip->base;unsigned long con;con = __raw_readl(base + GPIOCON_OFF);//读写三部曲con &= ~(0xf << con_4bit_shift(offset));__raw_writel(con, base + GPIOCON_OFF);gpio_dbg("%s: %p: CON now %08lx\n", __func__, base, con);return 0; }
查阅代码可知,s3c_gpiolib_add()首先完善chip的direction_input/direction_ouput/set/get这4个方法,然后调用gpiochip_add方法进行真正的注册操作。
__init void s3c_gpiolib_add(struct s3c_gpio_chip *chip) {struct gpio_chip *gc = &chip->chip;int ret;BUG_ON(!chip->base);BUG_ON(!gc->label);BUG_ON(!gc->ngpio);spin_lock_init(&chip->lock);if (!gc->direction_input)gc->direction_input = s3c_gpiolib_input;if (!gc->direction_output)gc->direction_output = s3c_gpiolib_output;if (!gc->set)gc->set = s3c_gpiolib_set;//设置data寄存器的值if (!gc->get)gc->get = s3c_gpiolib_get;//获取data寄存器的值#ifdef CONFIG_PMif (chip->pm != NULL) {if (!chip->pm->save || !chip->pm->resume)printk(KERN_ERR "gpio: %s has missing PM functions\n",gc->label);} elseprintk(KERN_ERR "gpio: %s has no PM function\n", gc->label); #endif/* gpiochip_add() prints own failure message on error. */ret = gpiochip_add(gc);if (ret >= 0)s3c_gpiolib_track(chip); }
static void s3c_gpiolib_set(struct gpio_chip *chip,unsigned offset, int value) {struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip);void __iomem *base = ourchip->base;unsigned long flags;unsigned long dat;s3c_gpio_lock(ourchip, flags);dat = __raw_readl(base + 0x04);dat &= ~(1 << offset);if (value)dat |= 1 << offset;__raw_writel(dat, base + 0x04);s3c_gpio_unlock(ourchip, flags); }static int s3c_gpiolib_get(struct gpio_chip *chip, unsigned offset) {struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip);unsigned long val;val = __raw_readl(ourchip->base + 0x04);val >>= offset;val &= 1;return val; }
/* *这个注册就是将(我们的封装了一个GPIO端口的所有信息的)chip结构体变量, *挂接到内核(gpiolib模块定义的一个)gpio_desc数组中的某一个格子中。 */ int gpiochip_add(struct gpio_chip *chip) {unsigned long flags;int status = 0;unsigned id;int base = chip->base;if ((!gpio_is_valid(base) || !gpio_is_valid(base + chip->ngpio - 1))&& base >= 0) {status = -EINVAL;goto fail;}spin_lock_irqsave(&gpio_lock, flags);if (base < 0) {base = gpiochip_find_base(chip->ngpio);if (base < 0) {status = base;goto unlock;}chip->base = base;}/* these GPIO numbers must not be managed by another gpio_chip */for (id = base; id < base + chip->ngpio; id++) {if (gpio_desc[id].chip != NULL) {status = -EBUSY;break;}}if (status == 0) {for (id = base; id < base + chip->ngpio; id++) {gpio_desc[id].chip = chip;/* REVISIT: most hardware initializes GPIOs as* inputs (often with pullups enabled) so power* usage is minimized. Linux code should set the* gpio direction first thing; but until it does,* we may expose the wrong direction in sysfs.*/gpio_desc[id].flags = !chip->direction_input? (1 << FLAG_IS_OUT): 0;}}unlock:spin_unlock_irqrestore(&gpio_lock, flags);if (status == 0)status = gpiochip_export(chip); fail:/* failures here can mean systems won't boot... */if (status)pr_err("gpiochip_add: gpios %d..%d (%s) failed to register\n",chip->base, chip->base + chip->ngpio - 1,chip->label ? : "generic");return status; }
四、第2条主线:gpiolib的使用方法
1、从驱动框架角度分析gpiolib
目前已经清楚第1条主线,即gpiolib的建立过程,但这只是厂商驱动工程师负责的那一部分;还有另一部分是内核开发者提供的驱动框架的那一部分,即第2条主线。
drivers/gpio/gpiolib.c文件中的函数构成第2部分,即内核开发者写的gpiolib框架部分。
(1)gpiochip_add
它是框架提供的接口,给厂商驱动工程师用(针对某个开发板GPIO的情况,对内核进行一定的修改,注册),用于向内核注册gpiolib(标记有多少组端口,属性细节等,让内核知道具体的GPIO信息)。
(2)gpio_request
它是框架提供的接口,给使用gpiolib来编写自己的驱动的驱动工程师用的,驱动中要想使用某一个gpio,就必须先调用gpio_request接口来向内核的gpiolib部分申请,得到允许后才可以去使用这个gpio。
(3)gpio_free
对应于gpio_request,用来释放gpio。
(4)gpio_request_one、gpio_request_array
这两个是gpio_request的变种。
(5)gpiochip_is_requested
接口用来判断某一个gpio是否已经被申请。
(6)gpio_direction_input、gpio_direction_output
接口用来设置GPIO为输入/输出模式,注意该函数内部实际并没有对硬件进行操作,只是通过chip结构体变量的函数指针,调用了(将来SoC厂商的驱动工程师写的)真正地操作硬件、实现gpio设置成输出模式的那个函数。
(7)其他接口
以上的接口,是给写其他驱动并且用到了gpiolib的人使用的。剩下的函数是gpiolib内部自己的一些功能实现的代码。
2、gpiolib的attribute部分
(1)CONFIG_GPIO_SYSFS
在内核中很多实现方式都是通过宏来配置的;在.config文件有,则必然在menuconfig中有。
(2)GPIO的attribute演示
static ssize_t chip_label_show(struct device *dev,struct device_attribute *attr, char *buf) {const struct gpio_chip *chip = dev_get_drvdata(dev);return sprintf(buf, "%s\n", chip->label ? : ""); } static DEVICE_ATTR(label, 0444, chip_label_show, NULL);
[root@xjh class]# ls backlight i2c-adapter misc regulator scsi_host //忽略部分 bdi i2c-dev mmc_host rfkill sound block ieee80211 mtd rtc spi_master firmware input net s3c_bc switch gpio lcd power_supply scsi_device timed_output graphics leds ppp scsi_disk tty hidraw mem pvr scsi_generic vc [root@xjh class]# cd gpio/ [root@xjh gpio]# ls export gpiochip164 gpiochip235 gpiochip316 gpiochip396 gpiochip56 gpiochip0 gpiochip172 gpiochip244 gpiochip325 gpiochip40 gpiochip62 gpiochip104 gpiochip181 gpiochip253 gpiochip334 gpiochip405 gpiochip71 gpiochip112 gpiochip188 gpiochip262 gpiochip343 gpiochip414 gpiochip80 gpiochip120 gpiochip197 gpiochip271 gpiochip35 gpiochip423 gpiochip89 gpiochip128 gpiochip206 gpiochip280 gpiochip351 gpiochip431 gpiochip9 gpiochip137 gpiochip212 gpiochip289 gpiochip360 gpiochip438 gpiochip96 gpiochip14 gpiochip221 gpiochip29 gpiochip369 gpiochip447 unexport gpiochip146 gpiochip226 gpiochip298 gpiochip378 gpiochip456 gpiochip155 gpiochip23 gpiochip307 gpiochip387 gpiochip47 [root@xjh gpio]# cd gpiochip0 [root@xjh gpiochip0]# ls base label ngpio power subsystem uevent [root@xjh gpiochip0]# cat base 0 [root@xjh gpiochip0]# cat label GPA0 [root@xjh gpiochip0]# cat ngpio 8 [root@xjh gpiochip0]#
(3)能够cat的相关代码分析
static int __init gpiolib_sysfs_init(void) {int status;unsigned long flags;unsigned gpio;idr_init(&pdesc_idr);status = class_register(&gpio_class);在/sys/class里定义了gpio这个类if (status < 0)return status;/* Scan and register the gpio_chips which registered very* early (e.g. before the class_register above was called).** We run before arch_initcall() so chip->dev nodes can have* registered, and so arch_initcall() can always gpio_export().*/spin_lock_irqsave(&gpio_lock, flags);for (gpio = 0; gpio < ARCH_NR_GPIOS; gpio++) {struct gpio_chip *chip;chip = gpio_desc[gpio].chip;if (!chip || chip->exported)continue;spin_unlock_irqrestore(&gpio_lock, flags);status = gpiochip_export(chip);//spin_lock_irqsave(&gpio_lock, flags);}spin_unlock_irqrestore(&gpio_lock, flags);return status; } postcore_initcall(gpiolib_sysfs_init);
static int gpiochip_export(struct gpio_chip *chip) {int status;struct device *dev;/* Many systems register gpio chips for SOC support very early,* before driver model support is available. In those cases we* export this later, in gpiolib_sysfs_init() ... here we just* verify that _some_ field of gpio_class got initialized.*/if (!gpio_class.p)return 0;/* use chip->base for the ID; it's already known to be unique */mutex_lock(&sysfs_lock);dev = device_create(&gpio_class, chip->dev, MKDEV(0, 0), chip,"gpiochip%d", chip->base);if (!IS_ERR(dev)) {status = sysfs_create_group(&dev->kobj,//用来创建许多attribute&gpiochip_attr_group);} elsestatus = PTR_ERR(dev);chip->exported = (status == 0);mutex_unlock(&sysfs_lock);if (status) {unsigned long flags;unsigned gpio;spin_lock_irqsave(&gpio_lock, flags);gpio = chip->base;while (gpio_desc[gpio].chip == chip)gpio_desc[gpio++].chip = NULL;spin_unlock_irqrestore(&gpio_lock, flags);pr_debug("%s: chip %s status %d\n", __func__,chip->label, status);}return status; }
[root@xjh ~]# cd /sys/class [root@xjh class]# cd gpio/ [root@xjh gpio]# ls export gpiochip164 gpiochip235 gpiochip316 gpiochip396 gpiochip56 gpiochip0 gpiochip172 gpiochip244 gpiochip325 gpiochip40 gpiochip62 gpiochip104 gpiochip181 gpiochip253 gpiochip334 gpiochip405 gpiochip71 gpiochip112 gpiochip188 gpiochip262 gpiochip343 gpiochip414 gpiochip80 gpiochip120 gpiochip197 gpiochip271 gpiochip35 gpiochip423 gpiochip89 gpiochip128 gpiochip206 gpiochip280 gpiochip351 gpiochip431 gpiochip9 gpiochip137 gpiochip212 gpiochip289 gpiochip360 gpiochip438 gpiochip96 gpiochip14 gpiochip221 gpiochip29 gpiochip369 gpiochip447 unexport gpiochip146 gpiochip226 gpiochip298 gpiochip378 gpiochip456 gpiochip155 gpiochip23 gpiochip307 gpiochip387 gpiochip47 [root@xjh gpio]# echo 23 >export [root@xjh gpio]# ls export gpiochip155 gpiochip23 gpiochip307 gpiochip387 gpiochip47 gpio23//多出 gpiochip164 gpiochip235 gpiochip316 gpiochip396 gpiochip56 gpiochip0 gpiochip172 gpiochip244 gpiochip325 gpiochip40 gpiochip62 gpiochip104 gpiochip181 gpiochip253 gpiochip334 gpiochip405 gpiochip71 gpiochip112 gpiochip188 gpiochip262 gpiochip343 gpiochip414 gpiochip80 gpiochip120 gpiochip197 gpiochip271 gpiochip35 gpiochip423 gpiochip89 gpiochip128 gpiochip206 gpiochip280 gpiochip351 gpiochip431 gpiochip9 gpiochip137 gpiochip212 gpiochip289 gpiochip360 gpiochip438 gpiochip96 gpiochip14 gpiochip221 gpiochip29 gpiochip369 gpiochip447 unexport gpiochip146 gpiochip226 gpiochip298 gpiochip378 gpiochip456 [root@xjh gpio]# cd gpio23 [root@xjh gpio23]# ls active_low direction edge power subsystem uevent value [root@xjh gpio23]# cat value 0 [root@xjh gpio23]# cat edge none [root@xjh gpio23]# cat direction in [root@xjh gpio23]#
五、使用gpiolib完成led驱动
1、流程分析
第1步:使用gpio_request申请要使用的一个GPIO。
第2步:使用gpio_direction_input、gpio_direction_output 来设置输入、输出模式。
第3步:使用gpio_set_value设置输出值,或者使用gpio_get_value获取IO口值。
2、代码实践
Linux中查看gpio使用情况的方法
内核中提供了虚拟文件系统debugfs,里面有一个gpio文件,提供了gpio的使用信息(比如谁被使用了谁没有被使用)。
使用方法:
(1)使用命令“mount -t debugfs debugfs /tmp”进行挂载;
(2)之后使用“cat /tmp/gpio”即可得到gpio的所有信息;
(3)使用完后“umount /tmp”卸载掉debugfs。
[root@xjh ]# cd tmp/ [root@xjh tmp]# ls [root@xjh tmp]# mount -t debugfs debugfs /tmp [root@xjh tmp]# ls [root@xjh tmp]# cd .. [root@xjh ]# ls bin etc lib mnt root sys usr dev home linuxrc proc sbin tmp var [root@xjh ]# cd tmp/ [root@xjh tmp]# ls apanic binder ieee80211 mmc2 pmstats asoc gpio mmc0 mmc3 sched_features bdi hid mmc1 pmem_gpu1 usb [root@xjh tmp]# cat gpio GPIOs 0-7, GPA0:GPIOs 9-12, GPA1:GPIOs 14-21, GPB:GPIOs 23-27, GPC0:gpio-23 (sysfs ) in loGPIOs 29-33, GPC1:GPIOs 35-38, GPD0:GPIOs 40-45, GPD1:GPIOs 47-54, GPE0:GPIOs 56-60, GPE1:GPIOs 62-69, GPF0:GPIOs 71-78, GPF1:GPIOs 80-87, GPF2:GPIOs 89-94, GPF3:GPIOs 96-102, GPG0:GPIOs 104-110, GPG1:GPIOs 112-118, GPG2:GPIOs 120-126, GPG3:GPIOs 128-135, GPH0:gpio-129 (GPH0 ) in higpio-130 (GPH0 ) in higpio-131 (GPH0 ) in hiGPIOs 137-144, GPH1:gpio-139 (GPH1 ) in lo irq-42 level-highGPIOs 146-153, GPH2:gpio-146 (GPH2 ) in higpio-147 (GPH1 ) in higpio-148 (GPH2 ) in higpio-149 (GPH2 ) in higpio-152 (GPH2 ) in logpio-153 (GPH3 ) in hiGPIOs 155-162, GPH3:GPIOs 164-170, GPI:GPIOs 172-179, GPJ0:GPIOs 181-186, GPJ1:GPIOs 188-195, GPJ2:GPIOs 197-204, GPJ3:GPIOs 206-210, GPJ4:GPIOs 212-219, MP01:GPIOs 221-224, MP02:GPIOs 226-233, MP03:GPIOs 235-242, MP04:GPIOs 244-251, MP05:GPIOs 253-260, MP06:GPIOs 262-269, MP07:GPIOs 271-278, MP10:GPIOs 280-287, MP11:GPIOs 289-296, MP12:GPIOs 298-305, MP13:GPIOs 307-314, MP14:GPIOs 316-323, MP15:GPIOs 325-332, MP16:GPIOs 334-341, MP17:GPIOs 343-349, MP18:GPIOs 351-358, MP20:GPIOs 360-367, MP21:GPIOs 369-376, MP22:GPIOs 378-385, MP23:GPIOs 387-394, MP24:GPIOs 396-403, MP25:GPIOs 405-412, MP26:GPIOs 414-421, MP27:GPIOs 423-429, MP28:GPIOs 431-436, ETC0:GPIOs 438-445, ETC1:GPIOs 447-454, ETC2:GPIOs 456-461, ETC4: [root@xjh tmp]#
代码(该驱动只申请了LED1资源)
#include <linux/module.h> // module_init module_exit #include <linux/init.h> // __init __exit #include <linux/fs.h> #include <linux/leds.h> #include <mach/regs-gpio.h> #include <mach/gpio-bank.h> #include <linux/io.h> #include <linux/ioport.h> #include <mach/gpio.h>#define GPIO_LED1 S5PV210_GPJ0(3) #define GPIO_LED2 S5PV210_GPJ0(4) #define GPIO_LED3 S5PV210_GPJ0(5)#define X210_LED_OFF 1 // X210中LED是正极接电源,负极节GPIO #define X210_LED_ON 0 // 所以1是灭,0是亮static struct led_classdev mydev1; // 定义结构体变量 static struct led_classdev mydev2; // 定义结构体变量 static struct led_classdev mydev3; // 定义结构体变量// 这个函数就是要去完成具体的硬件读写任务的 static void s5pv210_led1_set(struct led_classdev *led_cdev,enum led_brightness value) {printk(KERN_INFO "s5pv210_led1_set\n");//writel(0x11111111, GPJ0CON);// 在这里根据用户设置的值来操作硬件// 用户设置的值就是valueif (value == LED_OFF){// 用户给了个0,希望LED灭//writel(0x11111111, GPJ0CON);// 读改写三部曲//writel((readl(GPJ0DAT) | (1<<3)), GPJ0DAT);gpio_set_value(GPIO_LED1, X210_LED_OFF);}else{// 用户给的是非0,希望LED亮//writel(0x11111111, GPJ0CON);//writel((readl(GPJ0DAT) & ~(1<<3)), GPJ0DAT);gpio_set_value(GPIO_LED1, X210_LED_ON);} }static void s5pv210_led2_set(struct led_classdev *led_cdev,enum led_brightness value) {printk(KERN_INFO "s5pv2102_led_set\n");//writel(0x11111111, GPJ0CON);// 在这里根据用户设置的值来操作硬件// 用户设置的值就是valueif (value == LED_OFF){// 用户给了个0,希望LED灭//writel(0x11111111, GPJ0CON);// 读改写三部曲//writel((readl(GPJ0DAT) | (1<<4)), GPJ0DAT);}else{// 用户给的是非0,希望LED亮//writel(0x11111111, GPJ0CON);//writel((readl(GPJ0DAT) & ~(1<<4)), GPJ0DAT);} }static void s5pv210_led3_set(struct led_classdev *led_cdev,enum led_brightness value) {printk(KERN_INFO "s5pv210_led3_set\n");//writel(0x11111111, GPJ0CON);// 在这里根据用户设置的值来操作硬件// 用户设置的值就是valueif (value == LED_OFF){// 用户给了个0,希望LED灭//writel(0x11111111, GPJ0CON);// 读改写三部曲//writel((readl(GPJ0DAT) | (1<<5)), GPJ0DAT);}else{// 用户给的是非0,希望LED亮//writel(0x11111111, GPJ0CON);//writel((readl(GPJ0DAT) & ~(1<<5)), GPJ0DAT);} }static int __init s5pv210_led_init(void) {// 用户insmod安装驱动模块时会调用该函数// 该函数的主要任务就是去使用led驱动框架提供的设备注册函数来注册一个设备int ret = -1;// 在这里去申请驱动用到的各种资源,当前驱动中就是GPIO资源if (gpio_request(GPIO_LED1, "led1_gpj0.3")) //这里是申请失败{printk(KERN_ERR "gpio_request failed\n");} else //申请成功后{// 设置为输出模式,并且默认输出1让LED灯灭gpio_direction_output(GPIO_LED1, 1);}// led1mydev1.name = "led1";mydev1.brightness = 0; mydev1.brightness_set = s5pv210_led1_set;ret = led_classdev_register(NULL, &mydev1);if (ret < 0) {printk(KERN_ERR "led_classdev_register failed\n");return ret;}// led2mydev2.name = "led2";mydev2.brightness = 0; mydev2.brightness_set = s5pv210_led2_set;ret = led_classdev_register(NULL, &mydev2);if (ret < 0) {printk(KERN_ERR "led_classdev_register failed\n");return ret;}// led3mydev3.name = "led3";mydev3.brightness = 0; mydev3.brightness_set = s5pv210_led3_set;ret = led_classdev_register(NULL, &mydev3);if (ret < 0) {printk(KERN_ERR "led_classdev_register failed\n");return ret;}return 0; }static void __exit s5pv210_led_exit(void) {led_classdev_unregister(&mydev1);led_classdev_unregister(&mydev2);led_classdev_unregister(&mydev3);gpio_free(GPIO_LED1); }module_init(s5pv210_led_init); module_exit(s5pv210_led_exit);// MODULE_xxx这种宏作用是用来添加模块描述信息 MODULE_LICENSE("GPL"); // 描述模块的许可证 MODULE_AUTHOR("aston <1264671872@qq.com>"); // 描述模块的作者 MODULE_DESCRIPTION("s5pv210 led driver"); // 描述模块的介绍信息 MODULE_ALIAS("s5pv210_led"); // 描述模块的别名信息