Linux GPIO 注册和应用

Linux Kernel, GPIO, ARM

于Linux kernel代码。经常使用 GPIO 作为一个特殊的信号,如芯片片选信号。

GPIO 功能应用,我们经常使用的。例如 gpio_request ,那么 GPIO 是何时,以及怎样注冊的。本文就来探索一下。

基于的平台上 freesccale 的 i.MX6

先从函数 gpio_request 的实现開始。

/* These "optional" allocation calls help prevent drivers from stomping
* on each other, and help provide better diagnostics in debugfs.
* They're called even less than the "set direction" calls.
*/
int gpio_request(unsigned gpio, const char *label)
{
struct gpio_desc *desc;
struct gpio_chip *chip;
int status = -EINVAL;
unsigned long flags; spin_lock_irqsave(&gpio_lock, flags); if (!gpio_is_valid(gpio))
goto done;
// 这儿从 gpio_desc 数组中取了一个 gpio_desc 结构体
// 后面的代码基本上都是基于这个结构体进行的操作
// 我们是从数组中取了一个 gpio 的描写叙述,这个描写叙述应该是在 gpio 注冊的时候加入到这个数组的
// 以这个数组为线索。看看 gpio 是怎样注冊的
desc = &gpio_desc[gpio];
chip = desc->chip;
if (chip == NULL)
goto done; if (!try_module_get(chip->owner))
goto done; /* NOTE: gpio_request() can be called in early boot,
* before IRQs are enabled, for non-sleeping (SOC) GPIOs.
*/ if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) {
desc_set_label(desc, label ? : "?");
status = 0;
} else {
status = -EBUSY;
module_put(chip->owner);
goto done;
} if (chip->request) {
/* chip->request may sleep */
spin_unlock_irqrestore(&gpio_lock, flags);
status = chip->request(chip, gpio - chip->base);
spin_lock_irqsave(&gpio_lock, flags); if (status < 0) {
desc_set_label(desc, NULL);
module_put(chip->owner);
clear_bit(FLAG_REQUESTED, &desc->flags);
}
} done:
if (status)
pr_debug("gpio_request: gpio-%d (%s) status %d\n",
gpio, label ? : "?", status);
spin_unlock_irqrestore(&gpio_lock, flags);
return status;
}

以数组 gpio_desc 为线索。

既然我们申请 GPIO 的时候是从这个数字中取数据,那么注冊 GPIO 的时候就应该往这个数字中加入数据了。

反过来,往这个数组加入数据的地方应该也就是注冊 GPIO 的地方了。

这个数组定义在 Gpiolib.c 文件里:

static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];

搜索发现。 gpiochip_add 函数中有给数组 gpio_desc 赋值。

看看谁调用了函数 gpiochip_add 。

平台相关文件夹下的 Gpio.c 文件里的 mxc_gpio_init 函数调用了 gpiochip_add :

		if (!initialed)
/* its a serious configuration bug when it fails */
BUG_ON(gpiochip_add(&port[i].chip) < 0);

继续往上找,平台相关文件夹下的 Devices.c 有例如以下函数:

int mx6q_register_gpios(void)
{
/* 7 ports for Mx6 */
return mxc_gpio_init(mxc_gpio_ports, 7);
}

mxc_gpio_ports 的定义:

static struct mxc_gpio_port mxc_gpio_ports[] = {
{
.chip.label = "gpio-0",
.base = IO_ADDRESS(GPIO1_BASE_ADDR),
.irq = MXC_INT_GPIO1_INT15_0_NUM,
.irq_high = MXC_INT_GPIO1_INT31_16_NUM,
.virtual_irq_start = MXC_GPIO_IRQ_START
},
{
.chip.label = "gpio-1",
.base = IO_ADDRESS(GPIO2_BASE_ADDR),
.irq = MXC_INT_GPIO2_INT15_0_NUM,
.irq_high = MXC_INT_GPIO2_INT31_16_NUM,
.virtual_irq_start = MXC_GPIO_IRQ_START + 32 * 1
},
{
.chip.label = "gpio-2",
.base = IO_ADDRESS(GPIO3_BASE_ADDR),
.irq = MXC_INT_GPIO3_INT15_0_NUM,
.irq_high = MXC_INT_GPIO3_INT31_16_NUM,
.virtual_irq_start = MXC_GPIO_IRQ_START + 32 * 2
},
{
.chip.label = "gpio-3",
.base = IO_ADDRESS(GPIO4_BASE_ADDR),
.irq = MXC_INT_GPIO4_INT15_0_NUM,
.irq_high = MXC_INT_GPIO4_INT31_16_NUM,
.virtual_irq_start = MXC_GPIO_IRQ_START + 32 * 3
},
{
.chip.label = "gpio-4",
.base = IO_ADDRESS(GPIO5_BASE_ADDR),
.irq = MXC_INT_GPIO5_INT15_0_NUM,
.irq_high = MXC_INT_GPIO5_INT31_16_NUM,
.virtual_irq_start = MXC_GPIO_IRQ_START + 32 * 4
},
{
.chip.label = "gpio-5",
.base = IO_ADDRESS(GPIO6_BASE_ADDR),
.irq = MXC_INT_GPIO6_INT15_0_NUM,
.irq_high = MXC_INT_GPIO6_INT31_16_NUM,
.virtual_irq_start = MXC_GPIO_IRQ_START + 32 * 5
},
{
.chip.label = "gpio-6",
.base = IO_ADDRESS(GPIO7_BASE_ADDR),
.irq = MXC_INT_GPIO7_INT15_0_NUM,
.irq_high = MXC_INT_GPIO7_INT31_16_NUM,
.virtual_irq_start = MXC_GPIO_IRQ_START + 32 * 6
},
};

