Merge branch '2021-03-03-gpio-improvements' into next

- GPIO uclass improvements
This commit is contained in:
Tom Rini 2021-03-04 13:12:51 -05:00
commit 19a33a7b56
9 changed files with 704 additions and 220 deletions

View File

@ -23,6 +23,15 @@
*/
#include <asm-generic/gpio.h>
/* Our own private GPIO flags, which musn't conflict with GPIOD_... */
#define GPIOD_EXT_HIGH BIT(31) /* external source is high (else low) */
#define GPIOD_EXT_DRIVEN BIT(30) /* external source is driven */
#define GPIOD_EXT_PULL_UP BIT(29) /* GPIO has external pull-up */
#define GPIOD_EXT_PULL_DOWN BIT(28) /* GPIO has external pull-down */
#define GPIOD_EXT_PULL (BIT(28) | BIT(29))
#define GPIOD_SANDBOX_MASK GENMASK(31, 28)
/**
* Return the simulated value of a GPIO (used only in sandbox test code)
*
@ -69,17 +78,17 @@ int sandbox_gpio_set_direction(struct udevice *dev, unsigned int offset,
* @param offset GPIO offset within bank
* @return dir_flags: bitfield accesses by GPIOD_ defines
*/
ulong sandbox_gpio_get_dir_flags(struct udevice *dev, unsigned int offset);
ulong sandbox_gpio_get_flags(struct udevice *dev, unsigned int offset);
/**
* Set the simulated flags of a GPIO (used only in sandbox test code)
*
* @param dev device to use
* @param offset GPIO offset within bank
* @param flags dir_flags: bitfield accesses by GPIOD_ defines
* @param flags bitfield accesses by GPIOD_ defines
* @return -1 on error, 0 if ok
*/
int sandbox_gpio_set_dir_flags(struct udevice *dev, unsigned int offset,
ulong flags);
int sandbox_gpio_set_flags(struct udevice *dev, unsigned int offset,
ulong flags);
#endif

View File

@ -11,6 +11,11 @@
/* This file is included by device trees, so avoid BIT() macros */
#define GPIO_DW_SIZE(x) (sizeof(u32) * (x))
#define PAD_CFG_OFFSET(x, dw_num) ((x) + GPIO_DW_SIZE(dw_num))
#define PAD_CFG0_OFFSET(x) PAD_CFG_OFFSET(x, 0)
#define PAD_CFG1_OFFSET(x) PAD_CFG_OFFSET(x, 1)
#define PAD_CFG0_TX_STATE_BIT 0
#define PAD_CFG0_TX_STATE (1 << PAD_CFG0_TX_STATE_BIT)
#define PAD_CFG0_RX_STATE_BIT 1

View File

