Linux GPIO子系统分析

在设备驱动中对GPIO的操作是非常普遍的,linux内核为我们提供了GPIO子系统,方便用户使用,它为用户提供了GPIO的统一操作接口,用户不需要关心底层实现,因为这是芯片厂商需要关心的,芯片厂商会去做这一部分工作。

接着我们就从驱动入手来看一下gpio子系统的框架。

gpio_direction_output(gmac->resetgpio, 1); 
mdelay(30);
gpio_direction_output(gmac->resetgpio, 0);
mdelay(30);
gpio_direction_output(gmac->resetgpio, 1);
mdelay(40);

 上面几行代码就是从驱动中摘出来的,很简单,就是写指定gpio的值,并设定为输出模式。

gpio_direction_output是内核提供给我们的统一接口,方便我们去操作gpio,那么他是怎么操作到最终的芯片寄存器呢?

带着这个问题我们进入gpio_direction_output函数看一下。

//driver/gpio/gpiolib.c
int gpio_direction_output(unsigned gpio, int value)
{
	return gpiod_direction_output(gpio_to_desc(gpio), value);
}
EXPORT_SYMBOL_GPL(gpio_direction_output);

他直接调用了gpiod_direction_output;

static int gpiod_direction_output(struct gpio_desc *desc, int value)
{
	unsigned long		flags;
	struct gpio_chip	*chip;
	int			status = -EINVAL;
	int offset;

        ....  //前面做了一些各种检查相关的工作,这不是重点
	chip = desc->chip;
	if (!chip || !chip->set || !chip->direction_output)
		goto fail;
	status = gpio_ensure_requested(desc);
	if (status < 0)
		goto fail;

	might_sleep_if(chip->can_sleep);

	offset = gpio_chip_hwgpio(desc);
	if (status) {
		status = chip->request(chip, offset);
		if (status < 0) {
			pr_debug("GPIO-%d: chip request fail, %d\n",
				desc_to_gpio(desc), status);
			/* and it's not available to anyone else ...
			 * gpio_request() is the fully clean solution.
			 */
			goto lose;
		}
	}

	status = chip->direction_output(chip, offset, value); //重点在这个回调函数,应该是调用的这个函数去设置的CPU的寄存器,最终设置GPIO的。
	if (status == 0)
		set_bit(FLAG_IS_OUT, &desc->flags);
	trace_gpio_value(desc_to_gpio(desc), 0, value);
	trace_gpio_direction(desc_to_gpio(desc), 0, status);
}

我们猜测应该是调用的chip->direction_output(chip, offset, value);这个回调函数最终设置的寄存器,那么这接下来应该就是跟芯片紧密联系的,这部分工作应该是芯片厂商去完成的。

gpio子系统在kernel/drivers/gpio/目录下面。这里有跟平台相关的文件,这就是厂商提供的。

songchong@srv-artek-pad:~/GS700E/android/kernel/drivers/gpio$ ls
devres.c               gpio-bt8xx.c     gpio-generic.c   gpiolib-of.c       gpio-mcp23s08.c   gpio-mxc.c      gpio-pxa.c          gpio-sta2x11.c     gpio-tps65910.c    gpio-wm831x.c
gpio-74x164.c          gpio-clps711x.c  gpio-grgpio.c    gpio-lpc32xx.c     gpio-ml-ioh.c     gpio-mxs.c      gpio-rc5t583.c      gpio-stmpe.c       gpio-tps65912.c    gpio-wm8350.c
gpio-adnp.c            gpio-cs5535.c    gpio-ich.c       gpio-lynxpoint.c   gpio-mm-lantiq.c  gpio-omap.c     gpio-rcar.c         gpio-stp-xway.c    gpio-ts5500.c      gpio-wm8994.c
gpio-adp5520.c         gpio-da9052.c    gpio-it8761e.c   gpio-max7300.c     gpio-mpc5200.c    gpio-owl.c      gpio-rdc321x.c      gpio-sx150x.c      gpio-twl4030.c     gpio-xilinx.c
gpio-adp5588.c         gpio-da9055.c    gpio-janz-ttl.c  gpio-max7301.c     gpio-mpc8xxx.c    gpio-palmas.c   gpio-sa1100.c       gpio-tc3589x.c     gpio-twl6040.c     Kconfig
gpio-amd8111.c         gpio-davinci.c   gpio-ks8695.c    gpio-max730x.c     gpio-msic.c       gpio-pca953x.c  gpio-samsung.c      gpio-tegra.c       gpio-ucb1400.c     Makefile
gpio-arizona.c         gpio-em.c        gpio-langwell.c  gpio-max732x.c     gpio-msm-v1.c     gpio-pcf857x.c  gpio-sch.c          gpio-timberdale.c  gpio-viperboard.c
gpio-atc2603c-sgpio.c  gpio-ep93xx.c    gpiolib-acpi.c   gpio-mc33880.c     gpio-msm-v2.c     gpio-pch.c      gpio-sodaville.c    gpio-tnetv107x.c   gpio-vr41xx.c
gpio-atc260x.c         gpio-ge.c        gpiolib.c        gpio-mc9s08dz60.c  gpio-mvebu.c      gpio-pl061.c    gpio-spear-spics.c  gpio-tps6586x.c    gpio-vx855.c

