2013-05-15 22:13:28 +00:00
|
|
|
#define pr_fmt(fmt) "gpiolib: " fmt
|
|
|
|
|
2013-03-07 15:14:16 +00:00
|
|
|
#include <init.h>
|
2012-09-03 08:18:08 +00:00
|
|
|
#include <common.h>
|
2012-12-21 13:26:09 +00:00
|
|
|
#include <command.h>
|
|
|
|
#include <complete.h>
|
2012-09-03 08:18:08 +00:00
|
|
|
#include <gpio.h>
|
|
|
|
#include <errno.h>
|
2012-12-21 13:26:08 +00:00
|
|
|
#include <malloc.h>
|
2012-09-03 08:18:08 +00:00
|
|
|
|
|
|
|
static LIST_HEAD(chip_list);
|
|
|
|
|
2012-12-21 13:26:08 +00:00
|
|
|
struct gpio_info {
|
|
|
|
struct gpio_chip *chip;
|
|
|
|
bool requested;
|
|
|
|
char *label;
|
|
|
|
};
|
|
|
|
|
2013-03-07 15:14:16 +00:00
|
|
|
static struct gpio_info *gpio_desc;
|
|
|
|
|
|
|
|
static int gpio_desc_alloc(void)
|
|
|
|
{
|
|
|
|
gpio_desc = xzalloc(sizeof(struct gpio_info) * ARCH_NR_GPIOS);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
pure_initcall(gpio_desc_alloc);
|
2012-12-21 13:26:08 +00:00
|
|
|
|
|
|
|
static int gpio_ensure_requested(struct gpio_info *gi, int gpio)
|
|
|
|
{
|
|
|
|
if (gi->requested)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return gpio_request(gpio, "gpio");
|
|
|
|
}
|
|
|
|
|
2013-05-15 22:13:28 +00:00
|
|
|
static struct gpio_info *gpio_to_desc(unsigned gpio)
|
|
|
|
{
|
|
|
|
if (gpio_is_valid(gpio))
|
|
|
|
if (gpio_desc[gpio].chip)
|
|
|
|
return &gpio_desc[gpio];
|
|
|
|
|
|
|
|
pr_warning("invalid GPIO %d\n", gpio);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-12-21 13:26:08 +00:00
|
|
|
int gpio_request(unsigned gpio, const char *label)
|
|
|
|
{
|
2013-05-15 22:13:28 +00:00
|
|
|
struct gpio_info *gi = gpio_to_desc(gpio);
|
2012-12-21 13:26:08 +00:00
|
|
|
int ret;
|
|
|
|
|
2014-01-09 17:58:37 +00:00
|
|
|
if (!gi) {
|
|
|
|
ret = -ENODEV;
|
|
|
|
goto done;
|
|
|
|
}
|
2013-05-15 22:13:28 +00:00
|
|
|
|
2014-01-09 17:58:37 +00:00
|
|
|
if (gi->requested) {
|
|
|
|
ret = -EBUSY;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
2013-05-15 22:13:28 +00:00
|
|
|
|
|
|
|
if (gi->chip->ops->request) {
|
|
|
|
ret = gi->chip->ops->request(gi->chip, gpio - gi->chip->base);
|
2012-12-21 13:26:08 +00:00
|
|
|
if (ret)
|
2014-01-09 17:58:37 +00:00
|
|
|
goto done;
|
2012-12-21 13:26:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
gi->requested = true;
|
|
|
|
gi->label = xstrdup(label);
|
|
|
|
|
2014-01-09 17:58:37 +00:00
|
|
|
done:
|
|
|
|
if (ret)
|
|
|
|
pr_err("_gpio_request: gpio-%d (%s) status %d\n",
|
|
|
|
gpio, label ? : "?", ret);
|
|
|
|
|
|
|
|
return ret;
|
2012-12-21 13:26:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void gpio_free(unsigned gpio)
|
|
|
|
{
|
2013-05-15 22:13:28 +00:00
|
|
|
struct gpio_info *gi = gpio_to_desc(gpio);
|
2012-12-21 13:26:08 +00:00
|
|
|
|
2013-05-15 22:13:28 +00:00
|
|
|
if (!gi)
|
2012-12-21 13:26:08 +00:00
|
|
|
return;
|
2013-05-15 22:13:28 +00:00
|
|
|
|
2012-12-21 13:26:08 +00:00
|
|
|
if (!gi->requested)
|
|
|
|
return;
|
2013-05-15 22:13:28 +00:00
|
|
|
|
|
|
|
if (gi->chip->ops->free)
|
|
|
|
gi->chip->ops->free(gi->chip, gpio - gi->chip->base);
|
2012-12-21 13:26:08 +00:00
|
|
|
|
|
|
|
gi->requested = false;
|
|
|
|
free(gi->label);
|
2014-01-09 17:58:38 +00:00
|
|
|
gi->label = NULL;
|
2012-12-21 13:26:08 +00:00
|
|
|
}
|
2012-09-03 08:18:08 +00:00
|
|
|
|
2013-11-21 20:11:24 +00:00
|
|
|
/**
|
|
|
|
* gpio_request_one - request a single GPIO with initial configuration
|
|
|
|
* @gpio: the GPIO number
|
|
|
|
* @flags: GPIO configuration as specified by GPIOF_*
|
|
|
|
* @label: a literal description string of this GPIO
|
|
|
|
*/
|
|
|
|
int gpio_request_one(unsigned gpio, unsigned long flags, const char *label)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = gpio_request(gpio, label);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (flags & GPIOF_DIR_IN)
|
|
|
|
err = gpio_direction_input(gpio);
|
|
|
|
else
|
|
|
|
err = gpio_direction_output(gpio,
|
|
|
|
(flags & GPIOF_INIT_HIGH) ? 1 : 0);
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
goto free_gpio;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
free_gpio:
|
|
|
|
gpio_free(gpio);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(gpio_request_one);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gpio_request_array - request multiple GPIOs in a single call
|
|
|
|
* @array: array of the 'struct gpio'
|
|
|
|
* @num: how many GPIOs in the array
|
|
|
|
*/
|
|
|
|
int gpio_request_array(const struct gpio *array, size_t num)
|
|
|
|
{
|
|
|
|
int i, err;
|
|
|
|
|
|
|
|
for (i = 0; i < num; i++, array++) {
|
|
|
|
err = gpio_request_one(array->gpio, array->flags, array->label);
|
|
|
|
if (err)
|
|
|
|
goto err_free;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_free:
|
|
|
|
while (i--)
|
|
|
|
gpio_free((--array)->gpio);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(gpio_request_array);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gpio_free_array - release multiple GPIOs in a single call
|
|
|
|
* @array: array of the 'struct gpio'
|
|
|
|
* @num: how many GPIOs in the array
|
|
|
|
*/
|
|
|
|
void gpio_free_array(const struct gpio *array, size_t num)
|
|
|
|
{
|
|
|
|
while (num--)
|
|
|
|
gpio_free((array++)->gpio);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(gpio_free_array);
|
|
|
|
|
2012-09-03 08:18:08 +00:00
|
|
|
void gpio_set_value(unsigned gpio, int value)
|
|
|
|
{
|
2013-05-15 22:13:28 +00:00
|
|
|
struct gpio_info *gi = gpio_to_desc(gpio);
|
2012-09-03 08:18:08 +00:00
|
|
|
|
2013-05-15 22:13:28 +00:00
|
|
|
if (!gi)
|
2012-09-03 08:18:08 +00:00
|
|
|
return;
|
2013-05-15 22:13:28 +00:00
|
|
|
|
2012-12-21 13:26:08 +00:00
|
|
|
if (gpio_ensure_requested(gi, gpio))
|
|
|
|
return;
|
2013-05-15 22:13:28 +00:00
|
|
|
|
|
|
|
if (gi->chip->ops->set)
|
|
|
|
gi->chip->ops->set(gi->chip, gpio - gi->chip->base, value);
|
2012-09-03 08:18:08 +00:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(gpio_set_value);
|
|
|
|
|
|
|
|
int gpio_get_value(unsigned gpio)
|
|
|
|
{
|
2013-05-15 22:13:28 +00:00
|
|
|
struct gpio_info *gi = gpio_to_desc(gpio);
|
2012-12-21 13:26:08 +00:00
|
|
|
int ret;
|
2012-09-03 08:18:08 +00:00
|
|
|
|
2013-05-15 22:13:28 +00:00
|
|
|
if (!gi)
|
2012-09-03 08:18:08 +00:00
|
|
|
return -ENODEV;
|
2013-05-15 22:13:28 +00:00
|
|
|
|
2012-12-21 13:26:08 +00:00
|
|
|
ret = gpio_ensure_requested(gi, gpio);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2013-05-15 22:13:28 +00:00
|
|
|
|
|
|
|
if (!gi->chip->ops->get)
|
2012-09-03 08:18:08 +00:00
|
|
|
return -ENOSYS;
|
2013-05-15 22:13:28 +00:00
|
|
|
return gi->chip->ops->get(gi->chip, gpio - gi->chip->base);
|
2012-09-03 08:18:08 +00:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(gpio_get_value);
|
|
|
|
|
|
|
|
int gpio_direction_output(unsigned gpio, int value)
|
|
|
|
{
|
2013-05-15 22:13:28 +00:00
|
|
|
struct gpio_info *gi = gpio_to_desc(gpio);
|
2012-12-21 13:26:08 +00:00
|
|
|
int ret;
|
2012-09-03 08:18:08 +00:00
|
|
|
|
2013-05-15 22:13:28 +00:00
|
|
|
if (!gi)
|
2012-09-03 08:18:08 +00:00
|
|
|
return -ENODEV;
|
2013-05-15 22:13:28 +00:00
|
|
|
|
2012-12-21 13:26:08 +00:00
|
|
|
ret = gpio_ensure_requested(gi, gpio);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2013-05-15 22:13:28 +00:00
|
|
|
|
|
|
|
if (!gi->chip->ops->direction_output)
|
2012-09-03 08:18:08 +00:00
|
|
|
return -ENOSYS;
|
2013-05-15 22:13:28 +00:00
|
|
|
return gi->chip->ops->direction_output(gi->chip, gpio - gi->chip->base,
|
|
|
|
value);
|
2012-09-03 08:18:08 +00:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(gpio_direction_output);
|
|
|
|
|
|
|
|
int gpio_direction_input(unsigned gpio)
|
|
|
|
{
|
2013-05-15 22:13:28 +00:00
|
|
|
struct gpio_info *gi = gpio_to_desc(gpio);
|
2012-12-21 13:26:08 +00:00
|
|
|
int ret;
|
2012-09-03 08:18:08 +00:00
|
|
|
|
2013-05-15 22:13:28 +00:00
|
|
|
if (!gi)
|
2012-09-03 08:18:08 +00:00
|
|
|
return -ENODEV;
|
2013-05-15 22:13:28 +00:00
|
|
|
|
2012-12-21 13:26:08 +00:00
|
|
|
ret = gpio_ensure_requested(gi, gpio);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2013-05-15 22:13:28 +00:00
|
|
|
|
|
|
|
if (!gi->chip->ops->direction_input)
|
2012-09-03 08:18:08 +00:00
|
|
|
return -ENOSYS;
|
2013-05-15 22:13:28 +00:00
|
|
|
return gi->chip->ops->direction_input(gi->chip, gpio - gi->chip->base);
|
2012-09-03 08:18:08 +00:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(gpio_direction_input);
|
|
|
|
|
|
|
|
static int gpiochip_find_base(int start, int ngpio)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int spare = 0;
|
|
|
|
int base = -ENOSPC;
|
|
|
|
|
|
|
|
if (start < 0)
|
|
|
|
start = 0;
|
|
|
|
|
|
|
|
for (i = start; i < ARCH_NR_GPIOS; i++) {
|
2012-12-21 13:26:08 +00:00
|
|
|
struct gpio_chip *chip = gpio_desc[i].chip;
|
2012-09-03 08:18:08 +00:00
|
|
|
|
|
|
|
if (!chip) {
|
|
|
|
spare++;
|
|
|
|
if (spare == ngpio) {
|
|
|
|
base = i + 1 - ngpio;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
spare = 0;
|
|
|
|
i += chip->ngpio - 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gpio_is_valid(base))
|
|
|
|
debug("%s: found new base at %d\n", __func__, base);
|
|
|
|
return base;
|
|
|
|
}
|
|
|
|
|
|
|
|
int gpiochip_add(struct gpio_chip *chip)
|
|
|
|
{
|
|
|
|
int base, i;
|
|
|
|
|
|
|
|
base = gpiochip_find_base(chip->base, chip->ngpio);
|
|
|
|
if (base < 0)
|
|
|
|
return base;
|
|
|
|
|
|
|
|
if (chip->base >= 0 && chip->base != base)
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
chip->base = base;
|
|
|
|
|
|
|
|
list_add_tail(&chip->list, &chip_list);
|
|
|
|
|
|
|
|
for (i = chip->base; i < chip->base + chip->ngpio; i++)
|
2012-12-21 13:26:08 +00:00
|
|
|
gpio_desc[i].chip = chip;
|
2012-09-03 08:18:08 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-03-11 09:26:36 +00:00
|
|
|
void gpiochip_remove(struct gpio_chip *chip)
|
|
|
|
{
|
|
|
|
list_del(&chip->list);
|
|
|
|
}
|
|
|
|
|
2012-09-03 08:18:08 +00:00
|
|
|
int gpio_get_num(struct device_d *dev, int gpio)
|
|
|
|
{
|
|
|
|
struct gpio_chip *chip;
|
|
|
|
|
|
|
|
list_for_each_entry(chip, &chip_list, list) {
|
|
|
|
if (chip->dev == dev)
|
|
|
|
return chip->base + gpio;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
2012-12-21 13:26:09 +00:00
|
|
|
|
|
|
|
#ifdef CONFIG_CMD_GPIO
|
|
|
|
static int do_gpiolib(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARCH_NR_GPIOS; i++) {
|
|
|
|
struct gpio_info *gi = &gpio_desc[i];
|
2013-11-09 13:24:08 +00:00
|
|
|
int val = -1, dir = -1;
|
2012-12-21 13:26:09 +00:00
|
|
|
|
|
|
|
if (!gi->chip)
|
|
|
|
continue;
|
|
|
|
|
2013-11-09 13:24:08 +00:00
|
|
|
/* print chip information and header on first gpio */
|
|
|
|
if (gi->chip->base == i) {
|
2014-05-15 09:08:55 +00:00
|
|
|
printf("\nGPIOs %u-%u, chip %s:\n",
|
2013-11-09 13:24:08 +00:00
|
|
|
gi->chip->base,
|
|
|
|
gi->chip->base + gi->chip->ngpio - 1,
|
|
|
|
gi->chip->dev->name);
|
|
|
|
printf("%*cdir val requested label\n", 13, ' ');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gi->chip->ops->get_direction)
|
|
|
|
dir = gi->chip->ops->get_direction(gi->chip,
|
|
|
|
i - gi->chip->base);
|
|
|
|
if (gi->chip->ops->get)
|
|
|
|
val = gi->chip->ops->get(gi->chip,
|
|
|
|
i - gi->chip->base);
|
|
|
|
|
2014-05-15 09:08:55 +00:00
|
|
|
printf(" GPIO %*d: %*s %*s %*s %s\n", 4, i,
|
2013-11-21 20:11:23 +00:00
|
|
|
3, (dir < 0) ? "unk" : ((dir == GPIOF_DIR_IN) ? "in" : "out"),
|
2013-11-09 13:24:08 +00:00
|
|
|
3, (val < 0) ? "unk" : ((val == 0) ? "lo" : "hi"),
|
|
|
|
9, gi->requested ? "true" : "false",
|
2014-01-09 17:58:39 +00:00
|
|
|
(gi->requested && gi->label) ? gi->label : "");
|
2012-12-21 13:26:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-05-15 09:08:55 +00:00
|
|
|
BAREBOX_CMD_START(gpioinfo)
|
2012-12-21 13:26:09 +00:00
|
|
|
.cmd = do_gpiolib,
|
commands: harmonize in-barebox documentation
This patch does probably too much, but it's hard (and very
cumbersome/time consuming) to break it out. What is does is this:
* each command has one short description, e.g. "list MUX configuration"
* made sure the short descriptions start lowercase
* each command has one usage. That string contains just the
options, e.g. "[-npn]". It's not part of the long help text.
* that is, it doesn't say "[OPTIONS]" anymore, every usable option
is listed by character in this (short) option string (the long
description is in the long help text, as before)
* help texts have been reworked, to make them
- sometimes smaller
- sometimes describe the options better
- more often present themselves in a nicer format
* all long help texts are now created with BUSYBOX_CMD_HELP_
macros, no more 'static const __maybe_unused char cmd_foobar_help[]'
* made sure the long help texts starts uppercase
* because cmdtp->name and cmdtp->opts together provide the new usage,
all "Usage: foobar" texts have been removed from the long help texts
* BUSYBOX_CMD_HELP_TEXT() provides the trailing newline by itself, this
is nicer in the source code
* BUSYBOX_CMD_HELP_OPT() provides the trailing newline by itself
* made sure no line gets longer than 77 characters
* delibertely renamed cmdtp->usage, so that we can get compile-time
errors (e.g. in out-of-tree modules that use register_command()
* the 'help' command can now always emit the usage, even without
compiled long help texts
* 'help -v' gives a list of commands with their short description, this
is similar like the old "help" command before my patchset
* 'help -a' gives out help of all commands
Signed-off-by: Holger Schurig <holgerschurig@gmail.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
2014-05-13 08:28:42 +00:00
|
|
|
BAREBOX_CMD_DESC("list registered GPIOs")
|
commands: group 'help' output
The old output of "help" was just producing a long list, that usually
scrolled of the screen (even on a X11 terminal). This list is more
compact, and also sorted by groups.
The old output format (plus grouping) is now available with 'help -v'.
Example:
Information commands:
?, devinfo, help, iomem, meminfo, version
Boot commands:
boot, bootm, go, loadb, loads, loadx, loady, saves, uimage
...
Signed-off-by: Holger Schurig <holgerschurig@gmail.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
2014-05-13 14:14:05 +00:00
|
|
|
BAREBOX_CMD_GROUP(CMD_GRP_INFO)
|
2012-12-21 13:26:09 +00:00
|
|
|
BAREBOX_CMD_COMPLETE(empty_complete)
|
|
|
|
BAREBOX_CMD_END
|
|
|
|
#endif
|