summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2022-04-10 11:19:14 -0400
committerTom Rini <trini@konsulko.com>2022-04-10 11:19:14 -0400
commit22b7d140fa17a15e85b87cd7258fa4ff190a11a3 (patch)
treec8eb4bb3e20292dc879416ff5b9a5108b8730d04
parentd46e86d25c2a504b3e2e4ab17d70b2f0be440f34 (diff)
parentc170fe0a77738df1344a805acfe53133e5d15e43 (diff)
Merge branch '2022-04-08-gpio-updates'
- Add PCA957X GPIO support, enable GPIO hogging in SPL, add gpio_request_by_line_name() for later use and add some pytests for 'gpio'
-rw-r--r--common/spl/spl.c4
-rw-r--r--drivers/gpio/Kconfig9
-rw-r--r--drivers/gpio/gpio-uclass.c34
-rw-r--r--drivers/gpio/pca953x_gpio.c50
-rw-r--r--include/asm-generic/gpio.h19
-rw-r--r--test/py/tests/test_gpio.py175
6 files changed, 276 insertions, 15 deletions
diff --git a/common/spl/spl.c b/common/spl/spl.c
index b452d4feeb..c9750ee163 100644
--- a/common/spl/spl.c
+++ b/common/spl/spl.c
@@ -20,6 +20,7 @@
#include <serial.h>
#include <spl.h>
#include <asm/global_data.h>
+#include <asm-generic/gpio.h>
#include <asm/u-boot.h>
#include <nand.h>
#include <fat.h>
@@ -743,6 +744,9 @@ void board_init_r(gd_t *dummy1, ulong dummy2)
}
}
+ if (CONFIG_IS_ENABLED(GPIO_HOG))
+ gpio_hog_probe_all();
+
#if CONFIG_IS_ENABLED(BOARD_INIT)
spl_board_init();
#endif
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index a55e368693..89068c7800 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -57,6 +57,15 @@ config GPIO_HOG
is a mechanism providing automatic GPIO request and config-
uration as part of the gpio-controller's driver probe function.
+config SPL_GPIO_HOG
+ bool "Enable GPIO hog support in SPL"
+ depends on SPL_GPIO_SUPPORT
+ help
+ Enable gpio hog support in SPL
+ The GPIO chip may contain GPIO hog definitions. GPIO hogging
+ is a mechanism providing automatic GPIO request and config-
+ uration as part of the gpio-controller's driver probe function.
+
config DM_GPIO_LOOKUP_LABEL
bool "Enable searching for gpio labelnames"
depends on DM_GPIO
diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c
index 125ae53d61..0ed32b7217 100644
--- a/drivers/gpio/gpio-uclass.c
+++ b/drivers/gpio/gpio-uclass.c
@@ -1187,6 +1187,32 @@ int gpio_request_by_name(struct udevice *dev, const char *list_name, int index,
index, desc, flags, index > 0, NULL);
}
+int gpio_request_by_line_name(struct udevice *dev, const char *line_name,
+ struct gpio_desc *desc, int flags)
+{
+ int ret;
+
+ ret = dev_read_stringlist_search(dev, "gpio-line-names", line_name);
+ if (ret < 0)
+ return ret;
+
+ desc->dev = dev;
+ desc->offset = ret;
+ desc->flags = 0;
+
+ ret = dm_gpio_request(desc, line_name);
+ if (ret) {
+ debug("%s: dm_gpio_requestf failed\n", __func__);
+ return ret;
+ }
+
+ ret = dm_gpio_set_dir_flags(desc, flags | desc->flags);
+ if (ret)
+ debug("%s: dm_gpio_set_dir failed\n", __func__);
+
+ return ret;
+}
+
int gpio_request_list_by_name_nodev(ofnode node, const char *list_name,
struct gpio_desc *desc, int max_count,
int flags)
@@ -1432,9 +1458,6 @@ void devm_gpiod_put(struct udevice *dev, struct gpio_desc *desc)
static int gpio_post_bind(struct udevice *dev)
{
- struct udevice *child;
- ofnode node;
-
#if defined(CONFIG_NEEDS_MANUAL_RELOC)
struct dm_gpio_ops *ops = (struct dm_gpio_ops *)device_get_ops(dev);
static int reloc_done;
@@ -1465,7 +1488,10 @@ static int gpio_post_bind(struct udevice *dev)
}
#endif
- if (CONFIG_IS_ENABLED(OF_REAL) && IS_ENABLED(CONFIG_GPIO_HOG)) {
+ if (CONFIG_IS_ENABLED(GPIO_HOG)) {
+ struct udevice *child;
+ ofnode node;
+
dev_for_each_subnode(node, dev) {
if (ofnode_read_bool(node, "gpio-hog")) {
const char *name = ofnode_get_name(node);
diff --git a/drivers/gpio/pca953x_gpio.c b/drivers/gpio/pca953x_gpio.c
index dc8911a8eb..e98e1e56db 100644
--- a/drivers/gpio/pca953x_gpio.c
+++ b/drivers/gpio/pca953x_gpio.c
@@ -35,6 +35,12 @@
#define PCA953X_INVERT 2
#define PCA953X_DIRECTION 3
+#define PCA957X_INPUT 0
+#define PCA957X_OUTPUT 5
+#define PCA957X_INVERT 1
+#define PCA957X_DIRECTION 4
+
+
#define PCA_GPIO_MASK 0x00FF
#define PCA_INT 0x0100
#define PCA953X_TYPE 0x1000
@@ -50,8 +56,29 @@ enum {
#define MAX_BANK 5
#define BANK_SZ 8
+struct pca95xx_reg {
+ int input;
+ int output;
+ int invert;
+ int direction;
+};
+
+static const struct pca95xx_reg pca953x_regs = {
+ .direction = PCA953X_DIRECTION,
+ .output = PCA953X_OUTPUT,
+ .input = PCA953X_INPUT,
+ .invert = PCA953X_INVERT,
+};
+
+static const struct pca95xx_reg pca957x_regs = {
+ .direction = PCA957X_DIRECTION,
+ .output = PCA957X_OUTPUT,
+ .input = PCA957X_INPUT,
+ .invert = PCA957X_INVERT,
+};
+
/*
- * struct pca953x_info - Data for pca953x
+ * struct pca953x_info - Data for pca953x/pca957x
*
* @dev: udevice structure for the device
* @addr: i2c slave address
@@ -61,6 +88,7 @@ enum {
* @bank_count: the number of banks that the device supports
* @reg_output: array to hold the value of output registers
* @reg_direction: array to hold the value of direction registers
+ * @regs: struct to hold the registers addresses
*/
struct pca953x_info {
struct udevice *dev;
@@ -71,6 +99,7 @@ struct pca953x_info {
int bank_count;
u8 reg_output[MAX_BANK];
u8 reg_direction[MAX_BANK];
+ const struct pca95xx_reg *regs;
};
static int pca953x_write_single(struct udevice *dev, int reg, u8 val,
@@ -171,12 +200,13 @@ static int pca953x_is_output(struct udevice *dev, int offset)
static int pca953x_get_value(struct udevice *dev, uint offset)
{
+ struct pca953x_info *info = dev_get_plat(dev);
int ret;
u8 val = 0;
int off = offset % BANK_SZ;
- ret = pca953x_read_single(dev, PCA953X_INPUT, &val, offset);
+ ret = pca953x_read_single(dev, info->regs->input, &val, offset);
if (ret)
return ret;
@@ -196,7 +226,7 @@ static int pca953x_set_value(struct udevice *dev, uint offset, int value)
else
val = info->reg_output[bank] & ~(1 << off);
- ret = pca953x_write_single(dev, PCA953X_OUTPUT, val, offset);
+ ret = pca953x_write_single(dev, info->regs->output, val, offset);
if (ret)
return ret;
@@ -218,7 +248,7 @@ static int pca953x_set_direction(struct udevice *dev, uint offset, int dir)
else
val = info->reg_direction[bank] & ~(1 << off);
- ret = pca953x_write_single(dev, PCA953X_DIRECTION, val, offset);
+ ret = pca953x_write_single(dev, info->regs->direction, val, offset);
if (ret)
return ret;
@@ -296,14 +326,14 @@ static int pca953x_probe(struct udevice *dev)
}
info->chip_type = PCA_CHIP_TYPE(driver_data);
- if (info->chip_type != PCA953X_TYPE) {
- dev_err(dev, "Only support PCA953X chip type now.\n");
- return -EINVAL;
- }
+ if (info->chip_type == PCA953X_TYPE)
+ info->regs = &pca953x_regs;
+ else
+ info->regs = &pca957x_regs;
info->bank_count = DIV_ROUND_UP(info->gpio_count, BANK_SZ);
- ret = pca953x_read_regs(dev, PCA953X_OUTPUT, info->reg_output);
+ ret = pca953x_read_regs(dev, info->regs->output, info->reg_output);
if (ret) {
dev_err(dev, "Error reading output register\n");
return ret;
@@ -327,7 +357,7 @@ static int pca953x_probe(struct udevice *dev)
/* Clear the polarity registers to no invert */
memset(val, 0, MAX_BANK);
- ret = pca953x_write_regs(dev, PCA953X_INVERT, val);
+ ret = pca953x_write_regs(dev, info->regs->invert, val);
if (ret < 0) {
dev_err(dev, "Error writing invert register\n");
return ret;
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index adc19e9765..81f63f06f1 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -579,6 +579,25 @@ int gpio_claim_vector(const int *gpio_num_array, const char *fmt);
int gpio_request_by_name(struct udevice *dev, const char *list_name,
int index, struct gpio_desc *desc, int flags);
+/* gpio_request_by_line_name - Locate and request a GPIO by line name
+ *
+ * Request a GPIO using the offset of the provided line name in the
+ * gpio-line-names property found in the OF node of the GPIO udevice.
+ *
+ * This allows boards to implement common behaviours using GPIOs while not
+ * requiring specific GPIO offsets be used.
+ *
+ * @dev: An instance of a GPIO controller udevice
+ * @line_name: The name of the GPIO (e.g. "bmc-secure-boot")
+ * @desc: A GPIO descriptor that is populated with the requested GPIO
+ * upon return
+ * @flags: The GPIO settings apply to the request
+ * @return 0 if the named line was found and requested successfully, or a
+ * negative error code if the GPIO cannot be found or the request failed.
+ */
+int gpio_request_by_line_name(struct udevice *dev, const char *line_name,
+ struct gpio_desc *desc, int flags);
+
/**
* gpio_request_list_by_name() - Request a list of GPIOs
*
diff --git a/test/py/tests/test_gpio.py b/test/py/tests/test_gpio.py
index 8c64f686b0..109649e2c7 100644
--- a/test/py/tests/test_gpio.py
+++ b/test/py/tests/test_gpio.py
@@ -1,6 +1,16 @@
-# SPDX-License-Identifier: GPL-2.0+
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (c) 2021 Adarsh Babu Kalepalli <opensource.kab@gmail.com>
+# Copyright (c) 2020 Alex Kiernan <alex.kiernan@gmail.com>
import pytest
+import time
+import u_boot_utils
+
+"""
+ test_gpio_input is intended to test the fix 4dbc107f4683.
+ 4dbc107f4683:"cmd: gpio: Correct do_gpio() return value"
+"""
@pytest.mark.boardspec('sandbox')
@pytest.mark.buildconfigspec('cmd_gpio')
@@ -35,3 +45,166 @@ def test_gpio_exit_statuses(u_boot_console):
assert(expected_response in response)
response = u_boot_console.run_command('gpio input 200; echo rc:$?')
assert(expected_response in response)
+
+
+"""
+Generic Tests for 'gpio' command on sandbox and real hardware.
+The below sequence of tests rely on env__gpio_dev_config for configuration values of gpio pins.
+
+ Configuration data for gpio command.
+ The set,clear,toggle ,input and status options of 'gpio' command are verified.
+ For sake of verification,A LED/buzzer could be connected to GPIO pins configured as O/P.
+ Logic level '1'/'0' can be applied onto GPIO pins configured as I/P
+
+
+env__gpio_dev_config = {
+ #the number of 'gpio_str_x' strings should equal to
+ #'gpio_str_count' value
+ 'gpio_str_count':4 ,
+ 'gpio_str_1': '0',
+ 'gpio_str_2': '31',
+ 'gpio_str_3': '63',
+ 'gpio_str_4': '127',
+ 'gpio_op_pin': '64',
+ 'gpio_ip_pin_set':'65',
+ 'gpio_ip_pin_clear':'66',
+ 'gpio_clear_value': 'value is 0',
+ 'gpio_set_value': 'value is 1',
+}
+"""
+
+
+@pytest.mark.buildconfigspec('cmd_gpio')
+def test_gpio_status_all_generic(u_boot_console):
+ """Test the 'gpio status' command.
+
+ Displays all gpio pins available on the Board.
+ To verify if the status of pins is displayed or not,
+ the user can configure (gpio_str_count) and verify existence of certain
+ pins.The details of these can be configured in 'gpio_str_n'.
+ of boardenv_* (example above).User can configure any
+ number of such pins and mention that count in 'gpio_str_count'.
+ """
+
+ f = u_boot_console.config.env.get('env__gpio_dev_config',False)
+ if not f:
+ pytest.skip("gpio not configured")
+
+ gpio_str_count = f['gpio_str_count']
+
+ #Display all the GPIO ports
+ cmd = 'gpio status -a'
+ response = u_boot_console.run_command(cmd)
+
+ for str_value in range(1,gpio_str_count + 1):
+ assert f["gpio_str_%d" %(str_value)] in response
+
+
+@pytest.mark.buildconfigspec('cmd_gpio')
+def test_gpio_set_generic(u_boot_console):
+ """Test the 'gpio set' command.
+
+ A specific gpio pin configured by user as output
+ (mentioned in gpio_op_pin) is verified for
+ 'set' option
+
+ """
+
+ f = u_boot_console.config.env.get('env__gpio_dev_config',False)
+ if not f:
+ pytest.skip("gpio not configured")
+
+ gpio_pin_adr = f['gpio_op_pin'];
+ gpio_set_value = f['gpio_set_value'];
+
+
+ cmd = 'gpio set ' + gpio_pin_adr
+ response = u_boot_console.run_command(cmd)
+ good_response = gpio_set_value
+ assert good_response in response
+
+
+
+@pytest.mark.buildconfigspec('cmd_gpio')
+def test_gpio_clear_generic(u_boot_console):
+ """Test the 'gpio clear' command.
+
+ A specific gpio pin configured by user as output
+ (mentioned in gpio_op_pin) is verified for
+ 'clear' option
+ """
+
+ f = u_boot_console.config.env.get('env__gpio_dev_config',False)
+ if not f:
+ pytest.skip("gpio not configured")
+
+ gpio_pin_adr = f['gpio_op_pin'];
+ gpio_clear_value = f['gpio_clear_value'];
+
+
+ cmd = 'gpio clear ' + gpio_pin_adr
+ response = u_boot_console.run_command(cmd)
+ good_response = gpio_clear_value
+ assert good_response in response
+
+
+@pytest.mark.buildconfigspec('cmd_gpio')
+def test_gpio_toggle_generic(u_boot_console):
+ """Test the 'gpio toggle' command.
+
+ A specific gpio pin configured by user as output
+ (mentioned in gpio_op_pin) is verified for
+ 'toggle' option
+ """
+
+
+ f = u_boot_console.config.env.get('env__gpio_dev_config',False)
+ if not f:
+ pytest.skip("gpio not configured")
+
+ gpio_pin_adr = f['gpio_op_pin'];
+ gpio_set_value = f['gpio_set_value'];
+ gpio_clear_value = f['gpio_clear_value'];
+
+ cmd = 'gpio set ' + gpio_pin_adr
+ response = u_boot_console.run_command(cmd)
+ good_response = gpio_set_value
+ assert good_response in response
+
+ cmd = 'gpio toggle ' + gpio_pin_adr
+ response = u_boot_console.run_command(cmd)
+ good_response = gpio_clear_value
+ assert good_response in response
+
+
+@pytest.mark.buildconfigspec('cmd_gpio')
+def test_gpio_input_generic(u_boot_console):
+ """Test the 'gpio input' command.
+
+ Specific gpio pins configured by user as input
+ (mentioned in gpio_ip_pin_set and gpio_ip_pin_clear)
+ is verified for logic '1' and logic '0' states
+ """
+
+ f = u_boot_console.config.env.get('env__gpio_dev_config',False)
+ if not f:
+ pytest.skip("gpio not configured")
+
+ gpio_pin_adr = f['gpio_ip_pin_clear'];
+ gpio_clear_value = f['gpio_clear_value'];
+
+
+ cmd = 'gpio input ' + gpio_pin_adr
+ response = u_boot_console.run_command(cmd)
+ good_response = gpio_clear_value
+ assert good_response in response
+
+
+ gpio_pin_adr = f['gpio_ip_pin_set'];
+ gpio_set_value = f['gpio_set_value'];
+
+
+ cmd = 'gpio input ' + gpio_pin_adr
+ response = u_boot_console.run_command(cmd)
+ good_response = gpio_set_value
+ assert good_response in response