diff mbox

[U-Boot,5/7] dm: gpio: Add draft GPIO core and convert sandbox to use it

Message ID 1345564853-24500-6-git-send-email-marex@denx.de
State Changes Requested
Headers show

Commit Message

Marek Vasut Aug. 21, 2012, 4 p.m. UTC
Signed-off-by: Marek Vasut <marex@denx.de>
---
 arch/sandbox/lib/board.c    |    6 +
 drivers/gpio/Makefile       |    2 +
 drivers/gpio/core.c         |  365 +++++++++++++++++++++++++++++++++++++++++++
 drivers/gpio/sandbox.c      |   58 ++++++-
 include/asm-generic/gpio.h  |   19 +++
 include/configs/sandbox.h   |    2 +
 include/dm/core_numbering.h |    1 +
 7 files changed, 447 insertions(+), 6 deletions(-)
 create mode 100644 drivers/gpio/core.c

Comments

Pavel Herrmann Aug. 29, 2012, noon UTC | #1
Hi

On Tuesday 21 August 2012 18:00:51 Marek Vasut wrote:
...snip...
> +/**
> + * gpio_request() - [COMPAT] Request GPIO
> + * gpio:	GPIO number
> + * label:	Name for the requested GPIO
> + *
> + * This function implements the API that's compatible with current
> + * GPIO API used in U-Boot. The request is forwarded to particular
> + * GPIO driver. Returns 0 on success, negative value on error.
> + */
> +int gpio_request(unsigned gpio, const char *label)
> +{
> +	struct gpio_core_entry *e = gpio_to_entry(gpio);
> +	if (!e)
> +		return -EINVAL;
> +
> +	return e->ops->gpio_request(gpio, label);
> +}
...snip...


Your core should have a driver API (as described in the core document), which 
should be in form of gpio_$fname for each $fname in the gpio_ops. on top of 
those you can have the command API, which accesses the gpio pins in a linear 
fashion (like what you have now)

the reason for this is if you have a device on gpio (say some LEDs), which 
knows (from platform data) that pins 3-6 of the parent device are connected to 
this device. in your API, this has no way of working - even if you put global 
pin numbering in the platform data, this would stop working if you had a PnP 
GPIO controllers (say USB).

regards
Pavel Herrmann
diff mbox

Patch

diff --git a/arch/sandbox/lib/board.c b/arch/sandbox/lib/board.c
index b6b3768..c79cc62 100644
--- a/arch/sandbox/lib/board.c
+++ b/arch/sandbox/lib/board.c
@@ -239,6 +239,11 @@  void board_init_r(gd_t *id, ulong dest_addr)
 		.name = "demo_drv",
 		.platform_data = NULL
 	};
+	static const struct driver_info gs_info = {
+		.name = "gpio_sandbox",
+		.platform_data = NULL
+	};
+
 	struct instance *root = get_root_instance();
 	struct instance *demo1, *demo2, *demo3;
 	demo1 =	driver_bind(root, &info);
@@ -248,6 +253,7 @@  void board_init_r(gd_t *id, ulong dest_addr)
 			demo2 = driver_bind(demo1, &info);
 				demo3 = driver_bind(demo2, &info);
 	driver_bind(demo2, &info);
+	driver_bind(root, &gs_info);
 
 	demo_hello(demo2);
 
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 4b99b85..1d3aa02 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -25,6 +25,8 @@  include $(TOPDIR)/config.mk
 
 LIB 	:= $(obj)libgpio.o
 
+COBJS-$(CONFIG_DM)		+= core.o
+
 COBJS-$(CONFIG_AT91_GPIO)	+= at91_gpio.o
 COBJS-$(CONFIG_KIRKWOOD_GPIO)	+= kw_gpio.o
 COBJS-$(CONFIG_MARVELL_GPIO)	+= mvgpio.o