继续往上,找到了同文件夹下 Irq.c 文件里的 mx6_init_irq 函数调用了 mx6q_register_gpios 。

board.c 文件里将 mx6_init_irq 函数赋值给了 machine_desc 结构体的 init_irq 函数:

MACHINE_START(MX6XXXX, "Freescale i.MX 6 Board")
.boot_params = MX6_PHYS_OFFSET + 0x100,
.fixup = fixup_mxc_board,
.map_io = mx6_map_io,
.init_irq = mx6_init_irq,
.init_machine = mx6_board_init,
.timer = &mxc_timer,
.reserve = mx6q_reserve,
MACHINE_END

arch/arm/kernel/irq.c 文件里有下面函数:

void __init init_IRQ(void)
{
machine_desc->init_irq();
}

init/main.c 文件里的 start_kernel 函数调用了 init_IRQ 。

至于 start_kernel 函数何时被调用。有时间再作研究。

总结一下 GPIO 的注冊过程:

start_kernel 函数会调用 init_IRQ 函数。

init_IRQ 函数调用了 machine_desc 结构体的 init_irq 函数。

machine_desc 结构体在 board.c 文件里定义,当中 init_irq 被赋值为 mx6_init_irq 。

mx6_init_irq 函数中调用了 mx6q_register_gpios 函数。

mx6q_register_gpios 函数的定义见前文,当中调用了函数 mxc_gpio_init 。

函数 mxc_gpio_init 的实现:

int mxc_gpio_init(struct mxc_gpio_port *port, int cnt)
{
int i, j;
static bool initialed; /* save for local usage */
// port 是前面定义的数组 mxc_gpio_ports , cnt 是数组中元素的个数
mxc_gpio_ports = port;
gpio_table_size = cnt; printk(KERN_INFO "MXC GPIO hardware\n"); for (i = 0; i < cnt; i++) {
/* disable the interrupt and clear the status */
__raw_writel(0, port[i].base + GPIO_IMR);
__raw_writel(~0, port[i].base + GPIO_ISR);
for (j = port[i].virtual_irq_start;
j < port[i].virtual_irq_start + 32; j++) {
irq_set_lockdep_class(j, &gpio_lock_class);
/*
static struct irq_chip gpio_irq_chip = {
.name = "GPIO",
.irq_ack = gpio_ack_irq,
.irq_mask = gpio_mask_irq,
.irq_unmask = gpio_unmask_irq,
.irq_set_type = gpio_set_irq_type,
.irq_set_wake = gpio_set_wake_irq,
};
*/
/**
* handle_level_irq - Level type irq handler
* @irq: the interrupt number
* @desc: the interrupt description structure for this irq
*
* Level type interrupts are active as long as the hardware line has
* the active level. This may require to mask the interrupt and unmask
* it after the associated handler has acknowledged the device, so the
* interrupt line is back to inactive.
*/
irq_set_chip_and_handler(j, &gpio_irq_chip,
handle_level_irq);
set_irq_flags(j, IRQF_VALID);
} /* register gpio chip */
// mxc_gpio_direction_input 将相应 gpio 设置为输入。 mxc_gpio_direction_output 将相应 gpio 设置为输出。并会设置一个初始值
// 这儿的输入/输出是对 cpu 来说的
port[i].chip.direction_input = mxc_gpio_direction_input;
port[i].chip.direction_output = mxc_gpio_direction_output;
// 获取/设置 gpio 状态
port[i].chip.get = mxc_gpio_get;
port[i].chip.set = mxc_gpio_set;
port[i].chip.base = i * 32;
port[i].chip.ngpio = 32; spin_lock_init(&port[i].lock); if (!initialed)
/* its a serious configuration bug when it fails */
// 加入 gpio chip , 调用的是我们前面用到的一个线索函数, 该函数中有给 gpio_desc 数组赋值
BUG_ON(gpiochip_add(&port[i].chip) < 0); if (cpu_is_mx1() || cpu_is_mx3() || cpu_is_mx25() ||
cpu_is_mx51() || cpu_is_mx53() || cpu_is_mx6q() ||
cpu_is_mx6dl() || cpu_is_mx6sl()) {
/* setup one handler for each entry */
irq_set_chained_handler(port[i].irq,
mx3_gpio_irq_handler);
irq_set_handler_data(port[i].irq, &port[i]);
if (port[i].irq_high) {
/* setup handler for GPIO 16 to 31 */
irq_set_chained_handler(port[i].irq_high,
mx3_gpio_irq_handler);
irq_set_handler_data(port[i].irq_high,
&port[i]);
}
}
}
initialed = true;
if (cpu_is_mx2()) {
/* setup one handler for all GPIO interrupts */
irq_set_chained_handler(port[0].irq, mx2_gpio_irq_handler);
irq_set_handler_data(port[0].irq, port);
} return 0;
}

