Commit 3d5a4654 authored by Linus Walleij's avatar Linus Walleij
Browse files

Merge tag 'gpio-updates-for-v5.10-part2' of...

Merge tag 'gpio-updates-for-v5.10-part2' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux into devel

gpio updates for v5.10 - part 2

- refactor gpio-mockup testing module
- simplify the code in gpio-mpc8xxx
- implement v2 of the GPIO user API
parents 12d16b39 cf048e05
Loading
Loading
Loading
Loading
+50 −0
Original line number Diff line number Diff line
.. SPDX-License-Identifier: GPL-2.0-only

GPIO Testing Driver
===================

The GPIO Testing Driver (gpio-mockup) provides a way to create simulated GPIO
chips for testing purposes. The lines exposed by these chips can be accessed
using the standard GPIO character device interface as well as manipulated
using the dedicated debugfs directory structure.

Creating simulated chips using module params
--------------------------------------------

When loading the gpio-mockup driver a number of parameters can be passed to the
module.

    gpio_mockup_ranges

        This parameter takes an argument in the form of an array of integer
        pairs. Each pair defines the base GPIO number (if any) and the number
        of lines exposed by the chip. If the base GPIO is -1, the gpiolib
        will assign it automatically.

        Example: gpio_mockup_ranges=-1,8,-1,16,405,4

        The line above creates three chips. The first one will expose 8 lines,
        the second 16 and the third 4. The base GPIO for the third chip is set
        to 405 while for two first chips it will be assigned automatically.

    gpio_named_lines

        This parameter doesn't take any arguments. It lets the driver know that
        GPIO lines exposed by it should be named.

        The name format is: gpio-mockup-X-Y where X is mockup chip's ID
        and Y is the line offset.

Manipulating simulated lines
----------------------------

Each mockup chip creates its own subdirectory in /sys/kernel/debug/gpio-mockup/.
The directory is named after the chip's label. A symlink is also created, named
after the chip's name, which points to the label directory.

Inside each subdirectory, there's a separate attribute for each GPIO line. The
name of the attribute represents the line's offset in the chip.

Reading from a line attribute returns the current value. Writing to it (0 or 1)
changes the configuration of the simulated pull-up/pull-down resistor
(1 - pull-up, 0 - pull-down).
+27 −2
Original line number Diff line number Diff line
@@ -66,8 +66,33 @@ config GPIO_SYSFS

	  This ABI is deprecated. If you want to use GPIO from userspace,
	  use the character device /dev/gpiochipN with the appropriate
	  ioctl() operations instead. The character device is always
	  available.
	  ioctl() operations instead.

config GPIO_CDEV
	bool
	prompt "Character device (/dev/gpiochipN) support" if EXPERT
	default y
	help
	  Say Y here to add the character device /dev/gpiochipN interface
	  for GPIOs. The character device allows userspace to control GPIOs
	  using ioctl() operations.

	  Only say N if you are sure that the GPIO character device is not
	  required.

	  If unsure, say Y.

config GPIO_CDEV_V1
	bool "Support GPIO ABI Version 1"
	default y
	depends on GPIO_CDEV
	help
	  Say Y here to support version 1 of the GPIO CDEV ABI.

	  This ABI version is deprecated.
	  Please use the latest ABI for new developments.

	  If unsure, say Y.

config GPIO_GENERIC
	depends on HAS_IOMEM # Only for IOMEM drivers
+1 −1
Original line number Diff line number Diff line
@@ -6,8 +6,8 @@ ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG
obj-$(CONFIG_GPIOLIB)		+= gpiolib.o
obj-$(CONFIG_GPIOLIB)		+= gpiolib-devres.o
obj-$(CONFIG_GPIOLIB)		+= gpiolib-legacy.o
obj-$(CONFIG_GPIOLIB)		+= gpiolib-cdev.o
obj-$(CONFIG_OF_GPIO)		+= gpiolib-of.o
obj-$(CONFIG_GPIO_CDEV)		+= gpiolib-cdev.o
obj-$(CONFIG_GPIO_SYSFS)	+= gpiolib-sysfs.o
obj-$(CONFIG_GPIO_ACPI)		+= gpiolib-acpi.o

