gpio: Add a way to read 3-way strapping pins

Using the internal vs. external pull resistors it is possible to get
27 different combinations from 3 strapping pins. Add an implementation
of this.

This involves updating the sandbox GPIO driver to model external and
(weaker) internal pull resistors. The get_value() method now takes account
of what is driving a pin:

   sandbox: GPIOD_EXT_DRIVEN - in which case GPIO_EXT_HIGH provides the
          value
   outside source - in which case GPIO_EXT_PULL_UP/DOWN indicates the
          external state and we work the final state using those flags and
          the internal GPIOD_PULL_UP/DOWN flags

Of course the outside source does not really exist in sandbox. We are just
modelling it for test purpose.

Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
Simon Glass 2021-02-04 21:22:09 -07:00 committed by Tom Rini
parent be04f1ab42
commit 8a45b22057
5 changed files with 232 additions and 5 deletions

View File

@ -26,8 +26,11 @@
/* 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_SANDBOX_MASK GENMASK(31, 30)
#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)

View File

@ -23,6 +23,7 @@
#include <dm/device_compat.h>
#include <linux/bug.h>
#include <linux/ctype.h>
#include <linux/delay.h>
DECLARE_GLOBAL_DATA_PTR;
@ -723,6 +724,21 @@ int dm_gpio_set_dir(struct gpio_desc *desc)
return _dm_gpio_set_flags(desc, desc->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;
@ -989,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.
*

View File

@ -19,7 +19,6 @@
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/gpio/sandbox-gpio.h>
struct gpio_state {
const char *label; /* label given by requester */
ulong flags; /* flags (GPIOD_...) */
@ -81,10 +80,16 @@ int sandbox_gpio_get_value(struct udevice *dev, unsigned offset)
if (get_gpio_flag(dev, offset, GPIOD_IS_OUT))
debug("sandbox_gpio: get_value on output gpio %u\n", offset);
if (state->flags & GPIOD_EXT_DRIVEN)
if (state->flags & GPIOD_EXT_DRIVEN) {
val = state->flags & GPIOD_EXT_HIGH;
else
val = false;
} 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;
}

View File

@ -497,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
*
@ -725,6 +750,21 @@ int dm_gpio_clrset_flags(struct gpio_desc *desc, ulong clr, ulong set);
*/
int dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags);
/**
* dm_gpios_clrset_flags() - Sets flags for a set of GPIOs
*
* 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
*

View File

@ -680,3 +680,101 @@ static int dm_test_clrset_flags_invert(struct unit_test_state *uts)
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);