@ -3,6 +3,8 @@
* Copyright (c) 2013 Google, Inc
*/
#define LOG_CATEGORY UCLASS_GPIO
#include <common.h>
#include <dm.h>
#include <log.h>
@ -21,6 +23,7 @@
#include <dm/device_compat.h>
#include <linux/bug.h>
#include <linux/ctype.h>
#include <linux/delay.h>
DECLARE_GLOBAL_DATA_PTR;
@ -220,7 +223,7 @@ int gpio_xlate_offs_flags(struct udevice *dev, struct gpio_desc *desc,
static int gpio_find_and_xlate(struct gpio_desc *desc,
struct ofnode_phandle_args *args)
{
struct dm_gpio_ops *ops = gpio_get_ops(desc->dev);
const struct dm_gpio_ops *ops = gpio_get_ops(desc->dev);
if (ops->xlate)
return ops->xlate(desc->dev, desc, args);
@ -353,6 +356,7 @@ int gpio_hog_lookup_name(const char *name, struct gpio_desc **desc)
int dm_gpio_request(struct gpio_desc *desc, const char *label)
{
const struct dm_gpio_ops *ops = gpio_get_ops(desc->dev);
struct udevice *dev = desc->dev;
struct gpio_dev_priv *uc_priv;
char *str;
@ -364,8 +368,8 @@ int dm_gpio_request(struct gpio_desc *desc, const char *label)
str = strdup(label);
if (!str)
return -ENOMEM;
if (gpio_get_ops(dev)->request) {
ret = gpio_get_ops(dev)->request(dev, desc->offset, label);
if (ops->request) {
ret = ops->request(dev, desc->offset, label);
if (ret) {
free(str);
return ret;
@ -442,14 +446,15 @@ int gpio_requestf(unsigned gpio, const char *fmt, ...)
int _dm_gpio_free(struct udevice *dev, uint offset)
{
const struct dm_gpio_ops *ops = gpio_get_ops(dev);
struct gpio_dev_priv *uc_priv;
int ret;
uc_priv = dev_get_uclass_priv(dev);
if (!uc_priv->name[offset])
return -ENXIO;
if (gpio_get_ops(dev)->rfree) {
ret = gpio_get_ops(dev)->rfree(dev, offset);
if (ops->rfree) {
ret = ops->rfree(dev, offset);
if (ret)
return ret;
}
@ -513,13 +518,10 @@ int gpio_direction_input(unsigned gpio)
int ret;
ret = gpio_to_device(gpio, &desc);
if (ret)
return ret;
ret = check_reserved(&desc, "dir_input");
if (ret)
return ret;
return gpio_get_ops(desc.dev)->direction_input(desc.dev, desc.offset);
return dm_gpio_clrset_flags(&desc, GPIOD_MASK_DIR, GPIOD_IS_IN);
}
/**
@ -534,24 +536,25 @@ int gpio_direction_input(unsigned gpio)
int gpio_direction_output(unsigned gpio, int value)
{
struct gpio_desc desc;
ulong flags;
int ret;
ret = gpio_to_device(gpio, &desc);
if (ret)
return ret;
ret = check_reserved(&desc, "dir_output");
if (ret)
return ret;
return gpio_get_ops(desc.dev)->direction_output(desc.dev,
desc.offset, value);
flags = GPIOD_IS_OUT;
if (value)
flags |= GPIOD_IS_OUT_ACTIVE;
return dm_gpio_clrset_flags(&desc, GPIOD_MASK_DIR, flags);
}
static int _gpio_get_value(const struct gpio_desc *desc)
{
const struct dm_gpio_ops *ops = gpio_get_ops(desc->dev);
int value;
value = gpio_get_ops(desc->dev)->get_value(desc->dev, desc->offset);
value = ops->get_value(desc->dev, desc->offset);
return desc->flags & GPIOD_ACTIVE_LOW ? !value : value;
}
@ -569,6 +572,7 @@ int dm_gpio_get_value(const struct gpio_desc *desc)
int dm_gpio_set_value(const struct gpio_desc *desc, int value)
{
const struct dm_gpio_ops *ops;
int ret;
ret = check_reserved(desc, "set_value");
@ -578,21 +582,33 @@ int dm_gpio_set_value(const struct gpio_desc *desc, int value)
if (desc->flags & GPIOD_ACTIVE_LOW)
value = !value;
/* GPIOD_ are directly managed by driver in set_flags */
ops = gpio_get_ops(desc->dev);
if (ops->set_flags) {
ulong flags = desc->flags;
if (value)
flags |= GPIOD_IS_OUT_ACTIVE;
else
flags &= ~GPIOD_IS_OUT_ACTIVE;
return ops->set_flags(desc->dev, desc->offset, flags);
}
/*
* Emulate open drain by not actively driving the line high or
* Emulate open source by not actively driving the line low
*/
if ((desc->flags & GPIOD_OPEN_DRAIN && value) ||
(desc->flags & GPIOD_OPEN_SOURCE && !value))
return gpio_get_ops(desc->dev)->direction_input(desc->dev,
desc->offset);
return ops->direction_input(desc->dev, desc->offset);
else if (desc->flags & GPIOD_OPEN_DRAIN ||
desc->flags & GPIOD_OPEN_SOURCE)
return gpio_get_ops(desc->dev)->direction_output(desc->dev,
desc->offset,
value);
return ops->direction_output(desc->dev, desc->offset, value);
ret = ops->set_value(desc->dev, desc->offset, value);
if (ret)
return ret;
gpio_get_ops(desc->dev)->set_value(desc->dev, desc->offset, value);
return 0;
}
@ -620,10 +636,21 @@ static int check_dir_flags(ulong flags)
return 0;
}
static int _dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags)
/**
* _dm_gpio_set_flags() - Send flags to the driver
*
* This uses the best available method to send the given flags to the driver.
* Note that if flags & GPIOD_ACTIVE_LOW, the driver sees the opposite value
* of GPIOD_IS_OUT_ACTIVE.
*
* @desc: GPIO description
* @flags: flags value to set
* @return 0 if OK, -ve on error
*/
static int _dm_gpio_set_flags(struct gpio_desc *desc, ulong flags)
{
struct udevice *dev = desc->dev;
struct dm_gpio_ops *ops = gpio_get_ops(dev);
const struct dm_gpio_ops *ops = gpio_get_ops(dev);
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
int ret = 0;
@ -638,38 +665,52 @@ static int _dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags)
return ret;
}
/* GPIOD_ are directly managed by driver in set_dir_flags*/
if (ops->set_dir_flags) {
ret = ops->set_dir_flags(dev, desc->offset, flags);
/* If active low, invert the output state */
if ((flags & (GPIOD_IS_OUT | GPIOD_ACTIVE_LOW)) ==
(GPIOD_IS_OUT | GPIOD_ACTIVE_LOW))
flags ^= GPIOD_IS_OUT_ACTIVE;
/* GPIOD_ are directly managed by driver in set_flags */
if (ops->set_flags) {
ret = ops->set_flags(dev, desc->offset, flags);
} else {
if (flags & GPIOD_IS_OUT) {
ret = ops->direction_output(dev, desc->offset,
GPIOD_FLAGS_OUTPUT(flags));
bool value = flags & GPIOD_IS_OUT_ACTIVE;
ret = ops->direction_output(dev, desc->offset, value);
} else if (flags & GPIOD_IS_IN) {
ret = ops->direction_input(dev, desc->offset);
}
}
/* save the flags also in descriptor */
if (!ret)
desc->flags = flags;
return ret;
}
int dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags)
int dm_gpio_clrset_flags(struct gpio_desc *desc, ulong clr, ulong set)
{
ulong flags;
int ret;
ret = check_reserved(desc, "set_dir_flags");
if (ret)
return ret;
/* combine the requested flags (for IN/OUT) and the descriptor flags */
flags |= desc->flags;
ret = _dm_gpio_set_dir_flags(desc, flags);
flags = (desc->flags & ~clr) | set;
return ret;
ret = _dm_gpio_set_flags(desc, flags);
if (ret)
return ret;
/* save the flags also in descriptor */
desc->flags = flags;
return 0;
}
int dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags)
{
/* combine the requested flags (for IN/OUT) and the descriptor flags */
return dm_gpio_clrset_flags(desc, GPIOD_MASK_DIR, flags);
}
int dm_gpio_set_dir(struct gpio_desc *desc)
@ -680,42 +721,57 @@ int dm_gpio_set_dir(struct gpio_desc *desc)
if (ret)
return ret;
return _dm_gpio_set_dir_flags(desc, desc->flags);
return _dm_gpio_set_flags(desc, desc->flags);
}
int dm_gpio_get_dir_flags(struct gpio_desc *desc, ulong *flags)
int dm_gpios_clrset_flags(struct gpio_desc *desc, int count, ulong clr,
ulong set)
{
int ret;
int i;
for (i = 0; i < count; i++) {
ret = dm_gpio_clrset_flags(&desc[i], clr, set);
if (ret)
return log_ret(ret);
}
return 0;
}
int dm_gpio_get_flags(struct gpio_desc *desc, ulong *flagsp)
{
struct udevice *dev = desc->dev;
int ret, value;
struct dm_gpio_ops *ops = gpio_get_ops(dev);
ulong dir_flags;
const struct dm_gpio_ops *ops = gpio_get_ops(dev);
ulong flags;
ret = check_reserved(desc, "get_dir_flags");
ret = check_reserved(desc, "get_flags");
if (ret)
return ret;
/* GPIOD_ are directly provided by driver except GPIOD_ACTIVE_LOW */
if (ops->get_dir_flags) {
ret = ops->get_dir_flags(dev, desc->offset, &dir_flags);
if (ops->get_flags) {
ret = ops->get_flags(dev, desc->offset, &flags);
if (ret)
return ret;
/* GPIOD_ACTIVE_LOW is saved in desc->flags */
value = dir_flags & GPIOD_IS_OUT_ACTIVE ? 1 : 0;
value = flags & GPIOD_IS_OUT_ACTIVE ? 1 : 0;
if (desc->flags & GPIOD_ACTIVE_LOW)
value = !value;
dir_flags &= ~(GPIOD_ACTIVE_LOW | GPIOD_IS_OUT_ACTIVE);
dir_flags |= (desc->flags & GPIOD_ACTIVE_LOW);
flags &= ~(GPIOD_ACTIVE_LOW | GPIOD_IS_OUT_ACTIVE);
flags |= (desc->flags & GPIOD_ACTIVE_LOW);
if (value)
dir_flags |= GPIOD_IS_OUT_ACTIVE;
flags |= GPIOD_IS_OUT_ACTIVE;
} else {
dir_flags = desc->flags;
flags = desc->flags;
/* only GPIOD_IS_OUT_ACTIVE is provided by uclass */
dir_flags &= ~GPIOD_IS_OUT_ACTIVE;
flags &= ~GPIOD_IS_OUT_ACTIVE;
if ((desc->flags & GPIOD_IS_OUT) && _gpio_get_value(desc))
dir_flags |= GPIOD_IS_OUT_ACTIVE;
flags |= GPIOD_IS_OUT_ACTIVE;
}
*flags = dir_flags;
*flagsp = flags;
return 0;
}
@ -785,7 +841,7 @@ static int get_function(struct udevice *dev, int offset, bool skip_unused,
const char **namep)
{
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
struct dm_gpio_ops *ops = gpio_get_ops(dev);
const struct dm_gpio_ops *ops = gpio_get_ops(dev);
BUILD_BUG_ON(GPIOF_COUNT != ARRAY_SIZE(gpio_function));
if (!device_active(dev))
@ -822,7 +878,7 @@ int gpio_get_raw_function(struct udevice *dev, int offset, const char **namep)
int gpio_get_status(struct udevice *dev, int offset, char *buf, int buffsize)
{
struct dm_gpio_ops *ops = gpio_get_ops(dev);
const struct dm_gpio_ops *ops = gpio_get_ops(dev);
struct gpio_dev_priv *priv;
char *str = buf;
int func;
@ -862,7 +918,7 @@ int gpio_get_status(struct udevice *dev, int offset, char *buf, int buffsize)
#if CONFIG_IS_ENABLED(ACPIGEN)
int gpio_get_acpi(const struct gpio_desc *desc, struct acpi_gpio *gpio)
{
struct dm_gpio_ops *ops;
const struct dm_gpio_ops *ops;
memset(gpio, '\0', sizeof(*gpio));
if (!dm_gpio_is_valid(desc)) {
@ -949,6 +1005,71 @@ int dm_gpio_get_values_as_int(const struct gpio_desc *desc_list, int count)
return vector;
}
int dm_gpio_get_values_as_int_base3(struct gpio_desc *desc_list,
int count)
{
static const char tristate[] = "01z";
enum {
PULLUP,
PULLDOWN,
NUM_OPTIONS,
};
int vals[NUM_OPTIONS];
uint mask;
uint vector = 0;
int ret, i;
/*
* Limit to 19 digits which should be plenty. This avoids overflow of a
* 32-bit int
*/
assert(count < 20);
for (i = 0; i < NUM_OPTIONS; i++) {
uint flags = GPIOD_IS_IN;
flags |= (i == PULLDOWN) ? GPIOD_PULL_DOWN : GPIOD_PULL_UP;
ret = dm_gpios_clrset_flags(desc_list, count, GPIOD_MASK_PULL,
flags);
if (ret)
return log_msg_ret("pu", ret);
/* Give the lines time to settle */
udelay(10);
ret = dm_gpio_get_values_as_int(desc_list, count);
if (ret < 0)
return log_msg_ret("get1", ret);
vals[i] = ret;
}
log_debug("values: %x %x, count = %d\n", vals[0], vals[1], count);
for (i = count - 1, mask = 1 << i; i >= 0; i--, mask >>= 1) {
uint pd = vals[PULLDOWN] & mask ? 1 : 0;
uint pu = vals[PULLUP] & mask ? 1 : 0;
uint digit;
/*
* Get value with internal pulldown active. If this is 1 then
* there is a stronger external pullup, which we call 1. If not
* then call it 0.
*
* If the values differ then the pin is floating so we call
* this a 2.
*/
if (pu == pd)
digit = pd;
else
digit = 2;
log_debug("%c ", tristate[digit]);
vector = 3 * vector + digit;
}
log_debug("vector=%d\n", vector);
return vector;
}
/**
* gpio_request_tail: common work for requesting a gpio.
*
@ -1011,7 +1132,10 @@ static int gpio_request_tail(int ret, const char *nodename,
debug("%s: dm_gpio_requestf failed\n", __func__);
goto err;
}
ret = dm_gpio_set_dir_flags(desc, flags);
/* Keep any direction flags provided by the devicetree */
ret = dm_gpio_set_dir_flags(desc,
flags | (desc->flags & GPIOD_MASK_DIR));
if (ret) {
debug("%s: dm_gpio_set_dir failed\n", __func__);
goto err;
@ -1024,6 +1148,7 @@ err:
return ret;
}
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
static int _gpio_request_by_name_nodev(ofnode node, const char *list_name,
int index, struct gpio_desc *desc,
int flags, bool add_index)
@ -1110,6 +1235,7 @@ int gpio_get_list_count(struct udevice *dev, const char *list_name)
return ret;
}
#endif /* OF_PLATDATA */
int dm_gpio_free(struct udevice *dev, struct gpio_desc *desc)
{
@ -1306,10 +1432,10 @@ static int gpio_post_bind(struct udevice *dev)
ops->get_function += gd->reloc_off;
if (ops->xlate)
ops->xlate += gd->reloc_off;
if (ops->set_dir_flags)
ops->set_dir_flags += gd->reloc_off;
if (ops->get_dir_flags)
ops->get_dir_flags += gd->reloc_off;
if (ops->set_flags)
ops->set_flags += gd->reloc_off;
if (ops->get_flags)
ops->get_flags += gd->reloc_off;
reloc_done++;
}

View File

@ -3,6 +3,8 @@
* Copyright 2019 Google LLC
*/
#define LOG_CATEGORY UCLASS_GPIO
#include <common.h>
#include <dm.h>
#include <errno.h>
@ -23,38 +25,6 @@
#include <dm/acpi.h>
#include <dt-bindings/gpio/x86-gpio.h>
static int intel_gpio_direction_input(struct udevice *dev, uint offset)
{
struct udevice *pinctrl = dev_get_parent(dev);
uint config_offset;
config_offset = intel_pinctrl_get_config_reg_offset(pinctrl, offset);
pcr_clrsetbits32(pinctrl, config_offset,
PAD_CFG0_MODE_MASK | PAD_CFG0_TX_STATE |
PAD_CFG0_RX_DISABLE,
PAD_CFG0_MODE_GPIO | PAD_CFG0_TX_DISABLE);
return 0;
}
static int intel_gpio_direction_output(struct udevice *dev, uint offset,
int value)
{
struct udevice *pinctrl = dev_get_parent(dev);
uint config_offset;
config_offset = intel_pinctrl_get_config_reg_offset(pinctrl, offset);
pcr_clrsetbits32(pinctrl, config_offset,
PAD_CFG0_MODE_MASK | PAD_CFG0_RX_STATE |
PAD_CFG0_TX_DISABLE | PAD_CFG0_TX_STATE,
PAD_CFG0_MODE_GPIO | PAD_CFG0_RX_DISABLE |
(value ? PAD_CFG0_TX_STATE : 0));
return 0;
}
static int intel_gpio_get_value(struct udevice *dev, uint offset)
{
struct udevice *pinctrl = dev_get_parent(dev);
@ -130,6 +100,41 @@ static int intel_gpio_xlate(struct udevice *orig_dev, struct gpio_desc *desc,
return 0;
}
static int intel_gpio_set_flags(struct udevice *dev, unsigned int offset,
ulong flags)
{
struct udevice *pinctrl = dev_get_parent(dev);
u32 bic0 = 0, bic1 = 0;
u32 or0, or1;
uint config_offset;
config_offset = intel_pinctrl_get_config_reg_offset(pinctrl, offset);
if (flags & GPIOD_IS_OUT) {
bic0 |= PAD_CFG0_MODE_MASK | PAD_CFG0_RX_STATE |
PAD_CFG0_TX_DISABLE;
or0 |= PAD_CFG0_MODE_GPIO | PAD_CFG0_RX_DISABLE;
} else if (flags & GPIOD_IS_IN) {
bic0 |= PAD_CFG0_MODE_MASK | PAD_CFG0_TX_STATE |
PAD_CFG0_RX_DISABLE;
or0 |= PAD_CFG0_MODE_GPIO | PAD_CFG0_TX_DISABLE;
}
if (flags & GPIOD_PULL_UP) {
bic1 |= PAD_CFG1_PULL_MASK;
or1 |= PAD_CFG1_PULL_UP_20K;
} else if (flags & GPIOD_PULL_DOWN) {
bic1 |= PAD_CFG1_PULL_MASK;
or1 |= PAD_CFG1_PULL_DN_20K;
}
pcr_clrsetbits32(pinctrl, PAD_CFG0_OFFSET(config_offset), bic0, or0);
pcr_clrsetbits32(pinctrl, PAD_CFG1_OFFSET(config_offset), bic1, or1);
log_debug("%s: flags=%lx, offset=%x, config_offset=%x, %x/%x %x/%x\n",
dev->name, flags, offset, config_offset, bic0, or0, bic1, or1);
return 0;
}
#if CONFIG_IS_ENABLED(ACPIGEN)
static int intel_gpio_get_acpi(const struct gpio_desc *desc,
struct acpi_gpio *gpio)
@ -177,12 +182,11 @@ static int intel_gpio_of_to_plat(struct udevice *dev)
}
static const struct dm_gpio_ops gpio_intel_ops = {
.direction_input = intel_gpio_direction_input,
.direction_output = intel_gpio_direction_output,
.get_value = intel_gpio_get_value,
.set_value = intel_gpio_set_value,
.get_function = intel_gpio_get_function,
.xlate = intel_gpio_xlate,
.set_flags = intel_gpio_set_flags,
#if CONFIG_IS_ENABLED(ACPIGEN)
.get_acpi = intel_gpio_get_acpi,
#endif

View File

@ -19,42 +19,51 @@
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/gpio/sandbox-gpio.h>
struct gpio_state {
const char *label; /* label given by requester */
ulong dir_flags; /* dir_flags (GPIOD_...) */
ulong flags; /* flags (GPIOD_...) */
};
/* Access routines for GPIO dir flags */
static ulong *get_gpio_dir_flags(struct udevice *dev, unsigned int offset)
/* Access routines for GPIO info */
static struct gpio_state *get_gpio_state(struct udevice *dev, uint offset)
{
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
struct gpio_state *state = dev_get_priv(dev);
if (offset >= uc_priv->gpio_count) {
static ulong invalid_dir_flags;
printf("sandbox_gpio: error: invalid gpio %u\n", offset);
return &invalid_dir_flags;
return NULL;
}
return &state[offset].dir_flags;
return &state[offset];
}
/* Access routines for GPIO flags */
static ulong *get_gpio_flags(struct udevice *dev, unsigned int offset)
{
struct gpio_state *state = get_gpio_state(dev, offset);
if (!state)
return NULL;
return &state->flags;
}
static int get_gpio_flag(struct udevice *dev, unsigned int offset, ulong flag)
{
return (*get_gpio_dir_flags(dev, offset) & flag) != 0;
return (*get_gpio_flags(dev, offset) & flag) != 0;
}
static int set_gpio_flag(struct udevice *dev, unsigned int offset, ulong flag,
int value)
{
ulong *gpio = get_gpio_dir_flags(dev, offset);
struct gpio_state *state = get_gpio_state(dev, offset);
if (value)
*gpio |= flag;
state->flags |= flag;
else
*gpio &= ~flag;
state->flags &= ~flag;
return 0;
}
@ -65,14 +74,31 @@ static int set_gpio_flag(struct udevice *dev, unsigned int offset, ulong flag,
int sandbox_gpio_get_value(struct udevice *dev, unsigned offset)
{
struct gpio_state *state = get_gpio_state(dev, offset);
bool val;
if (get_gpio_flag(dev, offset, GPIOD_IS_OUT))
debug("sandbox_gpio: get_value on output gpio %u\n", offset);
return get_gpio_flag(dev, offset, GPIOD_IS_OUT_ACTIVE);
if (state->flags & GPIOD_EXT_DRIVEN) {
val = state->flags & GPIOD_EXT_HIGH;
} else {
if (state->flags & GPIOD_EXT_PULL_UP)
val = true;
else if (state->flags & GPIOD_EXT_PULL_DOWN)
val = false;
else
val = state->flags & GPIOD_PULL_UP;
}
return val;
}
int sandbox_gpio_set_value(struct udevice *dev, unsigned offset, int value)
{
return set_gpio_flag(dev, offset, GPIOD_IS_OUT_ACTIVE, value);
set_gpio_flag(dev, offset, GPIOD_EXT_DRIVEN | GPIOD_EXT_HIGH, value);
return 0;
}
int sandbox_gpio_get_direction(struct udevice *dev, unsigned offset)
@ -83,20 +109,23 @@ int sandbox_gpio_get_direction(struct udevice *dev, unsigned offset)
int sandbox_gpio_set_direction(struct udevice *dev, unsigned offset, int output)
{
set_gpio_flag(dev, offset, GPIOD_IS_OUT, output);
set_gpio_flag(dev, offset, GPIOD_IS_IN, !(output));
set_gpio_flag(dev, offset, GPIOD_IS_IN, !output);
return 0;
}
ulong sandbox_gpio_get_dir_flags(struct udevice *dev, unsigned int offset)
ulong sandbox_gpio_get_flags(struct udevice *dev, uint offset)
{
return *get_gpio_dir_flags(dev, offset);
ulong flags = *get_gpio_flags(dev, offset);
return flags & ~GPIOD_SANDBOX_MASK;
}
int sandbox_gpio_set_dir_flags(struct udevice *dev, unsigned int offset,
ulong flags)
int sandbox_gpio_set_flags(struct udevice *dev, uint offset, ulong flags)
{
*get_gpio_dir_flags(dev, offset) = flags;
struct gpio_state *state = get_gpio_state(dev, offset);
state->flags = flags;
return 0;
}
@ -117,10 +146,19 @@ static int sb_gpio_direction_input(struct udevice *dev, unsigned offset)
static int sb_gpio_direction_output(struct udevice *dev, unsigned offset,
int value)
{
int ret;
debug("%s: offset:%u, value = %d\n", __func__, offset, value);
return sandbox_gpio_set_direction(dev, offset, 1) |
sandbox_gpio_set_value(dev, offset, value);
ret = sandbox_gpio_set_direction(dev, offset, 1);
if (ret)
return ret;
ret = set_gpio_flag(dev, offset, GPIOD_IS_OUT_ACTIVE |
GPIOD_EXT_DRIVEN | GPIOD_EXT_HIGH, value);
if (ret)
return ret;
return 0;
}
/* read GPIO IN value of port 'offset' */
@ -134,6 +172,8 @@ static int sb_gpio_get_value(struct udevice *dev, unsigned offset)
/* write GPIO OUT value to port 'offset' */
static int sb_gpio_set_value(struct udevice *dev, unsigned offset, int value)
{
int ret;
debug("%s: offset:%u, value = %d\n", __func__, offset, value);
if (!sandbox_gpio_get_direction(dev, offset)) {
@ -142,7 +182,12 @@ static int sb_gpio_set_value(struct udevice *dev, unsigned offset, int value)
return -1;
}
return sandbox_gpio_set_value(dev, offset, value);
ret = set_gpio_flag(dev, offset, GPIOD_IS_OUT_ACTIVE |
GPIOD_EXT_DRIVEN | GPIOD_EXT_HIGH, value);
if (ret)
return ret;
return 0;
}
static int sb_gpio_get_function(struct udevice *dev, unsigned offset)
@ -177,33 +222,30 @@ static int sb_gpio_xlate(struct udevice *dev, struct gpio_desc *desc,
return 0;
}
static int sb_gpio_set_dir_flags(struct udevice *dev, unsigned int offset,
ulong flags)
static int sb_gpio_set_flags(struct udevice *dev, unsigned int offset,
ulong flags)
{
ulong *dir_flags;
debug("%s: offset:%u, flags = %lx\n", __func__, offset, flags);
struct gpio_state *state = get_gpio_state(dev, offset);
debug("%s: offset:%u, dir_flags = %lx\n", __func__, offset, flags);
dir_flags = get_gpio_dir_flags(dev, offset);
/*
* For testing purposes keep the output value when switching to input.
* This allows us to manipulate the input value via the gpio command.
*/
if (flags & GPIOD_IS_IN)
*dir_flags = (flags & ~GPIOD_IS_OUT_ACTIVE) |
(*dir_flags & GPIOD_IS_OUT_ACTIVE);
else
*dir_flags = flags;
if (flags & GPIOD_IS_OUT) {
flags |= GPIOD_EXT_DRIVEN;
if (flags & GPIOD_IS_OUT_ACTIVE)
flags |= GPIOD_EXT_HIGH;
else
flags &= ~GPIOD_EXT_HIGH;
} else {
flags |= state->flags & GPIOD_SANDBOX_MASK;
}
state->flags = flags;
return 0;
}
static int sb_gpio_get_dir_flags(struct udevice *dev, unsigned int offset,
ulong *flags)
static int sb_gpio_get_flags(struct udevice *dev, uint offset, ulong *flagsp)
{
debug("%s: offset:%u\n", __func__, offset);
*flags = *get_gpio_dir_flags(dev, offset);
*flagsp = *get_gpio_flags(dev, offset) & ~GPIOD_SANDBOX_MASK;
return 0;
}
@ -272,8 +314,8 @@ static const struct dm_gpio_ops gpio_sandbox_ops = {
.set_value = sb_gpio_set_value,
.get_function = sb_gpio_get_function,
.xlate = sb_gpio_xlate,
.set_dir_flags = sb_gpio_set_dir_flags,
.get_dir_flags = sb_gpio_get_dir_flags,
.set_flags = sb_gpio_set_flags,
.get_flags = sb_gpio_get_flags,
#if CONFIG_IS_ENABLED(ACPIGEN)
.get_acpi = sb_gpio_get_acpi,
#endif
@ -456,7 +498,7 @@ static const char *sb_pinctrl_get_pin_name(struct udevice *dev,
return pin_name;
}
static char *get_dir_flags_string(ulong flags)
static char *get_flags_string(ulong flags)
{
if (flags & GPIOD_OPEN_DRAIN)
return "drive-open-drain";
@ -475,7 +517,7 @@ static int sb_pinctrl_get_pin_muxing(struct udevice *dev,
{
struct udevice *gpio_dev;
unsigned int gpio_idx;
ulong dir_flags;
ulong flags;
int function;
/* look up for the bank which owns the requested pin */
@ -484,11 +526,11 @@ static int sb_pinctrl_get_pin_muxing(struct udevice *dev,
snprintf(buf, size, "Error");
} else {
function = sb_gpio_get_function(gpio_dev, gpio_idx);
dir_flags = *get_gpio_dir_flags(gpio_dev, gpio_idx);
flags = *get_gpio_flags(gpio_dev, gpio_idx);
snprintf(buf, size, "gpio %s %s",
function == GPIOF_OUTPUT ? "output" : "input",
get_dir_flags_string(dir_flags));
get_flags_string(flags));
}
return 0;

View File

@ -191,8 +191,8 @@ static int stm32_gpio_get_function(struct udevice *dev, unsigned int offset)
return GPIOF_FUNC;
}
static int stm32_gpio_set_dir_flags(struct udevice *dev, unsigned int offset,
ulong flags)
static int stm32_gpio_set_flags(struct udevice *dev, unsigned int offset,
ulong flags)
{
struct stm32_gpio_priv *priv = dev_get_priv(dev);
struct stm32_gpio_regs *regs = priv->regs;
@ -203,12 +203,13 @@ static int stm32_gpio_set_dir_flags(struct udevice *dev, unsigned int offset,
return idx;
if (flags & GPIOD_IS_OUT) {
int value = GPIOD_FLAGS_OUTPUT(flags);
bool value = flags & GPIOD_IS_OUT_ACTIVE;
if (flags & GPIOD_OPEN_DRAIN)
stm32_gpio_set_otype(regs, idx, STM32_GPIO_OTYPE_OD);
else
stm32_gpio_set_otype(regs, idx, STM32_GPIO_OTYPE_PP);
stm32_gpio_set_moder(regs, idx, STM32_GPIO_MODE_OUT);
writel(BSRR_BIT(idx, value), &regs->bsrr);
@ -223,8 +224,8 @@ static int stm32_gpio_set_dir_flags(struct udevice *dev, unsigned int offset,
return 0;
}
static int stm32_gpio_get_dir_flags(struct udevice *dev, unsigned int offset,
ulong *flags)
static int stm32_gpio_get_flags(struct udevice *dev, unsigned int offset,
ulong *flagsp)
{
struct stm32_gpio_priv *priv = dev_get_priv(dev);
struct stm32_gpio_regs *regs = priv->regs;
@ -259,7 +260,7 @@ static int stm32_gpio_get_dir_flags(struct udevice *dev, unsigned int offset,
default:
break;
}
*flags = dir_flags;
*flagsp = dir_flags;
return 0;
}
@ -270,8 +271,8 @@ static const struct dm_gpio_ops gpio_stm32_ops = {
.get_value = stm32_gpio_get_value,
.set_value = stm32_gpio_set_value,
.get_function = stm32_gpio_get_function,
.set_dir_flags = stm32_gpio_set_dir_flags,
.get_dir_flags = stm32_gpio_get_dir_flags,
.set_flags = stm32_gpio_set_flags,
.get_flags = stm32_gpio_get_flags,
};
static int gpio_stm32_probe(struct udevice *dev)

View File

@ -163,12 +163,14 @@ static int stmfx_gpio_direction_output(struct udevice *dev,
return stmfx_write_reg(dev, STMFX_REG_GPIO_DIR, offset, 1);
}
static int stmfx_gpio_set_dir_flags(struct udevice *dev, unsigned int offset,
ulong flags)
static int stmfx_gpio_set_flags(struct udevice *dev, unsigned int offset,
ulong flags)
{
int ret = -ENOTSUPP;
if (flags & GPIOD_IS_OUT) {
bool value = flags & GPIOD_IS_OUT_ACTIVE;
if (flags & GPIOD_OPEN_SOURCE)
return -ENOTSUPP;
if (flags & GPIOD_OPEN_DRAIN)
@ -177,8 +179,7 @@ static int stmfx_gpio_set_dir_flags(struct udevice *dev, unsigned int offset,
ret = stmfx_conf_set_type(dev, offset, 1);
if (ret)
return ret;
ret = stmfx_gpio_direction_output(dev, offset,
GPIOD_FLAGS_OUTPUT(flags));
ret = stmfx_gpio_direction_output(dev, offset, value);
} else if (flags & GPIOD_IS_IN) {
ret = stmfx_gpio_direction_input(dev, offset);
if (ret)
@ -199,8 +200,8 @@ static int stmfx_gpio_set_dir_flags(struct udevice *dev, unsigned int offset,
return ret;
}
static int stmfx_gpio_get_dir_flags(struct udevice *dev, unsigned int offset,
ulong *flags)
static int stmfx_gpio_get_flags(struct udevice *dev, unsigned int offset,
ulong *flagsp)
{
ulong dir_flags = 0;
int ret;
@ -233,7 +234,7 @@ static int stmfx_gpio_get_dir_flags(struct udevice *dev, unsigned int offset,
dir_flags |= GPIOD_PULL_DOWN;
}
}
*flags = dir_flags;
*flagsp = dir_flags;
return 0;
}
@ -266,8 +267,8 @@ static const struct dm_gpio_ops stmfx_gpio_ops = {
.get_function = stmfx_gpio_get_function,
.direction_input = stmfx_gpio_direction_input,
.direction_output = stmfx_gpio_direction_output,
.set_dir_flags = stmfx_gpio_set_dir_flags,
.get_dir_flags = stmfx_gpio_get_dir_flags,
.set_flags = stmfx_gpio_set_flags,
.get_flags = stmfx_gpio_get_flags,
};
U_BOOT_DRIVER(stmfx_gpio) = {

View File

@ -128,6 +128,12 @@ struct gpio_desc {
#define GPIOD_PULL_UP BIT(7) /* GPIO has pull-up enabled */
#define GPIOD_PULL_DOWN BIT(8) /* GPIO has pull-down enabled */
/* Flags for updating the above */
#define GPIOD_MASK_DIR (GPIOD_IS_OUT | GPIOD_IS_IN | \
GPIOD_IS_OUT_ACTIVE)
#define GPIOD_MASK_DSTYPE (GPIOD_OPEN_DRAIN | GPIOD_OPEN_SOURCE)
#define GPIOD_MASK_PULL (GPIOD_PULL_UP | GPIOD_PULL_DOWN)
uint offset; /* GPIO offset within the device */
/*
* We could consider adding the GPIO label in here. Possibly we could
@ -135,12 +141,6 @@ struct gpio_desc {
*/
};
/* helper to compute the value of the gpio output */
#define GPIOD_FLAGS_OUTPUT_MASK (GPIOD_ACTIVE_LOW | GPIOD_IS_OUT_ACTIVE)
#define GPIOD_FLAGS_OUTPUT(flags) \
(((((flags) & GPIOD_FLAGS_OUTPUT_MASK) == GPIOD_IS_OUT_ACTIVE) || \
(((flags) & GPIOD_FLAGS_OUTPUT_MASK) == GPIOD_ACTIVE_LOW)))
/**
* dm_gpio_is_valid() - Check if a GPIO is valid
*
@ -260,10 +260,32 @@ int gpio_xlate_offs_flags(struct udevice *dev, struct gpio_desc *desc,
struct dm_gpio_ops {
int (*request)(struct udevice *dev, unsigned offset, const char *label);
int (*rfree)(struct udevice *dev, unsigned int offset);
/**
* direction_input() - deprecated
*
* Equivalent to set_flags(...GPIOD_IS_IN)
*/
int (*direction_input)(struct udevice *dev, unsigned offset);
/**
* direction_output() - deprecated
*
* Equivalent to set_flags(...GPIOD_IS_OUT) with GPIOD_IS_OUT_ACTIVE
* also set if @value
*/
int (*direction_output)(struct udevice *dev, unsigned offset,
int value);
int (*get_value)(struct udevice *dev, unsigned offset);
/**
* set_value() - Sets the GPIO value of an output
*
* If the driver provides an @set_flags() method then that is used
* in preference to this, with GPIOD_IS_OUT_ACTIVE set according to
* @value.
*/
int (*set_value)(struct udevice *dev, unsigned offset, int value);
/**
* get_function() Get the GPIO function
@ -301,35 +323,54 @@ struct dm_gpio_ops {
struct ofnode_phandle_args *args);
/**
* set_dir_flags() - Set GPIO dir flags
* set_flags() - Adjust GPIO flags
*
* This function should set up the GPIO configuration according to the
* information provide by the direction flags bitfield.
* information provided by @flags.
*
* This method is optional.
* If any flags cannot be set (e.g. the driver or hardware does not
* support them or this particular GPIO does not have the requested
* feature), the driver should return -EINVAL.
*
* The uclass checks that flags do not obviously conflict (e.g. input
* and output). If the driver finds other conflicts it should return
* -ERECALLCONFLICT
*
* Note that GPIOD_ACTIVE_LOW should be ignored, since the uclass
* adjusts for it automatically. For example, for an output GPIO,
* GPIOD_ACTIVE_LOW causes GPIOD_IS_OUT_ACTIVE to be inverted by the
* uclass, so the driver always sees the value that should be set at the
* pin (1=high, 0=low).
*
* This method is required and should be implemented by new drivers. At
* some point, it will supersede direction_input() and
* direction_output(), which wil be removed.
*
* @dev: GPIO device
* @offset: GPIO offset within that device
* @flags: GPIO configuration to use
* @return 0 if OK, -ve on error
* @flags: New flags value (GPIOD_...)
*
* @return 0 if OK, -EINVAL if unsupported, -ERECALLCONFLICT if flags
* conflict in some * non-obvious way and were not applied,
* other -ve on error
*/
int (*set_dir_flags)(struct udevice *dev, unsigned int offset,
ulong flags);
int (*set_flags)(struct udevice *dev, unsigned int offset, ulong flags);
/**
* get_dir_flags() - Get GPIO dir flags
* get_flags() - Get GPIO flags
*
* This function return the GPIO direction flags used.
* This function return the GPIO flags used. It should read this from
* the hardware directly.
*
* This method is optional.
*
* @dev: GPIO device
* @offset: GPIO offset within that device
* @flags: place to put the used direction flags by GPIO
* @flagsp: place to put the current flags value
* @return 0 if OK, -ve on error
*/
int (*get_dir_flags)(struct udevice *dev, unsigned int offset,
ulong *flags);
int (*get_flags)(struct udevice *dev, unsigned int offset,
ulong *flagsp);
#if CONFIG_IS_ENABLED(ACPIGEN)
/**
@ -456,6 +497,31 @@ int gpio_get_values_as_int(const int *gpio_list);
*/
int dm_gpio_get_values_as_int(const struct gpio_desc *desc_list, int count);
/**
* dm_gpio_get_values_as_int_base3() - Create a base-3 int from a list of GPIOs
*
* This uses pull-ups/pull-downs to figure out whether a GPIO line is externally
* pulled down, pulled up or floating. This allows three different strap values
* for each pin:
* 0 : external pull-down
* 1 : external pull-up
* 2 : floating
*
* With this it is possible to obtain more combinations from the same number of
* strapping pins, when compared to dm_gpio_get_values_as_int(). The external
* pull resistors should be made stronger that the internal SoC pull resistors,
* for this to work.
*
* With 2 pins, 6 combinations are possible, compared with 4
* With 3 pins, 27 are possible, compared with 8
*
* @desc_list: List of GPIOs to collect
* @count: Number of GPIOs
* @return resulting integer value, or -ve on error
*/
int dm_gpio_get_values_as_int_base3(struct gpio_desc *desc_list,
int count);
/**
* gpio_claim_vector() - claim a number of GPIOs for input
*
@ -651,6 +717,25 @@ int dm_gpio_set_value(const struct gpio_desc *desc, int value);
*/
int dm_gpio_set_dir(struct gpio_desc *desc);
/**
* dm_gpio_clrset_flags() - Update flags
*
* This updates the flags as directled. Note that desc->flags is updated by this
* function on success. If any changes cannot be made, best efforts are made.
*
* By use of @clr and @set any of flags can be individually updated, or left
* alone
*
* @desc: GPIO description containing device, offset and flags,
* previously returned by gpio_request_by_name()
* @clr: Flags to clear (GPIOD_...)
* @set: Flags to set (GPIOD_...)
* @return 0 if OK, -EINVAL if the flags had obvious conflicts,
* -ERECALLCONFLICT if there was a non-obvious hardware conflict when attempting
* to set the flags
*/
int dm_gpio_clrset_flags(struct gpio_desc *desc, ulong clr, ulong set);
/**
* dm_gpio_set_dir_flags() - Set direction using description and added flags
*
@ -666,16 +751,31 @@ int dm_gpio_set_dir(struct gpio_desc *desc);
int dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags);
/**
* dm_gpio_get_dir_flags() - Get direction flags
* dm_gpios_clrset_flags() - Sets flags for a set of GPIOs
*
* read the current direction flags
* This clears and sets flags individually for each GPIO.
*
* @desc: List of GPIOs to update
* @count: Number of GPIOs in the list
* @clr: Flags to clear (GPIOD_...), e.g. GPIOD_MASK_DIR if you are
* changing the direction
* @set: Flags to set (GPIOD_...)
* @return 0 if OK, -ve on error
*/
int dm_gpios_clrset_flags(struct gpio_desc *desc, int count, ulong clr,
ulong set);
/**
* dm_gpio_get_flags() - Get flags
*
* Read the current flags
*
* @desc: GPIO description containing device, offset and flags,
* previously returned by gpio_request_by_name()
* @flags: place to put the used flags
* @return 0 if OK, -ve on error, in which case desc->flags is not updated
*/
int dm_gpio_get_dir_flags(struct gpio_desc *desc, ulong *flags);
int dm_gpio_get_flags(struct gpio_desc *desc, ulong *flags);
/**
* gpio_get_number() - Get the global GPIO number of a GPIO

View File

@ -80,15 +80,15 @@ static int dm_test_gpio(struct unit_test_state *uts)
/* Make it an open drain output, and reset it */
ut_asserteq(GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE,
sandbox_gpio_get_dir_flags(dev, offset));
ut_assertok(ops->set_dir_flags(dev, offset,
GPIOD_IS_OUT | GPIOD_OPEN_DRAIN));
sandbox_gpio_get_flags(dev, offset));
ut_assertok(ops->set_flags(dev, offset,
GPIOD_IS_OUT | GPIOD_OPEN_DRAIN));
ut_asserteq(GPIOD_IS_OUT | GPIOD_OPEN_DRAIN,
sandbox_gpio_get_dir_flags(dev, offset));
ut_assertok(ops->set_dir_flags(dev, offset,
GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE));
sandbox_gpio_get_flags(dev, offset));
ut_assertok(ops->set_flags(dev, offset,
GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE));
ut_asserteq(GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE,
sandbox_gpio_get_dir_flags(dev, offset));
sandbox_gpio_get_flags(dev, offset));
/* Make it an input */
ut_assertok(ops->direction_input(dev, offset));
@ -176,54 +176,64 @@ static int dm_test_gpio_opendrain_opensource(struct unit_test_state *uts)
/* GPIO 0 is (GPIO_OUT|GPIO_OPEN_DRAIN) */
ut_asserteq(GPIOD_IS_OUT | GPIOD_OPEN_DRAIN,
sandbox_gpio_get_dir_flags(gpio_c, 0));
sandbox_gpio_get_flags(gpio_c, 0));
/* Set it as output high, should become an input */
/* Set it as output high */
ut_assertok(dm_gpio_set_value(&desc_list[0], 1));
ut_assertok(gpio_get_status(gpio_c, 0, buf, sizeof(buf)));
ut_asserteq_str("c0: input: 0 [x] a-test.test3-gpios0", buf);
ut_asserteq(GPIOD_IS_OUT | GPIOD_OPEN_DRAIN | GPIOD_IS_OUT_ACTIVE,
sandbox_gpio_get_flags(gpio_c, 0));
/* Set it as output low, should become output low */
/* Set it as output low */
ut_assertok(dm_gpio_set_value(&desc_list[0], 0));
ut_assertok(gpio_get_status(gpio_c, 0, buf, sizeof(buf)));
ut_asserteq_str("c0: output: 0 [x] a-test.test3-gpios0", buf);
ut_asserteq(GPIOD_IS_OUT | GPIOD_OPEN_DRAIN,
sandbox_gpio_get_flags(gpio_c, 0));
/* GPIO 1 is (GPIO_OUT|GPIO_OPEN_SOURCE) */
ut_asserteq(GPIOD_IS_OUT | GPIOD_OPEN_SOURCE,
sandbox_gpio_get_dir_flags(gpio_c, 1));
sandbox_gpio_get_flags(gpio_c, 1));
/* Set it as output high, should become output high */
ut_assertok(dm_gpio_set_value(&desc_list[1], 1));
ut_assertok(gpio_get_status(gpio_c, 1, buf, sizeof(buf)));
ut_asserteq_str("c1: output: 1 [x] a-test.test3-gpios1", buf);
/* Set it as output low, should become an input */
/* Set it as output low */
ut_assertok(dm_gpio_set_value(&desc_list[1], 0));
ut_assertok(gpio_get_status(gpio_c, 1, buf, sizeof(buf)));
ut_asserteq_str("c1: input: 1 [x] a-test.test3-gpios1", buf);
ut_asserteq(GPIOD_IS_OUT | GPIOD_OPEN_SOURCE,
sandbox_gpio_get_flags(gpio_c, 1));
/* GPIO 6 is (GPIO_ACTIVE_LOW|GPIO_OUT|GPIO_OPEN_DRAIN) */
ut_asserteq(GPIOD_ACTIVE_LOW | GPIOD_IS_OUT | GPIOD_OPEN_DRAIN,
sandbox_gpio_get_dir_flags(gpio_c, 6));
ut_assertok(gpio_get_status(gpio_c, 1, buf, sizeof(buf)));
ut_asserteq_str("c1: output: 0 [x] a-test.test3-gpios1", buf);
/*
* GPIO 6 is (GPIO_ACTIVE_LOW|GPIO_OUT|GPIO_OPEN_DRAIN). Looking at it
* directlt from the driver, we get GPIOD_IS_OUT_ACTIVE also, since it
* is active low
*/
ut_asserteq(GPIOD_ACTIVE_LOW | GPIOD_IS_OUT | GPIOD_OPEN_DRAIN |
GPIOD_IS_OUT_ACTIVE,
sandbox_gpio_get_flags(gpio_c, 6));
/* Set it as output high, should become output low */
ut_assertok(dm_gpio_set_value(&desc_list[6], 1));
ut_assertok(gpio_get_status(gpio_c, 6, buf, sizeof(buf)));
ut_asserteq_str("c6: output: 0 [x] a-test.test3-gpios6", buf);
/* Set it as output low, should become an input */
/* Set it as output low */
ut_assertok(dm_gpio_set_value(&desc_list[6], 0));
ut_assertok(gpio_get_status(gpio_c, 6, buf, sizeof(buf)));
ut_asserteq_str("c6: input: 0 [x] a-test.test3-gpios6", buf);
ut_asserteq(GPIOD_ACTIVE_LOW | GPIOD_IS_OUT | GPIOD_OPEN_DRAIN |
GPIOD_IS_OUT_ACTIVE,
sandbox_gpio_get_flags(gpio_c, 6));
/* GPIO 7 is (GPIO_ACTIVE_LOW|GPIO_OUT|GPIO_OPEN_SOURCE) */
ut_asserteq(GPIOD_ACTIVE_LOW | GPIOD_IS_OUT | GPIOD_OPEN_SOURCE,
sandbox_gpio_get_dir_flags(gpio_c, 7));
ut_asserteq(GPIOD_ACTIVE_LOW | GPIOD_IS_OUT | GPIOD_OPEN_SOURCE |
GPIOD_IS_OUT_ACTIVE,
sandbox_gpio_get_flags(gpio_c, 7));
/* Set it as output high, should become an input */
/* Set it as output high */
ut_assertok(dm_gpio_set_value(&desc_list[7], 1));
ut_assertok(gpio_get_status(gpio_c, 7, buf, sizeof(buf)));
ut_asserteq_str("c7: input: 0 [x] a-test.test3-gpios7", buf);
ut_asserteq(GPIOD_ACTIVE_LOW | GPIOD_IS_OUT | GPIOD_OPEN_SOURCE,
sandbox_gpio_get_flags(gpio_c, 7));
/* Set it as output low, should become output high */
ut_assertok(dm_gpio_set_value(&desc_list[7], 0));
@ -363,12 +373,12 @@ static int dm_test_gpio_phandles(struct unit_test_state *uts)
ut_assertok(gpio_free_list(dev, desc_list, 3));
ut_asserteq(GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE,
sandbox_gpio_get_dir_flags(gpio_a, 1));
sandbox_gpio_get_flags(gpio_a, 1));
ut_asserteq(6, gpio_request_list_by_name(dev, "test2-gpios", desc_list,
ARRAY_SIZE(desc_list), 0));
/* This was set to output previously but flags resetted to 0 = INPUT */
ut_asserteq(0, sandbox_gpio_get_dir_flags(gpio_a, 1));
ut_asserteq(0, sandbox_gpio_get_flags(gpio_a, 1));
ut_asserteq(GPIOF_INPUT, gpio_get_function(gpio_a, 1, NULL));
/* Active low should invert the input value */
@ -397,22 +407,22 @@ static int dm_test_gpio_get_dir_flags(struct unit_test_state *uts)
ut_asserteq(6, gpio_request_list_by_name(dev, "test3-gpios", desc_list,
ARRAY_SIZE(desc_list), 0));
ut_assertok(dm_gpio_get_dir_flags(&desc_list[0], &flags));
ut_assertok(dm_gpio_get_flags(&desc_list[0], &flags));
ut_asserteq(GPIOD_IS_OUT | GPIOD_OPEN_DRAIN, flags);
ut_assertok(dm_gpio_get_dir_flags(&desc_list[1], &flags));
ut_assertok(dm_gpio_get_flags(&desc_list[1], &flags));
ut_asserteq(GPIOD_IS_OUT | GPIOD_OPEN_SOURCE, flags);
ut_assertok(dm_gpio_get_dir_flags(&desc_list[2], &flags));
ut_assertok(dm_gpio_get_flags(&desc_list[2], &flags));
ut_asserteq(GPIOD_IS_OUT, flags);
ut_assertok(dm_gpio_get_dir_flags(&desc_list[3], &flags));
ut_assertok(dm_gpio_get_flags(&desc_list[3], &flags));
ut_asserteq(GPIOD_IS_IN | GPIOD_PULL_UP, flags);
ut_assertok(dm_gpio_get_dir_flags(&desc_list[4], &flags));
ut_assertok(dm_gpio_get_flags(&desc_list[4], &flags));
ut_asserteq(GPIOD_IS_IN | GPIOD_PULL_DOWN, flags);
ut_assertok(dm_gpio_get_dir_flags(&desc_list[5], &flags));
ut_assertok(dm_gpio_get_flags(&desc_list[5], &flags));
ut_asserteq(GPIOD_IS_IN, flags);
ut_assertok(gpio_free_list(dev, desc_list, 6));
@ -582,3 +592,189 @@ static int dm_test_gpio_devm(struct unit_test_state *uts)
return 0;
}
DM_TEST(dm_test_gpio_devm, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
static int dm_test_clrset_flags(struct unit_test_state *uts)
{
struct gpio_desc desc;
struct udevice *dev;
ulong flags;
ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
ut_asserteq_str("a-test", dev->name);
ut_assertok(gpio_request_by_name(dev, "test-gpios", 1, &desc, 0));
ut_assertok(dm_gpio_clrset_flags(&desc, GPIOD_MASK_DIR, GPIOD_IS_OUT));
ut_assertok(dm_gpio_get_flags(&desc, &flags));
ut_asserteq(GPIOD_IS_OUT, flags);
ut_asserteq(GPIOD_IS_OUT, desc.flags);
ut_asserteq(0, sandbox_gpio_get_value(desc.dev, desc.offset));
ut_assertok(dm_gpio_clrset_flags(&desc, 0, GPIOD_IS_OUT_ACTIVE));
ut_assertok(dm_gpio_get_flags(&desc, &flags));
ut_asserteq(GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE, flags);
ut_asserteq(GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE, desc.flags);
ut_asserteq(1, sandbox_gpio_get_value(desc.dev, desc.offset));
ut_asserteq(1, dm_gpio_get_value(&desc));
ut_assertok(dm_gpio_clrset_flags(&desc, GPIOD_MASK_DIR, GPIOD_IS_IN));
ut_assertok(dm_gpio_get_flags(&desc, &flags));
ut_asserteq(GPIOD_IS_IN, flags & GPIOD_MASK_DIR);
ut_asserteq(GPIOD_IS_IN, desc.flags & GPIOD_MASK_DIR);
ut_assertok(dm_gpio_clrset_flags(&desc, GPIOD_MASK_PULL,
GPIOD_PULL_UP));
ut_assertok(dm_gpio_get_flags(&desc, &flags));
ut_asserteq(GPIOD_IS_IN | GPIOD_PULL_UP, flags);
ut_asserteq(GPIOD_IS_IN | GPIOD_PULL_UP, desc.flags);
/* Check we cannot set both PULL_UP and PULL_DOWN */
ut_asserteq(-EINVAL, dm_gpio_clrset_flags(&desc, 0, GPIOD_PULL_DOWN));
return 0;
}
DM_TEST(dm_test_clrset_flags, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
/* Check that an active-low GPIO works as expected */
static int dm_test_clrset_flags_invert(struct unit_test_state *uts)
{
struct gpio_desc desc;
struct udevice *dev;
ulong flags;
ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
ut_asserteq_str("a-test", dev->name);
ut_assertok(gpio_request_by_name(dev, "test-gpios", 1, &desc,
GPIOD_IS_OUT | GPIOD_ACTIVE_LOW));
/*
* From this size we see it as 0 (active low), but the sandbox driver
* sees the pin value high
*/
ut_asserteq(0, dm_gpio_get_value(&desc));
ut_asserteq(1, sandbox_gpio_get_value(desc.dev, desc.offset));
ut_assertok(dm_gpio_set_value(&desc, 1));
ut_asserteq(1, dm_gpio_get_value(&desc));
ut_asserteq(0, sandbox_gpio_get_value(desc.dev, desc.offset));
/* Do the same with dm_gpio_clrset_flags() */
ut_assertok(dm_gpio_clrset_flags(&desc, GPIOD_IS_OUT_ACTIVE, 0));
ut_asserteq(0, dm_gpio_get_value(&desc));
ut_asserteq(1, sandbox_gpio_get_value(desc.dev, desc.offset));
ut_assertok(dm_gpio_clrset_flags(&desc, 0, GPIOD_IS_OUT_ACTIVE));
ut_asserteq(1, dm_gpio_get_value(&desc));
ut_asserteq(0, sandbox_gpio_get_value(desc.dev, desc.offset));
ut_assertok(dm_gpio_get_flags(&desc, &flags));
ut_asserteq(GPIOD_IS_OUT | GPIOD_ACTIVE_LOW | GPIOD_IS_OUT_ACTIVE,
flags);
ut_asserteq(GPIOD_IS_OUT | GPIOD_ACTIVE_LOW | GPIOD_IS_OUT_ACTIVE,
desc.flags);
ut_assertok(dm_gpio_clrset_flags(&desc, GPIOD_IS_OUT_ACTIVE, 0));
ut_assertok(dm_gpio_get_flags(&desc, &flags));
ut_asserteq(GPIOD_IS_OUT | GPIOD_ACTIVE_LOW, flags);
ut_asserteq(GPIOD_IS_OUT | GPIOD_ACTIVE_LOW, desc.flags);
return 0;
}
DM_TEST(dm_test_clrset_flags_invert, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
static int set_gpios(struct unit_test_state *uts, struct gpio_desc *desc,
int count, uint value)
{
int i;
for (i = 0; i < count; i++) {
const uint mask = 1 << i;
ut_assertok(sandbox_gpio_set_value(desc[i].dev, desc[i].offset,
value & mask));
}
return 0;
}
/* Check that an active-low GPIO works as expected */
static int dm_test_gpio_get_values_as_int(struct unit_test_state *uts)
{
const int gpio_count = 3;
struct gpio_desc desc[gpio_count];
struct udevice *dev;
ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
ut_asserteq_str("a-test", dev->name);
ut_asserteq(3, gpio_request_list_by_name(dev, "test-gpios", desc,
gpio_count, GPIOD_IS_IN));
ut_assertok(set_gpios(uts, desc, gpio_count, 0));
ut_asserteq(0, dm_gpio_get_values_as_int(desc, gpio_count));
ut_assertok(set_gpios(uts, desc, gpio_count, 5));
ut_asserteq(5, dm_gpio_get_values_as_int(desc, gpio_count));
ut_assertok(set_gpios(uts, desc, gpio_count, 7));
ut_asserteq(7, dm_gpio_get_values_as_int(desc, gpio_count));
return 0;
}
DM_TEST(dm_test_gpio_get_values_as_int,
UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
/* Check that an active-low GPIO works as expected */
static int dm_test_gpio_get_values_as_int_base3(struct unit_test_state *uts)
{
const int gpio_count = 3;
struct gpio_desc desc[gpio_count];
struct udevice *dev;
ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
ut_asserteq_str("a-test", dev->name);
ut_asserteq(3, gpio_request_list_by_name(dev, "test-gpios", desc,
gpio_count, GPIOD_IS_IN));
/*
* First test the sandbox GPIO driver works as expected. The external
* pull resistor should be stronger than the internal one.
*/
sandbox_gpio_set_flags(desc[0].dev, desc[0].offset,
GPIOD_IS_IN | GPIOD_EXT_PULL_UP | GPIOD_PULL_UP);
ut_asserteq(1, dm_gpio_get_value(desc));
sandbox_gpio_set_flags(desc[0].dev, desc[0].offset, GPIOD_IS_IN |
GPIOD_EXT_PULL_DOWN | GPIOD_PULL_UP);
ut_asserteq(0, dm_gpio_get_value(desc));
sandbox_gpio_set_flags(desc[0].dev, desc[0].offset,
GPIOD_IS_IN | GPIOD_PULL_UP);
ut_asserteq(1, dm_gpio_get_value(desc));
sandbox_gpio_set_flags(desc[0].dev, desc[0].offset, GPIOD_PULL_DOWN);
ut_asserteq(0, dm_gpio_get_value(desc));
/*
* Set up pins: pull-up (1), pull-down (0) and floating (2). This should
* result in digits 2 0 1, i.e. 2 * 9 + 1 * 3 = 19
*/
sandbox_gpio_set_flags(desc[0].dev, desc[0].offset, GPIOD_EXT_PULL_UP);
sandbox_gpio_set_flags(desc[1].dev, desc[1].offset,
GPIOD_EXT_PULL_DOWN);
sandbox_gpio_set_flags(desc[2].dev, desc[2].offset, 0);
ut_asserteq(19, dm_gpio_get_values_as_int_base3(desc, gpio_count));
/*
* Set up pins: floating (2), pull-up (1) and pull-down (0). This should
* result in digits 0 1 2, i.e. 1 * 3 + 2 = 5
*/
sandbox_gpio_set_flags(desc[0].dev, desc[0].offset, 0);
sandbox_gpio_set_flags(desc[1].dev, desc[1].offset, GPIOD_EXT_PULL_UP);
sandbox_gpio_set_flags(desc[2].dev, desc[2].offset,
GPIOD_EXT_PULL_DOWN);
ut_asserteq(5, dm_gpio_get_values_as_int_base3(desc, gpio_count));
return 0;
}
DM_TEST(dm_test_gpio_get_values_as_int_base3,
UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);