+82 −78
Original line number Diff line number Diff line
@@ -7,10 +7,10 @@
 * Copyright (C) 2017 Bartosz Golaszewski <brgl@bgdev.pl>
 */

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/debugfs.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irq_sim.h>
@@ -19,21 +19,19 @@
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/slab.h>
#include <linux/string_helpers.h>
#include <linux/uaccess.h>

#include "gpiolib.h"

#define GPIO_MOCKUP_NAME	"gpio-mockup"
#define GPIO_MOCKUP_MAX_GC	10
/*
 * We're storing two values per chip: the GPIO base and the number
 * of GPIO lines.
 */
#define GPIO_MOCKUP_MAX_RANGES	(GPIO_MOCKUP_MAX_GC * 2)
/* Maximum of three properties + the sentinel. */
#define GPIO_MOCKUP_MAX_PROP	4

#define gpio_mockup_err(...)	pr_err(GPIO_MOCKUP_NAME ": " __VA_ARGS__)
/* Maximum of four properties + the sentinel. */
#define GPIO_MOCKUP_MAX_PROP	5

/*
 * struct gpio_pin_status - structure describing a GPIO status
@@ -375,31 +373,6 @@ static void gpio_mockup_debugfs_setup(struct device *dev,
		debugfs_create_file(name, 0200, chip->dbg_dir, priv,
				    &gpio_mockup_debugfs_ops);
	}

	return;
}

static int gpio_mockup_name_lines(struct device *dev,
				  struct gpio_mockup_chip *chip)
{
	struct gpio_chip *gc = &chip->gc;
	char **names;
	int i;

	names = devm_kcalloc(dev, gc->ngpio, sizeof(char *), GFP_KERNEL);
	if (!names)
		return -ENOMEM;

	for (i = 0; i < gc->ngpio; i++) {
		names[i] = devm_kasprintf(dev, GFP_KERNEL,
					  "%s-%d", gc->label, i);
		if (!names[i])
			return -ENOMEM;
	}

	gc->names = (const char *const *)names;

	return 0;
}

static void gpio_mockup_dispose_mappings(void *data)
@@ -434,21 +407,14 @@ static int gpio_mockup_probe(struct platform_device *pdev)
	if (rv)
		return rv;

	rv = device_property_read_string(dev, "chip-name", &name);
	rv = device_property_read_string(dev, "chip-label", &name);
	if (rv)
		name = NULL;
		name = dev_name(dev);

	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
	if (!chip)
		return -ENOMEM;

	if (!name) {
		name = devm_kasprintf(dev, GFP_KERNEL,
				      "%s-%c", pdev->name, pdev->id + 'A');
		if (!name)
			return -ENOMEM;
	}

	mutex_init(&chip->lock);

	gc = &chip->gc;
@@ -476,12 +442,6 @@ static int gpio_mockup_probe(struct platform_device *pdev)
	for (i = 0; i < gc->ngpio; i++)
		chip->lines[i].dir = GPIO_LINE_DIRECTION_IN;

	if (device_property_read_bool(dev, "named-gpio-lines")) {
		rv = gpio_mockup_name_lines(dev, chip);
		if (rv)
			return rv;
	}

	chip->irq_sim_domain = devm_irq_domain_create_sim(dev, NULL,
							  gc->ngpio);
	if (IS_ERR(chip->irq_sim_domain))
@@ -502,7 +462,7 @@ static int gpio_mockup_probe(struct platform_device *pdev)

static struct platform_driver gpio_mockup_driver = {
	.driver = {
		.name = GPIO_MOCKUP_NAME,
		.name = "gpio-mockup",
	},
	.probe = gpio_mockup_probe,
};
@@ -522,14 +482,80 @@ static void gpio_mockup_unregister_pdevs(void)
	}
}

static int __init gpio_mockup_init(void)
static __init char **gpio_mockup_make_line_names(const char *label,
						 unsigned int num_lines)
{
	unsigned int i;
	char **names;

	names = kcalloc(num_lines + 1, sizeof(char *), GFP_KERNEL);
	if (!names)
		return NULL;

	for (i = 0; i < num_lines; i++) {
		names[i] = kasprintf(GFP_KERNEL, "%s-%u", label, i);
		if (!names[i]) {
			kfree_strarray(names, i);
			return NULL;
		}
	}

	return names;
}

static int __init gpio_mockup_register_chip(int idx)
{
	struct property_entry properties[GPIO_MOCKUP_MAX_PROP];
	int i, prop, num_chips, err = 0, base;
	struct platform_device_info pdevinfo;
	struct platform_device *pdev;
	char **line_names = NULL;
	char chip_label[32];
	int prop = 0, base;
	u16 ngpio;

	memset(properties, 0, sizeof(properties));
	memset(&pdevinfo, 0, sizeof(pdevinfo));

	snprintf(chip_label, sizeof(chip_label), "gpio-mockup-%c", idx + 'A');
	properties[prop++] = PROPERTY_ENTRY_STRING("chip-label", chip_label);

	base = gpio_mockup_range_base(idx);
	if (base >= 0)
		properties[prop++] = PROPERTY_ENTRY_U32("gpio-base", base);

	ngpio = base < 0 ? gpio_mockup_range_ngpio(idx)
			 : gpio_mockup_range_ngpio(idx) - base;
	properties[prop++] = PROPERTY_ENTRY_U16("nr-gpios", ngpio);

	if (gpio_mockup_named_lines) {
		line_names = gpio_mockup_make_line_names(chip_label, ngpio);
		if (!line_names)
			return -ENOMEM;

		properties[prop++] = PROPERTY_ENTRY_STRING_ARRAY_LEN(
					"gpio-line-names", line_names, ngpio);
	}

	pdevinfo.name = "gpio-mockup";
	pdevinfo.id = idx;
	pdevinfo.properties = properties;

	pdev = platform_device_register_full(&pdevinfo);
	kfree_strarray(line_names, ngpio);
	if (IS_ERR(pdev)) {
		pr_err("error registering device");
		return PTR_ERR(pdev);
	}

	gpio_mockup_pdevs[idx] = pdev;

	return 0;
}

static int __init gpio_mockup_init(void)
{
	int i, num_chips, err;

	if ((gpio_mockup_num_ranges < 2) ||
	    (gpio_mockup_num_ranges % 2) ||
	    (gpio_mockup_num_ranges > GPIO_MOCKUP_MAX_RANGES))
@@ -551,41 +577,19 @@ static int __init gpio_mockup_init(void)

	err = platform_driver_register(&gpio_mockup_driver);
	if (err) {
		gpio_mockup_err("error registering platform driver\n");
		pr_err("error registering platform driver\n");
		debugfs_remove_recursive(gpio_mockup_dbg_dir);
		return err;
	}

	for (i = 0; i < num_chips; i++) {
		memset(properties, 0, sizeof(properties));
		memset(&pdevinfo, 0, sizeof(pdevinfo));
		prop = 0;

		base = gpio_mockup_range_base(i);
		if (base >= 0)
			properties[prop++] = PROPERTY_ENTRY_U32("gpio-base",
								base);

		ngpio = base < 0 ? gpio_mockup_range_ngpio(i)
				 : gpio_mockup_range_ngpio(i) - base;
		properties[prop++] = PROPERTY_ENTRY_U16("nr-gpios", ngpio);

		if (gpio_mockup_named_lines)
			properties[prop++] = PROPERTY_ENTRY_BOOL(
						"named-gpio-lines");

		pdevinfo.name = GPIO_MOCKUP_NAME;
		pdevinfo.id = i;
		pdevinfo.properties = properties;

		pdev = platform_device_register_full(&pdevinfo);
		if (IS_ERR(pdev)) {
			gpio_mockup_err("error registering device");
		err = gpio_mockup_register_chip(i);
		if (err) {
			platform_driver_unregister(&gpio_mockup_driver);
			gpio_mockup_unregister_pdevs();
			return PTR_ERR(pdev);
			debugfs_remove_recursive(gpio_mockup_dbg_dir);
			return err;
		}

		gpio_mockup_pdevs[i] = pdev;
	}

	return 0;
+12 −33
Original line number Diff line number Diff line
@@ -47,27 +47,6 @@ struct mpc8xxx_gpio_chip {
	unsigned int irqn;
};

/* The GPIO Input Buffer Enable register(GPIO_IBE) is used to
 * control the input enable of each individual GPIO port.
 * When an individual GPIO port’s direction is set to
 * input (GPIO_GPDIR[DRn=0]), the associated input enable must be
 * set (GPIOxGPIE[IEn]=1) to propagate the port value to the GPIO
 * Data Register.
 */
