mirror of
https://github.com/brain-hackers/u-boot-brain
synced 2024-09-27 23:20:26 +09:00
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:
parent
be04f1ab42
commit
8a45b22057
@ -26,8 +26,11 @@
|
|||||||
/* Our own private GPIO flags, which musn't conflict with GPIOD_... */
|
/* 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_HIGH BIT(31) /* external source is high (else low) */
|
||||||
#define GPIOD_EXT_DRIVEN BIT(30) /* external source is driven */
|
#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)
|
* Return the simulated value of a GPIO (used only in sandbox test code)
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include <dm/device_compat.h>
|
#include <dm/device_compat.h>
|
||||||
#include <linux/bug.h>
|
#include <linux/bug.h>
|
||||||
#include <linux/ctype.h>
|
#include <linux/ctype.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
|
||||||
DECLARE_GLOBAL_DATA_PTR;
|
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);
|
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)
|
int dm_gpio_get_flags(struct gpio_desc *desc, ulong *flagsp)
|
||||||
{
|
{
|
||||||
struct udevice *dev = desc->dev;
|
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;
|
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.
|
* gpio_request_tail: common work for requesting a gpio.
|
||||||
*
|
*
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
#include <dt-bindings/gpio/gpio.h>
|
#include <dt-bindings/gpio/gpio.h>
|
||||||
#include <dt-bindings/gpio/sandbox-gpio.h>
|
#include <dt-bindings/gpio/sandbox-gpio.h>
|
||||||
|
|
||||||
|
|
||||||
struct gpio_state {
|
struct gpio_state {
|
||||||
const char *label; /* label given by requester */
|
const char *label; /* label given by requester */
|
||||||
ulong flags; /* flags (GPIOD_...) */
|
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))
|
if (get_gpio_flag(dev, offset, GPIOD_IS_OUT))
|
||||||
debug("sandbox_gpio: get_value on output gpio %u\n", offset);
|
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;
|
val = state->flags & GPIOD_EXT_HIGH;
|
||||||
else
|
} else {
|
||||||
|
if (state->flags & GPIOD_EXT_PULL_UP)
|
||||||
|
val = true;
|
||||||
|
else if (state->flags & GPIOD_EXT_PULL_DOWN)
|
||||||
val = false;
|
val = false;
|
||||||
|
else
|
||||||
|
val = state->flags & GPIOD_PULL_UP;
|
||||||
|
}
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
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
|
* 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);
|
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
|
* dm_gpio_get_flags() - Get flags
|
||||||
*
|
*
|
||||||
|
@ -680,3 +680,101 @@ static int dm_test_clrset_flags_invert(struct unit_test_state *uts)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
DM_TEST(dm_test_clrset_flags_invert, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
|
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);
|
||||||
|
Loading…
Reference in New Issue
Block a user