[PATCH 1/6] gpio: make the gpiochip a real device

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



GPIO chips have been around for years, but were never real devices,
instead they were piggy-backing on a parent device (such as a
platform_device or amba_device) but this was always optional.
GPIO chips could also exist without any device at all, with its
struct device *dev pointer being set to null.

When sysfs was in use, a mock device would be created, with the
optional parent assigned, or just floating orphaned with NULL
as parent.

For a proper userspace ABI we need gpiochips to *always have a
populated struct device, so add this in the gpio_chip struct.
The name "dev" is unfortunately already take so we use "device"
to name it.

If sysfs is active, it will use this device as parent, and the
former parent device "dev" will be set as parent of the new
"device" struct member.

>From this point on, gpiochips are devices.

Cc: Johan Hovold <johan@xxxxxxxxxx>
Cc: Michael Welling <mwelling@xxxxxxxx>
Cc: Markus Pargmann <mpa@xxxxxxxxxxxxxx>
Signed-off-by: Linus Walleij <linus.walleij@xxxxxxxxxx>
---
 drivers/gpio/gpiolib-sysfs.c |  2 +-
 drivers/gpio/gpiolib.c       | 44 +++++++++++++++++++++++++++++++++++++++-----
 include/linux/gpio/driver.h  |  9 +++++++--
 3 files changed, 47 insertions(+), 8 deletions(-)

diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c
index b57ed8e55ab5..7e5bc5736e47 100644
--- a/drivers/gpio/gpiolib-sysfs.c
+++ b/drivers/gpio/gpiolib-sysfs.c
@@ -605,7 +605,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
 	if (chip->names && chip->names[offset])
 		ioname = chip->names[offset];
 
