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'
This commit is contained in:
Tom Rini 2022-04-10 11:19:14 -04:00
commit 22b7d140fa
6 changed files with 276 additions and 15 deletions

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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;

View file

@ -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
*

View file

@ -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