gpiochip_add 函数的实现:

/**
* gpiochip_add() - register a gpio_chip
* @chip: the chip to register, with chip->base initialized
* Context: potentially before irqs or kmalloc will work
*
* Returns a negative errno if the chip can't be registered, such as
* because the chip->base is invalid or already associated with a
* different chip. Otherwise it returns zero as a success code.
*
* When gpiochip_add() is called very early during boot, so that GPIOs
* can be freely used, the chip->dev device must be registered before
* the gpio framework's arch_initcall(). Otherwise sysfs initialization
* for GPIOs will fail rudely.
*
* If chip->base is negative, this requests dynamic assignment of
* a range of valid GPIOs.
*/
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 成员的 chip 成员
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;
}
} of_gpiochip_add(chip); unlock:
spin_unlock_irqrestore(&gpio_lock, flags); if (status)
goto fail; // 创建设备。 并加入相应的 sysfs
status = gpiochip_export(chip);
if (status)
goto fail; return 0;
fail:
/* failures here can mean systems won't boot... */
pr_err("gpiochip_add: gpios %d..%d (%s) failed to register\n",
chip->base, chip->base + chip->ngpio - 1,
chip->label ? : "generic");
return status;
}

gpio_desc 结构体的定义:

struct gpio_desc {
struct gpio_chip *chip;
unsigned long flags;
/* flag symbols are bit numbers */
#define FLAG_REQUESTED 0
#define FLAG_IS_OUT 1
#define FLAG_RESERVED 2
#define FLAG_EXPORT 3 /* protected by sysfs_lock */
#define FLAG_SYSFS 4 /* exported via /sys/class/gpio/control */
#define FLAG_TRIG_FALL 5 /* trigger on falling edge */
#define FLAG_TRIG_RISE 6 /* trigger on rising edge */
#define FLAG_ACTIVE_LOW 7 /* sysfs value has active low */ #define ID_SHIFT 16 /* add new flags before this one */ #define GPIO_FLAGS_MASK ((1 << ID_SHIFT) - 1)
#define GPIO_TRIGGER_MASK (BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE)) #ifdef CONFIG_DEBUG_FS
const char *label;
#endif
};

版权声明:本文博主原创文章,博客,未经同意不得转载。

