概述gpio子系统工作原理

        上篇pinctrl子系统我们说到:pinctrl是设置引脚的复用功能和电气特性,那么我们在驱动中使用gpio_request和gpio_direction_output,gpio_direction_input,gpio_set_value,gpio_get_value,gpio_free等API来使用gpio;这些api是怎么工作的呢,是怎么跟pinctrl子系统配合的呢?我们从gpio控制器驱动开始看最重要的两部分:gpio_chip的成员函数以及gpiochip_add_data。源码文件(pinctrl-rockchip.c/pinctrl-msm.c)

static int rockchip_gpiolib_register(struct platform_device *pdev,
						struct rockchip_pinctrl *info)
{
	struct rockchip_pin_ctrl *ctrl = info->ctrl;
	struct rockchip_pin_bank *bank = ctrl->pin_banks;
	struct gpio_chip *gc;
	int ret;
	int i;

	for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
		if (!bank->valid) {
			dev_warn(&pdev->dev, "bank %s is not valid\n",
				 bank->name);
			continue;
		}

		bank->gpio_chip = rockchip_gpiolib_chip;

		gc = &bank->gpio_chip;
		gc->base = bank->pin_base;
		gc->ngpio = bank->nr_pins;
		gc->parent = &pdev->dev;
		gc->of_node = bank->of_node;
		gc->label = bank->name;

		ret = gpiochip_add_data(gc, bank);
		if (ret) {
			dev_err(&pdev->dev, "failed to register gpio_chip %s, error code: %d\n",
							gc->label, ret);
			goto fail;
		}
	}

	rockchip_interrupts_register(pdev, info);

	return 0;

fail:
	for (--i, --bank; i >= 0; --i, --bank) {
		if (!bank->valid)
			continue;
		gpiochip_remove(&bank->gpio_chip);
	}
	return ret;
}

gpio_chip的成员函数

上面这个函数就涵盖了gpio子系统的关键信息;首先是rockchip_gpiolib_chip,这个gpio_chip实例的成员跟上面提到的驱动里面常用的几个api能够一一对上,除了.set和.get,get_direction是直接设置和读取寄存器,其他几个都是调用的pinctrl的接口(上篇pinctrl子系统里注册驱动时的那三个ops里面的各个实例)

static const struct gpio_chip rockchip_gpiolib_chip = {
	.request = gpiochip_generic_request,
	.free = gpiochip_generic_free,
	.set = rockchip_gpio_set,
	.get = rockchip_gpio_get,
	.get_direction	= rockchip_gpio_get_direction,
	.direction_input = rockchip_gpio_direction_input,
	.direction_output = rockchip_gpio_direction_output,
	.to_irq = rockchip_gpio_to_irq,
	.owner = THIS_MODULE,
};

gpio_request

我们先看一个gpio_request的简化代码,最终调用的request就是gpio_chip的request,也即gpiochip_generic_request

gpiod_request
    __gpiod_request
        chip->request(chip, gpio_chip_hwgpio(desc));
            

那么gpiochip_generic_request调用的pinctrl_request_gpio的具体实现如下:通过range来得到gpio对应的pin,然后通过pinmux_request_gpio来申请一个gpio

int pinctrl_request_gpio(unsigned gpio)
{
	struct pinctrl_dev *pctldev;
	struct pinctrl_gpio_range *range;
	int ret;
	int pin;

	ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range);
	if (ret) {
		if (pinctrl_ready_for_gpio_range(gpio))
			ret = 0;
		return ret;
	}

	mutex_lock(&pctldev->mutex);

	/* Convert to the pin controllers number space */
	pin = gpio_to_pin(range, gpio);

	ret = pinmux_request_gpio(pctldev, range, pin, gpio);

	mutex_unlock(&pctldev->mutex);

	return ret;
}

 pinmux_request_gpio调用的pin_request会先判断pin是否被占用,然后通过pinctrl驱动注册的pinmux_ops的gpio_request_enable或者request成员来将pin申请为gpio功能

