Linux gpio驱动学习

首先我们来看一下gpio驱动框架吧

GPIO驱动的头文件为:include/linux/gpio.h和include/asm-generic/gpio.h。
GPIO驱动框架的实现代码为:drivers/gpio/gpiolib.c。
gpio操作增删改查架构:

/*
 * Add a new chip to the global chips list, keeping the list of chips sorted
 * by base order.
 *
 * Return -EBUSY if the new chip overlaps with some other chip's integer
 * space.
 */
static int gpiochip_add_to_list(struct gpio_chip *chip)
{
	struct list_head *pos;
	struct gpio_chip *_chip;
	int err = 0;

	/* find where to insert our chip */
	list_for_each(pos, &gpio_chips) {
		_chip = list_entry(pos, struct gpio_chip, list);
		/* shall we insert before _chip? */
		if (_chip->base >= chip->base + chip->ngpio)
			break;
	}

	/* are we stepping on the chip right before? */
	if (pos != &gpio_chips && pos->prev != &gpio_chips) {
		_chip = list_entry(pos->prev, struct gpio_chip, list);
		if (_chip->base + _chip->ngpio > chip->base) {
			dev_err(chip->dev,
			       "GPIO integer space overlap, cannot add chip\n");
			err = -EBUSY;
		}
	}

	if (!err)
		list_add_tail(&chip->list, pos);

	return err;
}


int gpiochip_add(struct gpio_chip *chip)
{
	unsigned long	flags;
	int		status = 0;
	unsigned	id;
	int		base = chip->base;
	struct gpio_desc *descs;

	descs = kcalloc(chip->ngpio, sizeof(descs[0]), GFP_KERNEL);
	if (!descs)
		return -ENOMEM;

	spin_lock_irqsave(&gpio_lock, flags);

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

	status = gpiochip_add_to_list(chip);
	if (status) {
		spin_unlock_irqrestore(&gpio_lock, flags);
		goto err_free_descs;
	}

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

	chip->desc = descs;

	spin_unlock_irqrestore(&gpio_lock, flags);

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

	if (!chip->owner && chip->dev && chip->dev->driver)
		chip->owner = chip->dev->driver->owner;

	status = gpiochip_set_desc_names(chip);
	if (status)
		goto err_remove_from_list;

	status = of_gpiochip_add(chip);
	if (status)
		goto err_remove_chip;

	acpi_gpiochip_add(chip);

	status = gpiochip_sysfs_register(chip);
	if (status)
		goto err_remove_chip;

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

	return 0;

err_remove_chip:
	acpi_gpiochip_remove(chip);
	gpiochip_free_hogs(chip);
	of_gpiochip_remove(chip);
err_remove_from_list:
	spin_lock_irqsave(&gpio_lock, flags);
	list_del(&chip->list);
	spin_unlock_irqrestore(&gpio_lock, flags);
	chip->desc = NULL;
err_free_descs:
	kfree(descs);

	/* failures here can mean systems won't boot... */
	pr_err("%s: GPIOs %d..%d (%s) failed to register\n", __func__,
		chip->base, chip->base + chip->ngpio - 1,
		chip->label ? : "generic");
	return status;
}
EXPORT_SYMBOL_GPL(gpiochip_add);

/**
 * gpiochip_remove() - unregister a gpio_chip
 * @chip: the chip to unregister
 *
 * A gpio_chip with any GPIOs still requested may not be removed.
 */
void gpiochip_remove(struct gpio_chip *chip)
{
	struct gpio_desc *desc;
	unsigned long	flags;
	unsigned	id;
	bool		requested = false;

	gpiochip_sysfs_unregister(chip);

	gpiochip_irqchip_remove(chip);

	acpi_gpiochip_remove(chip);
	gpiochip_remove_pin_ranges(chip);
	gpiochip_free_hogs(chip);
	of_gpiochip_remove(chip);

	spin_lock_irqsave(&gpio_lock, flags);
	for (id = 0; id < chip->ngpio; id++) {
		desc = &chip->desc[id];
		desc->chip = NULL;
		if (test_bit(FLAG_REQUESTED, &desc->flags))
			requested = true;
	}
	list_del(&chip->list);
	spin_unlock_irqrestore(&gpio_lock, flags);

	if (requested)
		dev_crit(chip->dev, "REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n");

	kfree(chip->desc);
	chip->desc = NULL;
}
EXPORT_SYMBOL_GPL(gpiochip_remove);

/**
 * gpiochip_find() - iterator for locating a specific gpio_chip
 * @data: data to pass to match function
 * @callback: Callback function to check gpio_chip
 *
 * Similar to bus_find_device.  It returns a reference to a gpio_chip as
 * determined by a user supplied @match callback.  The callback should return
 * 0 if the device doesn't match and non-zero if it does.  If the callback is
 * non-zero, this function will return to the caller and not iterate over any
 * more gpio_chips.
 */
struct gpio_chip *gpiochip_find(void *data,
				int (*match)(struct gpio_chip *chip,
					     void *data))
{
	struct gpio_chip *chip;
	unsigned long flags;

	spin_lock_irqsave(&gpio_lock, flags);
	list_for_each_entry(chip, &gpio_chips, list)
		if (match(chip, data))
			break;

	/* No match? */
	if (&chip->list == &gpio_chips)
		chip = NULL;
	spin_unlock_irqrestore(&gpio_lock, flags);

	return chip;
}
EXPORT_SYMBOL_GPL(gpiochip_find);

