From 0e6557b448fa53247b3caf30009d3144c0f2171d Mon Sep 17 00:00:00 2001 From: Tomasz Duszynski Date: Thu, 17 Feb 2022 12:09:23 +0100 Subject: [PATCH] raw/cnxk_gpio: add self test Add support for performing selftest. Signed-off-by: Tomasz Duszynski --- doc/guides/rawdevs/cnxk_gpio.rst | 11 + drivers/raw/cnxk_gpio/cnxk_gpio.c | 1 + drivers/raw/cnxk_gpio/cnxk_gpio.h | 2 + drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c | 386 +++++++++++++++++++++ drivers/raw/cnxk_gpio/meson.build | 1 + 5 files changed, 401 insertions(+) create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c diff --git a/doc/guides/rawdevs/cnxk_gpio.rst b/doc/guides/rawdevs/cnxk_gpio.rst index ad93ec0d44..c03a5b937c 100644 --- a/doc/guides/rawdevs/cnxk_gpio.rst +++ b/doc/guides/rawdevs/cnxk_gpio.rst @@ -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=`` 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. diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 58553ad3d9..230016b078 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -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 diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.h b/drivers/raw/cnxk_gpio/cnxk_gpio.h index c052ca5735..1b31b5a486 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.h +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.h @@ -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); diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c b/drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c new file mode 100644 index 0000000000..6502902f86 --- /dev/null +++ b/drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c @@ -0,0 +1,386 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2021 Marvell. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#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; +} diff --git a/drivers/raw/cnxk_gpio/meson.build b/drivers/raw/cnxk_gpio/meson.build index 9b55f029c7..a75a5b9084 100644 --- a/drivers/raw/cnxk_gpio/meson.build +++ b/drivers/raw/cnxk_gpio/meson.build @@ -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')