Signed-off-by: Marek Vasut <ma...@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
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 -- 1.7.10.4 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot