raw/cnxk_gpio: add self test
Add support for performing selftest. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com>
This commit is contained in:
parent
aa22c0f384
commit
0e6557b448
@ -182,3 +182,14 @@ Message is used to remove installed interrupt handler.
|
||||
Message must have type set to ``CNXK_GPIO_MSG_TYPE_UNREGISTER_IRQ``.
|
||||
|
||||
Consider using ``rte_pmd_gpio_unregister_gpio()`` wrapper.
|
||||
|
||||
Self test
|
||||
---------
|
||||
|
||||
On EAL initialization CNXK GPIO device will be probed and populated into
|
||||
the list of raw devices on condition ``--vdev=cnxk_gpio,gpiochip=<number>`` was
|
||||
passed. ``rte_rawdev_get_dev_id("CNXK_GPIO")`` returns unique device id. Use
|
||||
this identifier for further rawdev function calls.
|
||||
|
||||
Selftest rawdev API can be used to verify the PMD functionality. Note it blindly
|
||||
assumes that all GPIOs are controllable so some errors during test are expected.
|
||||
|
@ -527,6 +527,7 @@ static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = {
|
||||
.queue_count = cnxk_gpio_queue_count,
|
||||
.queue_setup = cnxk_gpio_queue_setup,
|
||||
.queue_release = cnxk_gpio_queue_release,
|
||||
.dev_selftest = cnxk_gpio_selftest,
|
||||
};
|
||||
|
||||
static int
|
||||
|
@ -23,6 +23,8 @@ struct cnxk_gpiochip {
|
||||
struct cnxk_gpio **gpios;
|
||||
};
|
||||
|
||||
int cnxk_gpio_selftest(uint16_t dev_id);
|
||||
|
||||
int cnxk_gpio_irq_init(struct cnxk_gpiochip *gpiochip);
|
||||
void cnxk_gpio_irq_fini(void);
|
||||
int cnxk_gpio_irq_request(int gpio, int cpu);
|
||||
|
386
drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c
Normal file
386
drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c
Normal file
@ -0,0 +1,386 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright(C) 2021 Marvell.
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <rte_cycles.h>
|
||||
#include <rte_rawdev.h>
|
||||
#include <rte_rawdev_pmd.h>
|
||||
#include <rte_service.h>
|
||||
|
||||
#include "cnxk_gpio.h"
|
||||
#include "rte_pmd_cnxk_gpio.h"
|
||||
|
||||
#define CNXK_GPIO_BUFSZ 128
|
||||
|
||||
#define OTX_IOC_MAGIC 0xF2
|
||||
#define OTX_IOC_TRIGGER_GPIO_HANDLER \
|
||||
_IO(OTX_IOC_MAGIC, 3)
|
||||
|
||||
static int fd;
|
||||
|
||||
static int
|
||||
cnxk_gpio_attr_exists(const char *attr)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
return !stat(attr, &st);
|
||||
}
|
||||
|
||||
static int
|
||||
cnxk_gpio_read_attr(char *attr, char *val)
|
||||
{
|
||||
FILE *fp;
|
||||
int ret;
|
||||
|
||||
fp = fopen(attr, "r");
|
||||
if (!fp)
|
||||
return -errno;
|
||||
|
||||
ret = fscanf(fp, "%s", val);
|
||||
if (ret < 0)
|
||||
return -errno;
|
||||
if (ret != 1)
|
||||
return -EIO;
|
||||
|
||||
ret = fclose(fp);
|
||||
if (ret)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define CNXK_GPIO_ERR_STR(err, str, ...) do { \
|
||||
if (err) { \
|
||||
RTE_LOG(ERR, PMD, "%s:%d: " str " (%d)\n", __func__, __LINE__, \
|
||||
##__VA_ARGS__, err); \
|
||||
goto out; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static int
|
||||
cnxk_gpio_validate_attr(char *attr, const char *expected)
|
||||
{
|
||||
char buf[CNXK_GPIO_BUFSZ];
|
||||
int ret;
|
||||
|
||||
ret = cnxk_gpio_read_attr(attr, buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (strncmp(buf, expected, sizeof(buf)))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define CNXK_GPIO_PATH_FMT "/sys/class/gpio/gpio%d"
|
||||
|
||||
static int
|
||||
cnxk_gpio_test_input(uint16_t dev_id, int base, int gpio)
|
||||
{
|
||||
char buf[CNXK_GPIO_BUFSZ];
|
||||
int ret, n;
|
||||
|
||||
n = snprintf(buf, sizeof(buf), CNXK_GPIO_PATH_FMT, base + gpio);
|
||||
snprintf(buf + n, sizeof(buf) - n, "/direction");
|
||||
|
||||
ret = rte_pmd_gpio_set_pin_dir(dev_id, gpio, CNXK_GPIO_PIN_DIR_IN);
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to set dir to input");
|
||||
ret = cnxk_gpio_validate_attr(buf, "in");
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
|
||||
|
||||
ret = rte_pmd_gpio_set_pin_value(dev_id, gpio, 1) |
|
||||
rte_pmd_gpio_set_pin_value(dev_id, gpio, 0);
|
||||
if (!ret) {
|
||||
ret = -EIO;
|
||||
CNXK_GPIO_ERR_STR(ret, "input pin overwritten");
|
||||
}
|
||||
|
||||
snprintf(buf + n, sizeof(buf) - n, "/edge");
|
||||
|
||||
ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio,
|
||||
CNXK_GPIO_PIN_EDGE_FALLING);
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to set edge to falling");
|
||||
ret = cnxk_gpio_validate_attr(buf, "falling");
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
|
||||
|
||||
ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio,
|
||||
CNXK_GPIO_PIN_EDGE_RISING);
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to change edge to rising");
|
||||
ret = cnxk_gpio_validate_attr(buf, "rising");
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
|
||||
|
||||
ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_BOTH);
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to change edge to both");
|
||||
ret = cnxk_gpio_validate_attr(buf, "both");
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
|
||||
|
||||
ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_NONE);
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to set edge to none");
|
||||
ret = cnxk_gpio_validate_attr(buf, "none");
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
|
||||
|
||||
/*
|
||||
* calling this makes sure kernel driver switches off inverted
|
||||
* logic
|
||||
*/
|
||||
rte_pmd_gpio_set_pin_dir(dev_id, gpio, CNXK_GPIO_PIN_DIR_IN);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
cnxk_gpio_trigger_irq(int gpio)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ioctl(fd, OTX_IOC_TRIGGER_GPIO_HANDLER, gpio);
|
||||
|
||||
return ret == -1 ? -errno : 0;
|
||||
}
|
||||
|
||||
static void
|
||||
cnxk_gpio_irq_handler(int gpio, void *data)
|
||||
{
|
||||
*(int *)data = gpio;
|
||||
}
|
||||
|
||||
static int
|
||||
cnxk_gpio_test_irq(uint16_t dev_id, int gpio)
|
||||
{
|
||||
int irq_data, ret;
|
||||
|
||||
ret = rte_pmd_gpio_set_pin_dir(dev_id, gpio, CNXK_GPIO_PIN_DIR_IN);
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to set dir to input");
|
||||
|
||||
irq_data = 0;
|
||||
ret = rte_pmd_gpio_register_irq(dev_id, gpio, rte_lcore_id(),
|
||||
cnxk_gpio_irq_handler, &irq_data);
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to register irq handler");
|
||||
|
||||
ret = rte_pmd_gpio_enable_interrupt(dev_id, gpio,
|
||||
CNXK_GPIO_PIN_EDGE_RISING);
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to enable interrupt");
|
||||
|
||||
ret = cnxk_gpio_trigger_irq(gpio);
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to trigger irq");
|
||||
rte_delay_ms(1);
|
||||
ret = *(volatile int *)&irq_data == gpio ? 0 : -EIO;
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to test irq");
|
||||
|
||||
ret = rte_pmd_gpio_disable_interrupt(dev_id, gpio);
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to disable interrupt");
|
||||
|
||||
ret = rte_pmd_gpio_unregister_irq(dev_id, gpio);
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to unregister irq handler");
|
||||
out:
|
||||
rte_pmd_gpio_disable_interrupt(dev_id, gpio);
|
||||
rte_pmd_gpio_unregister_irq(dev_id, gpio);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
cnxk_gpio_test_output(uint16_t dev_id, int base, int gpio)
|
||||
{
|
||||
char buf[CNXK_GPIO_BUFSZ];
|
||||
int ret, val, n;
|
||||
|
||||
n = snprintf(buf, sizeof(buf), CNXK_GPIO_PATH_FMT, base + gpio);
|
||||
|
||||
snprintf(buf + n, sizeof(buf) - n, "/direction");
|
||||
ret = rte_pmd_gpio_set_pin_dir(dev_id, gpio, CNXK_GPIO_PIN_DIR_OUT);
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to set dir to out");
|
||||
ret = cnxk_gpio_validate_attr(buf, "out");
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
|
||||
|
||||
snprintf(buf + n, sizeof(buf) - n, "/value");
|
||||
ret = rte_pmd_gpio_set_pin_value(dev_id, gpio, 0);
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to set value to 0");
|
||||
ret = cnxk_gpio_validate_attr(buf, "0");
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
|
||||
ret = rte_pmd_gpio_get_pin_value(dev_id, gpio, &val);
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to read value");
|
||||
if (val)
|
||||
ret = -EIO;
|
||||
CNXK_GPIO_ERR_STR(ret, "read %d instead of 0", val);
|
||||
|
||||
ret = rte_pmd_gpio_set_pin_value(dev_id, gpio, 1);
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to set value to 1");
|
||||
ret = cnxk_gpio_validate_attr(buf, "1");
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
|
||||
ret = rte_pmd_gpio_get_pin_value(dev_id, gpio, &val);
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to read value");
|
||||
if (val != 1)
|
||||
ret = -EIO;
|
||||
CNXK_GPIO_ERR_STR(ret, "read %d instead of 1", val);
|
||||
|
||||
snprintf(buf + n, sizeof(buf) - n, "/direction");
|
||||
ret = rte_pmd_gpio_set_pin_dir(dev_id, gpio, CNXK_GPIO_PIN_DIR_LOW);
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to set dir to low");
|
||||
ret = cnxk_gpio_validate_attr(buf, "out");
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
|
||||
snprintf(buf + n, sizeof(buf) - n, "/value");
|
||||
ret = cnxk_gpio_validate_attr(buf, "0");
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
|
||||
|
||||
snprintf(buf + n, sizeof(buf) - n, "/direction");
|
||||
ret = rte_pmd_gpio_set_pin_dir(dev_id, gpio, CNXK_GPIO_PIN_DIR_HIGH);
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to set dir to high");
|
||||
ret = cnxk_gpio_validate_attr(buf, "out");
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
|
||||
snprintf(buf + n, sizeof(buf) - n, "/value");
|
||||
ret = cnxk_gpio_validate_attr(buf, "1");
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
|
||||
|
||||
snprintf(buf + n, sizeof(buf) - n, "/edge");
|
||||
ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio,
|
||||
CNXK_GPIO_PIN_EDGE_FALLING);
|
||||
ret = ret == 0 ? -EIO : 0;
|
||||
CNXK_GPIO_ERR_STR(ret, "changed edge to falling");
|
||||
ret = cnxk_gpio_validate_attr(buf, "none");
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
|
||||
|
||||
ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio,
|
||||
CNXK_GPIO_PIN_EDGE_RISING);
|
||||
ret = ret == 0 ? -EIO : 0;
|
||||
CNXK_GPIO_ERR_STR(ret, "changed edge to rising");
|
||||
ret = cnxk_gpio_validate_attr(buf, "none");
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
|
||||
|
||||
ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_BOTH);
|
||||
ret = ret == 0 ? -EIO : 0;
|
||||
CNXK_GPIO_ERR_STR(ret, "changed edge to both");
|
||||
ret = cnxk_gpio_validate_attr(buf, "none");
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
|
||||
|
||||
/* this one should succeed */
|
||||
ret = rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_NONE);
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to change edge to none");
|
||||
ret = cnxk_gpio_validate_attr(buf, "none");
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
|
||||
|
||||
snprintf(buf + n, sizeof(buf) - n, "/active_low");
|
||||
ret = rte_pmd_gpio_set_pin_active_low(dev_id, gpio, 1);
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to set active_low to 1");
|
||||
ret = cnxk_gpio_validate_attr(buf, "1");
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
|
||||
|
||||
ret = rte_pmd_gpio_get_pin_active_low(dev_id, gpio, &val);
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to read active_low");
|
||||
if (val != 1)
|
||||
ret = -EIO;
|
||||
CNXK_GPIO_ERR_STR(ret, "read %d instead of 1", val);
|
||||
|
||||
snprintf(buf + n, sizeof(buf) - n, "/value");
|
||||
ret = rte_pmd_gpio_set_pin_value(dev_id, gpio, 1);
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to set value to 1");
|
||||
ret = cnxk_gpio_validate_attr(buf, "1");
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
|
||||
|
||||
ret = rte_pmd_gpio_set_pin_value(dev_id, gpio, 0);
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to set value to 0");
|
||||
ret = cnxk_gpio_validate_attr(buf, "0");
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
|
||||
|
||||
snprintf(buf + n, sizeof(buf) - n, "/active_low");
|
||||
ret = rte_pmd_gpio_set_pin_active_low(dev_id, gpio, 0);
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to set active_low to 0");
|
||||
ret = cnxk_gpio_validate_attr(buf, "0");
|
||||
CNXK_GPIO_ERR_STR(ret, "failed to validate %s", buf);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
cnxk_gpio_selftest(uint16_t dev_id)
|
||||
{
|
||||
struct cnxk_gpiochip *gpiochip;
|
||||
unsigned int queues, i, size;
|
||||
char buf[CNXK_GPIO_BUFSZ];
|
||||
struct rte_rawdev *rawdev;
|
||||
struct cnxk_gpio *gpio;
|
||||
int ret;
|
||||
|
||||
rawdev = rte_rawdev_pmd_get_named_dev("cnxk_gpio");
|
||||
gpiochip = rawdev->dev_private;
|
||||
|
||||
queues = rte_rawdev_queue_count(dev_id);
|
||||
if (queues == 0)
|
||||
return -ENODEV;
|
||||
|
||||
ret = rte_rawdev_start(dev_id);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
fd = open("/dev/otx-gpio-ctr", O_RDWR | O_SYNC);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
for (i = 0; i < queues; i++) {
|
||||
RTE_LOG(INFO, PMD, "testing queue %d (gpio%d)\n", i,
|
||||
gpiochip->base + i);
|
||||
|
||||
ret = rte_rawdev_queue_conf_get(dev_id, i, &size, sizeof(size));
|
||||
if (ret) {
|
||||
RTE_LOG(ERR, PMD,
|
||||
"failed to read queue configuration (%d)\n",
|
||||
ret);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (size != 1) {
|
||||
RTE_LOG(ERR, PMD, "wrong queue size received\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = rte_rawdev_queue_setup(dev_id, i, NULL, 0);
|
||||
if (ret) {
|
||||
RTE_LOG(ERR, PMD, "failed to setup queue (%d)\n", ret);
|
||||
continue;
|
||||
}
|
||||
|
||||
gpio = gpiochip->gpios[i];
|
||||
snprintf(buf, sizeof(buf), CNXK_GPIO_PATH_FMT, gpio->num);
|
||||
if (!cnxk_gpio_attr_exists(buf)) {
|
||||
RTE_LOG(ERR, PMD, "%s does not exist\n", buf);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = cnxk_gpio_test_input(dev_id, gpiochip->base, i);
|
||||
if (ret)
|
||||
goto release;
|
||||
|
||||
ret = cnxk_gpio_test_irq(dev_id, i);
|
||||
if (ret)
|
||||
goto release;
|
||||
|
||||
ret = cnxk_gpio_test_output(dev_id, gpiochip->base, i);
|
||||
if (ret)
|
||||
goto release;
|
||||
|
||||
release:
|
||||
ret = rte_rawdev_queue_release(dev_id, i);
|
||||
if (ret) {
|
||||
RTE_LOG(ERR, PMD, "failed to release queue (%d)\n",
|
||||
ret);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cnxk_gpio_attr_exists(buf)) {
|
||||
RTE_LOG(ERR, PMD, "%s still exists\n", buf);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
@ -6,5 +6,6 @@ deps += ['bus_vdev', 'common_cnxk', 'rawdev', 'kvargs']
|
||||
sources = files(
|
||||
'cnxk_gpio.c',
|
||||
'cnxk_gpio_irq.c',
|
||||
'cnxk_gpio_selftest.c',
|
||||
)
|
||||
headers = files('rte_pmd_cnxk_gpio.h')
|
||||
|
Loading…
Reference in New Issue
Block a user