-	dev = device_create_with_groups(&gpio_class, chip->dev,
+	dev = device_create_with_groups(&gpio_class, &chip->device,
 					MKDEV(0, 0), data, gpio_groups,
 					ioname ? ioname : "gpio%u",
 					desc_to_gpio(desc));
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 6798355c61c6..0ab4f75b7f8e 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -16,6 +16,7 @@
 #include <linux/gpio/driver.h>
 #include <linux/gpio/machine.h>
 #include <linux/pinctrl/consumer.h>
+#include <linux/idr.h>
 
 #include "gpiolib.h"
 
@@ -42,6 +43,9 @@
 #define	extra_checks	0
 #endif
 
+/* Device and char device-related information */
+static DEFINE_IDA(gpio_ida);
+
 /* gpio_lock prevents conflicts during gpio_desc[] table updates.
  * While any GPIO is requested, its gpio_chip is not removable;
  * each GPIO's "requested" flag serves as a lock and refcount.
@@ -52,7 +56,6 @@ static DEFINE_MUTEX(gpio_lookup_lock);
 static LIST_HEAD(gpio_lookup_list);
 LIST_HEAD(gpio_chips);
 
-
 static void gpiochip_free_hogs(struct gpio_chip *chip);
 static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip);
 
@@ -300,7 +303,7 @@ int gpiochip_add(struct gpio_chip *chip)
 {
 	unsigned long	flags;
 	int		status = 0;
-	unsigned	id;
+	unsigned	i;
 	int		base = chip->base;
 	struct gpio_desc *descs;
 
@@ -308,6 +311,28 @@ int gpiochip_add(struct gpio_chip *chip)
 	if (!descs)
 		return -ENOMEM;
 
+	/*
+	 * The "dev" member of gpiochip is the parent, and the actual
+	 * device is named "device" for historical reasons.
+	 *
+	 * We memset the struct to zero to avoid reentrance issues.
+	 */
+	memset(&chip->device, 0, sizeof(chip->device));
+	if (chip->dev) {
+		chip->device.parent = chip->dev;
+		chip->device.of_node = chip->dev->of_node;
+	} else {
+#ifdef CONFIG_OF_GPIO
+	/* If the gpiochip has an assigned OF node this takes precedence */
+	if (chip->of_node)
+		chip->device.of_node = chip->of_node;
+#endif
+	}
+	chip->id = ida_simple_get(&gpio_ida, 0, 0, GFP_KERNEL);
+	dev_set_name(&chip->device, "gpiochip%d", chip->id);
+	device_initialize(&chip->device);
+	dev_set_drvdata(&chip->device, chip);
+
 	spin_lock_irqsave(&gpio_lock, flags);
 
 	if (base < 0) {
@@ -326,8 +351,8 @@ int gpiochip_add(struct gpio_chip *chip)
 		goto err_free_descs;
 	}
 
-	for (id = 0; id < chip->ngpio; id++) {
-		struct gpio_desc *desc = &descs[id];
+	for (i = 0; i < chip->ngpio; i++) {
+		struct gpio_desc *desc = &descs[i];
 
 		desc->chip = chip;
 
@@ -361,16 +386,22 @@ int gpiochip_add(struct gpio_chip *chip)
 
 	acpi_gpiochip_add(chip);
 
-	status = gpiochip_sysfs_register(chip);
+	status = device_add(&chip->device);
 	if (status)
 		goto err_remove_chip;
 
+	status = gpiochip_sysfs_register(chip);
+	if (status)
+		goto err_remove_device;
+
 	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_device:
+	device_del(&chip->device);
 err_remove_chip:
 	acpi_gpiochip_remove(chip);
 	gpiochip_free_hogs(chip);
@@ -381,6 +412,7 @@ err_remove_from_list:
 	spin_unlock_irqrestore(&gpio_lock, flags);
 	chip->desc = NULL;
 err_free_descs:
+	ida_simple_remove(&gpio_ida, chip->id);
 	kfree(descs);
 
 	/* failures here can mean systems won't boot... */
@@ -426,6 +458,8 @@ void gpiochip_remove(struct gpio_chip *chip)
 	if (requested)
 		dev_crit(chip->dev, "REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n");
 
+	device_del(&chip->device);
+	ida_simple_remove(&gpio_ida, chip->id);
 	kfree(chip->desc);
 	chip->desc = NULL;
 }
diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h
index d1baebf350d8..cf3028eccc4b 100644
--- a/include/linux/gpio/driver.h
+++ b/include/linux/gpio/driver.h
@@ -1,6 +1,7 @@
 #ifndef __LINUX_GPIO_DRIVER_H
 #define __LINUX_GPIO_DRIVER_H
 
+#include <linux/device.h>
 #include <linux/types.h>
 #include <linux/module.h>
 #include <linux/irq.h>
@@ -8,8 +9,8 @@
 #include <linux/irqdomain.h>
 #include <linux/lockdep.h>
 #include <linux/pinctrl/pinctrl.h>
+#include <linux/cdev.h>
 
-struct device;
 struct gpio_desc;
 struct of_phandle_args;
 struct device_node;
@@ -20,7 +21,9 @@ struct seq_file;
 /**
  * struct gpio_chip - abstract a GPIO controller
  * @label: for diagnostics
- * @dev: optional device providing the GPIOs
+ * @id: numerical ID number for the GPIO chip
+ * @device: the GPIO device struct.
+ * @dev: optional parent device (hardware) providing the GPIOs
  * @cdev: class device used by sysfs interface (may be NULL)
  * @owner: helps prevent removal of modules exporting active GPIOs
  * @list: links gpio_chips together for traversal
@@ -89,6 +92,8 @@ struct seq_file;
  */
 struct gpio_chip {
 	const char		*label;
+	int			id;
+	struct device		device;
 	struct device		*dev;
 	struct device		*cdev;
 	struct module		*owner;
-- 
2.4.3

--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux SPI]     [Linux Kernel]     [Linux ARM (vger)]     [Linux ARM MSM]     [Linux Omap]     [Linux Arm]     [Linux Tegra]     [Fedora ARM]     [Linux for Samsung SOC]     [eCos]     [Linux Fastboot]     [Gcc Help]     [Git]     [DCCP]     [IETF Announce]     [Security]     [Linux MIPS]     [Yosemite Campsites]

  Powered by Linux