Linux GPIO 注册和应用的更多相关文章

  1. Linux GPIO键盘驱动开发记录_OMAPL138

    Linux GPIO键盘驱动开发记录_OMAPL138 Linux基本配置完毕了,这几天开始着手Linux驱动的开发,从一个最简单的键盘驱动开始,逐步的了解开发驱动的过程有哪些.看了一下Linux3. ...

  2. R0-R37它是Arm 寄存器,那是,CPU内部。和GPIO注册所有外设。换句话说,要是arm的cpu,它包含了其他芯片公司将有R0-R37,和GPIO寄存器只有一个特定的芯片。

    R0-R37它是Arm 寄存器.那是,CPU内部.和GPIO注册所有外设. 换句话说,要是arm的cpu,它包含了其他芯片公司将有R0-R37,和GPIO有. 版权声明:本文博主原创文章.博客,未经同 ...

  3. Linux GPIO控制方法

    Linux GPIO控制方法 kernel version 4.4.12 在文件系统层: 1. 进入 /sys/class/gpio/ 目录 2. 假设你想控制的GPIO0_29,步骤如下: 1. e ...

  4. [gpio]Linux GPIO简单使用方式1-sysfs

    转自:http://blog.csdn.net/drivermonkey/article/details/20132241 1.1.References 1.2.GPIO Usage from a L ...

  5. RedHat Linux下注册Apache为系统服务并设为开机启动

    1.系统环境: 操作系统:Red Hat Enterprise Linux Server release 5.4 Apache版本:httpd-2.2.19 2.注册服务 #将apachectl复制到 ...

  6. 【linux】 linux gpio操作

    欢迎转载,转载时需保留作者信息,谢谢. 邮箱:tangzhongp@163.com 博客园地址:http://www.cnblogs.com/embedded-tzp Csdn博客地址:http:// ...

  7. Davinci DM6446开发攻略——LINUX GPIO驱动源码移植

    一.             DM6446 GPIO的介绍      说到LINUX 驱动移植,没有移植过的朋友,或刚刚进入LINUX领域的朋友,最好去看看<LINUX 设备驱动程序>第三 ...

  8. linux中注册系统服务—service命令的原理通俗

    能够使用service命令进行操作的,就是已经注册成为linux的系统服务了.window中也可以注册成为系统服务的办法. service命令用的次数真不少,就是比较多的关联点,用了很多次了,还是有些 ...

  9. Linux 系统服务注册

    Linux注册系统服务步骤 1.编写服务脚本 2.拷贝到/etc/init.d目录下 3.为服务脚本添加可执行权限   >>chmod a+x xxxd 4.添加到系统服务中        ...

随机推荐

  1. OpenCV原则解读HAAR+Adaboost

    因为人脸检测项目.用途OpenCV在旧分类中的训练效果.因此该检测方法中所使用的分类归纳.加上自己的一些理解.重印一些好文章记录. 文章http://www.61ic.com/Article/DaVi ...

  2. win8.1 64位环境建设android开发环境

    1.下载JDK,http://www.oracle.com/technetwork/java/javase/downloads/index.html,选择版本号 2.安装刚刚下载的JDK 3.环境变量 ...

  3. php编码规范个人小结

    1.原生态代码或者类的头上,注明作者,关键算法计算过程 例如 /** *@author zengmoumou *功能:根据列表ip,取得ip对应的运营商,省,市,县 */ 2.变量尽量用英文单词的组合 ...

  4. FTP文件操作之下载文件

    前面写了采用ftp上传文件,有了上传怎么能够没有下载呢?如果只有上传没有下载,那上传了也没啥用了.所以今天就跟大家一起学习学习使用ftp下载文件. 知道了怎么上传,那么下载也就变得很简单了,上传是把文 ...

  5. Directx11学习笔记【十六】 光照模型的实现

    本文由zhangbaochong原创,转载请注明出处http://www.cnblogs.com/zhangbaochong/p/5579289.html 在之前的场景绘制中我们都是给每个顶点指定了单 ...

  6. Extjs Web Desktop申请书

    今天我Web Desktop应用基本完成.多语言支持.现有asp,php,jsp版本号. 废话拍了几张照片让大家有一个直观的了解: watermark/2/text/aHR0cDovL2Jsb2cuY ...

  7. 一个vbs文件将指定文件夹下的文件名输出到指定文件夹下

    'on error resume NextConst MY_COMPUTER=&H11& Const WINDOW_HANDLE=0 Const OPTIONS=0 '设置我的电脑为根 ...

  8. 开源Math.NET基础数学类库使用(02)矩阵向量计算

    原文:[原创]开源Math.NET基础数学类库使用(02)矩阵向量计算 开源Math.NET基础数学类库使用系列文章总目录:   1.开源.NET基础数学计算组件Math.NET(一)综合介绍    ...

  9. 初学者cocos2dx 写2048 为了和大家一起分享

    第一个是在头文件 部分的代码是学习不变  大多数写自己. class HelloWorld : public cocos2d::CCLayer { public: virtual bool init( ...

  10. javascript 比量str今天的日期是,参数diff

    function isToday(str) { var d = new Date(str.replace(/-/g, "/")); var todaysDate = new Dat ...