static int gpiochip_match_name(struct gpio_chip *chip, void *data)
{
	const char *name = data;

	return !strcmp(chip->label, name);
}

static struct gpio_chip *find_chip_by_name(const char *name)
{
	return gpiochip_find((void *)name, gpiochip_match_name);
}

gpio驱动输入输出设置,高低电平设置:

static int nx_alive_gpio_direction_input(struct udevice *dev, unsigned pin)
{
	struct nx_gpio_platdata *plat = dev_get_platdata(dev);
	struct nx_alive_g+
	pio_regs *const regs = plat->regs;

	setbits_le32(&regs->outputenb_reset, 1 << pin);

	return 0;
}

static int nx_alive_gpio_direction_output(struct udevice *dev, unsigned pin,
				     int val)
{
	struct nx_gpio_platdata *plat = dev_get_platdata(dev);
	struct nx_alive_gpio_regs *const regs = plat->regs;

	if (val)
		setbits_le32(&regs->data, 1 << pin);
	else
		setbits_le32(&regs->pad_reset, 1 << pin);

	setbits_le32(&regs->outputenb, 1 << pin);

	return 0;
}

static int nx_alive_gpio_get_value(struct udevice *dev, unsigned pin)
{
	struct nx_gpio_platdata *plat = dev_get_platdata(dev);
	struct nx_alive_gpio_regs *const regs = plat->regs;
	unsigned int mask = 1UL << pin;
	unsigned int value;

	value = (readl(&regs->pad_read) & mask) >> pin;

	return value;
}


static int nx_alive_gpio_set_value(struct udevice *dev, unsigned pin, int val)
{
	struct nx_gpio_platdata *plat = dev_get_platdata(dev);
	struct nx_alive_gpio_regs *const regs = plat->regs;

	if (val)
		setbits_le32(&regs->data, 1 << pin);
	else
		clrbits_le32(&regs->pad_reset, 1 << pin);

	return 0;
}

static int nx_alive_gpio_get_function(struct udevice *dev, unsigned pin)
{
	struct nx_gpio_platdata *plat = dev_get_platdata(dev);
	struct nx_alive_gpio_regs *const regs = plat->regs;
	unsigned int mask = (1UL << pin);
	unsigned int output;

	output = readl(&regs->outputenb_read) & mask;

	if (output)
		return GPIOF_OUTPUT;
	else
		return GPIOF_INPUT;
}

static int nx_gpio_direction_input(struct udevice *dev, unsigned pin)
{
	struct nx_gpio_platdata *plat = dev_get_platdata(dev);
	struct nx_gpio_regs *const regs = plat->regs;

	if (nx_alive_gpio_is_check(dev))
		return nx_alive_gpio_direction_input(dev, pin);

	clrbits_le32(&regs->outputenb, 1 << pin);

	return 0;
}

static int nx_gpio_direction_output(struct udevice *dev, unsigned pin,
				     int val)
{
	struct nx_gpio_platdata *plat = dev_get_platdata(dev);
	struct nx_gpio_regs *const regs = plat->regs;

	if (nx_alive_gpio_is_check(dev))
		return nx_alive_gpio_direction_output(dev, pin, val);

	if (val)
		setbits_le32(&regs->data, 1 << pin);
	else
		clrbits_le32(&regs->data, 1 << pin);

	setbits_le32(&regs->outputenb, 1 << pin);

	return 0;
}

static int nx_gpio_get_value(struct udevice *dev, unsigned pin)
{
	struct nx_gpio_platdata *plat = dev_get_platdata(dev);
	struct nx_gpio_regs *const regs = plat->regs;
	unsigned int mask = 1UL << pin;
	unsigned int value;

	if (nx_alive_gpio_is_check(dev))
		return nx_alive_gpio_get_value(dev, pin);

	value = (readl(&regs->pad) & mask) >> pin;

	return value;
}


static int nx_gpio_set_value(struct udevice *dev, unsigned pin, int val)
{
	struct nx_gpio_platdata *plat = dev_get_platdata(dev);
	struct nx_gpio_regs *const regs = plat->regs;

	if (nx_alive_gpio_is_check(dev))
		return nx_alive_gpio_set_value(dev, pin, val);

	if (val)
		setbits_le32(&regs->data, 1 << pin);
	else
		clrbits_le32(&regs->data, 1 << pin);

	return 0;
}

static int nx_gpio_get_function(struct udevice *dev, unsigned pin)
{
	struct nx_gpio_platdata *plat = dev_get_platdata(dev);
	struct nx_gpio_regs *const regs = plat->regs;
	unsigned int mask = (1UL << pin);
	unsigned int output;

	if (nx_alive_gpio_is_check(dev))
		return nx_alive_gpio_get_function(dev, pin);

	output = readl(&regs->outputenb) & mask;

	if (output)
		return GPIOF_OUTPUT;
	else
		return GPIOF_INPUT;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陌上花开缓缓归以

你的鼓励将是我创作的最大动力,

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值