各种各样的厂商的都有的。我们看看炬芯的gpio-owl.c文件,直接去看probe函数。

//省略不必要的
static int owl_gpio_probe(struct platform_device *pdev)
{
	const struct of_device_id *of_id =
			of_match_device(owl_gpio_dt_ids, &pdev->dev);
	struct device_node *np = pdev->dev.of_node;
	struct device *dev = &pdev->dev;
	const struct owl_gpio_bank_data *pdata;
	struct owl_gpio_bank *bank;
	int ret;

	bank = devm_kzalloc(dev, sizeof(*bank), GFP_KERNEL); //分配一个bank结构体
	if (bank == NULL)
		return -ENOMEM;

	bank->id = of_alias_get_id(np, "gpio");
	if (bank->id < 0)
		return bank->id;
	bank->devid = (enum owl_gpio_id) of_id->data;

	pr_info("GPIO%c initialization\n", 'A' + bank->id);

	if (is_s900_gpio(bank)) {
		BUG_ON(bank->id >= ARRAY_SIZE(s900_banks_data));
		pdata = &s900_banks_data[bank->id];
	} else if (is_s700_gpio(bank)) {
		BUG_ON(bank->id >= ARRAY_SIZE(s700_banks_data));
		pdata = &s700_banks_data[bank->id];
	}  else if (is_ats3605_gpio(bank)) {
		BUG_ON(bank->id >= ARRAY_SIZE(ats3605_banks_data));
		pdata = &ats3605_banks_data[bank->id];
	}else {
		return -ENOENT;
	}

	bank->base = of_iomap(dev->of_node, 0);
	if (IS_ERR(bank->base))
		return -ENOENT;

	bank->irq = irq_of_parse_and_map(np, 0);
	if (bank->irq < 0) {
		dev_err(dev, "failed to get IRQ");
		return -ENOENT;
	}

	bank->nr_gpios = pdata->nr_gpios;
	bank->regs = &pdata->regs;
	bank->pdev = pdev;
	spin_lock_init(&bank->lock);

	owl_gpio_bank_setup(bank);   //设置各种bank回调函数
	platform_set_drvdata(pdev, bank);

	ret = owl_gpio_irq_setup(bank);
	if (ret < 0) {
		dev_err(dev, "failed to setup irq, ret %d\n", ret);
		return ret;
	}

	ret = gpiochip_add(&bank->gpio_chip);  //将设置好的bank结构注册进gpio子系统中去,注册进去之后驱动中就能使用封装好的接口了。
	if (ret < 0) {
		dev_err(dev, "failed to add GPIO chip, ret %d\n", ret);
		return ret;
	}

	return 0;
}

上面最重要的两个步骤就是设置回调函数,和将bank结构注册进gpio子系统。

//各种回调函数都是由厂商提供的,这里我们就不细看了,这跟芯片平台紧密相关。
static int owl_gpio_bank_setup(struct owl_gpio_bank *bank)
{
	struct gpio_chip *chip = &bank->gpio_chip;

	chip->base = bank->id * GPIO_PER_BANK;
	chip->ngpio = bank->nr_gpios;
	chip->request = owl_gpio_request;
	chip->free = owl_gpio_free;
	chip->get = owl_gpio_get;
	chip->set = owl_gpio_set;
	chip->direction_input = owl_gpio_direction_input;
	chip->direction_output = owl_gpio_direction_output;
	chip->to_irq = owl_gpio_to_irq;
	chip->owner = THIS_MODULE;
	chip->dev = &bank->pdev->dev;
	chip->label = dev_name(chip->dev);
	chip->of_node = chip->dev->of_node;
	chip->owner = THIS_MODULE;

	return 0;
}