static int pin_request(struct pinctrl_dev *pctldev,
		       int pin, const char *owner,
		       struct pinctrl_gpio_range *gpio_range)
{
	struct pin_desc *desc;
	const struct pinmux_ops *ops = pctldev->desc->pmxops;
	int status = -EINVAL;

	desc = pin_desc_get(pctldev, pin);
	if (desc == NULL) {
		dev_err(pctldev->dev,
			"pin %d is not registered so it cannot be requested\n",
			pin);
		goto out;
	}

	dev_dbg(pctldev->dev, "request pin %d (%s) for %s\n",
		pin, desc->name, owner);

	if (gpio_range) {
		/* There's no need to support multiple GPIO requests */
		if (desc->gpio_owner) {
			dev_err(pctldev->dev,
				"pin %s already requested by %s; cannot claim for %s\n",
				desc->name, desc->gpio_owner, owner);
			goto out;
		}
		if (ops->strict && desc->mux_usecount &&
		    strcmp(desc->mux_owner, owner)) {
			dev_err(pctldev->dev,
				"pin %s already requested by %s; cannot claim for %s\n",
				desc->name, desc->mux_owner, owner);
			goto out;
		}

		desc->gpio_owner = owner;
	} else {
		if (desc->mux_usecount && strcmp(desc->mux_owner, owner)) {
			dev_err(pctldev->dev,
				"pin %s already requested by %s; cannot claim for %s\n",
				desc->name, desc->mux_owner, owner);
			goto out;
		}
		if (ops->strict && desc->gpio_owner) {
			dev_err(pctldev->dev,
				"pin %s already requested by %s; cannot claim for %s\n",
				desc->name, desc->gpio_owner, owner);
			goto out;
		}

		desc->mux_usecount++;
		if (desc->mux_usecount > 1)
			return 0;

		desc->mux_owner = owner;
	}

	/* Let each pin increase references to this module */
	if (!try_module_get(pctldev->owner)) {
		dev_err(pctldev->dev,
			"could not increase module refcount for pin %d\n",
			pin);
		status = -EINVAL;
		goto out_free_pin;
	}

	/*
	 * If there is no kind of request function for the pin we just assume
	 * we got it by default and proceed.
	 */
	if (gpio_range && ops->gpio_request_enable)
		/* This requests and enables a single GPIO pin */
		status = ops->gpio_request_enable(pctldev, gpio_range, pin);
	else if (ops->request)
		status = ops->request(pctldev, pin);
	else
		status = 0;

	if (status) {
		dev_err(pctldev->dev, "request() failed for pin %d\n", pin);
		module_put(pctldev->owner);
	}

out_free_pin:
	if (status) {
		if (gpio_range) {
			desc->gpio_owner = NULL;
		} else {
			desc->mux_usecount--;
			if (!desc->mux_usecount)
				desc->mux_owner = NULL;
		}
	}
out:
	if (status)
		dev_err(pctldev->dev, "pin-%d (%s) status %d\n",
			pin, owner, status);

	return status;
}

gpio_direction_output/input

但是不同的平台这两个函数不一定被半导体厂商实现了,那么肯定会在其他地方来做这件事(复用为gpio功能),我们知道gpio_request之后,下一步就该用gpio_direction_output/input来设置方向了;那我们去看看gpio_chip的 direction_output成员究竟做没做

rockchip_gpio_direction_output
    rockchip_gpio_set(gc, offset, value);
	pinctrl_gpio_direction_output(gc->base + offset);
        pinctrl_gpio_direction
            pinctrl_gpio_direction
                ops->gpio_set_direction(pctldev, range, pin, input);
                    rockchip_pmx_gpio_set_direction
                        

 最终在下面这里设置了复用gpio功能(不同平台又有区别,有的平台的寄存器的某个位都是复用为gpio的,有的并不是,所以不同平台会做不同处理,暂不展开),并通过写对应寄存器来设置方向

static int _rockchip_pmx_gpio_set_direction(struct gpio_chip *chip,
					    int pin, bool input)
{
	struct rockchip_pin_bank *bank;
	int ret;
	unsigned long flags;
	u32 data;

	bank = gpiochip_get_data(chip);

	ret = rockchip_set_mux(bank, pin, RK_FUNC_GPIO);
	if (ret < 0)
		return ret;

	clk_enable(bank->clk);
	raw_spin_lock_irqsave(&bank->slock, flags);

	data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR);
	/* set bit to 1 for output, 0 for input */
	if (!input)
		data |= BIT(pin);
	else
		data &= ~BIT(pin);
	writel_relaxed(data, bank->reg_base + GPIO_SWPORT_DDR);

	raw_spin_unlock_irqrestore(&bank->slock, flags);
	clk_disable(bank->clk);

	return 0;
}

gpio_set_value

上面的配置都好了,设置值这个就好说了,就是单纯的gpio_chip.set=rockchip_gpio_set来设置数据寄存器,输出高低电平

static void rockchip_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
{
	struct rockchip_pin_bank *bank = gpiochip_get_data(gc);
	void __iomem *reg = bank->reg_base + GPIO_SWPORT_DR;
	unsigned long flags;
	u32 data;

	clk_enable(bank->clk);
	raw_spin_lock_irqsave(&bank->slock, flags);

	data = readl(reg);
	data &= ~BIT(offset);
	if (value)
		data |= BIT(offset);
	writel(data, reg);

	raw_spin_unlock_irqrestore(&bank->slock, flags);
	clk_disable(bank->clk);
}

gpio_add_data

先列出部分重要代码,再在小章节依次分析

int gpiochip_add_data(struct gpio_chip *chip, void *data)
{
.................
	if (base < 0) {
		base = gpiochip_find_base(chip->ngpio);
		if (base < 0) {
			status = base;
			spin_unlock_irqrestore(&gpio_lock, flags);
			goto err_free_descs;
		}
		chip->base = base;
	}
.................
	for (id = 0; id < chip->ngpio; id++) {
		struct gpio_desc *desc = &descs[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;
	}
    gpiochip_set_desc_names(chip);
................

	status = of_gpiochip_add(chip);
	acpi_gpiochip_add(chip);
................
	status = gpiochip_sysfs_register(chip);

}

gpio编号从何开始

   不知同学们注意到一个问题没,有些平台的gpio编号是从1023一次递减的,有些平台是从0递增的,那么选择那种方式,其实就是由base值而定的,小于0就动态分配

static int gpiochip_find_base(int ngpio)
{
	struct gpio_chip *chip;
	int base = ARCH_NR_GPIOS - ngpio;

	list_for_each_entry_reverse(chip, &gpio_chips, list) {
		/* found a free space? */
		if (chip->base + chip->ngpio <= base)
			break;
		else
			/* nope, check the space right before the chip */
			base = chip->base - ngpio;
	}

	if (gpio_is_valid(base)) {
		pr_debug("%s: found new base at %d\n", __func__, base);
		return base;
	} else {
		pr_err("%s: cannot find free range\n", __func__);
		return -ENOSPC;
	}
}

比如高通某平台的的三个gpio_chip(gpiochip0/1/2),的起始编号 = “现阶段最大的编号” 减去 “本gpio_chip的pin脚总数”。大于等于0,那么地址等于多少就是多少

chip = &pctrl->chip;
chip->base = -1;
chip->ngpio = ngpio;
gpiochip_add_data(&pctrl->chip, pctrl);

 gpio_desc

        我们看下这个结构体的定义,主要包含gpio_chip和flag,name,这个flag的不同bit代表了不同的状态标记,那么在上面的代码设置了FLAG_IS_OUT,以及gpiochip_set_desc_names(chip)

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_EXPORT	2	/* protected by sysfs_lock */
#define FLAG_SYSFS	3	/* exported via /sys/class/gpio/control */
#define FLAG_ACTIVE_LOW	6	/* value has active low */
#define FLAG_OPEN_DRAIN	7	/* Gpio is open drain type */
#define FLAG_OPEN_SOURCE 8	/* Gpio is open source type */
#define FLAG_USED_AS_IRQ 9	/* GPIO is connected to an IRQ */
#define FLAG_IS_HOGGED	11	/* GPIO is hogged */

	/* Connection label */
	const char		*label;
	/* Name of the GPIO */
	const char		*name;
};