static int ls1028a_gpio_dir_in_init(struct gpio_chip *gc)
{
	unsigned long flags;
	struct mpc8xxx_gpio_chip *mpc8xxx_gc = gpiochip_get_data(gc);

	spin_lock_irqsave(&gc->bgpio_lock, flags);

	gc->write_reg(mpc8xxx_gc->regs + GPIO_IBE, 0xffffffff);

	spin_unlock_irqrestore(&gc->bgpio_lock, flags);

	return 0;
}

/*
 * This hardware has a big endian bit assignment such that GPIO line 0 is
 * connected to bit 31, line 1 to bit 30 ... line 31 to bit 0.
@@ -283,7 +262,6 @@ static const struct irq_domain_ops mpc8xxx_gpio_irq_ops = {
};

struct mpc8xxx_gpio_devtype {
	int (*gpio_dir_in_init)(struct gpio_chip *chip);
	int (*gpio_dir_out)(struct gpio_chip *, unsigned int, int);
	int (*gpio_get)(struct gpio_chip *, unsigned int);
	int (*irq_set_type)(struct irq_data *, unsigned int);
@@ -294,11 +272,6 @@ static const struct mpc8xxx_gpio_devtype mpc512x_gpio_devtype = {
	.irq_set_type = mpc512x_irq_set_type,
};

static const struct mpc8xxx_gpio_devtype ls1028a_gpio_devtype = {
	.gpio_dir_in_init = ls1028a_gpio_dir_in_init,
	.irq_set_type = mpc8xxx_irq_set_type,
};

static const struct mpc8xxx_gpio_devtype mpc5125_gpio_devtype = {
	.gpio_dir_out = mpc5125_gpio_dir_out,
	.irq_set_type = mpc512x_irq_set_type,
@@ -319,8 +292,8 @@ static const struct of_device_id mpc8xxx_gpio_ids[] = {
	{ .compatible = "fsl,mpc5121-gpio", .data = &mpc512x_gpio_devtype, },
	{ .compatible = "fsl,mpc5125-gpio", .data = &mpc5125_gpio_devtype, },
	{ .compatible = "fsl,pq3-gpio",     },
	{ .compatible = "fsl,ls1028a-gpio", .data = &ls1028a_gpio_devtype, },
	{ .compatible = "fsl,ls1088a-gpio", .data = &ls1028a_gpio_devtype, },
	{ .compatible = "fsl,ls1028a-gpio", },
	{ .compatible = "fsl,ls1088a-gpio", },
	{ .compatible = "fsl,qoriq-gpio",   },
	{}
};
@@ -389,7 +362,16 @@ static int mpc8xxx_probe(struct platform_device *pdev)

	gc->to_irq = mpc8xxx_gpio_to_irq;

	if (of_device_is_compatible(np, "fsl,qoriq-gpio"))
	/*
	 * The GPIO Input Buffer Enable register(GPIO_IBE) is used to control
	 * the input enable of each individual GPIO port.  When an individual
	 * GPIO port’s direction is set to input (GPIO_GPDIR[DRn=0]), the
	 * associated input enable must be set (GPIOxGPIE[IEn]=1) to propagate
	 * the port value to the GPIO Data Register.
	 */
	if (of_device_is_compatible(np, "fsl,qoriq-gpio") ||
	    of_device_is_compatible(np, "fsl,ls1028a-gpio") ||
	    of_device_is_compatible(np, "fsl,ls1088a-gpio"))
		gc->write_reg(mpc8xxx_gc->regs + GPIO_IBE, 0xffffffff);

	ret = gpiochip_add_data(gc, mpc8xxx_gc);
@@ -411,9 +393,6 @@ static int mpc8xxx_probe(struct platform_device *pdev)
	/* ack and mask all irqs */
	gc->write_reg(mpc8xxx_gc->regs + GPIO_IER, 0xffffffff);
	gc->write_reg(mpc8xxx_gc->regs + GPIO_IMR, 0);
	/* enable input buffer  */
	if (devtype->gpio_dir_in_init)
		devtype->gpio_dir_in_init(gc);

	ret = devm_request_irq(&pdev->dev, mpc8xxx_gc->irqn,
			       mpc8xxx_gpio_irq_cascade,
Loading