接着看一下是怎么注册到gpio子系统的。

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;
	}

	status = gpiochip_add_to_list(chip);

	if (status == 0) {
		chip->desc = &gpio_desc[chip->base];

		for (id = 0; id < chip->ngpio; id++) {
			struct gpio_desc *desc = &chip->desc[id];
			desc->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,
			 * and in case chip->get_direction is not set,
			 * we may expose the wrong direction in sysfs.
			 */
			desc->flags = !chip->direction_input
				? (1 << FLAG_IS_OUT)
				: 0;
		}
	}

	spin_unlock_irqrestore(&gpio_lock, flags);

#ifdef CONFIG_PINCTRL
	INIT_LIST_HEAD(&chip->pin_ranges);
#endif

	of_gpiochip_add(chip);

	if (status)
		goto fail;

	status = gpiochip_export(chip);
	if (status)
		goto fail;

	pr_debug("gpiochip_add: registered GPIOs %d to %d on device: %s\n",
		chip->base, chip->base + chip->ngpio - 1,
		chip->label ? : "generic");

	return 0;

unlock:
	spin_unlock_irqrestore(&gpio_lock, flags);
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;
}
EXPORT_SYMBOL_GPL(gpiochip_add);
内核设计真是很美!




  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Linux GPIO系统是一个用于控制嵌入式系统中通用输入/输出(GPIO)的软件系统。它提供了一种标准的接口,使得应用程序可以通过文件系统接口来访问GPIO。这个系统可以用于控制各种设备,例如LED、按钮、传感器等等。如果你需要更多的信息,可以查看Linux内核文档。 ### 回答2: Linux GPIO系统是一种用于管理通用输入输出(GPIO)引脚的软件层。GPIO引脚是一种通用可编程引脚,可以在嵌入式系统中用来通过读取输入或设置输出与外部设备进行通信。 Linux GPIO系统负责将底层硬件 GPIO 引脚的操作抽象为文件系统的接口,使开发者可以通过读写文件的方式来访问和控制 GPIO 引脚。通过该系统,可以实现对 GPIO 引脚的配置、读取和写入等操作,以满足不同应用下对 GPIO 的需求。 Linux GPIO系统的核心是GPIO驱动程序,它与底层硬件层进行交互,完成对GPIO引脚的操作。驱动程序将GPIO引脚映射到内存,通过读写该内存地址即可对引脚进行操作。用户通过访问特定目录下的文件来和引脚进行交互,例如将引脚配置为输入模式、输出模式,以及读取或写入引脚的状态。 通过Linux GPIO系统,开发者可以方便地进行GPIO引脚的控制。可以根据不同的应用需求,灵活配置引脚的输入输出模式,监听引脚上的状态变化,并根据需要对其他外设进行控制。 总之,Linux GPIO系统为开发者提供了便捷的接口,使得在嵌入式系统中使用GPIO引脚更加简单和灵活。它允许开发者通过读写文件的方式访问和控制GPIO引脚,满足各种不同嵌入式应用对GPIO的需求。 ### 回答3: LinuxGPIO(General Purpose Input/Output)系统是通过软件对硬件上的通用输入/输出引脚进行控制的一种机制。它使得开发者可以利用这些GPIO引脚实现各种功能,比如控制LED灯、读取外部传感器的数据等。 LinuxGPIO系统提供了许多功能和接口来管理和操作GPIO。首先,它使用sysfs文件系统来组织GPIO资源的目录树,并通过文件的方式来读取和写入GPIO的状态。在/sys/class/gpio目录下,每个GPIO引脚都会有一个对应的目录,在该目录中的文件可以用于配置GPIO的方向(输入或输出)、读取和写入GPIO的电平状态。开发者可以使用命令行工具或者编程语言(如Python、C等)来操作这些文件,从而控制GPIO引脚的行为。 其次,LinuxGPIO系统还提供了设备树(Device Tree)来描述硬件平台上的GPIO资源。设备树是一种描述硬件的数据结构,在启动时通过设备树绑定机制将设备树中定义的GPIO资源与内核驱动程序关联起来。这样,开发者就可以通过调用相应的驱动程序来控制GPIO引脚,而不需要手动操作sysfs文件系统。 此外,LinuxGPIO系统还支持中断机制,可以让GPIO引脚在特定事件发生时触发中断。通过注册中断处理函数,开发者可以实现对GPIO输入信号的快速响应,提高系统的实时性。 总之,LinuxGPIO系统为开发者提供了一种方便且灵活的方式来控制硬件上的GPIO引脚。通过sysfs文件系统或设备树,开发者可以轻松地配置、读取和控制GPIO的状态,从而实现各种功能和应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值