of_gpiochip_add

这个函数的主要内容如下

of_gpiochip_add(chip);
		of_gpiochip_add_pin_range(chip);
	         of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3,index, &pinspec);
             pctldev = of_pinctrl_get(pinspec.np);
             gpiochip_add_pin_range(chip,
                                     pinctrl_dev_get_devname(pctldev),
                                     pinspec.args[0],
                                     pinspec.args[1],
                                     pinspec.args[2]);

rockchip平台的gpio节点没有这个属性,但是在pinctrl驱动里面用pinctrl_add_gpio_range添加了这个映射

for (bank = 0; bank < info->ctrl->nr_banks; ++bank) {
		pin_bank = &info->ctrl->pin_banks[bank];
		pin_bank->grange.name = pin_bank->name;
		pin_bank->grange.id = bank;
		pin_bank->grange.pin_base = pin_bank->pin_base;
		pin_bank->grange.base = pin_bank->gpio_chip.base;
		pin_bank->grange.npins = pin_bank->gpio_chip.ngpio;
		pin_bank->grange.gc = &pin_bank->gpio_chip;
		pinctrl_add_gpio_range(info->pctl_dev, &pin_bank->grange);
	}

不然gpio_request -> gpio_chip.request -> gpiochip_generic_request->pinctrl_request_gpio会去找range,找失败了直接就返回了

 gpio-ranges:<&iomuxc  0 23 10>代表了当前gpio1控制器的0号gpio,的gpio编号偏移为23,并且从0开始的连续10个脚的偏移都是23

gpio1: gpio@0209c000 {
    gpio-ranges = <&iomuxc    0 23 10>, <&iomuxc 10 17 6>,
              <&iomuxc 16 33 16>;
};

gpio2: gpio@020a0000 {
    gpio-ranges = <&iomuxc 0 49 16>, <&iomuxc 16 111 6>;
};

        但是高通的就是自己实现的request,其gpio字节点没有gpio_range属性,照样工作正常,因为其引脚范围都放进soc的groups里了

static int msm_pinmux_request_gpio(struct pinctrl_dev *pctldev,
				   struct pinctrl_gpio_range *range,
				   unsigned offset)
{
	struct msm_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
	const struct msm_pingroup *g = &pctrl->soc->groups[offset];

	/* No funcs? Probably ACPI so can't do anything here */
	if (!g->nfuncs)
		return 0;

	/* For now assume function 0 is GPIO because it always is */
	return msm_pinmux_set_mux(pctldev, g->funcs[0], offset);
}

static const struct pinmux_ops msm_pinmux_ops = {
	.request		= msm_pinmux_request,
	.free			= msm_pinmux_free,
	.get_functions_count	= msm_get_functions_count,
	.get_function_name	= msm_get_function_name,
	.get_function_groups	= msm_get_function_groups,
	.gpio_request_enable	= msm_pinmux_request_gpio,
	.set_mux		= msm_pinmux_set_mux,
};

         但是高通为了兼容acpi_gpiochip_add还是必须调用gpiochip_add_pin_range,这么做就是单纯的为了兼容acpi系统而已

if (!of_property_read_bool(pctrl->dev->of_node, "gpio-ranges")) {
		ret = gpiochip_add_pin_range(&pctrl->chip,
			dev_name(pctrl->dev), 0, 0, chip->ngpio);
		if (ret) {
			dev_err(pctrl->dev, "Failed to add pin range\n");
			gpiochip_remove(&pctrl->chip);
			return ret;
		}
	}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值