Add driver model support for GPIOs. Since existing GPIO drivers do not use driver model, this feature must be enabled by CONFIG_DM_GPIO. After all GPO drivers are converted over we can perhaps remove this config. Tests are provided for the sandbox implementation, and are a sufficient sanity check for basic operation. The GPIO uclass understands the concept of named banks of GPIOs, with each GPIO device providing a single bank. Within each bank the GPIOs are numbered using an offset from 0 to n-1. For example a bank named 'b' with 20 offsets will provide GPIOs named b0 to b19. Anonymous GPIO banks are also supported, and are just numbered without any prefix. Each time a GPIO driver is added to the uclass, the GPIOs are renumbered accordinging, so there is always a global GPIO numbering order. Signed-off-by: Simon Glass <sjg@chromium.org> Signed-off-by: Marek Vasut <marex@denx.de> Signed-off-by: Pavel Herrmann <morpheus.ibis@gmail.com> Signed-off-by: Viktor Křivák <viktor.krivak@gmail.com> Signed-off-by: Tomas Hlavacek <tmshlvck@gmail.com>master
parent
39f7611fec
commit
96495d90fe
@ -0,0 +1,266 @@ |
||||
/*
|
||||
* Copyright (c) 2013 Google, Inc |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <dm.h> |
||||
#include <errno.h> |
||||
#include <asm/gpio.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 device **devp, |
||||
unsigned int *offset) |
||||
{ |
||||
struct gpio_dev_priv *uc_priv; |
||||
struct device *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 device **devp, |
||||
unsigned int *offsetp, unsigned int *gpiop) |
||||
{ |
||||
struct gpio_dev_priv *uc_priv; |
||||
struct device *dev; |
||||
int ret; |
||||
|
||||
if (devp) |
||||
*devp = NULL; |
||||
for (ret = uclass_first_device(UCLASS_GPIO, &dev); |
||||
dev; |
||||
ret = uclass_next_device(&dev)) { |
||||
ulong offset; |
||||
int len; |
||||
|
||||
uc_priv = dev->uclass_priv; |
||||
len = uc_priv->bank_name ? strlen(uc_priv->bank_name) : 0; |
||||
|
||||
if (!strncmp(name, uc_priv->bank_name, len)) { |
||||
if (strict_strtoul(name + len, 10, &offset)) |
||||
continue; |
||||
if (devp) |
||||
*devp = dev; |
||||
if (offsetp) |
||||
*offsetp = offset; |
||||
if (gpiop) |
||||
*gpiop = uc_priv->gpio_base + offset; |
||||
return 0; |
||||
} |
||||
} |
||||
|
||||
return ret ? ret : -EINVAL; |
||||
} |
||||
|
||||
/**
|
||||
* 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) |
||||
{ |
||||
unsigned int offset; |
||||
struct device *dev; |
||||
int ret; |
||||
|
||||
ret = gpio_to_device(gpio, &dev, &offset); |
||||
if (ret) |
||||
return ret; |
||||
|
||||
if (!gpio_get_ops(dev)->request) |
||||
return 0; |
||||
|
||||
return gpio_get_ops(dev)->request(dev, offset, 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) |
||||
{ |
||||
unsigned int offset; |
||||
struct device *dev; |
||||
int ret; |
||||
|
||||
ret = gpio_to_device(gpio, &dev, &offset); |
||||
if (ret) |
||||
return ret; |
||||
|
||||
if (!gpio_get_ops(dev)->free) |
||||
return 0; |
||||
return gpio_get_ops(dev)->free(dev, offset); |
||||
} |
||||
|
||||
/**
|
||||
* 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 device *dev; |
||||
int ret; |
||||
|
||||
ret = gpio_to_device(gpio, &dev, &offset); |
||||
if (ret) |
||||
return ret; |
||||
|
||||
return 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 device *dev; |
||||
int ret; |
||||
|
||||
ret = gpio_to_device(gpio, &dev, &offset); |
||||
if (ret) |
||||
return ret; |
||||
|
||||
return 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 device *dev; |
||||
int ret; |
||||
|
||||
ret = gpio_to_device(gpio, &dev, &offset); |
||||
if (ret) |
||||
return ret; |
||||
|
||||
return 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 device *dev; |
||||
int ret; |
||||
|
||||
ret = gpio_to_device(gpio, &dev, &offset); |
||||
if (ret) |
||||
return ret; |
||||
|
||||
return gpio_get_ops(dev)->set_value(dev, offset, value); |
||||
} |
||||
|
||||
const char *gpio_get_bank_info(struct device *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(void) |
||||
{ |
||||
struct gpio_dev_priv *uc_priv; |
||||
struct device *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)) { |
||||
uc_priv = dev->uclass_priv; |
||||
uc_priv->gpio_base = base; |
||||
base += uc_priv->gpio_count; |
||||
} |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int gpio_post_probe(struct device *dev) |
||||
{ |
||||
return gpio_renumber(); |
||||
} |
||||
|
||||
static int gpio_pre_remove(struct device *dev) |
||||
{ |
||||
return gpio_renumber(); |
||||
} |
||||
|
||||
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), |
||||
}; |
@ -0,0 +1,111 @@ |
||||
/*
|
||||
* Copyright (C) 2013 Google, Inc |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <fdtdec.h> |
||||
#include <dm.h> |
||||
#include <dm/ut.h> |
||||
#include <dm/test.h> |
||||
#include <dm/util.h> |
||||
#include <asm/gpio.h> |
||||
|
||||
/* Test that sandbox GPIOs work correctly */ |
||||
static int dm_test_gpio(struct dm_test_state *dms) |
||||
{ |
||||
unsigned int offset, gpio; |
||||
struct dm_gpio_ops *ops; |
||||
struct device *dev; |
||||
const char *name; |
||||
int offset_count; |
||||
char buf[80]; |
||||
|
||||
/*
|
||||
* We expect to get 3 banks. One is anonymous (just numbered) and |
||||
* comes from platdata. The other two are named a (20 gpios) |
||||
* and b (10 gpios) and come from the device tree. See |
||||
* test/dm/test.dts. |
||||
*/ |
||||
ut_assertok(gpio_lookup_name("b4", &dev, &offset, &gpio)); |
||||
ut_asserteq_str(dev->name, "extra-gpios"); |
||||
ut_asserteq(4, offset); |
||||
ut_asserteq(CONFIG_SANDBOX_GPIO_COUNT + 20 + 4, gpio); |
||||
|
||||
name = gpio_get_bank_info(dev, &offset_count); |
||||
ut_asserteq_str("b", name); |
||||
ut_asserteq(10, offset_count); |
||||
|
||||
/* Get the operations for this device */ |
||||
ops = gpio_get_ops(dev); |
||||
ut_assert(ops->get_state); |
||||
|
||||
/* Cannot get a value until it is reserved */ |
||||
ut_asserteq(-1, ops->get_value(dev, offset)); |
||||
|
||||
/*
|
||||
* Now some tests that use the 'sandbox' back door. All GPIOs |
||||
* should default to input, include b4 that we are using here. |
||||
*/ |
||||
ut_assertok(ops->get_state(dev, offset, buf, sizeof(buf))); |
||||
ut_asserteq_str("b4: in: 0 [ ]", buf); |
||||
|
||||
/* Change it to an output */ |
||||
sandbox_gpio_set_direction(dev, offset, 1); |
||||
ut_assertok(ops->get_state(dev, offset, buf, sizeof(buf))); |
||||
ut_asserteq_str("b4: out: 0 [ ]", buf); |
||||
|
||||
sandbox_gpio_set_value(dev, offset, 1); |
||||
ut_assertok(ops->get_state(dev, offset, buf, sizeof(buf))); |
||||
ut_asserteq_str("b4: out: 1 [ ]", buf); |
||||
|
||||
ut_assertok(ops->request(dev, offset, "testing")); |
||||
ut_assertok(ops->get_state(dev, offset, buf, sizeof(buf))); |
||||
ut_asserteq_str("b4: out: 1 [x] testing", buf); |
||||
|
||||
/* Change the value a bit */ |
||||
ut_asserteq(1, ops->get_value(dev, offset)); |
||||
ut_assertok(ops->set_value(dev, offset, 0)); |
||||
ut_asserteq(0, ops->get_value(dev, offset)); |
||||
ut_assertok(ops->get_state(dev, offset, buf, sizeof(buf))); |
||||
ut_asserteq_str("b4: out: 0 [x] testing", buf); |
||||
ut_assertok(ops->set_value(dev, offset, 1)); |
||||
ut_asserteq(1, ops->get_value(dev, offset)); |
||||
|
||||
/* Make it an input */ |
||||
ut_assertok(ops->direction_input(dev, offset)); |
||||
ut_assertok(ops->get_state(dev, offset, buf, sizeof(buf))); |
||||
ut_asserteq_str("b4: in: 1 [x] testing", buf); |
||||
sandbox_gpio_set_value(dev, offset, 0); |
||||
ut_asserteq(0, sandbox_gpio_get_value(dev, offset)); |
||||
ut_assertok(ops->get_state(dev, offset, buf, sizeof(buf))); |
||||
ut_asserteq_str("b4: in: 0 [x] testing", buf); |
||||
|
||||
ut_assertok(ops->free(dev, offset)); |
||||
ut_assertok(ops->get_state(dev, offset, buf, sizeof(buf))); |
||||
ut_asserteq_str("b4: in: 0 [ ]", buf); |
||||
|
||||
/* Check the 'a' bank also */ |
||||
ut_assertok(gpio_lookup_name("a15", &dev, &offset, &gpio)); |
||||
ut_asserteq_str(dev->name, "base-gpios"); |
||||
ut_asserteq(15, offset); |
||||
ut_asserteq(CONFIG_SANDBOX_GPIO_COUNT + 15, gpio); |
||||
|
||||
name = gpio_get_bank_info(dev, &offset_count); |
||||
ut_asserteq_str("a", name); |
||||
ut_asserteq(20, offset_count); |
||||
|
||||
/* And the anonymous bank */ |
||||
ut_assertok(gpio_lookup_name("14", &dev, &offset, &gpio)); |
||||
ut_asserteq_str(dev->name, "gpio_sandbox"); |
||||
ut_asserteq(14, offset); |
||||
ut_asserteq(14, gpio); |
||||
|
||||
name = gpio_get_bank_info(dev, &offset_count); |
||||
ut_asserteq_ptr(NULL, name); |
||||
ut_asserteq(CONFIG_SANDBOX_GPIO_COUNT, offset_count); |
||||
|
||||
return 0; |
||||
} |
||||
DM_TEST(dm_test_gpio, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); |
Loading…
Reference in new issue