u-boot-brain/drivers/gpio/gpio-uclass.c
Simon Glass b892d127ff dm: gpio: Implement GPIO reservation in the uclass
We have several GPIO drivers now and all are doing similar things to record
which GPIOs are reserved.

Move this logic into the uclass to make the drivers similar.

We retain the request()/free() methods since currently one driver does use
these for setting up the pin.

Signed-off-by: Simon Glass <sjg@chromium.org>
2014-10-23 19:29:51 -06:00

344 lines
7.8 KiB
C

/*
* Copyright (c) 2013 Google, Inc
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <malloc.h>
#include <asm/gpio.h>
#include <linux/ctype.h>
/**
* gpio_to_device() - Convert global GPIO number to device, number
* 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 int gpio_to_device(unsigned int gpio, struct udevice **devp,
unsigned int *offset)
{
struct gpio_dev_priv *uc_priv;
struct udevice *dev;
int ret;
for (ret = uclass_first_device(UCLASS_GPIO, &dev);
dev;
ret = uclass_next_device(&dev)) {
uc_priv = dev->uclass_priv;
if (gpio >= uc_priv->gpio_base &&
gpio < uc_priv->gpio_base + uc_priv->gpio_count) {
*devp = dev;
*offset = gpio - uc_priv->gpio_base;
return 0;
}
}
/* No such GPIO */
return ret ? ret : -EINVAL;
}
int gpio_lookup_name(const char *name, struct udevice **devp,
unsigned int *offsetp, unsigned int *gpiop)
{
struct gpio_dev_priv *uc_priv = NULL;
struct udevice *dev;
ulong offset;
int numeric;
int ret;
if (devp)
*devp = NULL;
numeric = isdigit(*name) ? simple_strtoul(name, NULL, 10) : -1;
for (ret = uclass_first_device(UCLASS_GPIO, &dev);
dev;
ret = uclass_next_device(&dev)) {
int len;
uc_priv = dev->uclass_priv;
if (numeric != -1) {
offset = numeric - uc_priv->gpio_base;
/* Allow GPIOs to be numbered from 0 */
if (offset >= 0 && offset < uc_priv->gpio_count)
break;
}
len = uc_priv->bank_name ? strlen(uc_priv->bank_name) : 0;
if (!strncasecmp(name, uc_priv->bank_name, len)) {
if (!strict_strtoul(name + len, 10, &offset))
break;
}
}
if (!dev)
return ret ? ret : -EINVAL;
if (devp)
*devp = dev;
if (offsetp)
*offsetp = offset;
if (gpiop)
*gpiop = uc_priv->gpio_base + offset;
return 0;
}
/**
* gpio_request() - [COMPAT] Request GPIO
* gpio: GPIO number
* label: Name for the requested GPIO
*
* The label is copied and allocated so the caller does not need to keep
* the pointer around.
*
* 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_dev_priv *uc_priv;
unsigned int offset;
struct udevice *dev;
char *str;
int ret;
ret = gpio_to_device(gpio, &dev, &offset);
if (ret)
return ret;
uc_priv = dev->uclass_priv;
if (uc_priv->name[offset])
return -EBUSY;
str = strdup(label);
if (!str)
return -ENOMEM;
if (gpio_get_ops(dev)->request) {
ret = gpio_get_ops(dev)->request(dev, offset, label);
if (ret) {
free(str);
return ret;
}
}
uc_priv->name[offset] = str;
return 0;
}
/**
* 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_dev_priv *uc_priv;
unsigned int offset;
struct udevice *dev;
int ret;
ret = gpio_to_device(gpio, &dev, &offset);
if (ret)
return ret;
uc_priv = dev->uclass_priv;
if (!uc_priv->name[offset])
return -ENXIO;
if (gpio_get_ops(dev)->free) {
ret = gpio_get_ops(dev)->free(dev, offset);
if (ret)
return ret;
}
free(uc_priv->name[offset]);
uc_priv->name[offset] = NULL;
return 0;
}
static int check_reserved(struct udevice *dev, unsigned offset,
const char *func)
{
struct gpio_dev_priv *uc_priv = dev->uclass_priv;
if (!uc_priv->name[offset]) {
printf("%s: %s: error: gpio %s%d not reserved\n",
dev->name, func,
uc_priv->bank_name ? uc_priv->bank_name : "", offset);
return -EBUSY;
}
return 0;
}
/**
* 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)
{
unsigned int offset;
struct udevice *dev;
int ret;
ret = gpio_to_device(gpio, &dev, &offset);
if (ret)
return ret;
ret = check_reserved(dev, offset, "dir_input");
return ret ? ret : gpio_get_ops(dev)->direction_input(dev, offset);
}
/**
* 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)
{
unsigned int offset;
struct udevice *dev;
int ret;
ret = gpio_to_device(gpio, &dev, &offset);
if (ret)
return ret;
ret = check_reserved(dev, offset, "dir_output");
return ret ? ret :
gpio_get_ops(dev)->direction_output(dev, offset, 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)
{
unsigned int offset;
struct udevice *dev;
int ret;
ret = gpio_to_device(gpio, &dev, &offset);
if (ret)
return ret;
ret = check_reserved(dev, offset, "get_value");
return ret ? ret : gpio_get_ops(dev)->get_value(dev, offset);
}
/**
* 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)
{
unsigned int offset;
struct udevice *dev;
int ret;
ret = gpio_to_device(gpio, &dev, &offset);
if (ret)
return ret;
ret = check_reserved(dev, offset, "set_value");
return ret ? ret : gpio_get_ops(dev)->set_value(dev, offset, value);
}
const char *gpio_get_bank_info(struct udevice *dev, int *bit_count)
{
struct gpio_dev_priv *priv;
/* Must be called on an active device */
priv = dev->uclass_priv;
assert(priv);
*bit_count = priv->gpio_count;
return priv->bank_name;
}
/* We need to renumber the GPIOs when any driver is probed/removed */
static int gpio_renumber(struct udevice *removed_dev)
{
struct gpio_dev_priv *uc_priv;
struct udevice *dev;
struct uclass *uc;
unsigned base;
int ret;
ret = uclass_get(UCLASS_GPIO, &uc);
if (ret)
return ret;
/* Ensure that we have a base for each bank */
base = 0;
uclass_foreach_dev(dev, uc) {
if (device_active(dev) && dev != removed_dev) {
uc_priv = dev->uclass_priv;
uc_priv->gpio_base = base;
base += uc_priv->gpio_count;
}
}
return 0;
}
static int gpio_post_probe(struct udevice *dev)
{
struct gpio_dev_priv *uc_priv = dev->uclass_priv;
uc_priv->name = calloc(uc_priv->gpio_count, sizeof(char *));
if (!uc_priv->name)
return -ENOMEM;
return gpio_renumber(NULL);
}
static int gpio_pre_remove(struct udevice *dev)
{
struct gpio_dev_priv *uc_priv = dev->uclass_priv;
int i;
for (i = 0; i < uc_priv->gpio_count; i++) {
if (uc_priv->name[i])
free(uc_priv->name[i]);
}
free(uc_priv->name);
return gpio_renumber(dev);
}
UCLASS_DRIVER(gpio) = {
.id = UCLASS_GPIO,
.name = "gpio",
.post_probe = gpio_post_probe,
.pre_remove = gpio_pre_remove,
.per_device_auto_alloc_size = sizeof(struct gpio_dev_priv),
};