diff --git a/drivers/gpio/core.c b/drivers/gpio/core.c
new file mode 100644
index 0000000..8fd83b5
--- /dev/null
+++ b/drivers/gpio/core.c
@@ -0,0 +1,365 @@ 
+#include <common.h>
+#include <malloc.h>
+#include <asm/gpio.h>
+#include <dm/manager.h>
+#include <linux/list.h>
+
+/*
+ * The idea here is to have GPIOs numbered like this from user point of view:
+ *
+ * 32             24 23            16 15                             0
+ * [ GPIO block ID ] [ GPIO chip ID ] [ GPIO ID within the GPIO chip ]
+ *
+ */
+
+#define GPIO_TO_BLOCK_ID(x)	(((x) >> 24) & 0xff)
+#define GPIO_TO_CHIP_ID(x)	(((x) >> 16) & 0xff)
+#define GPIO_TO_CHIP_OFFSET(x)	((x) & 0xffff)
+
+struct gpio_core_entry {
+	struct list_head	list;
+	struct instance		*instance;
+	struct dm_gpio_ops	*ops;
+	int			id;
+};
+
+/**
+ * gpio_to_entry() - Convert GPIO number to entry in the list
+ * gpio:	The numeric representation of the GPIO
+ *
+ * Convert the GPIO number to an entry in the list of GPIOs
+ * or GPIO blocks registered with the GPIO controller. Returns
+ * entry on success, NULL on error.
+ */
+static struct gpio_core_entry *gpio_to_entry(unsigned gpio)
+{
+	uint8_t block = GPIO_TO_BLOCK_ID(gpio);
+	uint8_t chip = GPIO_TO_CHIP_ID(gpio);
+	uint8_t offset = GPIO_TO_CHIP_OFFSET(gpio);
+	struct core_instance *core = get_core_instance(CORE_GPIO);
+	struct gpio_core_entry *tmp, *ret = NULL;
+
+	list_for_each_entry(tmp, &core->succ, list) {
+		if (tmp->id != block)
+			continue;
+		if (tmp->ops->base != chip)
+			continue;
+		if (tmp->ops->ngpio < offset)
+			return NULL;
+		else {
+			ret = tmp;
+			break;
+		}
+	}
+
+	if (ret)
+		driver_activate(ret->instance);
+
+	return ret;
+}
+
+/**
+ * gpio_request() - [COMPAT] Request GPIO
+ * gpio:	GPIO number
+ * label:	Name for the requested GPIO
+ *
+ * This function implements the API that's compatible with current
+ * GPIO API used in U-Boot. The request is forwarded to particular
+ * GPIO driver. Returns 0 on success, negative value on error.
+ */
+int gpio_request(unsigned gpio, const char *label)
+{
+	struct gpio_core_entry *e = gpio_to_entry(gpio);
+	if (!e)
+		return -EINVAL;
+
+	return e->ops->gpio_request(gpio, label);
+}
+
+/**
+ * gpio_free() - [COMPAT] Relinquish GPIO
+ * gpio:	GPIO number
+ *
+ * This function implements the API that's compatible with current
+ * GPIO API used in U-Boot. The request is forwarded to particular
+ * GPIO driver. Returns 0 on success, negative value on error.
+ */
+int gpio_free(unsigned gpio)
+{
+	struct gpio_core_entry *e = gpio_to_entry(gpio);
+	if (!e)
+		return -EINVAL;
+
+	return e->ops->gpio_free(gpio);
+}
+
+/**
+ * gpio_direction_input() - [COMPAT] Set GPIO direction to input
+ * gpio:	GPIO number
+ *
+ * This function implements the API that's compatible with current
+ * GPIO API used in U-Boot. The request is forwarded to particular
+ * GPIO driver. Returns 0 on success, negative value on error.
+ */
+int gpio_direction_input(unsigned gpio)
+{
+	struct gpio_core_entry *e = gpio_to_entry(gpio);
+	if (!e)
+		return -EINVAL;
+
+	return e->ops->gpio_direction_input(gpio);
+}
+
+/**
+ * gpio_direction_output() - [COMPAT] Set GPIO direction to output and set value
+ * gpio:	GPIO number
+ * value:	Logical value to be set on the GPIO pin
+ *
+ * This function implements the API that's compatible with current
+ * GPIO API used in U-Boot. The request is forwarded to particular
+ * GPIO driver. Returns 0 on success, negative value on error.
+ */
+int gpio_direction_output(unsigned gpio, int value)
+{
+	struct gpio_core_entry *e = gpio_to_entry(gpio);
+	if (!e)
+		return -EINVAL;
+
+	return e->ops->gpio_direction_output(gpio, value);
+}
+
+/**
+ * gpio_get_value() - [COMPAT] Sample GPIO pin and return it's value
+ * gpio:	GPIO number
+ *
+ * This function implements the API that's compatible with current
+ * GPIO API used in U-Boot. The request is forwarded to particular
+ * GPIO driver. Returns the value of the GPIO pin, or negative value
+ * on error.
+ */
+int gpio_get_value(unsigned gpio)
+{
+	struct gpio_core_entry *e = gpio_to_entry(gpio);
+	if (!e)
+		return -EINVAL;
+
+	return e->ops->gpio_get_value(gpio);
+}
+
+/**
+ * gpio_set_value() - [COMPAT] Configure logical value on GPIO pin
+ * gpio:	GPIO number
+ * value:	Logical value to be set on the GPIO pin.
+ *
+ * This function implements the API that's compatible with current
+ * GPIO API used in U-Boot. The request is forwarded to particular
+ * GPIO driver. Returns 0 on success, negative value on error.
+ */
+int gpio_set_value(unsigned gpio, int value)
+{
+	struct gpio_core_entry *e = gpio_to_entry(gpio);
+	if (!e)
+		return -EINVAL;
+
+	return e->ops->gpio_set_value(gpio, value);
+}
+
+/**
+ * gpio_core_init() - Initialize the GPIO core
+ * core:	Instance of the GPIO core
+ *
+ * This function does the initial configuration of the GPIO core's
+ * internal structures. Returns 0 always.
+ */
+static int gpio_core_init(struct core_instance *core)
+{
+	INIT_LIST_HEAD(&core->succ);
+	core->private_data = NULL;
+
+	return 0;
+}
+
+/**
+ * gpio_core_reloc() - Relocate the GPIO core
+ * new:		New instance of the GPIO core
+ * old:		Old instance of the GPIO core
+ *
+ * This function relocates the GPIO core.
+ * FIXME: Implement this.
+ */
+static int gpio_core_reloc(struct core_instance *new, struct core_instance *old)
+{
+	return 0;	/* FIXME */
+}
+
+/**
+ * gpio_core_destroy() - Stop the GPIO core
+ * core:	Instance of the GPIO core
+ *
+ * This function stops the GPIO core operation and disconnects all drivers from
+ * it. Returns 0 always.
+ */
+static int gpio_core_destroy(struct core_instance *core)
+{
+	struct gpio_core_entry *tmp, *n;
+
+	list_for_each_entry_safe(tmp, n, &core->succ, list) {
+		list_del(&tmp->list);
+		free(tmp);
+	}
+
+	return 0;
+}
+
+/**
+ * gpio_core_get_count() - Return number of drivers connected to GPIO core
+ * core:	Instance of the GPIO core
+ *
+ * This function counts the number of driver instances associated with the
+ * GPIO core and returns this number.
+ */
+static int gpio_core_get_count(struct core_instance *core)
+{
+	struct gpio_core_entry *tmp;
+	int i = 0;
+
+	list_for_each_entry(tmp, &core->succ, list)
+		i++;
+
+	return i;
+}
+
+/**
+ * gpio_core_get_count() - Return n-th driver connected to GPIO core
+ * core:	Instance of the GPIO core
+ * idx:		Index of the driver
+ *
+ * This function returns the idx-th driver instance associated with the GPIO
+ * core. Returns the instance on success, NULL on error.
+ */
+static struct instance *gpio_core_get_child(struct core_instance *core, int idx)
+{
+	struct gpio_core_entry *tmp;
+	int i = 0;
+
+	list_for_each_entry(tmp, &core->succ, list) {
+		if (i == idx)
+			return tmp->instance;
+		i++;
+	}
+
+	return NULL;
+}
+
+/**
+ * gpio_core_bind() - Bind instance of a driver to a GPIO core
+ * core:	Instance of the GPIO core
+ * dev:		Instance of the driver
+ * ops:		Operations supplied by this driver, struct dm_gpio_ops *
+ * data:	Auxiliary data, must be NULL for GPIO core
+ *
+ * This function binds a driver with a GPIO core. The driver is inserted into
+ * the list of driver instances the GPIO core tracks, so the GPIO core is aware
+ * of the driver. The driver instance is not yet activated.
+ *
+ * Returns 0 on success, negative value on error.
+ */
+static int gpio_core_bind(struct core_instance *core, struct instance *dev,
+			void *ops, void *data)
+{
+	struct gpio_core_entry *new, *tmp, *last = NULL;
+
+	if (data || !ops)
+		return -EINVAL;
+
+	new = malloc(sizeof(*new));
+	if (!new)
+		return -ENOMEM;
+
+	new->instance = dev;
+	new->ops = ops;
+
+	list_for_each_entry(tmp, &core->succ, list) {
+		if (tmp->instance == dev)
+			last = tmp;
+	}
+
+	if (!last) {
+		if (list_empty(&core->succ)) {
+			new->id = 0;
+		} else {
+			tmp = list_entry(core->succ.prev,
+					struct gpio_core_entry, list);
+			new->id = tmp->id + 1;
+		}
+	} else {
+		tmp = list_entry(&last->list, struct gpio_core_entry, list);
+		new->id = tmp->id;
+	}
+
+	list_add_tail(&new->list, &core->succ);
+
+	return 0;
+}
+
+/**
+ * gpio_core_unbind() - Unbind instance of a driver from a GPIO core
+ * core:	Instance of the GPIO core
+ * dev:		Instance of the driver
+ *
+ * This function unbinds a driver from a GPIO core. The driver is removed from
+ * the list of driver instances the GPIO core tracks, so the GPIO core is no
+ * longer aware of the driver. The driver instance is not deactivated.
+ *
+ * Returns 0 always.
+ */
+static int gpio_core_unbind(struct core_instance *core, struct instance *dev)
+{
+	struct gpio_core_entry *tmp, *n;
+
+	list_for_each_entry_safe(tmp, n, &core->succ, list) {
+		if (tmp->instance == dev) {
+			list_del(&tmp->list);
+			free(tmp);
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * gpio_core_replace() - Find and replace instance of a driver in the list
+ * core:	Instance of the GPIO core
+ * new:		New instance of the driver, post relocation one
+ * old:		Old instance of the driver, pre relocation one
+ *
+ * This function replaces pointers to pre-relocation driver instances in the
+ * core's list of drivers with pointers to post-relocation driver instances.
+ * Returns 0 on success, negative value on error.
+ */
+static int gpio_core_replace(struct core_instance *core, struct instance *new,
+						struct instance *old)
+{
+	struct gpio_core_entry *tmp;
+
+	if (!core || !new || !old)
+		return -EINVAL;
+
+	list_for_each_entry(tmp, &core->succ, list) {
+		if (tmp->instance != old)
+			continue;
+		tmp->instance = new;
+	}
+
+	return 0;
+}
+
+U_BOOT_CORE(CORE_GPIO,
+	gpio_core_init,
+	gpio_core_reloc,
+	gpio_core_destroy,
+	gpio_core_get_count,
+	gpio_core_get_child,
+	gpio_core_bind,
+	gpio_core_unbind,
+	gpio_core_replace);
diff --git a/drivers/gpio/sandbox.c b/drivers/gpio/sandbox.c
index 19d2db0..89ecdc5 100644
--- a/drivers/gpio/sandbox.c
+++ b/drivers/gpio/sandbox.c
@@ -21,6 +21,7 @@ 
 
 #include <common.h>
 #include <asm/gpio.h>
+#include <dm/manager.h>
 
 /* Flags for each GPIO */
 #define GPIOF_OUTPUT	(1 << 0)	/* Currently set as an output */
@@ -111,7 +112,7 @@  int sandbox_gpio_set_direction(unsigned gp, int output)
  */
 
 /* set GPIO port 'gp' as an input */
-int gpio_direction_input(unsigned gp)
+static int sb_gpio_direction_input(unsigned gp)
 {
 	debug("%s: gp:%u\n", __func__, gp);
 
@@ -122,7 +123,7 @@  int gpio_direction_input(unsigned gp)
 }
 
 /* set GPIO port 'gp' as an output, with polarity 'value' */
-int gpio_direction_output(unsigned gp, int value)
+static int sb_gpio_direction_output(unsigned gp, int value)
 {
 	debug("%s: gp:%u, value = %d\n", __func__, gp, value);
 
@@ -134,7 +135,7 @@  int gpio_direction_output(unsigned gp, int value)
 }
 
 /* read GPIO IN value of port 'gp' */
-int gpio_get_value(unsigned gp)
+static int sb_gpio_get_value(unsigned gp)
 {
 	debug("%s: gp:%u\n", __func__, gp);
 
@@ -145,7 +146,7 @@  int gpio_get_value(unsigned gp)
 }
 
 /* write GPIO OUT value to port 'gp' */
-int gpio_set_value(unsigned gp, int value)
+static int sb_gpio_set_value(unsigned gp, int value)
 {
 	debug("%s: gp:%u, value = %d\n", __func__, gp, value);
 
@@ -160,7 +161,7 @@  int gpio_set_value(unsigned gp, int value)
 	return sandbox_gpio_set_value(gp, value);
 }
 
-int gpio_request(unsigned gp, const char *label)
+static int sb_gpio_request(unsigned gp, const char *label)
 {
 	debug("%s: gp:%u, label:%s\n", __func__, gp, label);
 
@@ -178,7 +179,7 @@  int gpio_request(unsigned gp, const char *label)
 	return set_gpio_flag(gp, GPIOF_RESERVED, 1);
 }
 
-int gpio_free(unsigned gp)
+static int sb_gpio_free(unsigned gp)
 {
 	debug("%s: gp:%u\n", __func__, gp);
 
@@ -207,3 +208,48 @@  void gpio_info(void)
 			label ? label : "");
 	}
 }
+
+#ifdef CONFIG_DM
+static struct dm_gpio_ops gpio_sandbox_ops = {
+	.base			= 0,
+	.ngpio			= CONFIG_SANDBOX_GPIO_COUNT,
+	.gpio_request		= sb_gpio_request,
+	.gpio_free		= sb_gpio_free,
+	.gpio_direction_input	= sb_gpio_direction_input,
+	.gpio_direction_output	= sb_gpio_direction_output,
+	.gpio_get_value		= sb_gpio_get_value,
+	.gpio_set_value		= sb_gpio_set_value,
+};
+
+static int gpio_sandbox_bind(struct instance *dev)
+{
+	return core_bind(CORE_GPIO, dev, &gpio_sandbox_ops, NULL);
+}
+
+static int gpio_sandbox_probe(struct instance *dev)
+{
+	return 0;
+}
+
+static int gpio_sandbox_reloc(struct instance *new, struct instance *old)
+{
+	return 0;
+}
+
+static int gpio_sandbox_remove(struct instance *dev)
+{
+	return 0;
+}
+
+static int gpio_sandbox_unbind(struct instance *dev)
+{
+	return core_unbind(CORE_GPIO, dev);
+}
+
+U_BOOT_DRIVER(gpio_sandbox,
+	gpio_sandbox_bind,
+	gpio_sandbox_probe,
+	gpio_sandbox_reloc,
+	gpio_sandbox_remove,
+	gpio_sandbox_unbind);
+#endif
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index c19e16c..fac73ae 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -94,4 +94,23 @@  int gpio_get_value(unsigned gpio);
  */
 int gpio_set_value(unsigned gpio, int value);
 
+/**
+ * Driver model GPIO operations, refer to functions above for description.
+ * These function copy the old API.
+ *
+ * This is trying to be close to Linux GPIO API. Once the U-Boot uses the
+ * new DM GPIO API, this should be really easy to flip over to the Linux
+ * GPIO API-alike interface.
+ */
+struct dm_gpio_ops {
+	int	base;
+	u16	ngpio;
+	int	(*gpio_request)(unsigned gpio, const char *label);
+	int	(*gpio_free)(unsigned gpio);
+	int	(*gpio_direction_input)(unsigned gpio);
+	int	(*gpio_direction_output)(unsigned gpio, int value);
+	int	(*gpio_get_value)(unsigned gpio);
+	int	(*gpio_set_value)(unsigned gpio, int value);
+};
+
 #endif	/* _ASM_GENERIC_GPIO_H_ */
diff --git a/include/configs/sandbox.h b/include/configs/sandbox.h
index 9c431bf..0220386 100644
--- a/include/configs/sandbox.h
+++ b/include/configs/sandbox.h
@@ -22,6 +22,8 @@ 
 #ifndef __CONFIG_H
 #define __CONFIG_H
 
+#define CONFIG_DM
+
 #define CONFIG_NR_DRAM_BANKS	1
 #define CONFIG_DRAM_SIZE	(128 << 20)
 
diff --git a/include/dm/core_numbering.h b/include/dm/core_numbering.h
index 75a023f..b543b48 100644
--- a/include/dm/core_numbering.h
+++ b/include/dm/core_numbering.h
@@ -28,6 +28,7 @@ 
 
 enum core_id {
 	CORE_INVALID = 0,
+	CORE_GPIO,
 };
 
 #endif