* [DPDK 22.02 PATCH 00/10] Add cnxk_gpio PMD @ 2021-11-17 0:21 Tomasz Duszynski 2021-11-17 0:21 ` [PATCH 01/10] raw/cnxk_gpio: add GPIO driver skeleton Tomasz Duszynski ` (10 more replies) 0 siblings, 11 replies; 75+ messages in thread From: Tomasz Duszynski @ 2021-11-17 0:21 UTC (permalink / raw) To: dev; +Cc: jerinj, Tomasz Duszynski This series introduces a new rawdevice PMD which allows to manage userspace GPIOs and install custom GPIO interrupt handlers which bypass kernel. This is especially useful for applications that, besides providing standard dataplane functionality, want to have fast and low latency access to GPIO pin state. It'd be great to have that merged during 22.02 merge window. Tomasz Duszynski (10): raw/cnxk_gpio: add GPIO driver skeleton raw/cnxk_gpio: support reading default queue conf raw/cnxk_gpio: support reading queue count raw/cnxk_gpio: support queue setup raw/cnxk_gpio: support queue release raw/cnxk_gpio: support enqueuing buffers raw/cnxk_gpio: support dequeuing buffers raw/cnxk_gpio: support standard GPIO operations raw/cnxk_gpio: support custom irq handlers raw/cnxk_gpio: support selftest doc/guides/rawdevs/cnxk_gpio.rst | 195 +++++++ doc/guides/rawdevs/index.rst | 1 + drivers/raw/cnxk_gpio/cnxk_gpio.c | 633 +++++++++++++++++++++ drivers/raw/cnxk_gpio/cnxk_gpio.h | 31 + drivers/raw/cnxk_gpio/cnxk_gpio_irq.c | 216 +++++++ drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c | 442 ++++++++++++++ drivers/raw/cnxk_gpio/meson.build | 11 + drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h | 429 ++++++++++++++ drivers/raw/cnxk_gpio/version.map | 3 + drivers/raw/meson.build | 1 + 10 files changed, 1962 insertions(+) create mode 100644 doc/guides/rawdevs/cnxk_gpio.rst create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio.c create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio.h create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio_irq.c create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c create mode 100644 drivers/raw/cnxk_gpio/meson.build create mode 100644 drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h create mode 100644 drivers/raw/cnxk_gpio/version.map -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH 01/10] raw/cnxk_gpio: add GPIO driver skeleton 2021-11-17 0:21 [DPDK 22.02 PATCH 00/10] Add cnxk_gpio PMD Tomasz Duszynski @ 2021-11-17 0:21 ` Tomasz Duszynski 2021-11-17 0:21 ` [PATCH 02/10] raw/cnxk_gpio: support reading default queue conf Tomasz Duszynski ` (9 subsequent siblings) 10 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2021-11-17 0:21 UTC (permalink / raw) To: dev, Ray Kinsella, Anatoly Burakov; +Cc: jerinj, Tomasz Duszynski Add initial support for PMD that allows to control particular pins form userspace. Moreover PMD allows to attach custom interrupt handlers to controllable GPIOs. Main users of this PMD are dataplain applications requiring fast and low latency access to pin state. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- doc/guides/rawdevs/cnxk_gpio.rst | 65 +++++++++ doc/guides/rawdevs/index.rst | 1 + drivers/raw/cnxk_gpio/cnxk_gpio.c | 235 ++++++++++++++++++++++++++++++ drivers/raw/cnxk_gpio/cnxk_gpio.h | 20 +++ drivers/raw/cnxk_gpio/meson.build | 8 + drivers/raw/cnxk_gpio/version.map | 3 + drivers/raw/meson.build | 1 + 7 files changed, 333 insertions(+) create mode 100644 doc/guides/rawdevs/cnxk_gpio.rst create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio.c create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio.h create mode 100644 drivers/raw/cnxk_gpio/meson.build create mode 100644 drivers/raw/cnxk_gpio/version.map diff --git a/doc/guides/rawdevs/cnxk_gpio.rst b/doc/guides/rawdevs/cnxk_gpio.rst new file mode 100644 index 0000000000..868302d07f --- /dev/null +++ b/doc/guides/rawdevs/cnxk_gpio.rst @@ -0,0 +1,65 @@ +.. SPDX-License-Identifier: BSD-3-Clause + Copyright(c) 2021 Marvell. + +Marvell CNXK GPIO Driver +======================== + +CNXK GPIO PMD configures and manages GPIOs available on the system using +standard enqueue/dequeue mechanism offered by raw device abstraction. PMD relies +both on standard sysfs GPIO interface provided by the Linux kernel and GPIO +kernel driver custom interface allowing one to install userspace interrupt +handlers. + +Features +-------- + +Following features are available: + +- export/unexport a GPIO +- read/write specific value from/to exported GPIO +- set GPIO direction +- set GPIO edge that triggers interrupt +- set GPIO active low +- register interrupt handler for specific GPIO + +Requirements +------------ + +PMD relies on modified kernel GPIO driver which exposes ``ioctl()`` interface +for installing interrupt handlers for low latency signal processing. + +Driver is shipped with Marvell SDK. + +Device Setup +------------ + +CNXK GPIO PMD binds to virtual device which gets created by passing +`--vdev=cnxk_gpio,gpiochip=<number>` command line to EAL. `gpiochip` parameter +tells PMD which GPIO controller should be used. Available controllers are +available under `/sys/class/gpio`. For further details on how Linux represents +GPIOs in userspace please refer to +`sysfs.txt <https://www.kernel.org/doc/Documentation/gpio/sysfs.txt>`_. + +If `gpiochip=<number>` was omitted then first gpiochip from the alphabetically +sort list of available gpiochips is used. + +.. code-block:: console + + $ ls /sys/class/gpio + export gpiochip448 unexport + +In above scenario only one GPIO controller is present hence +`--vdev=cnxk_gpio,gpiochip=448` should be passed to EAL. + +Before performing actual data transfer one needs to call +``rte_rawdev_queue_count()`` followed by ``rte_rawdev_queue_conf_get()``. The +former returns number GPIOs available in the system irrespective of GPIOs +being controllable or not. Thus it is user responsibility to pick the proper +ones. The latter call simply returns queue capacity. + +Respective queue needs to be configured with ``rte_rawdev_queue_setup()``. This +call barely exports GPIO to userspace. + +To perform actual data transfer use standard ``rte_rawdev_enqueue_buffers()`` +and ``rte_rawdev_dequeue_buffers()`` APIs. Not all messages produce sensible +responses hence dequeueing is not always necessary. diff --git a/doc/guides/rawdevs/index.rst b/doc/guides/rawdevs/index.rst index b6cf917443..0c02da6e90 100644 --- a/doc/guides/rawdevs/index.rst +++ b/doc/guides/rawdevs/index.rst @@ -12,6 +12,7 @@ application through rawdev API. :numbered: cnxk_bphy + cnxk_gpio dpaa2_cmdif dpaa2_qdma ifpga diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c new file mode 100644 index 0000000000..bcce4b8fb7 --- /dev/null +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -0,0 +1,235 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2021 Marvell. + */ + +#include <dirent.h> +#include <string.h> + +#include <rte_bus_vdev.h> +#include <rte_eal.h> +#include <rte_kvargs.h> +#include <rte_lcore.h> +#include <rte_rawdev_pmd.h> + +#include <roc_api.h> + +#include "cnxk_gpio.h" + +#define CNXK_GPIO_BUFSZ 128 +#define CNXK_GPIO_CLASS_PATH "/sys/class/gpio" + +static const char *const cnxk_gpio_args[] = { +#define CNXK_GPIO_ARG_GPIOCHIP "gpiochip" + CNXK_GPIO_ARG_GPIOCHIP, + NULL +}; + +static void +cnxk_gpio_format_name(char *name, size_t len) +{ + snprintf(name, len, "cnxk_gpio"); +} + +static int +cnxk_gpio_filter_gpiochip(const struct dirent *dirent) +{ + const char *pattern = "gpiochip"; + + return !strncmp(dirent->d_name, pattern, strlen(pattern)); +} + +static void +cnxk_gpio_set_defaults(struct cnxk_gpiochip *gpiochip) +{ + struct dirent **namelist; + int n; + + n = scandir(CNXK_GPIO_CLASS_PATH, &namelist, cnxk_gpio_filter_gpiochip, + alphasort); + if (n < 0 || n == 0) + return; + + sscanf(namelist[0]->d_name, "gpiochip%d", &gpiochip->num); + while (n--) + free(namelist[n]); + free(namelist); +} + +static int +cnxk_gpio_parse_arg_gpiochip(const char *key __rte_unused, const char *value, + void *extra_args) +{ + long val; + + errno = 0; + val = strtol(value, NULL, 10); + if (errno) + return -errno; + + *(int *)extra_args = (int)val; + + return 0; +} + +static int +cnxk_gpio_parse_args(struct cnxk_gpiochip *gpiochip, + struct rte_devargs *devargs) +{ + struct rte_kvargs *kvlist; + int ret; + + kvlist = rte_kvargs_parse(devargs->args, cnxk_gpio_args); + if (!kvlist) + return 0; + + ret = rte_kvargs_count(kvlist, CNXK_GPIO_ARG_GPIOCHIP); + if (ret == 1) { + ret = rte_kvargs_process(kvlist, CNXK_GPIO_ARG_GPIOCHIP, + cnxk_gpio_parse_arg_gpiochip, + &gpiochip->num); + if (ret) + return ret; + } + + return 0; +} + +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; +} + +static int +cnxk_gpio_read_attr_int(char *attr, int *val) +{ + char buf[CNXK_GPIO_BUFSZ]; + int ret; + + ret = cnxk_gpio_read_attr(attr, buf); + if (ret) + return ret; + + ret = sscanf(buf, "%d", val); + if (ret < 0) + return -errno; + + return 0; +} + +static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { +}; + +static int +cnxk_gpio_probe(struct rte_vdev_device *dev) +{ + char name[RTE_RAWDEV_NAME_MAX_LEN]; + struct cnxk_gpiochip *gpiochip; + struct rte_rawdev *rawdev; + char buf[CNXK_GPIO_BUFSZ]; + int ret; + + if (rte_eal_process_type() != RTE_PROC_PRIMARY) + return 0; + + cnxk_gpio_format_name(name, sizeof(name)); + rawdev = rte_rawdev_pmd_allocate(name, sizeof(*gpiochip), + rte_socket_id()); + if (!rawdev) { + RTE_LOG(ERR, PMD, "failed to allocate %s rawdev", name); + return -ENOMEM; + } + + rawdev->dev_ops = &cnxk_gpio_rawdev_ops; + rawdev->device = &dev->device; + rawdev->driver_name = dev->device.name; + + gpiochip = rawdev->dev_private; + cnxk_gpio_set_defaults(gpiochip); + + /* defaults may be overwritten by this call */ + ret = cnxk_gpio_parse_args(gpiochip, dev->device.devargs); + if (ret) + goto out; + + /* read gpio base */ + snprintf(buf, sizeof(buf), "%s/gpiochip%d/base", CNXK_GPIO_CLASS_PATH, + gpiochip->num); + ret = cnxk_gpio_read_attr_int(buf, &gpiochip->base); + if (ret) { + RTE_LOG(ERR, PMD, "failed to read %s", buf); + goto out; + } + + /* read number of available gpios */ + snprintf(buf, sizeof(buf), "%s/gpiochip%d/ngpio", CNXK_GPIO_CLASS_PATH, + gpiochip->num); + ret = cnxk_gpio_read_attr_int(buf, &gpiochip->num_gpios); + if (ret) { + RTE_LOG(ERR, PMD, "failed to read %s", buf); + goto out; + } + + gpiochip->gpios = rte_calloc(NULL, gpiochip->num_gpios, + sizeof(struct cnxk_gpio *), 0); + if (!gpiochip->gpios) { + RTE_LOG(ERR, PMD, "failed to allocate gpios memory"); + ret = -ENOMEM; + goto out; + } + + return 0; +out: + rte_rawdev_pmd_release(rawdev); + + return ret; +} + +static int +cnxk_gpio_remove(struct rte_vdev_device *dev) +{ + char name[RTE_RAWDEV_NAME_MAX_LEN]; + struct cnxk_gpiochip *gpiochip; + struct rte_rawdev *rawdev; + + RTE_SET_USED(dev); + + if (rte_eal_process_type() != RTE_PROC_PRIMARY) + return 0; + + cnxk_gpio_format_name(name, sizeof(name)); + rawdev = rte_rawdev_pmd_get_named_dev(name); + if (!rawdev) + return -ENODEV; + + gpiochip = rawdev->dev_private; + rte_free(gpiochip->gpios); + rte_rawdev_pmd_release(rawdev); + + return 0; +} + +static struct rte_vdev_driver cnxk_gpio_drv = { + .probe = cnxk_gpio_probe, + .remove = cnxk_gpio_remove, +}; + +RTE_PMD_REGISTER_VDEV(cnxk_gpio, cnxk_gpio_drv); +RTE_PMD_REGISTER_PARAM_STRING(cnxk_gpio, "gpiochip=<int>"); diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.h b/drivers/raw/cnxk_gpio/cnxk_gpio.h new file mode 100644 index 0000000000..0ee9789a01 --- /dev/null +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2021 Marvell. + */ + +#ifndef _CNXK_GPIO_H_ +#define _CNXK_GPIO_H_ + +struct cnxk_gpio { + struct cnxk_gpiochip *gpiochip; + int num; +}; + +struct cnxk_gpiochip { + int num; + int base; + int num_gpios; + struct cnxk_gpio **gpios; +}; + +#endif /* _CNXK_GPIO_H_ */ diff --git a/drivers/raw/cnxk_gpio/meson.build b/drivers/raw/cnxk_gpio/meson.build new file mode 100644 index 0000000000..9a7e716c1e --- /dev/null +++ b/drivers/raw/cnxk_gpio/meson.build @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(C) 2021 Marvell. +# + +deps += ['bus_vdev', 'common_cnxk', 'rawdev', 'kvargs'] +sources = files( + 'cnxk_gpio.c', +) diff --git a/drivers/raw/cnxk_gpio/version.map b/drivers/raw/cnxk_gpio/version.map new file mode 100644 index 0000000000..4a76d1d52d --- /dev/null +++ b/drivers/raw/cnxk_gpio/version.map @@ -0,0 +1,3 @@ +DPDK_21 { + local: *; +}; diff --git a/drivers/raw/meson.build b/drivers/raw/meson.build index 87694a758e..4b52e93945 100644 --- a/drivers/raw/meson.build +++ b/drivers/raw/meson.build @@ -7,6 +7,7 @@ endif drivers = [ 'cnxk_bphy', + 'cnxk_gpio', 'dpaa2_cmdif', 'dpaa2_qdma', 'ifpga', -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH 02/10] raw/cnxk_gpio: support reading default queue conf 2021-11-17 0:21 [DPDK 22.02 PATCH 00/10] Add cnxk_gpio PMD Tomasz Duszynski 2021-11-17 0:21 ` [PATCH 01/10] raw/cnxk_gpio: add GPIO driver skeleton Tomasz Duszynski @ 2021-11-17 0:21 ` Tomasz Duszynski 2021-11-17 0:21 ` [PATCH 03/10] raw/cnxk_gpio: support reading queue count Tomasz Duszynski ` (8 subsequent siblings) 10 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2021-11-17 0:21 UTC (permalink / raw) To: dev; +Cc: jerinj, Tomasz Duszynski Add support for reading default queue configuration. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index bcce4b8fb7..deedf98af2 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -134,7 +134,26 @@ cnxk_gpio_read_attr_int(char *attr, int *val) return 0; } +static int +cnxk_gpio_queue_def_conf(struct rte_rawdev *dev, uint16_t queue_id, + rte_rawdev_obj_t queue_conf, size_t queue_conf_size) +{ + unsigned int *conf; + + RTE_SET_USED(dev); + RTE_SET_USED(queue_id); + + if (queue_conf_size != sizeof(*conf)) + return -EINVAL; + + conf = (unsigned int *)queue_conf; + *conf = 1; + + return 0; +} + static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { + .queue_def_conf = cnxk_gpio_queue_def_conf, }; static int -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH 03/10] raw/cnxk_gpio: support reading queue count 2021-11-17 0:21 [DPDK 22.02 PATCH 00/10] Add cnxk_gpio PMD Tomasz Duszynski 2021-11-17 0:21 ` [PATCH 01/10] raw/cnxk_gpio: add GPIO driver skeleton Tomasz Duszynski 2021-11-17 0:21 ` [PATCH 02/10] raw/cnxk_gpio: support reading default queue conf Tomasz Duszynski @ 2021-11-17 0:21 ` Tomasz Duszynski 2021-11-17 0:21 ` [PATCH 04/10] raw/cnxk_gpio: support queue setup Tomasz Duszynski ` (7 subsequent siblings) 10 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2021-11-17 0:21 UTC (permalink / raw) To: dev; +Cc: jerinj, Tomasz Duszynski Add support for reading number of available queues. Single queue corresponds to GPIO. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index deedf98af2..84be7f861e 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -152,8 +152,17 @@ cnxk_gpio_queue_def_conf(struct rte_rawdev *dev, uint16_t queue_id, return 0; } +static uint16_t +cnxk_gpio_queue_count(struct rte_rawdev *dev) +{ + struct cnxk_gpiochip *gpiochip = dev->dev_private; + + return gpiochip->num_gpios; +} + static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { .queue_def_conf = cnxk_gpio_queue_def_conf, + .queue_count = cnxk_gpio_queue_count, }; static int -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH 04/10] raw/cnxk_gpio: support queue setup 2021-11-17 0:21 [DPDK 22.02 PATCH 00/10] Add cnxk_gpio PMD Tomasz Duszynski ` (2 preceding siblings ...) 2021-11-17 0:21 ` [PATCH 03/10] raw/cnxk_gpio: support reading queue count Tomasz Duszynski @ 2021-11-17 0:21 ` Tomasz Duszynski 2021-11-17 0:21 ` [PATCH 05/10] raw/cnxk_gpio: support queue release Tomasz Duszynski ` (6 subsequent siblings) 10 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2021-11-17 0:21 UTC (permalink / raw) To: dev; +Cc: jerinj, Tomasz Duszynski Add support for queue setup. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 80 +++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 84be7f861e..98b5dd9cd8 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -134,6 +134,85 @@ cnxk_gpio_read_attr_int(char *attr, int *val) return 0; } +static int +cnxk_gpio_write_attr(const char *attr, const char *val) +{ + FILE *fp; + int ret; + + if (!val) + return -EINVAL; + + fp = fopen(attr, "w"); + if (!fp) + return -errno; + + ret = fprintf(fp, "%s", val); + if (ret < 0) { + fclose(fp); + return ret; + } + + ret = fclose(fp); + if (ret) + return -errno; + + return 0; +} + +static int +cnxk_gpio_write_attr_int(const char *attr, int val) +{ + char buf[CNXK_GPIO_BUFSZ]; + + snprintf(buf, sizeof(buf), "%d", val); + + return cnxk_gpio_write_attr(attr, buf); +} + +static struct cnxk_gpio * +cnxk_gpio_lookup(struct cnxk_gpiochip *gpiochip, uint16_t queue) +{ + if (queue >= gpiochip->num_gpios) + return NULL; + + return gpiochip->gpios[queue]; +} + +static int +cnxk_gpio_queue_setup(struct rte_rawdev *dev, uint16_t queue_id, + rte_rawdev_obj_t queue_conf, size_t queue_conf_size) +{ + struct cnxk_gpiochip *gpiochip = dev->dev_private; + char buf[CNXK_GPIO_BUFSZ]; + struct cnxk_gpio *gpio; + int ret; + + RTE_SET_USED(queue_conf); + RTE_SET_USED(queue_conf_size); + + gpio = cnxk_gpio_lookup(gpiochip, queue_id); + if (gpio) + return -EEXIST; + + gpio = rte_zmalloc(NULL, sizeof(*gpio), 0); + if (!gpio) + return -ENOMEM; + gpio->num = queue_id + gpiochip->base; + gpio->gpiochip = gpiochip; + + snprintf(buf, sizeof(buf), "%s/export", CNXK_GPIO_CLASS_PATH); + ret = cnxk_gpio_write_attr_int(buf, gpio->num); + if (ret) { + rte_free(gpio); + return ret; + } + + gpiochip->gpios[queue_id] = gpio; + + return 0; +} + static int cnxk_gpio_queue_def_conf(struct rte_rawdev *dev, uint16_t queue_id, rte_rawdev_obj_t queue_conf, size_t queue_conf_size) @@ -163,6 +242,7 @@ cnxk_gpio_queue_count(struct rte_rawdev *dev) static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { .queue_def_conf = cnxk_gpio_queue_def_conf, .queue_count = cnxk_gpio_queue_count, + .queue_setup = cnxk_gpio_queue_setup, }; static int -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH 05/10] raw/cnxk_gpio: support queue release 2021-11-17 0:21 [DPDK 22.02 PATCH 00/10] Add cnxk_gpio PMD Tomasz Duszynski ` (3 preceding siblings ...) 2021-11-17 0:21 ` [PATCH 04/10] raw/cnxk_gpio: support queue setup Tomasz Duszynski @ 2021-11-17 0:21 ` Tomasz Duszynski 2021-11-17 0:21 ` [PATCH 06/10] raw/cnxk_gpio: support enqueuing buffers Tomasz Duszynski ` (5 subsequent siblings) 10 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2021-11-17 0:21 UTC (permalink / raw) To: dev; +Cc: jerinj, Tomasz Duszynski Add support for queue release. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 98b5dd9cd8..8ac3c5e1be 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -213,6 +213,29 @@ cnxk_gpio_queue_setup(struct rte_rawdev *dev, uint16_t queue_id, return 0; } +static int +cnxk_gpio_queue_release(struct rte_rawdev *dev, uint16_t queue_id) +{ + struct cnxk_gpiochip *gpiochip = dev->dev_private; + char buf[CNXK_GPIO_BUFSZ]; + struct cnxk_gpio *gpio; + int ret; + + gpio = cnxk_gpio_lookup(gpiochip, queue_id); + if (!gpio) + return -ENODEV; + + snprintf(buf, sizeof(buf), "%s/unexport", CNXK_GPIO_CLASS_PATH); + ret = cnxk_gpio_write_attr_int(buf, gpiochip->base + queue_id); + if (ret) + return ret; + + gpiochip->gpios[queue_id] = NULL; + rte_free(gpio); + + return 0; +} + static int cnxk_gpio_queue_def_conf(struct rte_rawdev *dev, uint16_t queue_id, rte_rawdev_obj_t queue_conf, size_t queue_conf_size) @@ -243,6 +266,7 @@ static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { .queue_def_conf = cnxk_gpio_queue_def_conf, .queue_count = cnxk_gpio_queue_count, .queue_setup = cnxk_gpio_queue_setup, + .queue_release = cnxk_gpio_queue_release, }; static int @@ -316,6 +340,8 @@ cnxk_gpio_remove(struct rte_vdev_device *dev) char name[RTE_RAWDEV_NAME_MAX_LEN]; struct cnxk_gpiochip *gpiochip; struct rte_rawdev *rawdev; + struct cnxk_gpio *gpio; + int i; RTE_SET_USED(dev); @@ -328,6 +354,14 @@ cnxk_gpio_remove(struct rte_vdev_device *dev) return -ENODEV; gpiochip = rawdev->dev_private; + for (i = 0; i < gpiochip->num_gpios; i++) { + gpio = gpiochip->gpios[i]; + if (!gpio) + continue; + + cnxk_gpio_queue_release(rawdev, gpio->num); + } + rte_free(gpiochip->gpios); rte_rawdev_pmd_release(rawdev); -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH 06/10] raw/cnxk_gpio: support enqueuing buffers 2021-11-17 0:21 [DPDK 22.02 PATCH 00/10] Add cnxk_gpio PMD Tomasz Duszynski ` (4 preceding siblings ...) 2021-11-17 0:21 ` [PATCH 05/10] raw/cnxk_gpio: support queue release Tomasz Duszynski @ 2021-11-17 0:21 ` Tomasz Duszynski 2021-11-17 0:21 ` [PATCH 07/10] raw/cnxk_gpio: support dequeuing buffers Tomasz Duszynski ` (4 subsequent siblings) 10 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2021-11-17 0:21 UTC (permalink / raw) To: dev; +Cc: jerinj, Tomasz Duszynski Add dummy support for enqueuing buffers. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 46 +++++++++++++++++++++++ drivers/raw/cnxk_gpio/cnxk_gpio.h | 1 + drivers/raw/cnxk_gpio/meson.build | 1 + drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h | 38 +++++++++++++++++++ 4 files changed, 86 insertions(+) create mode 100644 drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 8ac3c5e1be..c08ccdfbb8 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -14,6 +14,7 @@ #include <roc_api.h> #include "cnxk_gpio.h" +#include "rte_pmd_cnxk_gpio.h" #define CNXK_GPIO_BUFSZ 128 #define CNXK_GPIO_CLASS_PATH "/sys/class/gpio" @@ -262,7 +263,52 @@ cnxk_gpio_queue_count(struct rte_rawdev *dev) return gpiochip->num_gpios; } +static int +cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf) +{ + struct cnxk_gpio_msg *msg = rbuf->buf_addr; + void *rsp = NULL; + + switch (msg->type) { + default: + return -EINVAL; + } + + /* get rid of last response if any */ + if (gpio->rsp) { + RTE_LOG(WARNING, PMD, "previous response got overwritten\n"); + rte_free(gpio->rsp); + } + gpio->rsp = rsp; + + return 0; +} + +static int +cnxk_gpio_enqueue_bufs(struct rte_rawdev *dev, struct rte_rawdev_buf **buffers, + unsigned int count, rte_rawdev_obj_t context) +{ + struct cnxk_gpiochip *gpiochip = dev->dev_private; + unsigned int queue = (size_t)context; + struct cnxk_gpio *gpio; + int ret; + + if (count == 0) + return 0; + + gpio = cnxk_gpio_lookup(gpiochip, queue); + if (!gpio) + return -ENODEV; + + ret = cnxk_gpio_process_buf(gpio, buffers[0]); + if (ret) + return ret; + + return 1; +} + static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { + .enqueue_bufs = cnxk_gpio_enqueue_bufs, .queue_def_conf = cnxk_gpio_queue_def_conf, .queue_count = cnxk_gpio_queue_count, .queue_setup = cnxk_gpio_queue_setup, diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.h b/drivers/raw/cnxk_gpio/cnxk_gpio.h index 0ee9789a01..1eadde965e 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.h +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.h @@ -7,6 +7,7 @@ struct cnxk_gpio { struct cnxk_gpiochip *gpiochip; + void *rsp; int num; }; diff --git a/drivers/raw/cnxk_gpio/meson.build b/drivers/raw/cnxk_gpio/meson.build index 9a7e716c1e..3fbfdd838c 100644 --- a/drivers/raw/cnxk_gpio/meson.build +++ b/drivers/raw/cnxk_gpio/meson.build @@ -6,3 +6,4 @@ deps += ['bus_vdev', 'common_cnxk', 'rawdev', 'kvargs'] sources = files( 'cnxk_gpio.c', ) +headers = files('rte_pmd_cnxk_gpio.h') diff --git a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h new file mode 100644 index 0000000000..c71065e10c --- /dev/null +++ b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2021 Marvell. + */ + +#ifndef _RTE_PMD_CNXK_GPIO_H_ +#define _RTE_PMD_CNXK_GPIO_H_ + +/** + * @file rte_pmd_cnxk_gpio.h + * + * Marvell GPIO PMD specific structures and interface + * + * This API allows applications to manage GPIOs in user space along with + * installing interrupt handlers for low latency signal processing. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** Available message types */ +enum cnxk_gpio_msg_type { + /** Invalid message type */ + CNXK_GPIO_MSG_TYPE_INVALID, +}; + +struct cnxk_gpio_msg { + /** Message type */ + enum cnxk_gpio_msg_type type; + /** Message data passed to PMD or received from PMD */ + void *data; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _RTE_PMD_CNXK_GPIO_H_ */ -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH 07/10] raw/cnxk_gpio: support dequeuing buffers 2021-11-17 0:21 [DPDK 22.02 PATCH 00/10] Add cnxk_gpio PMD Tomasz Duszynski ` (5 preceding siblings ...) 2021-11-17 0:21 ` [PATCH 06/10] raw/cnxk_gpio: support enqueuing buffers Tomasz Duszynski @ 2021-11-17 0:21 ` Tomasz Duszynski 2021-11-17 0:21 ` [PATCH 08/10] raw/cnxk_gpio: support standard GPIO operations Tomasz Duszynski ` (3 subsequent siblings) 10 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2021-11-17 0:21 UTC (permalink / raw) To: dev; +Cc: jerinj, Tomasz Duszynski Add support for dequeuing buffers. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index c08ccdfbb8..244a625822 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -307,8 +307,34 @@ cnxk_gpio_enqueue_bufs(struct rte_rawdev *dev, struct rte_rawdev_buf **buffers, return 1; } +static int +cnxk_gpio_dequeue_bufs(struct rte_rawdev *dev, struct rte_rawdev_buf **buffers, + unsigned int count, rte_rawdev_obj_t context) +{ + struct cnxk_gpiochip *gpiochip = dev->dev_private; + unsigned int queue = (size_t)context; + struct cnxk_gpio *gpio; + + if (count == 0) + return 0; + + gpio = cnxk_gpio_lookup(gpiochip, queue); + if (!gpio) + return -ENODEV; + + if (gpio->rsp) { + buffers[0]->buf_addr = gpio->rsp; + gpio->rsp = NULL; + + return 1; + } + + return 0; +} + static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { .enqueue_bufs = cnxk_gpio_enqueue_bufs, + .dequeue_bufs = cnxk_gpio_dequeue_bufs, .queue_def_conf = cnxk_gpio_queue_def_conf, .queue_count = cnxk_gpio_queue_count, .queue_setup = cnxk_gpio_queue_setup, -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH 08/10] raw/cnxk_gpio: support standard GPIO operations 2021-11-17 0:21 [DPDK 22.02 PATCH 00/10] Add cnxk_gpio PMD Tomasz Duszynski ` (6 preceding siblings ...) 2021-11-17 0:21 ` [PATCH 07/10] raw/cnxk_gpio: support dequeuing buffers Tomasz Duszynski @ 2021-11-17 0:21 ` Tomasz Duszynski 2021-11-17 0:21 ` [PATCH 09/10] raw/cnxk_gpio: support custom irq handlers Tomasz Duszynski ` (2 subsequent siblings) 10 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2021-11-17 0:21 UTC (permalink / raw) To: dev; +Cc: jerinj, Tomasz Duszynski Add support for standard GPIO operations i.e ones normally provided by GPIO sysfs interface. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- doc/guides/rawdevs/cnxk_gpio.rst | 98 ++++++++ drivers/raw/cnxk_gpio/cnxk_gpio.c | 146 +++++++++++ drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h | 279 +++++++++++++++++++++- 3 files changed, 521 insertions(+), 2 deletions(-) diff --git a/doc/guides/rawdevs/cnxk_gpio.rst b/doc/guides/rawdevs/cnxk_gpio.rst index 868302d07f..f6c3c942c5 100644 --- a/doc/guides/rawdevs/cnxk_gpio.rst +++ b/doc/guides/rawdevs/cnxk_gpio.rst @@ -63,3 +63,101 @@ call barely exports GPIO to userspace. To perform actual data transfer use standard ``rte_rawdev_enqueue_buffers()`` and ``rte_rawdev_dequeue_buffers()`` APIs. Not all messages produce sensible responses hence dequeueing is not always necessary. + +CNXK GPIO PMD +------------- + +PMD accepts ``struct cnxk_gpio_msg`` messages which differ by type and payload. +Message types along with description are listed below. As for the usage examples +please refer to ``cnxk_gpio_selftest()``. There's a set of convenient wrappers +available, one for each existing command. + +Set GPIO value +~~~~~~~~~~~~~~ + +Message is used to set output to low or high. This does not work for GPIOs +configured as input. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_SET_PIN_VALUE``. + +Payload must be an integer set to 0 (low) or 1 (high). + +Consider using ``rte_pmd_gpio_set_pin_value()`` wrapper. + +Set GPIO edge +~~~~~~~~~~~~~ + +Message is used to set edge that triggers interrupt. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_SET_PIN_EDGE``. + +Payload must be `enum cnxk_gpio_pin_edge`. + +Consider using ``rte_pmd_gpio_set_pin_edge()`` wrapper. + +Set GPIO direction +~~~~~~~~~~~~~~~~~~ + +Message is used to change GPIO direction to either input or output. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_SET_PIN_DIR``. + +Payload must be `enum cnxk_gpio_pin_dir`. + +Consider using ``rte_pmd_gpio_set_pin_dir()`` wrapper. + +Set GPIO active low +~~~~~~~~~~~~~~~~~~~ + +Message is used to set whether pin is active low. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_SET_PIN_ACTIVE_LOW``. + +Payload must be an integer set to 0 or 1. The latter activates inversion. + +Consider using ``rte_pmd_gpio_set_pin_active_low()`` wrapper. + +Get GPIO value +~~~~~~~~~~~~~~ + +Message is used to read GPIO value. Value can be 0 (low) or 1 (high). + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_GET_PIN_VALUE``. + +Payload contains integer set to either 0 or 1. + +Consider using ``rte_pmd_gpio_get_pin_value()`` wrapper. + +Get GPIO edge +~~~~~~~~~~~~~ + +Message is used to read GPIO edge. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_GET_PIN_EDGE``. + +Payload contains `enum cnxk_gpio_pin_edge`. + +Consider using ``rte_pmd_gpio_get_pin_edge()`` wrapper. + +Get GPIO direction +~~~~~~~~~~~~~~~~~~ + +Message is used to read GPIO direction. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_GET_PIN_DIR``. + +Payload contains `enum cnxk_gpio_pin_dir`. + +Consider using ``rte_pmd_gpio_get_pin_dir()`` wrapper. + +Get GPIO active low +~~~~~~~~~~~~~~~~~~~ + +Message is used check whether inverted logic is active. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW``. + +Payload contains an integer set to 0 or 1. The latter means inverted logic +is turned on. + +Consider using ``rte_pmd_gpio_get_pin_active_low()`` wrapper. diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 244a625822..c09bc5d56e 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -263,13 +263,159 @@ cnxk_gpio_queue_count(struct rte_rawdev *dev) return gpiochip->num_gpios; } +static const struct { + enum cnxk_gpio_pin_edge edge; + const char *name; +} cnxk_gpio_edge_name[] = { + { CNXK_GPIO_PIN_EDGE_NONE, "none" }, + { CNXK_GPIO_PIN_EDGE_FALLING, "falling" }, + { CNXK_GPIO_PIN_EDGE_RISING, "rising" }, + { CNXK_GPIO_PIN_EDGE_BOTH, "both" }, +}; + +static const char * +cnxk_gpio_edge_to_name(enum cnxk_gpio_pin_edge edge) +{ + unsigned int i; + + for (i = 0; i < RTE_DIM(cnxk_gpio_edge_name); i++) { + if (cnxk_gpio_edge_name[i].edge == edge) + return cnxk_gpio_edge_name[i].name; + } + + return NULL; +} + +static enum cnxk_gpio_pin_edge +cnxk_gpio_name_to_edge(const char *name) +{ + unsigned int i; + + for (i = 0; i < RTE_DIM(cnxk_gpio_edge_name); i++) { + if (!strcmp(cnxk_gpio_edge_name[i].name, name)) + break; + } + + return cnxk_gpio_edge_name[i].edge; +} + +static const struct { + enum cnxk_gpio_pin_dir dir; + const char *name; +} cnxk_gpio_dir_name[] = { + { CNXK_GPIO_PIN_DIR_IN, "in" }, + { CNXK_GPIO_PIN_DIR_OUT, "out" }, + { CNXK_GPIO_PIN_DIR_HIGH, "high" }, + { CNXK_GPIO_PIN_DIR_LOW, "low" }, +}; + +static const char * +cnxk_gpio_dir_to_name(enum cnxk_gpio_pin_dir dir) +{ + unsigned int i; + + for (i = 0; i < RTE_DIM(cnxk_gpio_dir_name); i++) { + if (cnxk_gpio_dir_name[i].dir == dir) + return cnxk_gpio_dir_name[i].name; + } + + return NULL; +} + +static enum cnxk_gpio_pin_dir +cnxk_gpio_name_to_dir(const char *name) +{ + unsigned int i; + + for (i = 0; i < RTE_DIM(cnxk_gpio_dir_name); i++) { + if (!strcmp(cnxk_gpio_dir_name[i].name, name)) + break; + } + + return cnxk_gpio_dir_name[i].dir; +} + static int cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf) { struct cnxk_gpio_msg *msg = rbuf->buf_addr; + enum cnxk_gpio_pin_edge edge; + enum cnxk_gpio_pin_dir dir; + char buf[CNXK_GPIO_BUFSZ]; void *rsp = NULL; + int ret, val, n; + + n = snprintf(buf, sizeof(buf), "%s/gpio%d", CNXK_GPIO_CLASS_PATH, + gpio->num); switch (msg->type) { + case CNXK_GPIO_MSG_TYPE_SET_PIN_VALUE: + snprintf(buf + n, sizeof(buf) - n, "/value"); + ret = cnxk_gpio_write_attr_int(buf, !!*(int *)msg->data); + break; + case CNXK_GPIO_MSG_TYPE_SET_PIN_EDGE: + snprintf(buf + n, sizeof(buf) - n, "/edge"); + edge = *(enum cnxk_gpio_pin_edge *)msg->data; + ret = cnxk_gpio_write_attr(buf, cnxk_gpio_edge_to_name(edge)); + break; + case CNXK_GPIO_MSG_TYPE_SET_PIN_DIR: + snprintf(buf + n, sizeof(buf) - n, "/direction"); + dir = *(enum cnxk_gpio_pin_dir *)msg->data; + ret = cnxk_gpio_write_attr(buf, cnxk_gpio_dir_to_name(dir)); + break; + case CNXK_GPIO_MSG_TYPE_SET_PIN_ACTIVE_LOW: + snprintf(buf + n, sizeof(buf) - n, "/active_low"); + val = *(int *)msg->data; + ret = cnxk_gpio_write_attr_int(buf, val); + break; + case CNXK_GPIO_MSG_TYPE_GET_PIN_VALUE: + snprintf(buf + n, sizeof(buf) - n, "/value"); + ret = cnxk_gpio_read_attr_int(buf, &val); + if (ret) + break; + + rsp = rte_zmalloc(NULL, sizeof(int), 0); + if (!rsp) + return -ENOMEM; + + *(int *)rsp = val; + break; + case CNXK_GPIO_MSG_TYPE_GET_PIN_EDGE: + snprintf(buf + n, sizeof(buf) - n, "/edge"); + ret = cnxk_gpio_read_attr(buf, buf); + if (ret) + break; + + rsp = rte_zmalloc(NULL, sizeof(enum cnxk_gpio_pin_edge), 0); + if (!rsp) + return -ENOMEM; + + *(enum cnxk_gpio_pin_edge *)rsp = cnxk_gpio_name_to_edge(buf); + break; + case CNXK_GPIO_MSG_TYPE_GET_PIN_DIR: + snprintf(buf + n, sizeof(buf) - n, "/direction"); + ret = cnxk_gpio_read_attr(buf, buf); + if (ret) + break; + + rsp = rte_zmalloc(NULL, sizeof(enum cnxk_gpio_pin_dir), 0); + if (!rsp) + return -ENOMEM; + + *(enum cnxk_gpio_pin_dir *)rsp = cnxk_gpio_name_to_dir(buf); + break; + case CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW: + snprintf(buf + n, sizeof(buf) - n, "/active_low"); + ret = cnxk_gpio_read_attr_int(buf, &val); + if (ret) + break; + + rsp = rte_zmalloc(NULL, sizeof(int), 0); + if (!rsp) + return -ENOMEM; + + *(int *)rsp = val; + break; default: return -EINVAL; } diff --git a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h index c71065e10c..7c3dc225ca 100644 --- a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h +++ b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h @@ -5,6 +5,10 @@ #ifndef _RTE_PMD_CNXK_GPIO_H_ #define _RTE_PMD_CNXK_GPIO_H_ +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_rawdev.h> + /** * @file rte_pmd_cnxk_gpio.h * @@ -20,8 +24,46 @@ extern "C" { /** Available message types */ enum cnxk_gpio_msg_type { - /** Invalid message type */ - CNXK_GPIO_MSG_TYPE_INVALID, + /** Type used to set output value */ + CNXK_GPIO_MSG_TYPE_SET_PIN_VALUE, + /** Type used to set edge */ + CNXK_GPIO_MSG_TYPE_SET_PIN_EDGE, + /** Type used to set direction */ + CNXK_GPIO_MSG_TYPE_SET_PIN_DIR, + /** Type used to set inverted logic */ + CNXK_GPIO_MSG_TYPE_SET_PIN_ACTIVE_LOW, + /** Type used to read value */ + CNXK_GPIO_MSG_TYPE_GET_PIN_VALUE, + /** Type used to read edge */ + CNXK_GPIO_MSG_TYPE_GET_PIN_EDGE, + /** Type used to read direction */ + CNXK_GPIO_MSG_TYPE_GET_PIN_DIR, + /** Type used to read inverted logic state */ + CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW, +}; + +/** Available edges */ +enum cnxk_gpio_pin_edge { + /** Set edge to none */ + CNXK_GPIO_PIN_EDGE_NONE, + /** Set edge to falling */ + CNXK_GPIO_PIN_EDGE_FALLING, + /** Set edge to rising */ + CNXK_GPIO_PIN_EDGE_RISING, + /** Set edge to both rising and falling */ + CNXK_GPIO_PIN_EDGE_BOTH, +}; + +/** Available directions */ +enum cnxk_gpio_pin_dir { + /** Set direction to input */ + CNXK_GPIO_PIN_DIR_IN, + /** Set direction to output */ + CNXK_GPIO_PIN_DIR_OUT, + /** Set direction to output and value to 1 */ + CNXK_GPIO_PIN_DIR_HIGH, + /* Set direction to output and value to 0 */ + CNXK_GPIO_PIN_DIR_LOW, }; struct cnxk_gpio_msg { @@ -31,6 +73,239 @@ struct cnxk_gpio_msg { void *data; }; +/** @internal helper routine for enqueuing/dequeuing messages */ +static __rte_always_inline int +__rte_pmd_gpio_enq_deq(uint16_t dev_id, int gpio, void *req, void *rsp, + size_t rsp_size) +{ + struct rte_rawdev_buf *bufs[1]; + struct rte_rawdev_buf buf; + void *q; + int ret; + + q = (void *)(size_t)gpio; + buf.buf_addr = req; + bufs[0] = &buf; + + ret = rte_rawdev_enqueue_buffers(dev_id, bufs, RTE_DIM(bufs), q); + if (ret < 0) + return ret; + if (ret != RTE_DIM(bufs)) + return -EIO; + + if (!rsp) + return 0; + + ret = rte_rawdev_dequeue_buffers(dev_id, bufs, RTE_DIM(bufs), q); + if (ret < 0) + return ret; + if (ret != RTE_DIM(bufs)) + return -EIO; + + rte_memcpy(rsp, buf.buf_addr, rsp_size); + rte_free(buf.buf_addr); + + return 0; +} + +/** + * Set output to specific value + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param val + * Value output will be set to. 0 represents low state while + * 1 high state + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_set_pin_value(uint16_t dev_id, int gpio, int val) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_SET_PIN_VALUE, + .data = &val, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Select signal edge that triggers interrupt + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param edge + * Signal edge that triggers interrupt + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_set_pin_edge(uint16_t dev_id, int gpio, + enum cnxk_gpio_pin_edge edge) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_SET_PIN_EDGE, + .data = &edge + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Configure GPIO as input or output + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param dir + * Direction of the GPIO + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_set_pin_dir(uint16_t dev_id, int gpio, enum cnxk_gpio_pin_dir dir) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_SET_PIN_DIR, + .data = &dir, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Enable or disable inverted logic + * + * If GPIO is configured as output then writing 1 or 0 will result in setting + * output to respectively low or high + * + * If GPIO is configured as input then logic inversion applies to edges. Both + * current and future settings are affected + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param val + * 0 to disable, 1 to enable inverted logic + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_set_pin_active_low(uint16_t dev_id, int gpio, int val) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_SET_PIN_ACTIVE_LOW, + .data = &val, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Read GPIO value + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param val + * Where to store read logical signal value i.e 0 or 1 + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_get_pin_value(uint16_t dev_id, int gpio, int *val) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_GET_PIN_VALUE, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, val, sizeof(*val)); +} + +/** + * Read GPIO edge + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param edge + * Where to store edge + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_get_pin_edge(uint16_t dev_id, int gpio, + enum cnxk_gpio_pin_edge *edge) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_GET_PIN_EDGE, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, edge, sizeof(*edge)); +} + +/** + * Read GPIO direction + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param dir + * Where to store direction + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_get_pin_dir(uint16_t dev_id, int gpio, enum cnxk_gpio_pin_dir *dir) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_GET_PIN_DIR, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, dir, sizeof(*dir)); +} + +/** + * Read whether GPIO is active low + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param val + * Where to store active low state + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_get_pin_active_low(uint16_t dev_id, int gpio, int *val) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW, + .data = &val, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, val, sizeof(*val)); +} + #ifdef __cplusplus } #endif -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH 09/10] raw/cnxk_gpio: support custom irq handlers 2021-11-17 0:21 [DPDK 22.02 PATCH 00/10] Add cnxk_gpio PMD Tomasz Duszynski ` (7 preceding siblings ...) 2021-11-17 0:21 ` [PATCH 08/10] raw/cnxk_gpio: support standard GPIO operations Tomasz Duszynski @ 2021-11-17 0:21 ` Tomasz Duszynski 2021-11-17 0:21 ` [PATCH 10/10] raw/cnxk_gpio: support selftest Tomasz Duszynski 2021-11-28 15:44 ` [DPDK 22.02 PATCH v2 00/10] Add cnxk_gpio PMD Tomasz Duszynski 10 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2021-11-17 0:21 UTC (permalink / raw) To: dev; +Cc: jerinj, Tomasz Duszynski Add support for custom interrupt handlers. Custom interrupt handlers bypass kernel completely and are meant for fast and low latency access to GPIO state. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- doc/guides/rawdevs/cnxk_gpio.rst | 21 +++ drivers/raw/cnxk_gpio/cnxk_gpio.c | 37 ++++ drivers/raw/cnxk_gpio/cnxk_gpio.h | 8 + drivers/raw/cnxk_gpio/cnxk_gpio_irq.c | 216 ++++++++++++++++++++++ drivers/raw/cnxk_gpio/meson.build | 1 + drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h | 116 ++++++++++++ 6 files changed, 399 insertions(+) create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio_irq.c diff --git a/doc/guides/rawdevs/cnxk_gpio.rst b/doc/guides/rawdevs/cnxk_gpio.rst index f6c3c942c5..ad93ec0d44 100644 --- a/doc/guides/rawdevs/cnxk_gpio.rst +++ b/doc/guides/rawdevs/cnxk_gpio.rst @@ -161,3 +161,24 @@ Payload contains an integer set to 0 or 1. The latter means inverted logic is turned on. Consider using ``rte_pmd_gpio_get_pin_active_low()`` wrapper. + +Request interrupt +~~~~~~~~~~~~~~~~~ + +Message is used to install custom interrupt handler. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_REGISTER_IRQ``. + +Payload needs to be set to ``struct cnxk_gpio_irq`` which describes interrupt +being requested. + +Consider using ``rte_pmd_gpio_register_gpio()`` wrapper. + +Free interrupt +~~~~~~~~~~~~~~ + +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. diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index c09bc5d56e..d39d203245 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -335,6 +335,28 @@ cnxk_gpio_name_to_dir(const char *name) return cnxk_gpio_dir_name[i].dir; } +static int +cnxk_gpio_register_irq(struct cnxk_gpio *gpio, struct cnxk_gpio_irq *irq) +{ + int ret; + + ret = cnxk_gpio_irq_request(gpio->num - gpio->gpiochip->base, irq->cpu); + if (ret) + return ret; + + gpio->handler = irq->handler; + gpio->data = irq->data; + gpio->cpu = irq->cpu; + + return 0; +} + +static int +cnxk_gpio_unregister_irq(struct cnxk_gpio *gpio) +{ + return cnxk_gpio_irq_free(gpio->num - gpio->gpiochip->base); +} + static int cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf) { @@ -416,6 +438,13 @@ cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf) *(int *)rsp = val; break; + case CNXK_GPIO_MSG_TYPE_REGISTER_IRQ: + ret = cnxk_gpio_register_irq(gpio, + (struct cnxk_gpio_irq *)msg->data); + break; + case CNXK_GPIO_MSG_TYPE_UNREGISTER_IRQ: + ret = cnxk_gpio_unregister_irq(gpio); + break; default: return -EINVAL; } @@ -519,6 +548,10 @@ cnxk_gpio_probe(struct rte_vdev_device *dev) if (ret) goto out; + ret = cnxk_gpio_irq_init(gpiochip); + if (ret) + goto out; + /* read gpio base */ snprintf(buf, sizeof(buf), "%s/gpiochip%d/base", CNXK_GPIO_CLASS_PATH, gpiochip->num); @@ -577,10 +610,14 @@ cnxk_gpio_remove(struct rte_vdev_device *dev) if (!gpio) continue; + if (gpio->handler) + cnxk_gpio_unregister_irq(gpio); + cnxk_gpio_queue_release(rawdev, gpio->num); } rte_free(gpiochip->gpios); + cnxk_gpio_irq_fini(); rte_rawdev_pmd_release(rawdev); return 0; diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.h b/drivers/raw/cnxk_gpio/cnxk_gpio.h index 1eadde965e..a2e44fefbe 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.h +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.h @@ -9,6 +9,9 @@ struct cnxk_gpio { struct cnxk_gpiochip *gpiochip; void *rsp; int num; + void (*handler)(int gpio, void *data); + void *data; + int cpu; }; struct cnxk_gpiochip { @@ -18,4 +21,9 @@ struct cnxk_gpiochip { struct cnxk_gpio **gpios; }; +int cnxk_gpio_irq_init(struct cnxk_gpiochip *gpiochip); +void cnxk_gpio_irq_fini(void); +int cnxk_gpio_irq_request(int gpio, int cpu); +int cnxk_gpio_irq_free(int gpio); + #endif /* _CNXK_GPIO_H_ */ diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio_irq.c b/drivers/raw/cnxk_gpio/cnxk_gpio_irq.c new file mode 100644 index 0000000000..2fa8e69899 --- /dev/null +++ b/drivers/raw/cnxk_gpio/cnxk_gpio_irq.c @@ -0,0 +1,216 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2021 Marvell. + */ + +#include <fcntl.h> +#include <pthread.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/queue.h> +#include <unistd.h> + +#include <rte_rawdev_pmd.h> + +#include <roc_api.h> + +#include "cnxk_gpio.h" + +#define OTX_IOC_MAGIC 0xF2 +#define OTX_IOC_SET_GPIO_HANDLER \ + _IOW(OTX_IOC_MAGIC, 1, struct otx_gpio_usr_data) +#define OTX_IOC_CLR_GPIO_HANDLER \ + _IO(OTX_IOC_MAGIC, 2) + +struct otx_gpio_usr_data { + uint64_t isr_base; + uint64_t sp; + uint64_t cpu; + uint64_t gpio_num; +}; + +struct cnxk_gpio_irq_stack { + LIST_ENTRY(cnxk_gpio_irq_stack) next; + void *sp_buffer; + int cpu; + int inuse; +}; + +struct cnxk_gpio_irqchip { + int fd; + /* serialize access to this struct */ + pthread_mutex_t lock; + LIST_HEAD(, cnxk_gpio_irq_stack) stacks; + + struct cnxk_gpiochip *gpiochip; +}; + +static struct cnxk_gpio_irqchip *irqchip; + +static void +cnxk_gpio_irq_stack_free(int cpu) +{ + struct cnxk_gpio_irq_stack *stack; + + LIST_FOREACH(stack, &irqchip->stacks, next) { + if (stack->cpu == cpu) + break; + } + + if (!stack) + return; + + if (stack->inuse) + stack->inuse--; + + if (stack->inuse == 0) { + LIST_REMOVE(stack, next); + rte_free(stack->sp_buffer); + rte_free(stack); + } +} + +static void * +cnxk_gpio_irq_stack_alloc(int cpu) +{ +#define ARM_STACK_ALIGNMENT (2 * sizeof(void *)) +#define IRQ_STACK_SIZE 0x200000 + + struct cnxk_gpio_irq_stack *stack; + + LIST_FOREACH(stack, &irqchip->stacks, next) { + if (stack->cpu == cpu) + break; + } + + if (stack) { + stack->inuse++; + return (char *)stack->sp_buffer + IRQ_STACK_SIZE; + } + + stack = rte_malloc(NULL, sizeof(*stack), 0); + if (!stack) + return NULL; + + stack->sp_buffer = + rte_zmalloc(NULL, IRQ_STACK_SIZE * 2, ARM_STACK_ALIGNMENT); + if (!stack->sp_buffer) { + rte_free(stack); + return NULL; + } + + stack->cpu = cpu; + stack->inuse = 1; + LIST_INSERT_HEAD(&irqchip->stacks, stack, next); + + return (char *)stack->sp_buffer + IRQ_STACK_SIZE; +} + +static void +cnxk_gpio_irq_handler(int gpio_num) +{ + struct cnxk_gpiochip *gpiochip = irqchip->gpiochip; + struct cnxk_gpio *gpio; + + if (gpio_num >= gpiochip->num_gpios) + goto out; + + gpio = gpiochip->gpios[gpio_num]; + if (likely(gpio->handler)) + gpio->handler(gpio_num, gpio->data); + +out: + roc_atf_ret(); +} + +int +cnxk_gpio_irq_init(struct cnxk_gpiochip *gpiochip) +{ + if (irqchip) + return 0; + + irqchip = rte_zmalloc(NULL, sizeof(*irqchip), 0); + if (!irqchip) + return -ENOMEM; + + irqchip->fd = open("/dev/otx-gpio-ctr", O_RDWR | O_SYNC); + if (irqchip->fd < 0) { + rte_free(irqchip); + return -errno; + } + + pthread_mutex_init(&irqchip->lock, NULL); + LIST_INIT(&irqchip->stacks); + irqchip->gpiochip = gpiochip; + + return 0; +} + +void +cnxk_gpio_irq_fini(void) +{ + if (!irqchip) + return; + + close(irqchip->fd); + rte_free(irqchip); + irqchip = NULL; +} + +int +cnxk_gpio_irq_request(int gpio, int cpu) +{ + struct otx_gpio_usr_data data; + void *sp; + int ret; + + pthread_mutex_lock(&irqchip->lock); + + sp = cnxk_gpio_irq_stack_alloc(cpu); + if (!sp) { + ret = -ENOMEM; + goto out_unlock; + } + + data.isr_base = (uint64_t)cnxk_gpio_irq_handler; + data.sp = (uint64_t)sp; + data.cpu = (uint64_t)cpu; + data.gpio_num = (uint64_t)gpio; + + mlockall(MCL_CURRENT | MCL_FUTURE); + ret = ioctl(irqchip->fd, OTX_IOC_SET_GPIO_HANDLER, &data); + if (ret) { + ret = -errno; + goto out_free_stack; + } + + pthread_mutex_unlock(&irqchip->lock); + + return 0; + +out_free_stack: + cnxk_gpio_irq_stack_free(cpu); +out_unlock: + pthread_mutex_unlock(&irqchip->lock); + + return ret; +} + +int +cnxk_gpio_irq_free(int gpio) +{ + int ret; + + pthread_mutex_lock(&irqchip->lock); + + ret = ioctl(irqchip->fd, OTX_IOC_CLR_GPIO_HANDLER, gpio); + if (ret) { + pthread_mutex_unlock(&irqchip->lock); + return -errno; + } + + cnxk_gpio_irq_stack_free(irqchip->gpiochip->gpios[gpio]->cpu); + + pthread_mutex_unlock(&irqchip->lock); + + return 0; +} diff --git a/drivers/raw/cnxk_gpio/meson.build b/drivers/raw/cnxk_gpio/meson.build index 3fbfdd838c..9b55f029c7 100644 --- a/drivers/raw/cnxk_gpio/meson.build +++ b/drivers/raw/cnxk_gpio/meson.build @@ -5,5 +5,6 @@ deps += ['bus_vdev', 'common_cnxk', 'rawdev', 'kvargs'] sources = files( 'cnxk_gpio.c', + 'cnxk_gpio_irq.c', ) headers = files('rte_pmd_cnxk_gpio.h') diff --git a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h index 7c3dc225ca..e3096dc14f 100644 --- a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h +++ b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h @@ -40,6 +40,10 @@ enum cnxk_gpio_msg_type { CNXK_GPIO_MSG_TYPE_GET_PIN_DIR, /** Type used to read inverted logic state */ CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW, + /** Type used to register interrupt handler */ + CNXK_GPIO_MSG_TYPE_REGISTER_IRQ, + /** Type used to remove interrupt handler */ + CNXK_GPIO_MSG_TYPE_UNREGISTER_IRQ, }; /** Available edges */ @@ -66,6 +70,25 @@ enum cnxk_gpio_pin_dir { CNXK_GPIO_PIN_DIR_LOW, }; +/** + * GPIO interrupt handler + * + * @param gpio + * Zero-based GPIO number + * @param data + * Cookie passed to interrupt handler + */ +typedef void (*cnxk_gpio_irq_handler_t)(int gpio, void *data); + +struct cnxk_gpio_irq { + /** Interrupt handler */ + cnxk_gpio_irq_handler_t handler; + /** User data passed to irq handler */ + void *data; + /** CPU which will run irq handler */ + int cpu; +}; + struct cnxk_gpio_msg { /** Message type */ enum cnxk_gpio_msg_type type; @@ -306,6 +329,99 @@ rte_pmd_gpio_get_pin_active_low(uint16_t dev_id, int gpio, int *val) return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, val, sizeof(*val)); } +/** + * Attach interrupt handler to GPIO + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param cpu + * CPU which will be handling interrupt + * @param handler + * Interrupt handler to be executed + * @param data + * Data to be passed to interrupt handler + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_register_irq(uint16_t dev_id, int gpio, int cpu, + cnxk_gpio_irq_handler_t handler, void *data) +{ + struct cnxk_gpio_irq irq = { + .handler = handler, + .data = data, + .cpu = cpu, + }; + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_REGISTER_IRQ, + .data = &irq, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Detach interrupt handler from GPIO + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_unregister_irq(uint16_t dev_id, int gpio) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_UNREGISTER_IRQ, + .data = &gpio, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Enable interrupt + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param edge + * Edge that should trigger interrupt + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_enable_interrupt(uint16_t dev_id, int gpio, + enum cnxk_gpio_pin_edge edge) +{ + return rte_pmd_gpio_set_pin_edge(dev_id, gpio, edge); +} + +/** + * Disable interrupt + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_disable_interrupt(uint16_t dev_id, int gpio) +{ + return rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_NONE); +} + #ifdef __cplusplus } #endif -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH 10/10] raw/cnxk_gpio: support selftest 2021-11-17 0:21 [DPDK 22.02 PATCH 00/10] Add cnxk_gpio PMD Tomasz Duszynski ` (8 preceding siblings ...) 2021-11-17 0:21 ` [PATCH 09/10] raw/cnxk_gpio: support custom irq handlers Tomasz Duszynski @ 2021-11-17 0:21 ` Tomasz Duszynski 2021-11-17 1:17 ` Stephen Hemminger 2021-11-28 15:44 ` [DPDK 22.02 PATCH v2 00/10] Add cnxk_gpio PMD Tomasz Duszynski 10 siblings, 1 reply; 75+ messages in thread From: Tomasz Duszynski @ 2021-11-17 0:21 UTC (permalink / raw) To: dev; +Cc: jerinj, Tomasz Duszynski Add support for performing selftest. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- 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 | 442 +++++++++++++++++++++ drivers/raw/cnxk_gpio/meson.build | 1 + 5 files changed, 457 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=<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. diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index d39d203245..fa81b45f89 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -514,6 +514,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 a2e44fefbe..dfbad47c51 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.h +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.h @@ -21,6 +21,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..3fbba4af81 --- /dev/null +++ b/drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c @@ -0,0 +1,442 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2021 Marvell. + */ + +#include <fcntl.h> +#include <sched.h> +#include <sys/mman.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 + +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_open_mem(void) +{ + int ret = 0, fd; + + fd = open("/dev/mem", O_RDWR | O_SYNC); + if (fd < 0) + ret = -errno; + + return ret; +} + +static void +cnxk_gpio_close_mem(int fd) +{ + if (fd >= 0) + close(fd); +} + +#define GPIO_INTRX(a) (803000000800ull + (a) * 0x8) + +static int +cnxk_gpio_map_gpio_intrx(int fd, int gpio, void **va) +{ + uint64_t mask; + long size; + + size = sysconf(_SC_PAGESIZE); + mask = (uint64_t)size - 1; + *va = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, + GPIO_INTRX(gpio) & ~mask); + if (*va == MAP_FAILED) + return -errno; + + *(char *)va += GPIO_INTRX(gpio) & mask; + + return 0; +} + +static void +cnxk_gpio_unmap_gpio_intrx(int gpio, void *va) +{ + uint64_t mask; + long size; + + if (!va) + return; + + size = sysconf(_SC_PAGESIZE); + mask = (uint64_t)size - 1; + munmap((char *)va - (GPIO_INTRX(gpio) & mask), size); +} + +static int +cnxk_gpio_trigger_irq(int gpio) +{ + void *va; + int ret, fd; + + fd = cnxk_gpio_open_mem(); + if (fd < 0) + return fd; + + ret = cnxk_gpio_map_gpio_intrx(fd, gpio, &va); + if (ret) { + cnxk_gpio_close_mem(fd); + return ret; + } + + /* set INTR_W1S bit */ + *(volatile uint64_t *)va = 2; + cnxk_gpio_unmap_gpio_intrx(gpio, va); + cnxk_gpio_close_mem(fd); + + return 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; + + 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; + } + } + + 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') -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH 10/10] raw/cnxk_gpio: support selftest 2021-11-17 0:21 ` [PATCH 10/10] raw/cnxk_gpio: support selftest Tomasz Duszynski @ 2021-11-17 1:17 ` Stephen Hemminger 0 siblings, 0 replies; 75+ messages in thread From: Stephen Hemminger @ 2021-11-17 1:17 UTC (permalink / raw) To: Tomasz Duszynski; +Cc: dev, jerinj On Wed, 17 Nov 2021 01:21:55 +0100 Tomasz Duszynski <tduszynski@marvell.com> wrote: > +static int > +cnxk_gpio_open_mem(void) > +{ > + int ret = 0, fd; > + > + fd = open("/dev/mem", O_RDWR | O_SYNC); > + if (fd < 0) > + ret = -errno; > + > + return ret; > +} This is a dangerous security violation and most distributions will not allow it. Also, why are you opening it? The code is forgetting the file descriptor so it is useless anyway. ^ permalink raw reply [flat|nested] 75+ messages in thread
* [DPDK 22.02 PATCH v2 00/10] Add cnxk_gpio PMD 2021-11-17 0:21 [DPDK 22.02 PATCH 00/10] Add cnxk_gpio PMD Tomasz Duszynski ` (9 preceding siblings ...) 2021-11-17 0:21 ` [PATCH 10/10] raw/cnxk_gpio: support selftest Tomasz Duszynski @ 2021-11-28 15:44 ` Tomasz Duszynski 2021-11-28 15:44 ` [PATCH v2 01/10] raw/cnxk_gpio: add GPIO driver skeleton Tomasz Duszynski ` (10 more replies) 10 siblings, 11 replies; 75+ messages in thread From: Tomasz Duszynski @ 2021-11-28 15:44 UTC (permalink / raw) To: dev; +Cc: jerinj, stephen, Tomasz Duszynski This series introduces a new rawdevice PMD which allows to manage userspace GPIOs and install custom GPIO interrupt handlers which bypass kernel. This is especially useful for applications that, besides providing standard dataplane functionality, want to have fast and low latency access to GPIO pin state. It'd be great to have that merged during 22.02 merge window. v2: - do not trigger irq by writing to /dev/mem, use ioctl() instead Tomasz Duszynski (10): raw/cnxk_gpio: add GPIO driver skeleton raw/cnxk_gpio: support reading default queue conf raw/cnxk_gpio: support reading queue count raw/cnxk_gpio: support queue setup raw/cnxk_gpio: support queue release raw/cnxk_gpio: support enqueuing buffers raw/cnxk_gpio: support dequeuing buffers raw/cnxk_gpio: support standard GPIO operations raw/cnxk_gpio: support custom irq handlers raw/cnxk_gpio: support selftest doc/guides/rawdevs/cnxk_gpio.rst | 195 +++++++ doc/guides/rawdevs/index.rst | 1 + drivers/raw/cnxk_gpio/cnxk_gpio.c | 633 +++++++++++++++++++++ drivers/raw/cnxk_gpio/cnxk_gpio.h | 33 ++ drivers/raw/cnxk_gpio/cnxk_gpio_irq.c | 216 +++++++ drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c | 386 +++++++++++++ drivers/raw/cnxk_gpio/meson.build | 11 + drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h | 429 ++++++++++++++ drivers/raw/cnxk_gpio/version.map | 3 + drivers/raw/meson.build | 1 + 10 files changed, 1908 insertions(+) create mode 100644 doc/guides/rawdevs/cnxk_gpio.rst create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio.c create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio.h create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio_irq.c create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c create mode 100644 drivers/raw/cnxk_gpio/meson.build create mode 100644 drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h create mode 100644 drivers/raw/cnxk_gpio/version.map -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v2 01/10] raw/cnxk_gpio: add GPIO driver skeleton 2021-11-28 15:44 ` [DPDK 22.02 PATCH v2 00/10] Add cnxk_gpio PMD Tomasz Duszynski @ 2021-11-28 15:44 ` Tomasz Duszynski 2021-11-28 15:44 ` [PATCH v2 02/10] raw/cnxk_gpio: support reading default queue conf Tomasz Duszynski ` (9 subsequent siblings) 10 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2021-11-28 15:44 UTC (permalink / raw) To: dev; +Cc: jerinj, stephen, Tomasz Duszynski Add initial support for PMD that allows to control particular pins form userspace. Moreover PMD allows to attach custom interrupt handlers to controllable GPIOs. Main users of this PMD are dataplain applications requiring fast and low latency access to pin state. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- doc/guides/rawdevs/cnxk_gpio.rst | 65 +++++++++ doc/guides/rawdevs/index.rst | 1 + drivers/raw/cnxk_gpio/cnxk_gpio.c | 235 ++++++++++++++++++++++++++++++ drivers/raw/cnxk_gpio/cnxk_gpio.h | 22 +++ drivers/raw/cnxk_gpio/meson.build | 8 + drivers/raw/cnxk_gpio/version.map | 3 + drivers/raw/meson.build | 1 + 7 files changed, 335 insertions(+) create mode 100644 doc/guides/rawdevs/cnxk_gpio.rst create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio.c create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio.h create mode 100644 drivers/raw/cnxk_gpio/meson.build create mode 100644 drivers/raw/cnxk_gpio/version.map diff --git a/doc/guides/rawdevs/cnxk_gpio.rst b/doc/guides/rawdevs/cnxk_gpio.rst new file mode 100644 index 0000000000..868302d07f --- /dev/null +++ b/doc/guides/rawdevs/cnxk_gpio.rst @@ -0,0 +1,65 @@ +.. SPDX-License-Identifier: BSD-3-Clause + Copyright(c) 2021 Marvell. + +Marvell CNXK GPIO Driver +======================== + +CNXK GPIO PMD configures and manages GPIOs available on the system using +standard enqueue/dequeue mechanism offered by raw device abstraction. PMD relies +both on standard sysfs GPIO interface provided by the Linux kernel and GPIO +kernel driver custom interface allowing one to install userspace interrupt +handlers. + +Features +-------- + +Following features are available: + +- export/unexport a GPIO +- read/write specific value from/to exported GPIO +- set GPIO direction +- set GPIO edge that triggers interrupt +- set GPIO active low +- register interrupt handler for specific GPIO + +Requirements +------------ + +PMD relies on modified kernel GPIO driver which exposes ``ioctl()`` interface +for installing interrupt handlers for low latency signal processing. + +Driver is shipped with Marvell SDK. + +Device Setup +------------ + +CNXK GPIO PMD binds to virtual device which gets created by passing +`--vdev=cnxk_gpio,gpiochip=<number>` command line to EAL. `gpiochip` parameter +tells PMD which GPIO controller should be used. Available controllers are +available under `/sys/class/gpio`. For further details on how Linux represents +GPIOs in userspace please refer to +`sysfs.txt <https://www.kernel.org/doc/Documentation/gpio/sysfs.txt>`_. + +If `gpiochip=<number>` was omitted then first gpiochip from the alphabetically +sort list of available gpiochips is used. + +.. code-block:: console + + $ ls /sys/class/gpio + export gpiochip448 unexport + +In above scenario only one GPIO controller is present hence +`--vdev=cnxk_gpio,gpiochip=448` should be passed to EAL. + +Before performing actual data transfer one needs to call +``rte_rawdev_queue_count()`` followed by ``rte_rawdev_queue_conf_get()``. The +former returns number GPIOs available in the system irrespective of GPIOs +being controllable or not. Thus it is user responsibility to pick the proper +ones. The latter call simply returns queue capacity. + +Respective queue needs to be configured with ``rte_rawdev_queue_setup()``. This +call barely exports GPIO to userspace. + +To perform actual data transfer use standard ``rte_rawdev_enqueue_buffers()`` +and ``rte_rawdev_dequeue_buffers()`` APIs. Not all messages produce sensible +responses hence dequeueing is not always necessary. diff --git a/doc/guides/rawdevs/index.rst b/doc/guides/rawdevs/index.rst index b6cf917443..0c02da6e90 100644 --- a/doc/guides/rawdevs/index.rst +++ b/doc/guides/rawdevs/index.rst @@ -12,6 +12,7 @@ application through rawdev API. :numbered: cnxk_bphy + cnxk_gpio dpaa2_cmdif dpaa2_qdma ifpga diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c new file mode 100644 index 0000000000..bcce4b8fb7 --- /dev/null +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -0,0 +1,235 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2021 Marvell. + */ + +#include <dirent.h> +#include <string.h> + +#include <rte_bus_vdev.h> +#include <rte_eal.h> +#include <rte_kvargs.h> +#include <rte_lcore.h> +#include <rte_rawdev_pmd.h> + +#include <roc_api.h> + +#include "cnxk_gpio.h" + +#define CNXK_GPIO_BUFSZ 128 +#define CNXK_GPIO_CLASS_PATH "/sys/class/gpio" + +static const char *const cnxk_gpio_args[] = { +#define CNXK_GPIO_ARG_GPIOCHIP "gpiochip" + CNXK_GPIO_ARG_GPIOCHIP, + NULL +}; + +static void +cnxk_gpio_format_name(char *name, size_t len) +{ + snprintf(name, len, "cnxk_gpio"); +} + +static int +cnxk_gpio_filter_gpiochip(const struct dirent *dirent) +{ + const char *pattern = "gpiochip"; + + return !strncmp(dirent->d_name, pattern, strlen(pattern)); +} + +static void +cnxk_gpio_set_defaults(struct cnxk_gpiochip *gpiochip) +{ + struct dirent **namelist; + int n; + + n = scandir(CNXK_GPIO_CLASS_PATH, &namelist, cnxk_gpio_filter_gpiochip, + alphasort); + if (n < 0 || n == 0) + return; + + sscanf(namelist[0]->d_name, "gpiochip%d", &gpiochip->num); + while (n--) + free(namelist[n]); + free(namelist); +} + +static int +cnxk_gpio_parse_arg_gpiochip(const char *key __rte_unused, const char *value, + void *extra_args) +{ + long val; + + errno = 0; + val = strtol(value, NULL, 10); + if (errno) + return -errno; + + *(int *)extra_args = (int)val; + + return 0; +} + +static int +cnxk_gpio_parse_args(struct cnxk_gpiochip *gpiochip, + struct rte_devargs *devargs) +{ + struct rte_kvargs *kvlist; + int ret; + + kvlist = rte_kvargs_parse(devargs->args, cnxk_gpio_args); + if (!kvlist) + return 0; + + ret = rte_kvargs_count(kvlist, CNXK_GPIO_ARG_GPIOCHIP); + if (ret == 1) { + ret = rte_kvargs_process(kvlist, CNXK_GPIO_ARG_GPIOCHIP, + cnxk_gpio_parse_arg_gpiochip, + &gpiochip->num); + if (ret) + return ret; + } + + return 0; +} + +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; +} + +static int +cnxk_gpio_read_attr_int(char *attr, int *val) +{ + char buf[CNXK_GPIO_BUFSZ]; + int ret; + + ret = cnxk_gpio_read_attr(attr, buf); + if (ret) + return ret; + + ret = sscanf(buf, "%d", val); + if (ret < 0) + return -errno; + + return 0; +} + +static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { +}; + +static int +cnxk_gpio_probe(struct rte_vdev_device *dev) +{ + char name[RTE_RAWDEV_NAME_MAX_LEN]; + struct cnxk_gpiochip *gpiochip; + struct rte_rawdev *rawdev; + char buf[CNXK_GPIO_BUFSZ]; + int ret; + + if (rte_eal_process_type() != RTE_PROC_PRIMARY) + return 0; + + cnxk_gpio_format_name(name, sizeof(name)); + rawdev = rte_rawdev_pmd_allocate(name, sizeof(*gpiochip), + rte_socket_id()); + if (!rawdev) { + RTE_LOG(ERR, PMD, "failed to allocate %s rawdev", name); + return -ENOMEM; + } + + rawdev->dev_ops = &cnxk_gpio_rawdev_ops; + rawdev->device = &dev->device; + rawdev->driver_name = dev->device.name; + + gpiochip = rawdev->dev_private; + cnxk_gpio_set_defaults(gpiochip); + + /* defaults may be overwritten by this call */ + ret = cnxk_gpio_parse_args(gpiochip, dev->device.devargs); + if (ret) + goto out; + + /* read gpio base */ + snprintf(buf, sizeof(buf), "%s/gpiochip%d/base", CNXK_GPIO_CLASS_PATH, + gpiochip->num); + ret = cnxk_gpio_read_attr_int(buf, &gpiochip->base); + if (ret) { + RTE_LOG(ERR, PMD, "failed to read %s", buf); + goto out; + } + + /* read number of available gpios */ + snprintf(buf, sizeof(buf), "%s/gpiochip%d/ngpio", CNXK_GPIO_CLASS_PATH, + gpiochip->num); + ret = cnxk_gpio_read_attr_int(buf, &gpiochip->num_gpios); + if (ret) { + RTE_LOG(ERR, PMD, "failed to read %s", buf); + goto out; + } + + gpiochip->gpios = rte_calloc(NULL, gpiochip->num_gpios, + sizeof(struct cnxk_gpio *), 0); + if (!gpiochip->gpios) { + RTE_LOG(ERR, PMD, "failed to allocate gpios memory"); + ret = -ENOMEM; + goto out; + } + + return 0; +out: + rte_rawdev_pmd_release(rawdev); + + return ret; +} + +static int +cnxk_gpio_remove(struct rte_vdev_device *dev) +{ + char name[RTE_RAWDEV_NAME_MAX_LEN]; + struct cnxk_gpiochip *gpiochip; + struct rte_rawdev *rawdev; + + RTE_SET_USED(dev); + + if (rte_eal_process_type() != RTE_PROC_PRIMARY) + return 0; + + cnxk_gpio_format_name(name, sizeof(name)); + rawdev = rte_rawdev_pmd_get_named_dev(name); + if (!rawdev) + return -ENODEV; + + gpiochip = rawdev->dev_private; + rte_free(gpiochip->gpios); + rte_rawdev_pmd_release(rawdev); + + return 0; +} + +static struct rte_vdev_driver cnxk_gpio_drv = { + .probe = cnxk_gpio_probe, + .remove = cnxk_gpio_remove, +}; + +RTE_PMD_REGISTER_VDEV(cnxk_gpio, cnxk_gpio_drv); +RTE_PMD_REGISTER_PARAM_STRING(cnxk_gpio, "gpiochip=<int>"); diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.h b/drivers/raw/cnxk_gpio/cnxk_gpio.h new file mode 100644 index 0000000000..4dae8316ba --- /dev/null +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2021 Marvell. + */ + +#ifndef _CNXK_GPIO_H_ +#define _CNXK_GPIO_H_ + +struct cnxk_gpiochip; + +struct cnxk_gpio { + struct cnxk_gpiochip *gpiochip; + int num; +}; + +struct cnxk_gpiochip { + int num; + int base; + int num_gpios; + struct cnxk_gpio **gpios; +}; + +#endif /* _CNXK_GPIO_H_ */ diff --git a/drivers/raw/cnxk_gpio/meson.build b/drivers/raw/cnxk_gpio/meson.build new file mode 100644 index 0000000000..9a7e716c1e --- /dev/null +++ b/drivers/raw/cnxk_gpio/meson.build @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(C) 2021 Marvell. +# + +deps += ['bus_vdev', 'common_cnxk', 'rawdev', 'kvargs'] +sources = files( + 'cnxk_gpio.c', +) diff --git a/drivers/raw/cnxk_gpio/version.map b/drivers/raw/cnxk_gpio/version.map new file mode 100644 index 0000000000..4a76d1d52d --- /dev/null +++ b/drivers/raw/cnxk_gpio/version.map @@ -0,0 +1,3 @@ +DPDK_21 { + local: *; +}; diff --git a/drivers/raw/meson.build b/drivers/raw/meson.build index 87694a758e..4b52e93945 100644 --- a/drivers/raw/meson.build +++ b/drivers/raw/meson.build @@ -7,6 +7,7 @@ endif drivers = [ 'cnxk_bphy', + 'cnxk_gpio', 'dpaa2_cmdif', 'dpaa2_qdma', 'ifpga', -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v2 02/10] raw/cnxk_gpio: support reading default queue conf 2021-11-28 15:44 ` [DPDK 22.02 PATCH v2 00/10] Add cnxk_gpio PMD Tomasz Duszynski 2021-11-28 15:44 ` [PATCH v2 01/10] raw/cnxk_gpio: add GPIO driver skeleton Tomasz Duszynski @ 2021-11-28 15:44 ` Tomasz Duszynski 2021-11-28 15:44 ` [PATCH v2 03/10] raw/cnxk_gpio: support reading queue count Tomasz Duszynski ` (8 subsequent siblings) 10 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2021-11-28 15:44 UTC (permalink / raw) To: dev; +Cc: jerinj, stephen, Tomasz Duszynski Add support for reading default queue configuration. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index bcce4b8fb7..deedf98af2 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -134,7 +134,26 @@ cnxk_gpio_read_attr_int(char *attr, int *val) return 0; } +static int +cnxk_gpio_queue_def_conf(struct rte_rawdev *dev, uint16_t queue_id, + rte_rawdev_obj_t queue_conf, size_t queue_conf_size) +{ + unsigned int *conf; + + RTE_SET_USED(dev); + RTE_SET_USED(queue_id); + + if (queue_conf_size != sizeof(*conf)) + return -EINVAL; + + conf = (unsigned int *)queue_conf; + *conf = 1; + + return 0; +} + static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { + .queue_def_conf = cnxk_gpio_queue_def_conf, }; static int -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v2 03/10] raw/cnxk_gpio: support reading queue count 2021-11-28 15:44 ` [DPDK 22.02 PATCH v2 00/10] Add cnxk_gpio PMD Tomasz Duszynski 2021-11-28 15:44 ` [PATCH v2 01/10] raw/cnxk_gpio: add GPIO driver skeleton Tomasz Duszynski 2021-11-28 15:44 ` [PATCH v2 02/10] raw/cnxk_gpio: support reading default queue conf Tomasz Duszynski @ 2021-11-28 15:44 ` Tomasz Duszynski 2021-11-28 15:44 ` [PATCH v2 04/10] raw/cnxk_gpio: support queue setup Tomasz Duszynski ` (7 subsequent siblings) 10 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2021-11-28 15:44 UTC (permalink / raw) To: dev; +Cc: jerinj, stephen, Tomasz Duszynski Add support for reading number of available queues. Single queue corresponds to GPIO. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index deedf98af2..84be7f861e 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -152,8 +152,17 @@ cnxk_gpio_queue_def_conf(struct rte_rawdev *dev, uint16_t queue_id, return 0; } +static uint16_t +cnxk_gpio_queue_count(struct rte_rawdev *dev) +{ + struct cnxk_gpiochip *gpiochip = dev->dev_private; + + return gpiochip->num_gpios; +} + static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { .queue_def_conf = cnxk_gpio_queue_def_conf, + .queue_count = cnxk_gpio_queue_count, }; static int -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v2 04/10] raw/cnxk_gpio: support queue setup 2021-11-28 15:44 ` [DPDK 22.02 PATCH v2 00/10] Add cnxk_gpio PMD Tomasz Duszynski ` (2 preceding siblings ...) 2021-11-28 15:44 ` [PATCH v2 03/10] raw/cnxk_gpio: support reading queue count Tomasz Duszynski @ 2021-11-28 15:44 ` Tomasz Duszynski 2021-11-28 15:44 ` [PATCH v2 05/10] raw/cnxk_gpio: support queue release Tomasz Duszynski ` (6 subsequent siblings) 10 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2021-11-28 15:44 UTC (permalink / raw) To: dev; +Cc: jerinj, stephen, Tomasz Duszynski Add support for queue setup. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 80 +++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 84be7f861e..98b5dd9cd8 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -134,6 +134,85 @@ cnxk_gpio_read_attr_int(char *attr, int *val) return 0; } +static int +cnxk_gpio_write_attr(const char *attr, const char *val) +{ + FILE *fp; + int ret; + + if (!val) + return -EINVAL; + + fp = fopen(attr, "w"); + if (!fp) + return -errno; + + ret = fprintf(fp, "%s", val); + if (ret < 0) { + fclose(fp); + return ret; + } + + ret = fclose(fp); + if (ret) + return -errno; + + return 0; +} + +static int +cnxk_gpio_write_attr_int(const char *attr, int val) +{ + char buf[CNXK_GPIO_BUFSZ]; + + snprintf(buf, sizeof(buf), "%d", val); + + return cnxk_gpio_write_attr(attr, buf); +} + +static struct cnxk_gpio * +cnxk_gpio_lookup(struct cnxk_gpiochip *gpiochip, uint16_t queue) +{ + if (queue >= gpiochip->num_gpios) + return NULL; + + return gpiochip->gpios[queue]; +} + +static int +cnxk_gpio_queue_setup(struct rte_rawdev *dev, uint16_t queue_id, + rte_rawdev_obj_t queue_conf, size_t queue_conf_size) +{ + struct cnxk_gpiochip *gpiochip = dev->dev_private; + char buf[CNXK_GPIO_BUFSZ]; + struct cnxk_gpio *gpio; + int ret; + + RTE_SET_USED(queue_conf); + RTE_SET_USED(queue_conf_size); + + gpio = cnxk_gpio_lookup(gpiochip, queue_id); + if (gpio) + return -EEXIST; + + gpio = rte_zmalloc(NULL, sizeof(*gpio), 0); + if (!gpio) + return -ENOMEM; + gpio->num = queue_id + gpiochip->base; + gpio->gpiochip = gpiochip; + + snprintf(buf, sizeof(buf), "%s/export", CNXK_GPIO_CLASS_PATH); + ret = cnxk_gpio_write_attr_int(buf, gpio->num); + if (ret) { + rte_free(gpio); + return ret; + } + + gpiochip->gpios[queue_id] = gpio; + + return 0; +} + static int cnxk_gpio_queue_def_conf(struct rte_rawdev *dev, uint16_t queue_id, rte_rawdev_obj_t queue_conf, size_t queue_conf_size) @@ -163,6 +242,7 @@ cnxk_gpio_queue_count(struct rte_rawdev *dev) static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { .queue_def_conf = cnxk_gpio_queue_def_conf, .queue_count = cnxk_gpio_queue_count, + .queue_setup = cnxk_gpio_queue_setup, }; static int -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v2 05/10] raw/cnxk_gpio: support queue release 2021-11-28 15:44 ` [DPDK 22.02 PATCH v2 00/10] Add cnxk_gpio PMD Tomasz Duszynski ` (3 preceding siblings ...) 2021-11-28 15:44 ` [PATCH v2 04/10] raw/cnxk_gpio: support queue setup Tomasz Duszynski @ 2021-11-28 15:44 ` Tomasz Duszynski 2021-11-28 15:44 ` [PATCH v2 06/10] raw/cnxk_gpio: support enqueuing buffers Tomasz Duszynski ` (5 subsequent siblings) 10 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2021-11-28 15:44 UTC (permalink / raw) To: dev; +Cc: jerinj, stephen, Tomasz Duszynski Add support for queue release. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 98b5dd9cd8..8ac3c5e1be 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -213,6 +213,29 @@ cnxk_gpio_queue_setup(struct rte_rawdev *dev, uint16_t queue_id, return 0; } +static int +cnxk_gpio_queue_release(struct rte_rawdev *dev, uint16_t queue_id) +{ + struct cnxk_gpiochip *gpiochip = dev->dev_private; + char buf[CNXK_GPIO_BUFSZ]; + struct cnxk_gpio *gpio; + int ret; + + gpio = cnxk_gpio_lookup(gpiochip, queue_id); + if (!gpio) + return -ENODEV; + + snprintf(buf, sizeof(buf), "%s/unexport", CNXK_GPIO_CLASS_PATH); + ret = cnxk_gpio_write_attr_int(buf, gpiochip->base + queue_id); + if (ret) + return ret; + + gpiochip->gpios[queue_id] = NULL; + rte_free(gpio); + + return 0; +} + static int cnxk_gpio_queue_def_conf(struct rte_rawdev *dev, uint16_t queue_id, rte_rawdev_obj_t queue_conf, size_t queue_conf_size) @@ -243,6 +266,7 @@ static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { .queue_def_conf = cnxk_gpio_queue_def_conf, .queue_count = cnxk_gpio_queue_count, .queue_setup = cnxk_gpio_queue_setup, + .queue_release = cnxk_gpio_queue_release, }; static int @@ -316,6 +340,8 @@ cnxk_gpio_remove(struct rte_vdev_device *dev) char name[RTE_RAWDEV_NAME_MAX_LEN]; struct cnxk_gpiochip *gpiochip; struct rte_rawdev *rawdev; + struct cnxk_gpio *gpio; + int i; RTE_SET_USED(dev); @@ -328,6 +354,14 @@ cnxk_gpio_remove(struct rte_vdev_device *dev) return -ENODEV; gpiochip = rawdev->dev_private; + for (i = 0; i < gpiochip->num_gpios; i++) { + gpio = gpiochip->gpios[i]; + if (!gpio) + continue; + + cnxk_gpio_queue_release(rawdev, gpio->num); + } + rte_free(gpiochip->gpios); rte_rawdev_pmd_release(rawdev); -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v2 06/10] raw/cnxk_gpio: support enqueuing buffers 2021-11-28 15:44 ` [DPDK 22.02 PATCH v2 00/10] Add cnxk_gpio PMD Tomasz Duszynski ` (4 preceding siblings ...) 2021-11-28 15:44 ` [PATCH v2 05/10] raw/cnxk_gpio: support queue release Tomasz Duszynski @ 2021-11-28 15:44 ` Tomasz Duszynski 2021-11-28 15:44 ` [PATCH v2 07/10] raw/cnxk_gpio: support dequeuing buffers Tomasz Duszynski ` (4 subsequent siblings) 10 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2021-11-28 15:44 UTC (permalink / raw) To: dev; +Cc: jerinj, stephen, Tomasz Duszynski Add dummy support for enqueuing buffers. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 46 +++++++++++++++++++++++ drivers/raw/cnxk_gpio/cnxk_gpio.h | 1 + drivers/raw/cnxk_gpio/meson.build | 1 + drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h | 38 +++++++++++++++++++ 4 files changed, 86 insertions(+) create mode 100644 drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 8ac3c5e1be..c08ccdfbb8 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -14,6 +14,7 @@ #include <roc_api.h> #include "cnxk_gpio.h" +#include "rte_pmd_cnxk_gpio.h" #define CNXK_GPIO_BUFSZ 128 #define CNXK_GPIO_CLASS_PATH "/sys/class/gpio" @@ -262,7 +263,52 @@ cnxk_gpio_queue_count(struct rte_rawdev *dev) return gpiochip->num_gpios; } +static int +cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf) +{ + struct cnxk_gpio_msg *msg = rbuf->buf_addr; + void *rsp = NULL; + + switch (msg->type) { + default: + return -EINVAL; + } + + /* get rid of last response if any */ + if (gpio->rsp) { + RTE_LOG(WARNING, PMD, "previous response got overwritten\n"); + rte_free(gpio->rsp); + } + gpio->rsp = rsp; + + return 0; +} + +static int +cnxk_gpio_enqueue_bufs(struct rte_rawdev *dev, struct rte_rawdev_buf **buffers, + unsigned int count, rte_rawdev_obj_t context) +{ + struct cnxk_gpiochip *gpiochip = dev->dev_private; + unsigned int queue = (size_t)context; + struct cnxk_gpio *gpio; + int ret; + + if (count == 0) + return 0; + + gpio = cnxk_gpio_lookup(gpiochip, queue); + if (!gpio) + return -ENODEV; + + ret = cnxk_gpio_process_buf(gpio, buffers[0]); + if (ret) + return ret; + + return 1; +} + static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { + .enqueue_bufs = cnxk_gpio_enqueue_bufs, .queue_def_conf = cnxk_gpio_queue_def_conf, .queue_count = cnxk_gpio_queue_count, .queue_setup = cnxk_gpio_queue_setup, diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.h b/drivers/raw/cnxk_gpio/cnxk_gpio.h index 4dae8316ba..6b54ebe6e6 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.h +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.h @@ -9,6 +9,7 @@ struct cnxk_gpiochip; struct cnxk_gpio { struct cnxk_gpiochip *gpiochip; + void *rsp; int num; }; diff --git a/drivers/raw/cnxk_gpio/meson.build b/drivers/raw/cnxk_gpio/meson.build index 9a7e716c1e..3fbfdd838c 100644 --- a/drivers/raw/cnxk_gpio/meson.build +++ b/drivers/raw/cnxk_gpio/meson.build @@ -6,3 +6,4 @@ deps += ['bus_vdev', 'common_cnxk', 'rawdev', 'kvargs'] sources = files( 'cnxk_gpio.c', ) +headers = files('rte_pmd_cnxk_gpio.h') diff --git a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h new file mode 100644 index 0000000000..c71065e10c --- /dev/null +++ b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2021 Marvell. + */ + +#ifndef _RTE_PMD_CNXK_GPIO_H_ +#define _RTE_PMD_CNXK_GPIO_H_ + +/** + * @file rte_pmd_cnxk_gpio.h + * + * Marvell GPIO PMD specific structures and interface + * + * This API allows applications to manage GPIOs in user space along with + * installing interrupt handlers for low latency signal processing. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** Available message types */ +enum cnxk_gpio_msg_type { + /** Invalid message type */ + CNXK_GPIO_MSG_TYPE_INVALID, +}; + +struct cnxk_gpio_msg { + /** Message type */ + enum cnxk_gpio_msg_type type; + /** Message data passed to PMD or received from PMD */ + void *data; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _RTE_PMD_CNXK_GPIO_H_ */ -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v2 07/10] raw/cnxk_gpio: support dequeuing buffers 2021-11-28 15:44 ` [DPDK 22.02 PATCH v2 00/10] Add cnxk_gpio PMD Tomasz Duszynski ` (5 preceding siblings ...) 2021-11-28 15:44 ` [PATCH v2 06/10] raw/cnxk_gpio: support enqueuing buffers Tomasz Duszynski @ 2021-11-28 15:44 ` Tomasz Duszynski 2021-11-28 15:44 ` [PATCH v2 08/10] raw/cnxk_gpio: support standard GPIO operations Tomasz Duszynski ` (3 subsequent siblings) 10 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2021-11-28 15:44 UTC (permalink / raw) To: dev; +Cc: jerinj, stephen, Tomasz Duszynski Add support for dequeuing buffers. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index c08ccdfbb8..244a625822 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -307,8 +307,34 @@ cnxk_gpio_enqueue_bufs(struct rte_rawdev *dev, struct rte_rawdev_buf **buffers, return 1; } +static int +cnxk_gpio_dequeue_bufs(struct rte_rawdev *dev, struct rte_rawdev_buf **buffers, + unsigned int count, rte_rawdev_obj_t context) +{ + struct cnxk_gpiochip *gpiochip = dev->dev_private; + unsigned int queue = (size_t)context; + struct cnxk_gpio *gpio; + + if (count == 0) + return 0; + + gpio = cnxk_gpio_lookup(gpiochip, queue); + if (!gpio) + return -ENODEV; + + if (gpio->rsp) { + buffers[0]->buf_addr = gpio->rsp; + gpio->rsp = NULL; + + return 1; + } + + return 0; +} + static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { .enqueue_bufs = cnxk_gpio_enqueue_bufs, + .dequeue_bufs = cnxk_gpio_dequeue_bufs, .queue_def_conf = cnxk_gpio_queue_def_conf, .queue_count = cnxk_gpio_queue_count, .queue_setup = cnxk_gpio_queue_setup, -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v2 08/10] raw/cnxk_gpio: support standard GPIO operations 2021-11-28 15:44 ` [DPDK 22.02 PATCH v2 00/10] Add cnxk_gpio PMD Tomasz Duszynski ` (6 preceding siblings ...) 2021-11-28 15:44 ` [PATCH v2 07/10] raw/cnxk_gpio: support dequeuing buffers Tomasz Duszynski @ 2021-11-28 15:44 ` Tomasz Duszynski 2021-11-28 15:44 ` [PATCH v2 09/10] raw/cnxk_gpio: support custom irq handlers Tomasz Duszynski ` (2 subsequent siblings) 10 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2021-11-28 15:44 UTC (permalink / raw) To: dev; +Cc: jerinj, stephen, Tomasz Duszynski Add support for standard GPIO operations i.e ones normally provided by GPIO sysfs interface. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- doc/guides/rawdevs/cnxk_gpio.rst | 98 ++++++++ drivers/raw/cnxk_gpio/cnxk_gpio.c | 146 +++++++++++ drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h | 279 +++++++++++++++++++++- 3 files changed, 521 insertions(+), 2 deletions(-) diff --git a/doc/guides/rawdevs/cnxk_gpio.rst b/doc/guides/rawdevs/cnxk_gpio.rst index 868302d07f..f6c3c942c5 100644 --- a/doc/guides/rawdevs/cnxk_gpio.rst +++ b/doc/guides/rawdevs/cnxk_gpio.rst @@ -63,3 +63,101 @@ call barely exports GPIO to userspace. To perform actual data transfer use standard ``rte_rawdev_enqueue_buffers()`` and ``rte_rawdev_dequeue_buffers()`` APIs. Not all messages produce sensible responses hence dequeueing is not always necessary. + +CNXK GPIO PMD +------------- + +PMD accepts ``struct cnxk_gpio_msg`` messages which differ by type and payload. +Message types along with description are listed below. As for the usage examples +please refer to ``cnxk_gpio_selftest()``. There's a set of convenient wrappers +available, one for each existing command. + +Set GPIO value +~~~~~~~~~~~~~~ + +Message is used to set output to low or high. This does not work for GPIOs +configured as input. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_SET_PIN_VALUE``. + +Payload must be an integer set to 0 (low) or 1 (high). + +Consider using ``rte_pmd_gpio_set_pin_value()`` wrapper. + +Set GPIO edge +~~~~~~~~~~~~~ + +Message is used to set edge that triggers interrupt. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_SET_PIN_EDGE``. + +Payload must be `enum cnxk_gpio_pin_edge`. + +Consider using ``rte_pmd_gpio_set_pin_edge()`` wrapper. + +Set GPIO direction +~~~~~~~~~~~~~~~~~~ + +Message is used to change GPIO direction to either input or output. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_SET_PIN_DIR``. + +Payload must be `enum cnxk_gpio_pin_dir`. + +Consider using ``rte_pmd_gpio_set_pin_dir()`` wrapper. + +Set GPIO active low +~~~~~~~~~~~~~~~~~~~ + +Message is used to set whether pin is active low. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_SET_PIN_ACTIVE_LOW``. + +Payload must be an integer set to 0 or 1. The latter activates inversion. + +Consider using ``rte_pmd_gpio_set_pin_active_low()`` wrapper. + +Get GPIO value +~~~~~~~~~~~~~~ + +Message is used to read GPIO value. Value can be 0 (low) or 1 (high). + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_GET_PIN_VALUE``. + +Payload contains integer set to either 0 or 1. + +Consider using ``rte_pmd_gpio_get_pin_value()`` wrapper. + +Get GPIO edge +~~~~~~~~~~~~~ + +Message is used to read GPIO edge. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_GET_PIN_EDGE``. + +Payload contains `enum cnxk_gpio_pin_edge`. + +Consider using ``rte_pmd_gpio_get_pin_edge()`` wrapper. + +Get GPIO direction +~~~~~~~~~~~~~~~~~~ + +Message is used to read GPIO direction. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_GET_PIN_DIR``. + +Payload contains `enum cnxk_gpio_pin_dir`. + +Consider using ``rte_pmd_gpio_get_pin_dir()`` wrapper. + +Get GPIO active low +~~~~~~~~~~~~~~~~~~~ + +Message is used check whether inverted logic is active. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW``. + +Payload contains an integer set to 0 or 1. The latter means inverted logic +is turned on. + +Consider using ``rte_pmd_gpio_get_pin_active_low()`` wrapper. diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 244a625822..c09bc5d56e 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -263,13 +263,159 @@ cnxk_gpio_queue_count(struct rte_rawdev *dev) return gpiochip->num_gpios; } +static const struct { + enum cnxk_gpio_pin_edge edge; + const char *name; +} cnxk_gpio_edge_name[] = { + { CNXK_GPIO_PIN_EDGE_NONE, "none" }, + { CNXK_GPIO_PIN_EDGE_FALLING, "falling" }, + { CNXK_GPIO_PIN_EDGE_RISING, "rising" }, + { CNXK_GPIO_PIN_EDGE_BOTH, "both" }, +}; + +static const char * +cnxk_gpio_edge_to_name(enum cnxk_gpio_pin_edge edge) +{ + unsigned int i; + + for (i = 0; i < RTE_DIM(cnxk_gpio_edge_name); i++) { + if (cnxk_gpio_edge_name[i].edge == edge) + return cnxk_gpio_edge_name[i].name; + } + + return NULL; +} + +static enum cnxk_gpio_pin_edge +cnxk_gpio_name_to_edge(const char *name) +{ + unsigned int i; + + for (i = 0; i < RTE_DIM(cnxk_gpio_edge_name); i++) { + if (!strcmp(cnxk_gpio_edge_name[i].name, name)) + break; + } + + return cnxk_gpio_edge_name[i].edge; +} + +static const struct { + enum cnxk_gpio_pin_dir dir; + const char *name; +} cnxk_gpio_dir_name[] = { + { CNXK_GPIO_PIN_DIR_IN, "in" }, + { CNXK_GPIO_PIN_DIR_OUT, "out" }, + { CNXK_GPIO_PIN_DIR_HIGH, "high" }, + { CNXK_GPIO_PIN_DIR_LOW, "low" }, +}; + +static const char * +cnxk_gpio_dir_to_name(enum cnxk_gpio_pin_dir dir) +{ + unsigned int i; + + for (i = 0; i < RTE_DIM(cnxk_gpio_dir_name); i++) { + if (cnxk_gpio_dir_name[i].dir == dir) + return cnxk_gpio_dir_name[i].name; + } + + return NULL; +} + +static enum cnxk_gpio_pin_dir +cnxk_gpio_name_to_dir(const char *name) +{ + unsigned int i; + + for (i = 0; i < RTE_DIM(cnxk_gpio_dir_name); i++) { + if (!strcmp(cnxk_gpio_dir_name[i].name, name)) + break; + } + + return cnxk_gpio_dir_name[i].dir; +} + static int cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf) { struct cnxk_gpio_msg *msg = rbuf->buf_addr; + enum cnxk_gpio_pin_edge edge; + enum cnxk_gpio_pin_dir dir; + char buf[CNXK_GPIO_BUFSZ]; void *rsp = NULL; + int ret, val, n; + + n = snprintf(buf, sizeof(buf), "%s/gpio%d", CNXK_GPIO_CLASS_PATH, + gpio->num); switch (msg->type) { + case CNXK_GPIO_MSG_TYPE_SET_PIN_VALUE: + snprintf(buf + n, sizeof(buf) - n, "/value"); + ret = cnxk_gpio_write_attr_int(buf, !!*(int *)msg->data); + break; + case CNXK_GPIO_MSG_TYPE_SET_PIN_EDGE: + snprintf(buf + n, sizeof(buf) - n, "/edge"); + edge = *(enum cnxk_gpio_pin_edge *)msg->data; + ret = cnxk_gpio_write_attr(buf, cnxk_gpio_edge_to_name(edge)); + break; + case CNXK_GPIO_MSG_TYPE_SET_PIN_DIR: + snprintf(buf + n, sizeof(buf) - n, "/direction"); + dir = *(enum cnxk_gpio_pin_dir *)msg->data; + ret = cnxk_gpio_write_attr(buf, cnxk_gpio_dir_to_name(dir)); + break; + case CNXK_GPIO_MSG_TYPE_SET_PIN_ACTIVE_LOW: + snprintf(buf + n, sizeof(buf) - n, "/active_low"); + val = *(int *)msg->data; + ret = cnxk_gpio_write_attr_int(buf, val); + break; + case CNXK_GPIO_MSG_TYPE_GET_PIN_VALUE: + snprintf(buf + n, sizeof(buf) - n, "/value"); + ret = cnxk_gpio_read_attr_int(buf, &val); + if (ret) + break; + + rsp = rte_zmalloc(NULL, sizeof(int), 0); + if (!rsp) + return -ENOMEM; + + *(int *)rsp = val; + break; + case CNXK_GPIO_MSG_TYPE_GET_PIN_EDGE: + snprintf(buf + n, sizeof(buf) - n, "/edge"); + ret = cnxk_gpio_read_attr(buf, buf); + if (ret) + break; + + rsp = rte_zmalloc(NULL, sizeof(enum cnxk_gpio_pin_edge), 0); + if (!rsp) + return -ENOMEM; + + *(enum cnxk_gpio_pin_edge *)rsp = cnxk_gpio_name_to_edge(buf); + break; + case CNXK_GPIO_MSG_TYPE_GET_PIN_DIR: + snprintf(buf + n, sizeof(buf) - n, "/direction"); + ret = cnxk_gpio_read_attr(buf, buf); + if (ret) + break; + + rsp = rte_zmalloc(NULL, sizeof(enum cnxk_gpio_pin_dir), 0); + if (!rsp) + return -ENOMEM; + + *(enum cnxk_gpio_pin_dir *)rsp = cnxk_gpio_name_to_dir(buf); + break; + case CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW: + snprintf(buf + n, sizeof(buf) - n, "/active_low"); + ret = cnxk_gpio_read_attr_int(buf, &val); + if (ret) + break; + + rsp = rte_zmalloc(NULL, sizeof(int), 0); + if (!rsp) + return -ENOMEM; + + *(int *)rsp = val; + break; default: return -EINVAL; } diff --git a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h index c71065e10c..7c3dc225ca 100644 --- a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h +++ b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h @@ -5,6 +5,10 @@ #ifndef _RTE_PMD_CNXK_GPIO_H_ #define _RTE_PMD_CNXK_GPIO_H_ +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_rawdev.h> + /** * @file rte_pmd_cnxk_gpio.h * @@ -20,8 +24,46 @@ extern "C" { /** Available message types */ enum cnxk_gpio_msg_type { - /** Invalid message type */ - CNXK_GPIO_MSG_TYPE_INVALID, + /** Type used to set output value */ + CNXK_GPIO_MSG_TYPE_SET_PIN_VALUE, + /** Type used to set edge */ + CNXK_GPIO_MSG_TYPE_SET_PIN_EDGE, + /** Type used to set direction */ + CNXK_GPIO_MSG_TYPE_SET_PIN_DIR, + /** Type used to set inverted logic */ + CNXK_GPIO_MSG_TYPE_SET_PIN_ACTIVE_LOW, + /** Type used to read value */ + CNXK_GPIO_MSG_TYPE_GET_PIN_VALUE, + /** Type used to read edge */ + CNXK_GPIO_MSG_TYPE_GET_PIN_EDGE, + /** Type used to read direction */ + CNXK_GPIO_MSG_TYPE_GET_PIN_DIR, + /** Type used to read inverted logic state */ + CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW, +}; + +/** Available edges */ +enum cnxk_gpio_pin_edge { + /** Set edge to none */ + CNXK_GPIO_PIN_EDGE_NONE, + /** Set edge to falling */ + CNXK_GPIO_PIN_EDGE_FALLING, + /** Set edge to rising */ + CNXK_GPIO_PIN_EDGE_RISING, + /** Set edge to both rising and falling */ + CNXK_GPIO_PIN_EDGE_BOTH, +}; + +/** Available directions */ +enum cnxk_gpio_pin_dir { + /** Set direction to input */ + CNXK_GPIO_PIN_DIR_IN, + /** Set direction to output */ + CNXK_GPIO_PIN_DIR_OUT, + /** Set direction to output and value to 1 */ + CNXK_GPIO_PIN_DIR_HIGH, + /* Set direction to output and value to 0 */ + CNXK_GPIO_PIN_DIR_LOW, }; struct cnxk_gpio_msg { @@ -31,6 +73,239 @@ struct cnxk_gpio_msg { void *data; }; +/** @internal helper routine for enqueuing/dequeuing messages */ +static __rte_always_inline int +__rte_pmd_gpio_enq_deq(uint16_t dev_id, int gpio, void *req, void *rsp, + size_t rsp_size) +{ + struct rte_rawdev_buf *bufs[1]; + struct rte_rawdev_buf buf; + void *q; + int ret; + + q = (void *)(size_t)gpio; + buf.buf_addr = req; + bufs[0] = &buf; + + ret = rte_rawdev_enqueue_buffers(dev_id, bufs, RTE_DIM(bufs), q); + if (ret < 0) + return ret; + if (ret != RTE_DIM(bufs)) + return -EIO; + + if (!rsp) + return 0; + + ret = rte_rawdev_dequeue_buffers(dev_id, bufs, RTE_DIM(bufs), q); + if (ret < 0) + return ret; + if (ret != RTE_DIM(bufs)) + return -EIO; + + rte_memcpy(rsp, buf.buf_addr, rsp_size); + rte_free(buf.buf_addr); + + return 0; +} + +/** + * Set output to specific value + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param val + * Value output will be set to. 0 represents low state while + * 1 high state + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_set_pin_value(uint16_t dev_id, int gpio, int val) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_SET_PIN_VALUE, + .data = &val, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Select signal edge that triggers interrupt + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param edge + * Signal edge that triggers interrupt + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_set_pin_edge(uint16_t dev_id, int gpio, + enum cnxk_gpio_pin_edge edge) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_SET_PIN_EDGE, + .data = &edge + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Configure GPIO as input or output + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param dir + * Direction of the GPIO + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_set_pin_dir(uint16_t dev_id, int gpio, enum cnxk_gpio_pin_dir dir) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_SET_PIN_DIR, + .data = &dir, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Enable or disable inverted logic + * + * If GPIO is configured as output then writing 1 or 0 will result in setting + * output to respectively low or high + * + * If GPIO is configured as input then logic inversion applies to edges. Both + * current and future settings are affected + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param val + * 0 to disable, 1 to enable inverted logic + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_set_pin_active_low(uint16_t dev_id, int gpio, int val) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_SET_PIN_ACTIVE_LOW, + .data = &val, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Read GPIO value + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param val + * Where to store read logical signal value i.e 0 or 1 + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_get_pin_value(uint16_t dev_id, int gpio, int *val) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_GET_PIN_VALUE, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, val, sizeof(*val)); +} + +/** + * Read GPIO edge + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param edge + * Where to store edge + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_get_pin_edge(uint16_t dev_id, int gpio, + enum cnxk_gpio_pin_edge *edge) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_GET_PIN_EDGE, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, edge, sizeof(*edge)); +} + +/** + * Read GPIO direction + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param dir + * Where to store direction + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_get_pin_dir(uint16_t dev_id, int gpio, enum cnxk_gpio_pin_dir *dir) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_GET_PIN_DIR, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, dir, sizeof(*dir)); +} + +/** + * Read whether GPIO is active low + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param val + * Where to store active low state + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_get_pin_active_low(uint16_t dev_id, int gpio, int *val) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW, + .data = &val, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, val, sizeof(*val)); +} + #ifdef __cplusplus } #endif -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v2 09/10] raw/cnxk_gpio: support custom irq handlers 2021-11-28 15:44 ` [DPDK 22.02 PATCH v2 00/10] Add cnxk_gpio PMD Tomasz Duszynski ` (7 preceding siblings ...) 2021-11-28 15:44 ` [PATCH v2 08/10] raw/cnxk_gpio: support standard GPIO operations Tomasz Duszynski @ 2021-11-28 15:44 ` Tomasz Duszynski 2021-11-28 15:44 ` [PATCH v2 10/10] raw/cnxk_gpio: support selftest Tomasz Duszynski 2021-12-13 8:17 ` [PATCH v3 00/10] Add cnxk_gpio PMD Tomasz Duszynski 10 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2021-11-28 15:44 UTC (permalink / raw) To: dev; +Cc: jerinj, stephen, Tomasz Duszynski Add support for custom interrupt handlers. Custom interrupt handlers bypass kernel completely and are meant for fast and low latency access to GPIO state. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- doc/guides/rawdevs/cnxk_gpio.rst | 21 +++ drivers/raw/cnxk_gpio/cnxk_gpio.c | 37 ++++ drivers/raw/cnxk_gpio/cnxk_gpio.h | 8 + drivers/raw/cnxk_gpio/cnxk_gpio_irq.c | 216 ++++++++++++++++++++++ drivers/raw/cnxk_gpio/meson.build | 1 + drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h | 116 ++++++++++++ 6 files changed, 399 insertions(+) create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio_irq.c diff --git a/doc/guides/rawdevs/cnxk_gpio.rst b/doc/guides/rawdevs/cnxk_gpio.rst index f6c3c942c5..ad93ec0d44 100644 --- a/doc/guides/rawdevs/cnxk_gpio.rst +++ b/doc/guides/rawdevs/cnxk_gpio.rst @@ -161,3 +161,24 @@ Payload contains an integer set to 0 or 1. The latter means inverted logic is turned on. Consider using ``rte_pmd_gpio_get_pin_active_low()`` wrapper. + +Request interrupt +~~~~~~~~~~~~~~~~~ + +Message is used to install custom interrupt handler. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_REGISTER_IRQ``. + +Payload needs to be set to ``struct cnxk_gpio_irq`` which describes interrupt +being requested. + +Consider using ``rte_pmd_gpio_register_gpio()`` wrapper. + +Free interrupt +~~~~~~~~~~~~~~ + +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. diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index c09bc5d56e..d39d203245 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -335,6 +335,28 @@ cnxk_gpio_name_to_dir(const char *name) return cnxk_gpio_dir_name[i].dir; } +static int +cnxk_gpio_register_irq(struct cnxk_gpio *gpio, struct cnxk_gpio_irq *irq) +{ + int ret; + + ret = cnxk_gpio_irq_request(gpio->num - gpio->gpiochip->base, irq->cpu); + if (ret) + return ret; + + gpio->handler = irq->handler; + gpio->data = irq->data; + gpio->cpu = irq->cpu; + + return 0; +} + +static int +cnxk_gpio_unregister_irq(struct cnxk_gpio *gpio) +{ + return cnxk_gpio_irq_free(gpio->num - gpio->gpiochip->base); +} + static int cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf) { @@ -416,6 +438,13 @@ cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf) *(int *)rsp = val; break; + case CNXK_GPIO_MSG_TYPE_REGISTER_IRQ: + ret = cnxk_gpio_register_irq(gpio, + (struct cnxk_gpio_irq *)msg->data); + break; + case CNXK_GPIO_MSG_TYPE_UNREGISTER_IRQ: + ret = cnxk_gpio_unregister_irq(gpio); + break; default: return -EINVAL; } @@ -519,6 +548,10 @@ cnxk_gpio_probe(struct rte_vdev_device *dev) if (ret) goto out; + ret = cnxk_gpio_irq_init(gpiochip); + if (ret) + goto out; + /* read gpio base */ snprintf(buf, sizeof(buf), "%s/gpiochip%d/base", CNXK_GPIO_CLASS_PATH, gpiochip->num); @@ -577,10 +610,14 @@ cnxk_gpio_remove(struct rte_vdev_device *dev) if (!gpio) continue; + if (gpio->handler) + cnxk_gpio_unregister_irq(gpio); + cnxk_gpio_queue_release(rawdev, gpio->num); } rte_free(gpiochip->gpios); + cnxk_gpio_irq_fini(); rte_rawdev_pmd_release(rawdev); return 0; diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.h b/drivers/raw/cnxk_gpio/cnxk_gpio.h index 6b54ebe6e6..c052ca5735 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.h +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.h @@ -11,6 +11,9 @@ struct cnxk_gpio { struct cnxk_gpiochip *gpiochip; void *rsp; int num; + void (*handler)(int gpio, void *data); + void *data; + int cpu; }; struct cnxk_gpiochip { @@ -20,4 +23,9 @@ struct cnxk_gpiochip { struct cnxk_gpio **gpios; }; +int cnxk_gpio_irq_init(struct cnxk_gpiochip *gpiochip); +void cnxk_gpio_irq_fini(void); +int cnxk_gpio_irq_request(int gpio, int cpu); +int cnxk_gpio_irq_free(int gpio); + #endif /* _CNXK_GPIO_H_ */ diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio_irq.c b/drivers/raw/cnxk_gpio/cnxk_gpio_irq.c new file mode 100644 index 0000000000..2fa8e69899 --- /dev/null +++ b/drivers/raw/cnxk_gpio/cnxk_gpio_irq.c @@ -0,0 +1,216 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2021 Marvell. + */ + +#include <fcntl.h> +#include <pthread.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/queue.h> +#include <unistd.h> + +#include <rte_rawdev_pmd.h> + +#include <roc_api.h> + +#include "cnxk_gpio.h" + +#define OTX_IOC_MAGIC 0xF2 +#define OTX_IOC_SET_GPIO_HANDLER \ + _IOW(OTX_IOC_MAGIC, 1, struct otx_gpio_usr_data) +#define OTX_IOC_CLR_GPIO_HANDLER \ + _IO(OTX_IOC_MAGIC, 2) + +struct otx_gpio_usr_data { + uint64_t isr_base; + uint64_t sp; + uint64_t cpu; + uint64_t gpio_num; +}; + +struct cnxk_gpio_irq_stack { + LIST_ENTRY(cnxk_gpio_irq_stack) next; + void *sp_buffer; + int cpu; + int inuse; +}; + +struct cnxk_gpio_irqchip { + int fd; + /* serialize access to this struct */ + pthread_mutex_t lock; + LIST_HEAD(, cnxk_gpio_irq_stack) stacks; + + struct cnxk_gpiochip *gpiochip; +}; + +static struct cnxk_gpio_irqchip *irqchip; + +static void +cnxk_gpio_irq_stack_free(int cpu) +{ + struct cnxk_gpio_irq_stack *stack; + + LIST_FOREACH(stack, &irqchip->stacks, next) { + if (stack->cpu == cpu) + break; + } + + if (!stack) + return; + + if (stack->inuse) + stack->inuse--; + + if (stack->inuse == 0) { + LIST_REMOVE(stack, next); + rte_free(stack->sp_buffer); + rte_free(stack); + } +} + +static void * +cnxk_gpio_irq_stack_alloc(int cpu) +{ +#define ARM_STACK_ALIGNMENT (2 * sizeof(void *)) +#define IRQ_STACK_SIZE 0x200000 + + struct cnxk_gpio_irq_stack *stack; + + LIST_FOREACH(stack, &irqchip->stacks, next) { + if (stack->cpu == cpu) + break; + } + + if (stack) { + stack->inuse++; + return (char *)stack->sp_buffer + IRQ_STACK_SIZE; + } + + stack = rte_malloc(NULL, sizeof(*stack), 0); + if (!stack) + return NULL; + + stack->sp_buffer = + rte_zmalloc(NULL, IRQ_STACK_SIZE * 2, ARM_STACK_ALIGNMENT); + if (!stack->sp_buffer) { + rte_free(stack); + return NULL; + } + + stack->cpu = cpu; + stack->inuse = 1; + LIST_INSERT_HEAD(&irqchip->stacks, stack, next); + + return (char *)stack->sp_buffer + IRQ_STACK_SIZE; +} + +static void +cnxk_gpio_irq_handler(int gpio_num) +{ + struct cnxk_gpiochip *gpiochip = irqchip->gpiochip; + struct cnxk_gpio *gpio; + + if (gpio_num >= gpiochip->num_gpios) + goto out; + + gpio = gpiochip->gpios[gpio_num]; + if (likely(gpio->handler)) + gpio->handler(gpio_num, gpio->data); + +out: + roc_atf_ret(); +} + +int +cnxk_gpio_irq_init(struct cnxk_gpiochip *gpiochip) +{ + if (irqchip) + return 0; + + irqchip = rte_zmalloc(NULL, sizeof(*irqchip), 0); + if (!irqchip) + return -ENOMEM; + + irqchip->fd = open("/dev/otx-gpio-ctr", O_RDWR | O_SYNC); + if (irqchip->fd < 0) { + rte_free(irqchip); + return -errno; + } + + pthread_mutex_init(&irqchip->lock, NULL); + LIST_INIT(&irqchip->stacks); + irqchip->gpiochip = gpiochip; + + return 0; +} + +void +cnxk_gpio_irq_fini(void) +{ + if (!irqchip) + return; + + close(irqchip->fd); + rte_free(irqchip); + irqchip = NULL; +} + +int +cnxk_gpio_irq_request(int gpio, int cpu) +{ + struct otx_gpio_usr_data data; + void *sp; + int ret; + + pthread_mutex_lock(&irqchip->lock); + + sp = cnxk_gpio_irq_stack_alloc(cpu); + if (!sp) { + ret = -ENOMEM; + goto out_unlock; + } + + data.isr_base = (uint64_t)cnxk_gpio_irq_handler; + data.sp = (uint64_t)sp; + data.cpu = (uint64_t)cpu; + data.gpio_num = (uint64_t)gpio; + + mlockall(MCL_CURRENT | MCL_FUTURE); + ret = ioctl(irqchip->fd, OTX_IOC_SET_GPIO_HANDLER, &data); + if (ret) { + ret = -errno; + goto out_free_stack; + } + + pthread_mutex_unlock(&irqchip->lock); + + return 0; + +out_free_stack: + cnxk_gpio_irq_stack_free(cpu); +out_unlock: + pthread_mutex_unlock(&irqchip->lock); + + return ret; +} + +int +cnxk_gpio_irq_free(int gpio) +{ + int ret; + + pthread_mutex_lock(&irqchip->lock); + + ret = ioctl(irqchip->fd, OTX_IOC_CLR_GPIO_HANDLER, gpio); + if (ret) { + pthread_mutex_unlock(&irqchip->lock); + return -errno; + } + + cnxk_gpio_irq_stack_free(irqchip->gpiochip->gpios[gpio]->cpu); + + pthread_mutex_unlock(&irqchip->lock); + + return 0; +} diff --git a/drivers/raw/cnxk_gpio/meson.build b/drivers/raw/cnxk_gpio/meson.build index 3fbfdd838c..9b55f029c7 100644 --- a/drivers/raw/cnxk_gpio/meson.build +++ b/drivers/raw/cnxk_gpio/meson.build @@ -5,5 +5,6 @@ deps += ['bus_vdev', 'common_cnxk', 'rawdev', 'kvargs'] sources = files( 'cnxk_gpio.c', + 'cnxk_gpio_irq.c', ) headers = files('rte_pmd_cnxk_gpio.h') diff --git a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h index 7c3dc225ca..e3096dc14f 100644 --- a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h +++ b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h @@ -40,6 +40,10 @@ enum cnxk_gpio_msg_type { CNXK_GPIO_MSG_TYPE_GET_PIN_DIR, /** Type used to read inverted logic state */ CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW, + /** Type used to register interrupt handler */ + CNXK_GPIO_MSG_TYPE_REGISTER_IRQ, + /** Type used to remove interrupt handler */ + CNXK_GPIO_MSG_TYPE_UNREGISTER_IRQ, }; /** Available edges */ @@ -66,6 +70,25 @@ enum cnxk_gpio_pin_dir { CNXK_GPIO_PIN_DIR_LOW, }; +/** + * GPIO interrupt handler + * + * @param gpio + * Zero-based GPIO number + * @param data + * Cookie passed to interrupt handler + */ +typedef void (*cnxk_gpio_irq_handler_t)(int gpio, void *data); + +struct cnxk_gpio_irq { + /** Interrupt handler */ + cnxk_gpio_irq_handler_t handler; + /** User data passed to irq handler */ + void *data; + /** CPU which will run irq handler */ + int cpu; +}; + struct cnxk_gpio_msg { /** Message type */ enum cnxk_gpio_msg_type type; @@ -306,6 +329,99 @@ rte_pmd_gpio_get_pin_active_low(uint16_t dev_id, int gpio, int *val) return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, val, sizeof(*val)); } +/** + * Attach interrupt handler to GPIO + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param cpu + * CPU which will be handling interrupt + * @param handler + * Interrupt handler to be executed + * @param data + * Data to be passed to interrupt handler + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_register_irq(uint16_t dev_id, int gpio, int cpu, + cnxk_gpio_irq_handler_t handler, void *data) +{ + struct cnxk_gpio_irq irq = { + .handler = handler, + .data = data, + .cpu = cpu, + }; + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_REGISTER_IRQ, + .data = &irq, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Detach interrupt handler from GPIO + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_unregister_irq(uint16_t dev_id, int gpio) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_UNREGISTER_IRQ, + .data = &gpio, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Enable interrupt + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param edge + * Edge that should trigger interrupt + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_enable_interrupt(uint16_t dev_id, int gpio, + enum cnxk_gpio_pin_edge edge) +{ + return rte_pmd_gpio_set_pin_edge(dev_id, gpio, edge); +} + +/** + * Disable interrupt + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_disable_interrupt(uint16_t dev_id, int gpio) +{ + return rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_NONE); +} + #ifdef __cplusplus } #endif -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v2 10/10] raw/cnxk_gpio: support selftest 2021-11-28 15:44 ` [DPDK 22.02 PATCH v2 00/10] Add cnxk_gpio PMD Tomasz Duszynski ` (8 preceding siblings ...) 2021-11-28 15:44 ` [PATCH v2 09/10] raw/cnxk_gpio: support custom irq handlers Tomasz Duszynski @ 2021-11-28 15:44 ` Tomasz Duszynski 2021-12-13 8:17 ` [PATCH v3 00/10] Add cnxk_gpio PMD Tomasz Duszynski 10 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2021-11-28 15:44 UTC (permalink / raw) To: dev; +Cc: jerinj, stephen, Tomasz Duszynski Add support for performing selftest. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- 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=<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. diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index d39d203245..fa81b45f89 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -514,6 +514,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 <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; +} 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') -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v3 00/10] Add cnxk_gpio PMD 2021-11-28 15:44 ` [DPDK 22.02 PATCH v2 00/10] Add cnxk_gpio PMD Tomasz Duszynski ` (9 preceding siblings ...) 2021-11-28 15:44 ` [PATCH v2 10/10] raw/cnxk_gpio: support selftest Tomasz Duszynski @ 2021-12-13 8:17 ` Tomasz Duszynski 2021-12-13 8:17 ` [PATCH v3 01/10] raw/cnxk_gpio: add GPIO driver skeleton Tomasz Duszynski ` (10 more replies) 10 siblings, 11 replies; 75+ messages in thread From: Tomasz Duszynski @ 2021-12-13 8:17 UTC (permalink / raw) To: dev; +Cc: jerinj, stephen, Tomasz Duszynski This series introduces a new rawdevice PMD which allows to manage userspace GPIOs and install custom GPIO interrupt handlers which bypass kernel. This is especially useful for applications that, besides providing standard dataplane functionality, want to have fast and low latency access to GPIO pin state. It'd be great to have that merged during 22.02 merge window. v3: - fix meson formatting - fix cnxk_gpio_process_buf() return value v2: - do not trigger irq by writing to /dev/mem, use ioctl() instead Tomasz Duszynski (10): raw/cnxk_gpio: add GPIO driver skeleton raw/cnxk_gpio: support reading default queue conf raw/cnxk_gpio: support reading queue count raw/cnxk_gpio: support queue setup raw/cnxk_gpio: support queue release raw/cnxk_gpio: support enqueuing buffers raw/cnxk_gpio: support dequeuing buffers raw/cnxk_gpio: support standard GPIO operations raw/cnxk_gpio: support custom irq handlers raw/cnxk_gpio: support selftest doc/guides/rawdevs/cnxk_gpio.rst | 195 +++++++ doc/guides/rawdevs/index.rst | 1 + drivers/raw/cnxk_gpio/cnxk_gpio.c | 633 +++++++++++++++++++++ drivers/raw/cnxk_gpio/cnxk_gpio.h | 33 ++ drivers/raw/cnxk_gpio/cnxk_gpio_irq.c | 216 +++++++ drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c | 386 +++++++++++++ drivers/raw/cnxk_gpio/meson.build | 11 + drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h | 429 ++++++++++++++ drivers/raw/cnxk_gpio/version.map | 3 + drivers/raw/meson.build | 1 + 10 files changed, 1908 insertions(+) create mode 100644 doc/guides/rawdevs/cnxk_gpio.rst create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio.c create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio.h create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio_irq.c create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c create mode 100644 drivers/raw/cnxk_gpio/meson.build create mode 100644 drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h create mode 100644 drivers/raw/cnxk_gpio/version.map -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v3 01/10] raw/cnxk_gpio: add GPIO driver skeleton 2021-12-13 8:17 ` [PATCH v3 00/10] Add cnxk_gpio PMD Tomasz Duszynski @ 2021-12-13 8:17 ` Tomasz Duszynski 2021-12-13 8:17 ` [PATCH v3 02/10] raw/cnxk_gpio: support reading default queue conf Tomasz Duszynski ` (9 subsequent siblings) 10 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2021-12-13 8:17 UTC (permalink / raw) To: dev; +Cc: jerinj, stephen, Tomasz Duszynski Add initial support for PMD that allows to control particular pins form userspace. Moreover PMD allows to attach custom interrupt handlers to controllable GPIOs. Main users of this PMD are dataplain applications requiring fast and low latency access to pin state. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- doc/guides/rawdevs/cnxk_gpio.rst | 65 +++++++++ doc/guides/rawdevs/index.rst | 1 + drivers/raw/cnxk_gpio/cnxk_gpio.c | 235 ++++++++++++++++++++++++++++++ drivers/raw/cnxk_gpio/cnxk_gpio.h | 22 +++ drivers/raw/cnxk_gpio/meson.build | 8 + drivers/raw/cnxk_gpio/version.map | 3 + drivers/raw/meson.build | 1 + 7 files changed, 335 insertions(+) create mode 100644 doc/guides/rawdevs/cnxk_gpio.rst create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio.c create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio.h create mode 100644 drivers/raw/cnxk_gpio/meson.build create mode 100644 drivers/raw/cnxk_gpio/version.map diff --git a/doc/guides/rawdevs/cnxk_gpio.rst b/doc/guides/rawdevs/cnxk_gpio.rst new file mode 100644 index 0000000000..868302d07f --- /dev/null +++ b/doc/guides/rawdevs/cnxk_gpio.rst @@ -0,0 +1,65 @@ +.. SPDX-License-Identifier: BSD-3-Clause + Copyright(c) 2021 Marvell. + +Marvell CNXK GPIO Driver +======================== + +CNXK GPIO PMD configures and manages GPIOs available on the system using +standard enqueue/dequeue mechanism offered by raw device abstraction. PMD relies +both on standard sysfs GPIO interface provided by the Linux kernel and GPIO +kernel driver custom interface allowing one to install userspace interrupt +handlers. + +Features +-------- + +Following features are available: + +- export/unexport a GPIO +- read/write specific value from/to exported GPIO +- set GPIO direction +- set GPIO edge that triggers interrupt +- set GPIO active low +- register interrupt handler for specific GPIO + +Requirements +------------ + +PMD relies on modified kernel GPIO driver which exposes ``ioctl()`` interface +for installing interrupt handlers for low latency signal processing. + +Driver is shipped with Marvell SDK. + +Device Setup +------------ + +CNXK GPIO PMD binds to virtual device which gets created by passing +`--vdev=cnxk_gpio,gpiochip=<number>` command line to EAL. `gpiochip` parameter +tells PMD which GPIO controller should be used. Available controllers are +available under `/sys/class/gpio`. For further details on how Linux represents +GPIOs in userspace please refer to +`sysfs.txt <https://www.kernel.org/doc/Documentation/gpio/sysfs.txt>`_. + +If `gpiochip=<number>` was omitted then first gpiochip from the alphabetically +sort list of available gpiochips is used. + +.. code-block:: console + + $ ls /sys/class/gpio + export gpiochip448 unexport + +In above scenario only one GPIO controller is present hence +`--vdev=cnxk_gpio,gpiochip=448` should be passed to EAL. + +Before performing actual data transfer one needs to call +``rte_rawdev_queue_count()`` followed by ``rte_rawdev_queue_conf_get()``. The +former returns number GPIOs available in the system irrespective of GPIOs +being controllable or not. Thus it is user responsibility to pick the proper +ones. The latter call simply returns queue capacity. + +Respective queue needs to be configured with ``rte_rawdev_queue_setup()``. This +call barely exports GPIO to userspace. + +To perform actual data transfer use standard ``rte_rawdev_enqueue_buffers()`` +and ``rte_rawdev_dequeue_buffers()`` APIs. Not all messages produce sensible +responses hence dequeueing is not always necessary. diff --git a/doc/guides/rawdevs/index.rst b/doc/guides/rawdevs/index.rst index b6cf917443..0c02da6e90 100644 --- a/doc/guides/rawdevs/index.rst +++ b/doc/guides/rawdevs/index.rst @@ -12,6 +12,7 @@ application through rawdev API. :numbered: cnxk_bphy + cnxk_gpio dpaa2_cmdif dpaa2_qdma ifpga diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c new file mode 100644 index 0000000000..bcce4b8fb7 --- /dev/null +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -0,0 +1,235 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2021 Marvell. + */ + +#include <dirent.h> +#include <string.h> + +#include <rte_bus_vdev.h> +#include <rte_eal.h> +#include <rte_kvargs.h> +#include <rte_lcore.h> +#include <rte_rawdev_pmd.h> + +#include <roc_api.h> + +#include "cnxk_gpio.h" + +#define CNXK_GPIO_BUFSZ 128 +#define CNXK_GPIO_CLASS_PATH "/sys/class/gpio" + +static const char *const cnxk_gpio_args[] = { +#define CNXK_GPIO_ARG_GPIOCHIP "gpiochip" + CNXK_GPIO_ARG_GPIOCHIP, + NULL +}; + +static void +cnxk_gpio_format_name(char *name, size_t len) +{ + snprintf(name, len, "cnxk_gpio"); +} + +static int +cnxk_gpio_filter_gpiochip(const struct dirent *dirent) +{ + const char *pattern = "gpiochip"; + + return !strncmp(dirent->d_name, pattern, strlen(pattern)); +} + +static void +cnxk_gpio_set_defaults(struct cnxk_gpiochip *gpiochip) +{ + struct dirent **namelist; + int n; + + n = scandir(CNXK_GPIO_CLASS_PATH, &namelist, cnxk_gpio_filter_gpiochip, + alphasort); + if (n < 0 || n == 0) + return; + + sscanf(namelist[0]->d_name, "gpiochip%d", &gpiochip->num); + while (n--) + free(namelist[n]); + free(namelist); +} + +static int +cnxk_gpio_parse_arg_gpiochip(const char *key __rte_unused, const char *value, + void *extra_args) +{ + long val; + + errno = 0; + val = strtol(value, NULL, 10); + if (errno) + return -errno; + + *(int *)extra_args = (int)val; + + return 0; +} + +static int +cnxk_gpio_parse_args(struct cnxk_gpiochip *gpiochip, + struct rte_devargs *devargs) +{ + struct rte_kvargs *kvlist; + int ret; + + kvlist = rte_kvargs_parse(devargs->args, cnxk_gpio_args); + if (!kvlist) + return 0; + + ret = rte_kvargs_count(kvlist, CNXK_GPIO_ARG_GPIOCHIP); + if (ret == 1) { + ret = rte_kvargs_process(kvlist, CNXK_GPIO_ARG_GPIOCHIP, + cnxk_gpio_parse_arg_gpiochip, + &gpiochip->num); + if (ret) + return ret; + } + + return 0; +} + +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; +} + +static int +cnxk_gpio_read_attr_int(char *attr, int *val) +{ + char buf[CNXK_GPIO_BUFSZ]; + int ret; + + ret = cnxk_gpio_read_attr(attr, buf); + if (ret) + return ret; + + ret = sscanf(buf, "%d", val); + if (ret < 0) + return -errno; + + return 0; +} + +static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { +}; + +static int +cnxk_gpio_probe(struct rte_vdev_device *dev) +{ + char name[RTE_RAWDEV_NAME_MAX_LEN]; + struct cnxk_gpiochip *gpiochip; + struct rte_rawdev *rawdev; + char buf[CNXK_GPIO_BUFSZ]; + int ret; + + if (rte_eal_process_type() != RTE_PROC_PRIMARY) + return 0; + + cnxk_gpio_format_name(name, sizeof(name)); + rawdev = rte_rawdev_pmd_allocate(name, sizeof(*gpiochip), + rte_socket_id()); + if (!rawdev) { + RTE_LOG(ERR, PMD, "failed to allocate %s rawdev", name); + return -ENOMEM; + } + + rawdev->dev_ops = &cnxk_gpio_rawdev_ops; + rawdev->device = &dev->device; + rawdev->driver_name = dev->device.name; + + gpiochip = rawdev->dev_private; + cnxk_gpio_set_defaults(gpiochip); + + /* defaults may be overwritten by this call */ + ret = cnxk_gpio_parse_args(gpiochip, dev->device.devargs); + if (ret) + goto out; + + /* read gpio base */ + snprintf(buf, sizeof(buf), "%s/gpiochip%d/base", CNXK_GPIO_CLASS_PATH, + gpiochip->num); + ret = cnxk_gpio_read_attr_int(buf, &gpiochip->base); + if (ret) { + RTE_LOG(ERR, PMD, "failed to read %s", buf); + goto out; + } + + /* read number of available gpios */ + snprintf(buf, sizeof(buf), "%s/gpiochip%d/ngpio", CNXK_GPIO_CLASS_PATH, + gpiochip->num); + ret = cnxk_gpio_read_attr_int(buf, &gpiochip->num_gpios); + if (ret) { + RTE_LOG(ERR, PMD, "failed to read %s", buf); + goto out; + } + + gpiochip->gpios = rte_calloc(NULL, gpiochip->num_gpios, + sizeof(struct cnxk_gpio *), 0); + if (!gpiochip->gpios) { + RTE_LOG(ERR, PMD, "failed to allocate gpios memory"); + ret = -ENOMEM; + goto out; + } + + return 0; +out: + rte_rawdev_pmd_release(rawdev); + + return ret; +} + +static int +cnxk_gpio_remove(struct rte_vdev_device *dev) +{ + char name[RTE_RAWDEV_NAME_MAX_LEN]; + struct cnxk_gpiochip *gpiochip; + struct rte_rawdev *rawdev; + + RTE_SET_USED(dev); + + if (rte_eal_process_type() != RTE_PROC_PRIMARY) + return 0; + + cnxk_gpio_format_name(name, sizeof(name)); + rawdev = rte_rawdev_pmd_get_named_dev(name); + if (!rawdev) + return -ENODEV; + + gpiochip = rawdev->dev_private; + rte_free(gpiochip->gpios); + rte_rawdev_pmd_release(rawdev); + + return 0; +} + +static struct rte_vdev_driver cnxk_gpio_drv = { + .probe = cnxk_gpio_probe, + .remove = cnxk_gpio_remove, +}; + +RTE_PMD_REGISTER_VDEV(cnxk_gpio, cnxk_gpio_drv); +RTE_PMD_REGISTER_PARAM_STRING(cnxk_gpio, "gpiochip=<int>"); diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.h b/drivers/raw/cnxk_gpio/cnxk_gpio.h new file mode 100644 index 0000000000..4dae8316ba --- /dev/null +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2021 Marvell. + */ + +#ifndef _CNXK_GPIO_H_ +#define _CNXK_GPIO_H_ + +struct cnxk_gpiochip; + +struct cnxk_gpio { + struct cnxk_gpiochip *gpiochip; + int num; +}; + +struct cnxk_gpiochip { + int num; + int base; + int num_gpios; + struct cnxk_gpio **gpios; +}; + +#endif /* _CNXK_GPIO_H_ */ diff --git a/drivers/raw/cnxk_gpio/meson.build b/drivers/raw/cnxk_gpio/meson.build new file mode 100644 index 0000000000..9a7e716c1e --- /dev/null +++ b/drivers/raw/cnxk_gpio/meson.build @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(C) 2021 Marvell. +# + +deps += ['bus_vdev', 'common_cnxk', 'rawdev', 'kvargs'] +sources = files( + 'cnxk_gpio.c', +) diff --git a/drivers/raw/cnxk_gpio/version.map b/drivers/raw/cnxk_gpio/version.map new file mode 100644 index 0000000000..4a76d1d52d --- /dev/null +++ b/drivers/raw/cnxk_gpio/version.map @@ -0,0 +1,3 @@ +DPDK_21 { + local: *; +}; diff --git a/drivers/raw/meson.build b/drivers/raw/meson.build index 87694a758e..05e7de1bfe 100644 --- a/drivers/raw/meson.build +++ b/drivers/raw/meson.build @@ -7,6 +7,7 @@ endif drivers = [ 'cnxk_bphy', + 'cnxk_gpio', 'dpaa2_cmdif', 'dpaa2_qdma', 'ifpga', -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v3 02/10] raw/cnxk_gpio: support reading default queue conf 2021-12-13 8:17 ` [PATCH v3 00/10] Add cnxk_gpio PMD Tomasz Duszynski 2021-12-13 8:17 ` [PATCH v3 01/10] raw/cnxk_gpio: add GPIO driver skeleton Tomasz Duszynski @ 2021-12-13 8:17 ` Tomasz Duszynski 2021-12-13 8:17 ` [PATCH v3 03/10] raw/cnxk_gpio: support reading queue count Tomasz Duszynski ` (8 subsequent siblings) 10 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2021-12-13 8:17 UTC (permalink / raw) To: dev; +Cc: jerinj, stephen, Tomasz Duszynski Add support for reading default queue configuration. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index bcce4b8fb7..deedf98af2 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -134,7 +134,26 @@ cnxk_gpio_read_attr_int(char *attr, int *val) return 0; } +static int +cnxk_gpio_queue_def_conf(struct rte_rawdev *dev, uint16_t queue_id, + rte_rawdev_obj_t queue_conf, size_t queue_conf_size) +{ + unsigned int *conf; + + RTE_SET_USED(dev); + RTE_SET_USED(queue_id); + + if (queue_conf_size != sizeof(*conf)) + return -EINVAL; + + conf = (unsigned int *)queue_conf; + *conf = 1; + + return 0; +} + static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { + .queue_def_conf = cnxk_gpio_queue_def_conf, }; static int -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v3 03/10] raw/cnxk_gpio: support reading queue count 2021-12-13 8:17 ` [PATCH v3 00/10] Add cnxk_gpio PMD Tomasz Duszynski 2021-12-13 8:17 ` [PATCH v3 01/10] raw/cnxk_gpio: add GPIO driver skeleton Tomasz Duszynski 2021-12-13 8:17 ` [PATCH v3 02/10] raw/cnxk_gpio: support reading default queue conf Tomasz Duszynski @ 2021-12-13 8:17 ` Tomasz Duszynski 2021-12-13 8:17 ` [PATCH v3 04/10] raw/cnxk_gpio: support queue setup Tomasz Duszynski ` (7 subsequent siblings) 10 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2021-12-13 8:17 UTC (permalink / raw) To: dev; +Cc: jerinj, stephen, Tomasz Duszynski Add support for reading number of available queues. Single queue corresponds to GPIO. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index deedf98af2..84be7f861e 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -152,8 +152,17 @@ cnxk_gpio_queue_def_conf(struct rte_rawdev *dev, uint16_t queue_id, return 0; } +static uint16_t +cnxk_gpio_queue_count(struct rte_rawdev *dev) +{ + struct cnxk_gpiochip *gpiochip = dev->dev_private; + + return gpiochip->num_gpios; +} + static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { .queue_def_conf = cnxk_gpio_queue_def_conf, + .queue_count = cnxk_gpio_queue_count, }; static int -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v3 04/10] raw/cnxk_gpio: support queue setup 2021-12-13 8:17 ` [PATCH v3 00/10] Add cnxk_gpio PMD Tomasz Duszynski ` (2 preceding siblings ...) 2021-12-13 8:17 ` [PATCH v3 03/10] raw/cnxk_gpio: support reading queue count Tomasz Duszynski @ 2021-12-13 8:17 ` Tomasz Duszynski 2021-12-13 8:17 ` [PATCH v3 05/10] raw/cnxk_gpio: support queue release Tomasz Duszynski ` (6 subsequent siblings) 10 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2021-12-13 8:17 UTC (permalink / raw) To: dev; +Cc: jerinj, stephen, Tomasz Duszynski Add support for queue setup. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 80 +++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 84be7f861e..98b5dd9cd8 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -134,6 +134,85 @@ cnxk_gpio_read_attr_int(char *attr, int *val) return 0; } +static int +cnxk_gpio_write_attr(const char *attr, const char *val) +{ + FILE *fp; + int ret; + + if (!val) + return -EINVAL; + + fp = fopen(attr, "w"); + if (!fp) + return -errno; + + ret = fprintf(fp, "%s", val); + if (ret < 0) { + fclose(fp); + return ret; + } + + ret = fclose(fp); + if (ret) + return -errno; + + return 0; +} + +static int +cnxk_gpio_write_attr_int(const char *attr, int val) +{ + char buf[CNXK_GPIO_BUFSZ]; + + snprintf(buf, sizeof(buf), "%d", val); + + return cnxk_gpio_write_attr(attr, buf); +} + +static struct cnxk_gpio * +cnxk_gpio_lookup(struct cnxk_gpiochip *gpiochip, uint16_t queue) +{ + if (queue >= gpiochip->num_gpios) + return NULL; + + return gpiochip->gpios[queue]; +} + +static int +cnxk_gpio_queue_setup(struct rte_rawdev *dev, uint16_t queue_id, + rte_rawdev_obj_t queue_conf, size_t queue_conf_size) +{ + struct cnxk_gpiochip *gpiochip = dev->dev_private; + char buf[CNXK_GPIO_BUFSZ]; + struct cnxk_gpio *gpio; + int ret; + + RTE_SET_USED(queue_conf); + RTE_SET_USED(queue_conf_size); + + gpio = cnxk_gpio_lookup(gpiochip, queue_id); + if (gpio) + return -EEXIST; + + gpio = rte_zmalloc(NULL, sizeof(*gpio), 0); + if (!gpio) + return -ENOMEM; + gpio->num = queue_id + gpiochip->base; + gpio->gpiochip = gpiochip; + + snprintf(buf, sizeof(buf), "%s/export", CNXK_GPIO_CLASS_PATH); + ret = cnxk_gpio_write_attr_int(buf, gpio->num); + if (ret) { + rte_free(gpio); + return ret; + } + + gpiochip->gpios[queue_id] = gpio; + + return 0; +} + static int cnxk_gpio_queue_def_conf(struct rte_rawdev *dev, uint16_t queue_id, rte_rawdev_obj_t queue_conf, size_t queue_conf_size) @@ -163,6 +242,7 @@ cnxk_gpio_queue_count(struct rte_rawdev *dev) static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { .queue_def_conf = cnxk_gpio_queue_def_conf, .queue_count = cnxk_gpio_queue_count, + .queue_setup = cnxk_gpio_queue_setup, }; static int -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v3 05/10] raw/cnxk_gpio: support queue release 2021-12-13 8:17 ` [PATCH v3 00/10] Add cnxk_gpio PMD Tomasz Duszynski ` (3 preceding siblings ...) 2021-12-13 8:17 ` [PATCH v3 04/10] raw/cnxk_gpio: support queue setup Tomasz Duszynski @ 2021-12-13 8:17 ` Tomasz Duszynski 2021-12-13 8:17 ` [PATCH v3 06/10] raw/cnxk_gpio: support enqueuing buffers Tomasz Duszynski ` (5 subsequent siblings) 10 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2021-12-13 8:17 UTC (permalink / raw) To: dev; +Cc: jerinj, stephen, Tomasz Duszynski Add support for queue release. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 98b5dd9cd8..8ac3c5e1be 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -213,6 +213,29 @@ cnxk_gpio_queue_setup(struct rte_rawdev *dev, uint16_t queue_id, return 0; } +static int +cnxk_gpio_queue_release(struct rte_rawdev *dev, uint16_t queue_id) +{ + struct cnxk_gpiochip *gpiochip = dev->dev_private; + char buf[CNXK_GPIO_BUFSZ]; + struct cnxk_gpio *gpio; + int ret; + + gpio = cnxk_gpio_lookup(gpiochip, queue_id); + if (!gpio) + return -ENODEV; + + snprintf(buf, sizeof(buf), "%s/unexport", CNXK_GPIO_CLASS_PATH); + ret = cnxk_gpio_write_attr_int(buf, gpiochip->base + queue_id); + if (ret) + return ret; + + gpiochip->gpios[queue_id] = NULL; + rte_free(gpio); + + return 0; +} + static int cnxk_gpio_queue_def_conf(struct rte_rawdev *dev, uint16_t queue_id, rte_rawdev_obj_t queue_conf, size_t queue_conf_size) @@ -243,6 +266,7 @@ static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { .queue_def_conf = cnxk_gpio_queue_def_conf, .queue_count = cnxk_gpio_queue_count, .queue_setup = cnxk_gpio_queue_setup, + .queue_release = cnxk_gpio_queue_release, }; static int @@ -316,6 +340,8 @@ cnxk_gpio_remove(struct rte_vdev_device *dev) char name[RTE_RAWDEV_NAME_MAX_LEN]; struct cnxk_gpiochip *gpiochip; struct rte_rawdev *rawdev; + struct cnxk_gpio *gpio; + int i; RTE_SET_USED(dev); @@ -328,6 +354,14 @@ cnxk_gpio_remove(struct rte_vdev_device *dev) return -ENODEV; gpiochip = rawdev->dev_private; + for (i = 0; i < gpiochip->num_gpios; i++) { + gpio = gpiochip->gpios[i]; + if (!gpio) + continue; + + cnxk_gpio_queue_release(rawdev, gpio->num); + } + rte_free(gpiochip->gpios); rte_rawdev_pmd_release(rawdev); -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v3 06/10] raw/cnxk_gpio: support enqueuing buffers 2021-12-13 8:17 ` [PATCH v3 00/10] Add cnxk_gpio PMD Tomasz Duszynski ` (4 preceding siblings ...) 2021-12-13 8:17 ` [PATCH v3 05/10] raw/cnxk_gpio: support queue release Tomasz Duszynski @ 2021-12-13 8:17 ` Tomasz Duszynski 2021-12-13 8:17 ` [PATCH v3 07/10] raw/cnxk_gpio: support dequeuing buffers Tomasz Duszynski ` (4 subsequent siblings) 10 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2021-12-13 8:17 UTC (permalink / raw) To: dev; +Cc: jerinj, stephen, Tomasz Duszynski Add dummy support for enqueuing buffers. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 46 +++++++++++++++++++++++ drivers/raw/cnxk_gpio/cnxk_gpio.h | 1 + drivers/raw/cnxk_gpio/meson.build | 1 + drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h | 38 +++++++++++++++++++ 4 files changed, 86 insertions(+) create mode 100644 drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 8ac3c5e1be..9477e7293a 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -14,6 +14,7 @@ #include <roc_api.h> #include "cnxk_gpio.h" +#include "rte_pmd_cnxk_gpio.h" #define CNXK_GPIO_BUFSZ 128 #define CNXK_GPIO_CLASS_PATH "/sys/class/gpio" @@ -262,7 +263,52 @@ cnxk_gpio_queue_count(struct rte_rawdev *dev) return gpiochip->num_gpios; } +static int +cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf) +{ + struct cnxk_gpio_msg *msg = rbuf->buf_addr; + void *rsp = NULL; + + switch (msg->type) { + default: + return -EINVAL; + } + + /* get rid of last response if any */ + if (gpio->rsp) { + RTE_LOG(WARNING, PMD, "previous response got overwritten\n"); + rte_free(gpio->rsp); + } + gpio->rsp = rsp; + + return ret; +} + +static int +cnxk_gpio_enqueue_bufs(struct rte_rawdev *dev, struct rte_rawdev_buf **buffers, + unsigned int count, rte_rawdev_obj_t context) +{ + struct cnxk_gpiochip *gpiochip = dev->dev_private; + unsigned int queue = (size_t)context; + struct cnxk_gpio *gpio; + int ret; + + if (count == 0) + return 0; + + gpio = cnxk_gpio_lookup(gpiochip, queue); + if (!gpio) + return -ENODEV; + + ret = cnxk_gpio_process_buf(gpio, buffers[0]); + if (ret) + return ret; + + return 1; +} + static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { + .enqueue_bufs = cnxk_gpio_enqueue_bufs, .queue_def_conf = cnxk_gpio_queue_def_conf, .queue_count = cnxk_gpio_queue_count, .queue_setup = cnxk_gpio_queue_setup, diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.h b/drivers/raw/cnxk_gpio/cnxk_gpio.h index 4dae8316ba..6b54ebe6e6 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.h +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.h @@ -9,6 +9,7 @@ struct cnxk_gpiochip; struct cnxk_gpio { struct cnxk_gpiochip *gpiochip; + void *rsp; int num; }; diff --git a/drivers/raw/cnxk_gpio/meson.build b/drivers/raw/cnxk_gpio/meson.build index 9a7e716c1e..3fbfdd838c 100644 --- a/drivers/raw/cnxk_gpio/meson.build +++ b/drivers/raw/cnxk_gpio/meson.build @@ -6,3 +6,4 @@ deps += ['bus_vdev', 'common_cnxk', 'rawdev', 'kvargs'] sources = files( 'cnxk_gpio.c', ) +headers = files('rte_pmd_cnxk_gpio.h') diff --git a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h new file mode 100644 index 0000000000..c71065e10c --- /dev/null +++ b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2021 Marvell. + */ + +#ifndef _RTE_PMD_CNXK_GPIO_H_ +#define _RTE_PMD_CNXK_GPIO_H_ + +/** + * @file rte_pmd_cnxk_gpio.h + * + * Marvell GPIO PMD specific structures and interface + * + * This API allows applications to manage GPIOs in user space along with + * installing interrupt handlers for low latency signal processing. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** Available message types */ +enum cnxk_gpio_msg_type { + /** Invalid message type */ + CNXK_GPIO_MSG_TYPE_INVALID, +}; + +struct cnxk_gpio_msg { + /** Message type */ + enum cnxk_gpio_msg_type type; + /** Message data passed to PMD or received from PMD */ + void *data; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _RTE_PMD_CNXK_GPIO_H_ */ -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v3 07/10] raw/cnxk_gpio: support dequeuing buffers 2021-12-13 8:17 ` [PATCH v3 00/10] Add cnxk_gpio PMD Tomasz Duszynski ` (5 preceding siblings ...) 2021-12-13 8:17 ` [PATCH v3 06/10] raw/cnxk_gpio: support enqueuing buffers Tomasz Duszynski @ 2021-12-13 8:17 ` Tomasz Duszynski 2021-12-13 8:17 ` [PATCH v3 08/10] raw/cnxk_gpio: support standard GPIO operations Tomasz Duszynski ` (3 subsequent siblings) 10 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2021-12-13 8:17 UTC (permalink / raw) To: dev; +Cc: jerinj, stephen, Tomasz Duszynski Add support for dequeuing buffers. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 9477e7293a..570d9abb17 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -307,8 +307,34 @@ cnxk_gpio_enqueue_bufs(struct rte_rawdev *dev, struct rte_rawdev_buf **buffers, return 1; } +static int +cnxk_gpio_dequeue_bufs(struct rte_rawdev *dev, struct rte_rawdev_buf **buffers, + unsigned int count, rte_rawdev_obj_t context) +{ + struct cnxk_gpiochip *gpiochip = dev->dev_private; + unsigned int queue = (size_t)context; + struct cnxk_gpio *gpio; + + if (count == 0) + return 0; + + gpio = cnxk_gpio_lookup(gpiochip, queue); + if (!gpio) + return -ENODEV; + + if (gpio->rsp) { + buffers[0]->buf_addr = gpio->rsp; + gpio->rsp = NULL; + + return 1; + } + + return 0; +} + static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { .enqueue_bufs = cnxk_gpio_enqueue_bufs, + .dequeue_bufs = cnxk_gpio_dequeue_bufs, .queue_def_conf = cnxk_gpio_queue_def_conf, .queue_count = cnxk_gpio_queue_count, .queue_setup = cnxk_gpio_queue_setup, -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v3 08/10] raw/cnxk_gpio: support standard GPIO operations 2021-12-13 8:17 ` [PATCH v3 00/10] Add cnxk_gpio PMD Tomasz Duszynski ` (6 preceding siblings ...) 2021-12-13 8:17 ` [PATCH v3 07/10] raw/cnxk_gpio: support dequeuing buffers Tomasz Duszynski @ 2021-12-13 8:17 ` Tomasz Duszynski 2021-12-13 8:17 ` [PATCH v3 09/10] raw/cnxk_gpio: support custom irq handlers Tomasz Duszynski ` (2 subsequent siblings) 10 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2021-12-13 8:17 UTC (permalink / raw) To: dev; +Cc: jerinj, stephen, Tomasz Duszynski Add support for standard GPIO operations i.e ones normally provided by GPIO sysfs interface. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- doc/guides/rawdevs/cnxk_gpio.rst | 98 ++++++++ drivers/raw/cnxk_gpio/cnxk_gpio.c | 146 +++++++++++ drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h | 279 +++++++++++++++++++++- 3 files changed, 521 insertions(+), 2 deletions(-) diff --git a/doc/guides/rawdevs/cnxk_gpio.rst b/doc/guides/rawdevs/cnxk_gpio.rst index 868302d07f..f6c3c942c5 100644 --- a/doc/guides/rawdevs/cnxk_gpio.rst +++ b/doc/guides/rawdevs/cnxk_gpio.rst @@ -63,3 +63,101 @@ call barely exports GPIO to userspace. To perform actual data transfer use standard ``rte_rawdev_enqueue_buffers()`` and ``rte_rawdev_dequeue_buffers()`` APIs. Not all messages produce sensible responses hence dequeueing is not always necessary. + +CNXK GPIO PMD +------------- + +PMD accepts ``struct cnxk_gpio_msg`` messages which differ by type and payload. +Message types along with description are listed below. As for the usage examples +please refer to ``cnxk_gpio_selftest()``. There's a set of convenient wrappers +available, one for each existing command. + +Set GPIO value +~~~~~~~~~~~~~~ + +Message is used to set output to low or high. This does not work for GPIOs +configured as input. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_SET_PIN_VALUE``. + +Payload must be an integer set to 0 (low) or 1 (high). + +Consider using ``rte_pmd_gpio_set_pin_value()`` wrapper. + +Set GPIO edge +~~~~~~~~~~~~~ + +Message is used to set edge that triggers interrupt. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_SET_PIN_EDGE``. + +Payload must be `enum cnxk_gpio_pin_edge`. + +Consider using ``rte_pmd_gpio_set_pin_edge()`` wrapper. + +Set GPIO direction +~~~~~~~~~~~~~~~~~~ + +Message is used to change GPIO direction to either input or output. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_SET_PIN_DIR``. + +Payload must be `enum cnxk_gpio_pin_dir`. + +Consider using ``rte_pmd_gpio_set_pin_dir()`` wrapper. + +Set GPIO active low +~~~~~~~~~~~~~~~~~~~ + +Message is used to set whether pin is active low. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_SET_PIN_ACTIVE_LOW``. + +Payload must be an integer set to 0 or 1. The latter activates inversion. + +Consider using ``rte_pmd_gpio_set_pin_active_low()`` wrapper. + +Get GPIO value +~~~~~~~~~~~~~~ + +Message is used to read GPIO value. Value can be 0 (low) or 1 (high). + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_GET_PIN_VALUE``. + +Payload contains integer set to either 0 or 1. + +Consider using ``rte_pmd_gpio_get_pin_value()`` wrapper. + +Get GPIO edge +~~~~~~~~~~~~~ + +Message is used to read GPIO edge. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_GET_PIN_EDGE``. + +Payload contains `enum cnxk_gpio_pin_edge`. + +Consider using ``rte_pmd_gpio_get_pin_edge()`` wrapper. + +Get GPIO direction +~~~~~~~~~~~~~~~~~~ + +Message is used to read GPIO direction. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_GET_PIN_DIR``. + +Payload contains `enum cnxk_gpio_pin_dir`. + +Consider using ``rte_pmd_gpio_get_pin_dir()`` wrapper. + +Get GPIO active low +~~~~~~~~~~~~~~~~~~~ + +Message is used check whether inverted logic is active. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW``. + +Payload contains an integer set to 0 or 1. The latter means inverted logic +is turned on. + +Consider using ``rte_pmd_gpio_get_pin_active_low()`` wrapper. diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 570d9abb17..fa5b1359d0 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -263,13 +263,159 @@ cnxk_gpio_queue_count(struct rte_rawdev *dev) return gpiochip->num_gpios; } +static const struct { + enum cnxk_gpio_pin_edge edge; + const char *name; +} cnxk_gpio_edge_name[] = { + { CNXK_GPIO_PIN_EDGE_NONE, "none" }, + { CNXK_GPIO_PIN_EDGE_FALLING, "falling" }, + { CNXK_GPIO_PIN_EDGE_RISING, "rising" }, + { CNXK_GPIO_PIN_EDGE_BOTH, "both" }, +}; + +static const char * +cnxk_gpio_edge_to_name(enum cnxk_gpio_pin_edge edge) +{ + unsigned int i; + + for (i = 0; i < RTE_DIM(cnxk_gpio_edge_name); i++) { + if (cnxk_gpio_edge_name[i].edge == edge) + return cnxk_gpio_edge_name[i].name; + } + + return NULL; +} + +static enum cnxk_gpio_pin_edge +cnxk_gpio_name_to_edge(const char *name) +{ + unsigned int i; + + for (i = 0; i < RTE_DIM(cnxk_gpio_edge_name); i++) { + if (!strcmp(cnxk_gpio_edge_name[i].name, name)) + break; + } + + return cnxk_gpio_edge_name[i].edge; +} + +static const struct { + enum cnxk_gpio_pin_dir dir; + const char *name; +} cnxk_gpio_dir_name[] = { + { CNXK_GPIO_PIN_DIR_IN, "in" }, + { CNXK_GPIO_PIN_DIR_OUT, "out" }, + { CNXK_GPIO_PIN_DIR_HIGH, "high" }, + { CNXK_GPIO_PIN_DIR_LOW, "low" }, +}; + +static const char * +cnxk_gpio_dir_to_name(enum cnxk_gpio_pin_dir dir) +{ + unsigned int i; + + for (i = 0; i < RTE_DIM(cnxk_gpio_dir_name); i++) { + if (cnxk_gpio_dir_name[i].dir == dir) + return cnxk_gpio_dir_name[i].name; + } + + return NULL; +} + +static enum cnxk_gpio_pin_dir +cnxk_gpio_name_to_dir(const char *name) +{ + unsigned int i; + + for (i = 0; i < RTE_DIM(cnxk_gpio_dir_name); i++) { + if (!strcmp(cnxk_gpio_dir_name[i].name, name)) + break; + } + + return cnxk_gpio_dir_name[i].dir; +} + static int cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf) { struct cnxk_gpio_msg *msg = rbuf->buf_addr; + enum cnxk_gpio_pin_edge edge; + enum cnxk_gpio_pin_dir dir; + char buf[CNXK_GPIO_BUFSZ]; void *rsp = NULL; + int ret, val, n; + + n = snprintf(buf, sizeof(buf), "%s/gpio%d", CNXK_GPIO_CLASS_PATH, + gpio->num); switch (msg->type) { + case CNXK_GPIO_MSG_TYPE_SET_PIN_VALUE: + snprintf(buf + n, sizeof(buf) - n, "/value"); + ret = cnxk_gpio_write_attr_int(buf, !!*(int *)msg->data); + break; + case CNXK_GPIO_MSG_TYPE_SET_PIN_EDGE: + snprintf(buf + n, sizeof(buf) - n, "/edge"); + edge = *(enum cnxk_gpio_pin_edge *)msg->data; + ret = cnxk_gpio_write_attr(buf, cnxk_gpio_edge_to_name(edge)); + break; + case CNXK_GPIO_MSG_TYPE_SET_PIN_DIR: + snprintf(buf + n, sizeof(buf) - n, "/direction"); + dir = *(enum cnxk_gpio_pin_dir *)msg->data; + ret = cnxk_gpio_write_attr(buf, cnxk_gpio_dir_to_name(dir)); + break; + case CNXK_GPIO_MSG_TYPE_SET_PIN_ACTIVE_LOW: + snprintf(buf + n, sizeof(buf) - n, "/active_low"); + val = *(int *)msg->data; + ret = cnxk_gpio_write_attr_int(buf, val); + break; + case CNXK_GPIO_MSG_TYPE_GET_PIN_VALUE: + snprintf(buf + n, sizeof(buf) - n, "/value"); + ret = cnxk_gpio_read_attr_int(buf, &val); + if (ret) + break; + + rsp = rte_zmalloc(NULL, sizeof(int), 0); + if (!rsp) + return -ENOMEM; + + *(int *)rsp = val; + break; + case CNXK_GPIO_MSG_TYPE_GET_PIN_EDGE: + snprintf(buf + n, sizeof(buf) - n, "/edge"); + ret = cnxk_gpio_read_attr(buf, buf); + if (ret) + break; + + rsp = rte_zmalloc(NULL, sizeof(enum cnxk_gpio_pin_edge), 0); + if (!rsp) + return -ENOMEM; + + *(enum cnxk_gpio_pin_edge *)rsp = cnxk_gpio_name_to_edge(buf); + break; + case CNXK_GPIO_MSG_TYPE_GET_PIN_DIR: + snprintf(buf + n, sizeof(buf) - n, "/direction"); + ret = cnxk_gpio_read_attr(buf, buf); + if (ret) + break; + + rsp = rte_zmalloc(NULL, sizeof(enum cnxk_gpio_pin_dir), 0); + if (!rsp) + return -ENOMEM; + + *(enum cnxk_gpio_pin_dir *)rsp = cnxk_gpio_name_to_dir(buf); + break; + case CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW: + snprintf(buf + n, sizeof(buf) - n, "/active_low"); + ret = cnxk_gpio_read_attr_int(buf, &val); + if (ret) + break; + + rsp = rte_zmalloc(NULL, sizeof(int), 0); + if (!rsp) + return -ENOMEM; + + *(int *)rsp = val; + break; default: return -EINVAL; } diff --git a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h index c71065e10c..7c3dc225ca 100644 --- a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h +++ b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h @@ -5,6 +5,10 @@ #ifndef _RTE_PMD_CNXK_GPIO_H_ #define _RTE_PMD_CNXK_GPIO_H_ +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_rawdev.h> + /** * @file rte_pmd_cnxk_gpio.h * @@ -20,8 +24,46 @@ extern "C" { /** Available message types */ enum cnxk_gpio_msg_type { - /** Invalid message type */ - CNXK_GPIO_MSG_TYPE_INVALID, + /** Type used to set output value */ + CNXK_GPIO_MSG_TYPE_SET_PIN_VALUE, + /** Type used to set edge */ + CNXK_GPIO_MSG_TYPE_SET_PIN_EDGE, + /** Type used to set direction */ + CNXK_GPIO_MSG_TYPE_SET_PIN_DIR, + /** Type used to set inverted logic */ + CNXK_GPIO_MSG_TYPE_SET_PIN_ACTIVE_LOW, + /** Type used to read value */ + CNXK_GPIO_MSG_TYPE_GET_PIN_VALUE, + /** Type used to read edge */ + CNXK_GPIO_MSG_TYPE_GET_PIN_EDGE, + /** Type used to read direction */ + CNXK_GPIO_MSG_TYPE_GET_PIN_DIR, + /** Type used to read inverted logic state */ + CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW, +}; + +/** Available edges */ +enum cnxk_gpio_pin_edge { + /** Set edge to none */ + CNXK_GPIO_PIN_EDGE_NONE, + /** Set edge to falling */ + CNXK_GPIO_PIN_EDGE_FALLING, + /** Set edge to rising */ + CNXK_GPIO_PIN_EDGE_RISING, + /** Set edge to both rising and falling */ + CNXK_GPIO_PIN_EDGE_BOTH, +}; + +/** Available directions */ +enum cnxk_gpio_pin_dir { + /** Set direction to input */ + CNXK_GPIO_PIN_DIR_IN, + /** Set direction to output */ + CNXK_GPIO_PIN_DIR_OUT, + /** Set direction to output and value to 1 */ + CNXK_GPIO_PIN_DIR_HIGH, + /* Set direction to output and value to 0 */ + CNXK_GPIO_PIN_DIR_LOW, }; struct cnxk_gpio_msg { @@ -31,6 +73,239 @@ struct cnxk_gpio_msg { void *data; }; +/** @internal helper routine for enqueuing/dequeuing messages */ +static __rte_always_inline int +__rte_pmd_gpio_enq_deq(uint16_t dev_id, int gpio, void *req, void *rsp, + size_t rsp_size) +{ + struct rte_rawdev_buf *bufs[1]; + struct rte_rawdev_buf buf; + void *q; + int ret; + + q = (void *)(size_t)gpio; + buf.buf_addr = req; + bufs[0] = &buf; + + ret = rte_rawdev_enqueue_buffers(dev_id, bufs, RTE_DIM(bufs), q); + if (ret < 0) + return ret; + if (ret != RTE_DIM(bufs)) + return -EIO; + + if (!rsp) + return 0; + + ret = rte_rawdev_dequeue_buffers(dev_id, bufs, RTE_DIM(bufs), q); + if (ret < 0) + return ret; + if (ret != RTE_DIM(bufs)) + return -EIO; + + rte_memcpy(rsp, buf.buf_addr, rsp_size); + rte_free(buf.buf_addr); + + return 0; +} + +/** + * Set output to specific value + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param val + * Value output will be set to. 0 represents low state while + * 1 high state + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_set_pin_value(uint16_t dev_id, int gpio, int val) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_SET_PIN_VALUE, + .data = &val, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Select signal edge that triggers interrupt + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param edge + * Signal edge that triggers interrupt + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_set_pin_edge(uint16_t dev_id, int gpio, + enum cnxk_gpio_pin_edge edge) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_SET_PIN_EDGE, + .data = &edge + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Configure GPIO as input or output + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param dir + * Direction of the GPIO + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_set_pin_dir(uint16_t dev_id, int gpio, enum cnxk_gpio_pin_dir dir) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_SET_PIN_DIR, + .data = &dir, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Enable or disable inverted logic + * + * If GPIO is configured as output then writing 1 or 0 will result in setting + * output to respectively low or high + * + * If GPIO is configured as input then logic inversion applies to edges. Both + * current and future settings are affected + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param val + * 0 to disable, 1 to enable inverted logic + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_set_pin_active_low(uint16_t dev_id, int gpio, int val) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_SET_PIN_ACTIVE_LOW, + .data = &val, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Read GPIO value + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param val + * Where to store read logical signal value i.e 0 or 1 + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_get_pin_value(uint16_t dev_id, int gpio, int *val) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_GET_PIN_VALUE, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, val, sizeof(*val)); +} + +/** + * Read GPIO edge + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param edge + * Where to store edge + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_get_pin_edge(uint16_t dev_id, int gpio, + enum cnxk_gpio_pin_edge *edge) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_GET_PIN_EDGE, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, edge, sizeof(*edge)); +} + +/** + * Read GPIO direction + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param dir + * Where to store direction + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_get_pin_dir(uint16_t dev_id, int gpio, enum cnxk_gpio_pin_dir *dir) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_GET_PIN_DIR, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, dir, sizeof(*dir)); +} + +/** + * Read whether GPIO is active low + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param val + * Where to store active low state + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_get_pin_active_low(uint16_t dev_id, int gpio, int *val) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW, + .data = &val, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, val, sizeof(*val)); +} + #ifdef __cplusplus } #endif -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v3 09/10] raw/cnxk_gpio: support custom irq handlers 2021-12-13 8:17 ` [PATCH v3 00/10] Add cnxk_gpio PMD Tomasz Duszynski ` (7 preceding siblings ...) 2021-12-13 8:17 ` [PATCH v3 08/10] raw/cnxk_gpio: support standard GPIO operations Tomasz Duszynski @ 2021-12-13 8:17 ` Tomasz Duszynski 2021-12-13 8:17 ` [PATCH v3 10/10] raw/cnxk_gpio: support selftest Tomasz Duszynski 2022-01-05 14:00 ` [PATCH v4 00/11] Add cnxk_gpio Tomasz Duszynski 10 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2021-12-13 8:17 UTC (permalink / raw) To: dev; +Cc: jerinj, stephen, Tomasz Duszynski Add support for custom interrupt handlers. Custom interrupt handlers bypass kernel completely and are meant for fast and low latency access to GPIO state. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- doc/guides/rawdevs/cnxk_gpio.rst | 21 +++ drivers/raw/cnxk_gpio/cnxk_gpio.c | 37 ++++ drivers/raw/cnxk_gpio/cnxk_gpio.h | 8 + drivers/raw/cnxk_gpio/cnxk_gpio_irq.c | 216 ++++++++++++++++++++++ drivers/raw/cnxk_gpio/meson.build | 1 + drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h | 116 ++++++++++++ 6 files changed, 399 insertions(+) create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio_irq.c diff --git a/doc/guides/rawdevs/cnxk_gpio.rst b/doc/guides/rawdevs/cnxk_gpio.rst index f6c3c942c5..ad93ec0d44 100644 --- a/doc/guides/rawdevs/cnxk_gpio.rst +++ b/doc/guides/rawdevs/cnxk_gpio.rst @@ -161,3 +161,24 @@ Payload contains an integer set to 0 or 1. The latter means inverted logic is turned on. Consider using ``rte_pmd_gpio_get_pin_active_low()`` wrapper. + +Request interrupt +~~~~~~~~~~~~~~~~~ + +Message is used to install custom interrupt handler. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_REGISTER_IRQ``. + +Payload needs to be set to ``struct cnxk_gpio_irq`` which describes interrupt +being requested. + +Consider using ``rte_pmd_gpio_register_gpio()`` wrapper. + +Free interrupt +~~~~~~~~~~~~~~ + +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. diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index fa5b1359d0..f3fdd5a380 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -335,6 +335,28 @@ cnxk_gpio_name_to_dir(const char *name) return cnxk_gpio_dir_name[i].dir; } +static int +cnxk_gpio_register_irq(struct cnxk_gpio *gpio, struct cnxk_gpio_irq *irq) +{ + int ret; + + ret = cnxk_gpio_irq_request(gpio->num - gpio->gpiochip->base, irq->cpu); + if (ret) + return ret; + + gpio->handler = irq->handler; + gpio->data = irq->data; + gpio->cpu = irq->cpu; + + return 0; +} + +static int +cnxk_gpio_unregister_irq(struct cnxk_gpio *gpio) +{ + return cnxk_gpio_irq_free(gpio->num - gpio->gpiochip->base); +} + static int cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf) { @@ -416,6 +438,13 @@ cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf) *(int *)rsp = val; break; + case CNXK_GPIO_MSG_TYPE_REGISTER_IRQ: + ret = cnxk_gpio_register_irq(gpio, + (struct cnxk_gpio_irq *)msg->data); + break; + case CNXK_GPIO_MSG_TYPE_UNREGISTER_IRQ: + ret = cnxk_gpio_unregister_irq(gpio); + break; default: return -EINVAL; } @@ -519,6 +548,10 @@ cnxk_gpio_probe(struct rte_vdev_device *dev) if (ret) goto out; + ret = cnxk_gpio_irq_init(gpiochip); + if (ret) + goto out; + /* read gpio base */ snprintf(buf, sizeof(buf), "%s/gpiochip%d/base", CNXK_GPIO_CLASS_PATH, gpiochip->num); @@ -577,10 +610,14 @@ cnxk_gpio_remove(struct rte_vdev_device *dev) if (!gpio) continue; + if (gpio->handler) + cnxk_gpio_unregister_irq(gpio); + cnxk_gpio_queue_release(rawdev, gpio->num); } rte_free(gpiochip->gpios); + cnxk_gpio_irq_fini(); rte_rawdev_pmd_release(rawdev); return 0; diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.h b/drivers/raw/cnxk_gpio/cnxk_gpio.h index 6b54ebe6e6..c052ca5735 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.h +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.h @@ -11,6 +11,9 @@ struct cnxk_gpio { struct cnxk_gpiochip *gpiochip; void *rsp; int num; + void (*handler)(int gpio, void *data); + void *data; + int cpu; }; struct cnxk_gpiochip { @@ -20,4 +23,9 @@ struct cnxk_gpiochip { struct cnxk_gpio **gpios; }; +int cnxk_gpio_irq_init(struct cnxk_gpiochip *gpiochip); +void cnxk_gpio_irq_fini(void); +int cnxk_gpio_irq_request(int gpio, int cpu); +int cnxk_gpio_irq_free(int gpio); + #endif /* _CNXK_GPIO_H_ */ diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio_irq.c b/drivers/raw/cnxk_gpio/cnxk_gpio_irq.c new file mode 100644 index 0000000000..2fa8e69899 --- /dev/null +++ b/drivers/raw/cnxk_gpio/cnxk_gpio_irq.c @@ -0,0 +1,216 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2021 Marvell. + */ + +#include <fcntl.h> +#include <pthread.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/queue.h> +#include <unistd.h> + +#include <rte_rawdev_pmd.h> + +#include <roc_api.h> + +#include "cnxk_gpio.h" + +#define OTX_IOC_MAGIC 0xF2 +#define OTX_IOC_SET_GPIO_HANDLER \ + _IOW(OTX_IOC_MAGIC, 1, struct otx_gpio_usr_data) +#define OTX_IOC_CLR_GPIO_HANDLER \ + _IO(OTX_IOC_MAGIC, 2) + +struct otx_gpio_usr_data { + uint64_t isr_base; + uint64_t sp; + uint64_t cpu; + uint64_t gpio_num; +}; + +struct cnxk_gpio_irq_stack { + LIST_ENTRY(cnxk_gpio_irq_stack) next; + void *sp_buffer; + int cpu; + int inuse; +}; + +struct cnxk_gpio_irqchip { + int fd; + /* serialize access to this struct */ + pthread_mutex_t lock; + LIST_HEAD(, cnxk_gpio_irq_stack) stacks; + + struct cnxk_gpiochip *gpiochip; +}; + +static struct cnxk_gpio_irqchip *irqchip; + +static void +cnxk_gpio_irq_stack_free(int cpu) +{ + struct cnxk_gpio_irq_stack *stack; + + LIST_FOREACH(stack, &irqchip->stacks, next) { + if (stack->cpu == cpu) + break; + } + + if (!stack) + return; + + if (stack->inuse) + stack->inuse--; + + if (stack->inuse == 0) { + LIST_REMOVE(stack, next); + rte_free(stack->sp_buffer); + rte_free(stack); + } +} + +static void * +cnxk_gpio_irq_stack_alloc(int cpu) +{ +#define ARM_STACK_ALIGNMENT (2 * sizeof(void *)) +#define IRQ_STACK_SIZE 0x200000 + + struct cnxk_gpio_irq_stack *stack; + + LIST_FOREACH(stack, &irqchip->stacks, next) { + if (stack->cpu == cpu) + break; + } + + if (stack) { + stack->inuse++; + return (char *)stack->sp_buffer + IRQ_STACK_SIZE; + } + + stack = rte_malloc(NULL, sizeof(*stack), 0); + if (!stack) + return NULL; + + stack->sp_buffer = + rte_zmalloc(NULL, IRQ_STACK_SIZE * 2, ARM_STACK_ALIGNMENT); + if (!stack->sp_buffer) { + rte_free(stack); + return NULL; + } + + stack->cpu = cpu; + stack->inuse = 1; + LIST_INSERT_HEAD(&irqchip->stacks, stack, next); + + return (char *)stack->sp_buffer + IRQ_STACK_SIZE; +} + +static void +cnxk_gpio_irq_handler(int gpio_num) +{ + struct cnxk_gpiochip *gpiochip = irqchip->gpiochip; + struct cnxk_gpio *gpio; + + if (gpio_num >= gpiochip->num_gpios) + goto out; + + gpio = gpiochip->gpios[gpio_num]; + if (likely(gpio->handler)) + gpio->handler(gpio_num, gpio->data); + +out: + roc_atf_ret(); +} + +int +cnxk_gpio_irq_init(struct cnxk_gpiochip *gpiochip) +{ + if (irqchip) + return 0; + + irqchip = rte_zmalloc(NULL, sizeof(*irqchip), 0); + if (!irqchip) + return -ENOMEM; + + irqchip->fd = open("/dev/otx-gpio-ctr", O_RDWR | O_SYNC); + if (irqchip->fd < 0) { + rte_free(irqchip); + return -errno; + } + + pthread_mutex_init(&irqchip->lock, NULL); + LIST_INIT(&irqchip->stacks); + irqchip->gpiochip = gpiochip; + + return 0; +} + +void +cnxk_gpio_irq_fini(void) +{ + if (!irqchip) + return; + + close(irqchip->fd); + rte_free(irqchip); + irqchip = NULL; +} + +int +cnxk_gpio_irq_request(int gpio, int cpu) +{ + struct otx_gpio_usr_data data; + void *sp; + int ret; + + pthread_mutex_lock(&irqchip->lock); + + sp = cnxk_gpio_irq_stack_alloc(cpu); + if (!sp) { + ret = -ENOMEM; + goto out_unlock; + } + + data.isr_base = (uint64_t)cnxk_gpio_irq_handler; + data.sp = (uint64_t)sp; + data.cpu = (uint64_t)cpu; + data.gpio_num = (uint64_t)gpio; + + mlockall(MCL_CURRENT | MCL_FUTURE); + ret = ioctl(irqchip->fd, OTX_IOC_SET_GPIO_HANDLER, &data); + if (ret) { + ret = -errno; + goto out_free_stack; + } + + pthread_mutex_unlock(&irqchip->lock); + + return 0; + +out_free_stack: + cnxk_gpio_irq_stack_free(cpu); +out_unlock: + pthread_mutex_unlock(&irqchip->lock); + + return ret; +} + +int +cnxk_gpio_irq_free(int gpio) +{ + int ret; + + pthread_mutex_lock(&irqchip->lock); + + ret = ioctl(irqchip->fd, OTX_IOC_CLR_GPIO_HANDLER, gpio); + if (ret) { + pthread_mutex_unlock(&irqchip->lock); + return -errno; + } + + cnxk_gpio_irq_stack_free(irqchip->gpiochip->gpios[gpio]->cpu); + + pthread_mutex_unlock(&irqchip->lock); + + return 0; +} diff --git a/drivers/raw/cnxk_gpio/meson.build b/drivers/raw/cnxk_gpio/meson.build index 3fbfdd838c..9b55f029c7 100644 --- a/drivers/raw/cnxk_gpio/meson.build +++ b/drivers/raw/cnxk_gpio/meson.build @@ -5,5 +5,6 @@ deps += ['bus_vdev', 'common_cnxk', 'rawdev', 'kvargs'] sources = files( 'cnxk_gpio.c', + 'cnxk_gpio_irq.c', ) headers = files('rte_pmd_cnxk_gpio.h') diff --git a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h index 7c3dc225ca..e3096dc14f 100644 --- a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h +++ b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h @@ -40,6 +40,10 @@ enum cnxk_gpio_msg_type { CNXK_GPIO_MSG_TYPE_GET_PIN_DIR, /** Type used to read inverted logic state */ CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW, + /** Type used to register interrupt handler */ + CNXK_GPIO_MSG_TYPE_REGISTER_IRQ, + /** Type used to remove interrupt handler */ + CNXK_GPIO_MSG_TYPE_UNREGISTER_IRQ, }; /** Available edges */ @@ -66,6 +70,25 @@ enum cnxk_gpio_pin_dir { CNXK_GPIO_PIN_DIR_LOW, }; +/** + * GPIO interrupt handler + * + * @param gpio + * Zero-based GPIO number + * @param data + * Cookie passed to interrupt handler + */ +typedef void (*cnxk_gpio_irq_handler_t)(int gpio, void *data); + +struct cnxk_gpio_irq { + /** Interrupt handler */ + cnxk_gpio_irq_handler_t handler; + /** User data passed to irq handler */ + void *data; + /** CPU which will run irq handler */ + int cpu; +}; + struct cnxk_gpio_msg { /** Message type */ enum cnxk_gpio_msg_type type; @@ -306,6 +329,99 @@ rte_pmd_gpio_get_pin_active_low(uint16_t dev_id, int gpio, int *val) return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, val, sizeof(*val)); } +/** + * Attach interrupt handler to GPIO + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param cpu + * CPU which will be handling interrupt + * @param handler + * Interrupt handler to be executed + * @param data + * Data to be passed to interrupt handler + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_register_irq(uint16_t dev_id, int gpio, int cpu, + cnxk_gpio_irq_handler_t handler, void *data) +{ + struct cnxk_gpio_irq irq = { + .handler = handler, + .data = data, + .cpu = cpu, + }; + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_REGISTER_IRQ, + .data = &irq, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Detach interrupt handler from GPIO + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_unregister_irq(uint16_t dev_id, int gpio) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_UNREGISTER_IRQ, + .data = &gpio, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Enable interrupt + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param edge + * Edge that should trigger interrupt + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_enable_interrupt(uint16_t dev_id, int gpio, + enum cnxk_gpio_pin_edge edge) +{ + return rte_pmd_gpio_set_pin_edge(dev_id, gpio, edge); +} + +/** + * Disable interrupt + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_disable_interrupt(uint16_t dev_id, int gpio) +{ + return rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_NONE); +} + #ifdef __cplusplus } #endif -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v3 10/10] raw/cnxk_gpio: support selftest 2021-12-13 8:17 ` [PATCH v3 00/10] Add cnxk_gpio PMD Tomasz Duszynski ` (8 preceding siblings ...) 2021-12-13 8:17 ` [PATCH v3 09/10] raw/cnxk_gpio: support custom irq handlers Tomasz Duszynski @ 2021-12-13 8:17 ` Tomasz Duszynski 2022-01-05 14:00 ` [PATCH v4 00/11] Add cnxk_gpio Tomasz Duszynski 10 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2021-12-13 8:17 UTC (permalink / raw) To: dev; +Cc: jerinj, stephen, Tomasz Duszynski Add support for performing selftest. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- 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=<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. diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index f3fdd5a380..c9f87a315a 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -514,6 +514,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 <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; +} 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') -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v4 00/11] Add cnxk_gpio 2021-12-13 8:17 ` [PATCH v3 00/10] Add cnxk_gpio PMD Tomasz Duszynski ` (9 preceding siblings ...) 2021-12-13 8:17 ` [PATCH v3 10/10] raw/cnxk_gpio: support selftest Tomasz Duszynski @ 2022-01-05 14:00 ` Tomasz Duszynski 2022-01-05 14:00 ` [PATCH v4 01/11] raw/cnxk_gpio: add GPIO driver skeleton Tomasz Duszynski ` (12 more replies) 10 siblings, 13 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-01-05 14:00 UTC (permalink / raw) To: dev; +Cc: thomas, jerinj, Tomasz Duszynski This series introduces a new rawdevice PMD which allows to manage userspace GPIOs and install custom GPIO interrupt handlers which bypass kernel. This is especially useful for applications that, besides providing standard dataplane functionality, want to have fast and low latency access to GPIO pin state. It'd be great to have that merged during 22.02 merge window. v4: - free kvargs after parsing arguments - add support for allowing only subset of available GPIOs v3: - fix meson formatting - fix cnxk_gpio_process_buf() return value v2: - do not trigger irq by writing to /dev/mem, use ioctl() instead Tomasz Duszynski (11): raw/cnxk_gpio: add GPIO driver skeleton raw/cnxk_gpio: support reading default queue conf raw/cnxk_gpio: support reading queue count raw/cnxk_gpio: support queue setup raw/cnxk_gpio: support queue release raw/cnxk_gpio: support enqueuing buffers raw/cnxk_gpio: support dequeuing buffers raw/cnxk_gpio: support standard GPIO operations raw/cnxk_gpio: support custom irq handlers raw/cnxk_gpio: support selftest raw/cnxk_gpio: add option to allow using subset of GPIOs doc/guides/rawdevs/cnxk_gpio.rst | 200 ++++++ doc/guides/rawdevs/index.rst | 1 + drivers/raw/cnxk_gpio/cnxk_gpio.c | 754 +++++++++++++++++++++ drivers/raw/cnxk_gpio/cnxk_gpio.h | 35 + drivers/raw/cnxk_gpio/cnxk_gpio_irq.c | 216 ++++++ drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c | 386 +++++++++++ drivers/raw/cnxk_gpio/meson.build | 11 + drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h | 437 ++++++++++++ drivers/raw/cnxk_gpio/version.map | 3 + drivers/raw/meson.build | 1 + 10 files changed, 2044 insertions(+) create mode 100644 doc/guides/rawdevs/cnxk_gpio.rst create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio.c create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio.h create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio_irq.c create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c create mode 100644 drivers/raw/cnxk_gpio/meson.build create mode 100644 drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h create mode 100644 drivers/raw/cnxk_gpio/version.map -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v4 01/11] raw/cnxk_gpio: add GPIO driver skeleton 2022-01-05 14:00 ` [PATCH v4 00/11] Add cnxk_gpio Tomasz Duszynski @ 2022-01-05 14:00 ` Tomasz Duszynski 2022-01-05 14:00 ` [PATCH v4 02/11] raw/cnxk_gpio: support reading default queue conf Tomasz Duszynski ` (11 subsequent siblings) 12 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-01-05 14:00 UTC (permalink / raw) To: dev, Ray Kinsella, Anatoly Burakov; +Cc: thomas, jerinj, Tomasz Duszynski Add initial support for PMD that allows to control particular pins form userspace. Moreover PMD allows to attach custom interrupt handlers to controllable GPIOs. Main users of this PMD are dataplain applications requiring fast and low latency access to pin state. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- doc/guides/rawdevs/cnxk_gpio.rst | 65 ++++++++ doc/guides/rawdevs/index.rst | 1 + drivers/raw/cnxk_gpio/cnxk_gpio.c | 239 ++++++++++++++++++++++++++++++ drivers/raw/cnxk_gpio/cnxk_gpio.h | 22 +++ drivers/raw/cnxk_gpio/meson.build | 8 + drivers/raw/cnxk_gpio/version.map | 3 + drivers/raw/meson.build | 1 + 7 files changed, 339 insertions(+) create mode 100644 doc/guides/rawdevs/cnxk_gpio.rst create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio.c create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio.h create mode 100644 drivers/raw/cnxk_gpio/meson.build create mode 100644 drivers/raw/cnxk_gpio/version.map diff --git a/doc/guides/rawdevs/cnxk_gpio.rst b/doc/guides/rawdevs/cnxk_gpio.rst new file mode 100644 index 0000000000..868302d07f --- /dev/null +++ b/doc/guides/rawdevs/cnxk_gpio.rst @@ -0,0 +1,65 @@ +.. SPDX-License-Identifier: BSD-3-Clause + Copyright(c) 2021 Marvell. + +Marvell CNXK GPIO Driver +======================== + +CNXK GPIO PMD configures and manages GPIOs available on the system using +standard enqueue/dequeue mechanism offered by raw device abstraction. PMD relies +both on standard sysfs GPIO interface provided by the Linux kernel and GPIO +kernel driver custom interface allowing one to install userspace interrupt +handlers. + +Features +-------- + +Following features are available: + +- export/unexport a GPIO +- read/write specific value from/to exported GPIO +- set GPIO direction +- set GPIO edge that triggers interrupt +- set GPIO active low +- register interrupt handler for specific GPIO + +Requirements +------------ + +PMD relies on modified kernel GPIO driver which exposes ``ioctl()`` interface +for installing interrupt handlers for low latency signal processing. + +Driver is shipped with Marvell SDK. + +Device Setup +------------ + +CNXK GPIO PMD binds to virtual device which gets created by passing +`--vdev=cnxk_gpio,gpiochip=<number>` command line to EAL. `gpiochip` parameter +tells PMD which GPIO controller should be used. Available controllers are +available under `/sys/class/gpio`. For further details on how Linux represents +GPIOs in userspace please refer to +`sysfs.txt <https://www.kernel.org/doc/Documentation/gpio/sysfs.txt>`_. + +If `gpiochip=<number>` was omitted then first gpiochip from the alphabetically +sort list of available gpiochips is used. + +.. code-block:: console + + $ ls /sys/class/gpio + export gpiochip448 unexport + +In above scenario only one GPIO controller is present hence +`--vdev=cnxk_gpio,gpiochip=448` should be passed to EAL. + +Before performing actual data transfer one needs to call +``rte_rawdev_queue_count()`` followed by ``rte_rawdev_queue_conf_get()``. The +former returns number GPIOs available in the system irrespective of GPIOs +being controllable or not. Thus it is user responsibility to pick the proper +ones. The latter call simply returns queue capacity. + +Respective queue needs to be configured with ``rte_rawdev_queue_setup()``. This +call barely exports GPIO to userspace. + +To perform actual data transfer use standard ``rte_rawdev_enqueue_buffers()`` +and ``rte_rawdev_dequeue_buffers()`` APIs. Not all messages produce sensible +responses hence dequeueing is not always necessary. diff --git a/doc/guides/rawdevs/index.rst b/doc/guides/rawdevs/index.rst index b6cf917443..0c02da6e90 100644 --- a/doc/guides/rawdevs/index.rst +++ b/doc/guides/rawdevs/index.rst @@ -12,6 +12,7 @@ application through rawdev API. :numbered: cnxk_bphy + cnxk_gpio dpaa2_cmdif dpaa2_qdma ifpga diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c new file mode 100644 index 0000000000..881615d62e --- /dev/null +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -0,0 +1,239 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2021 Marvell. + */ + +#include <dirent.h> +#include <string.h> + +#include <rte_bus_vdev.h> +#include <rte_eal.h> +#include <rte_kvargs.h> +#include <rte_lcore.h> +#include <rte_rawdev_pmd.h> + +#include <roc_api.h> + +#include "cnxk_gpio.h" + +#define CNXK_GPIO_BUFSZ 128 +#define CNXK_GPIO_CLASS_PATH "/sys/class/gpio" + +static const char *const cnxk_gpio_args[] = { +#define CNXK_GPIO_ARG_GPIOCHIP "gpiochip" + CNXK_GPIO_ARG_GPIOCHIP, + NULL +}; + +static void +cnxk_gpio_format_name(char *name, size_t len) +{ + snprintf(name, len, "cnxk_gpio"); +} + +static int +cnxk_gpio_filter_gpiochip(const struct dirent *dirent) +{ + const char *pattern = "gpiochip"; + + return !strncmp(dirent->d_name, pattern, strlen(pattern)); +} + +static void +cnxk_gpio_set_defaults(struct cnxk_gpiochip *gpiochip) +{ + struct dirent **namelist; + int n; + + n = scandir(CNXK_GPIO_CLASS_PATH, &namelist, cnxk_gpio_filter_gpiochip, + alphasort); + if (n < 0 || n == 0) + return; + + sscanf(namelist[0]->d_name, "gpiochip%d", &gpiochip->num); + while (n--) + free(namelist[n]); + free(namelist); +} + +static int +cnxk_gpio_parse_arg_gpiochip(const char *key __rte_unused, const char *value, + void *extra_args) +{ + long val; + + errno = 0; + val = strtol(value, NULL, 10); + if (errno) + return -errno; + + *(int *)extra_args = (int)val; + + return 0; +} + +static int +cnxk_gpio_parse_args(struct cnxk_gpiochip *gpiochip, + struct rte_devargs *devargs) +{ + struct rte_kvargs *kvlist; + int ret; + + kvlist = rte_kvargs_parse(devargs->args, cnxk_gpio_args); + if (!kvlist) + return 0; + + ret = rte_kvargs_count(kvlist, CNXK_GPIO_ARG_GPIOCHIP); + if (ret == 1) { + ret = rte_kvargs_process(kvlist, CNXK_GPIO_ARG_GPIOCHIP, + cnxk_gpio_parse_arg_gpiochip, + &gpiochip->num); + if (ret) + goto out; + } + + ret = 0; +out: + rte_kvargs_free(kvlist); + + return ret; +} + +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; +} + +static int +cnxk_gpio_read_attr_int(char *attr, int *val) +{ + char buf[CNXK_GPIO_BUFSZ]; + int ret; + + ret = cnxk_gpio_read_attr(attr, buf); + if (ret) + return ret; + + ret = sscanf(buf, "%d", val); + if (ret < 0) + return -errno; + + return 0; +} + +static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { +}; + +static int +cnxk_gpio_probe(struct rte_vdev_device *dev) +{ + char name[RTE_RAWDEV_NAME_MAX_LEN]; + struct cnxk_gpiochip *gpiochip; + struct rte_rawdev *rawdev; + char buf[CNXK_GPIO_BUFSZ]; + int ret; + + if (rte_eal_process_type() != RTE_PROC_PRIMARY) + return 0; + + cnxk_gpio_format_name(name, sizeof(name)); + rawdev = rte_rawdev_pmd_allocate(name, sizeof(*gpiochip), + rte_socket_id()); + if (!rawdev) { + RTE_LOG(ERR, PMD, "failed to allocate %s rawdev", name); + return -ENOMEM; + } + + rawdev->dev_ops = &cnxk_gpio_rawdev_ops; + rawdev->device = &dev->device; + rawdev->driver_name = dev->device.name; + + gpiochip = rawdev->dev_private; + cnxk_gpio_set_defaults(gpiochip); + + /* defaults may be overwritten by this call */ + ret = cnxk_gpio_parse_args(gpiochip, dev->device.devargs); + if (ret) + goto out; + + /* read gpio base */ + snprintf(buf, sizeof(buf), "%s/gpiochip%d/base", CNXK_GPIO_CLASS_PATH, + gpiochip->num); + ret = cnxk_gpio_read_attr_int(buf, &gpiochip->base); + if (ret) { + RTE_LOG(ERR, PMD, "failed to read %s", buf); + goto out; + } + + /* read number of available gpios */ + snprintf(buf, sizeof(buf), "%s/gpiochip%d/ngpio", CNXK_GPIO_CLASS_PATH, + gpiochip->num); + ret = cnxk_gpio_read_attr_int(buf, &gpiochip->num_gpios); + if (ret) { + RTE_LOG(ERR, PMD, "failed to read %s", buf); + goto out; + } + + gpiochip->gpios = rte_calloc(NULL, gpiochip->num_gpios, + sizeof(struct cnxk_gpio *), 0); + if (!gpiochip->gpios) { + RTE_LOG(ERR, PMD, "failed to allocate gpios memory"); + ret = -ENOMEM; + goto out; + } + + return 0; +out: + rte_rawdev_pmd_release(rawdev); + + return ret; +} + +static int +cnxk_gpio_remove(struct rte_vdev_device *dev) +{ + char name[RTE_RAWDEV_NAME_MAX_LEN]; + struct cnxk_gpiochip *gpiochip; + struct rte_rawdev *rawdev; + + RTE_SET_USED(dev); + + if (rte_eal_process_type() != RTE_PROC_PRIMARY) + return 0; + + cnxk_gpio_format_name(name, sizeof(name)); + rawdev = rte_rawdev_pmd_get_named_dev(name); + if (!rawdev) + return -ENODEV; + + gpiochip = rawdev->dev_private; + rte_free(gpiochip->gpios); + rte_rawdev_pmd_release(rawdev); + + return 0; +} + +static struct rte_vdev_driver cnxk_gpio_drv = { + .probe = cnxk_gpio_probe, + .remove = cnxk_gpio_remove, +}; + +RTE_PMD_REGISTER_VDEV(cnxk_gpio, cnxk_gpio_drv); +RTE_PMD_REGISTER_PARAM_STRING(cnxk_gpio, "gpiochip=<int>"); diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.h b/drivers/raw/cnxk_gpio/cnxk_gpio.h new file mode 100644 index 0000000000..4dae8316ba --- /dev/null +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2021 Marvell. + */ + +#ifndef _CNXK_GPIO_H_ +#define _CNXK_GPIO_H_ + +struct cnxk_gpiochip; + +struct cnxk_gpio { + struct cnxk_gpiochip *gpiochip; + int num; +}; + +struct cnxk_gpiochip { + int num; + int base; + int num_gpios; + struct cnxk_gpio **gpios; +}; + +#endif /* _CNXK_GPIO_H_ */ diff --git a/drivers/raw/cnxk_gpio/meson.build b/drivers/raw/cnxk_gpio/meson.build new file mode 100644 index 0000000000..9a7e716c1e --- /dev/null +++ b/drivers/raw/cnxk_gpio/meson.build @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(C) 2021 Marvell. +# + +deps += ['bus_vdev', 'common_cnxk', 'rawdev', 'kvargs'] +sources = files( + 'cnxk_gpio.c', +) diff --git a/drivers/raw/cnxk_gpio/version.map b/drivers/raw/cnxk_gpio/version.map new file mode 100644 index 0000000000..4a76d1d52d --- /dev/null +++ b/drivers/raw/cnxk_gpio/version.map @@ -0,0 +1,3 @@ +DPDK_21 { + local: *; +}; diff --git a/drivers/raw/meson.build b/drivers/raw/meson.build index 87694a758e..05e7de1bfe 100644 --- a/drivers/raw/meson.build +++ b/drivers/raw/meson.build @@ -7,6 +7,7 @@ endif drivers = [ 'cnxk_bphy', + 'cnxk_gpio', 'dpaa2_cmdif', 'dpaa2_qdma', 'ifpga', -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v4 02/11] raw/cnxk_gpio: support reading default queue conf 2022-01-05 14:00 ` [PATCH v4 00/11] Add cnxk_gpio Tomasz Duszynski 2022-01-05 14:00 ` [PATCH v4 01/11] raw/cnxk_gpio: add GPIO driver skeleton Tomasz Duszynski @ 2022-01-05 14:00 ` Tomasz Duszynski 2022-01-05 14:00 ` [PATCH v4 03/11] raw/cnxk_gpio: support reading queue count Tomasz Duszynski ` (10 subsequent siblings) 12 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-01-05 14:00 UTC (permalink / raw) To: dev; +Cc: thomas, jerinj, Tomasz Duszynski Add support for reading default queue configuration. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 881615d62e..f80788f7fb 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -138,7 +138,26 @@ cnxk_gpio_read_attr_int(char *attr, int *val) return 0; } +static int +cnxk_gpio_queue_def_conf(struct rte_rawdev *dev, uint16_t queue_id, + rte_rawdev_obj_t queue_conf, size_t queue_conf_size) +{ + unsigned int *conf; + + RTE_SET_USED(dev); + RTE_SET_USED(queue_id); + + if (queue_conf_size != sizeof(*conf)) + return -EINVAL; + + conf = (unsigned int *)queue_conf; + *conf = 1; + + return 0; +} + static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { + .queue_def_conf = cnxk_gpio_queue_def_conf, }; static int -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v4 03/11] raw/cnxk_gpio: support reading queue count 2022-01-05 14:00 ` [PATCH v4 00/11] Add cnxk_gpio Tomasz Duszynski 2022-01-05 14:00 ` [PATCH v4 01/11] raw/cnxk_gpio: add GPIO driver skeleton Tomasz Duszynski 2022-01-05 14:00 ` [PATCH v4 02/11] raw/cnxk_gpio: support reading default queue conf Tomasz Duszynski @ 2022-01-05 14:00 ` Tomasz Duszynski 2022-01-05 14:00 ` [PATCH v4 04/11] raw/cnxk_gpio: support queue setup Tomasz Duszynski ` (9 subsequent siblings) 12 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-01-05 14:00 UTC (permalink / raw) To: dev; +Cc: thomas, jerinj, Tomasz Duszynski Add support for reading number of available queues. Single queue corresponds to GPIO. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index f80788f7fb..3455d6258c 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -156,8 +156,17 @@ cnxk_gpio_queue_def_conf(struct rte_rawdev *dev, uint16_t queue_id, return 0; } +static uint16_t +cnxk_gpio_queue_count(struct rte_rawdev *dev) +{ + struct cnxk_gpiochip *gpiochip = dev->dev_private; + + return gpiochip->num_gpios; +} + static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { .queue_def_conf = cnxk_gpio_queue_def_conf, + .queue_count = cnxk_gpio_queue_count, }; static int -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v4 04/11] raw/cnxk_gpio: support queue setup 2022-01-05 14:00 ` [PATCH v4 00/11] Add cnxk_gpio Tomasz Duszynski ` (2 preceding siblings ...) 2022-01-05 14:00 ` [PATCH v4 03/11] raw/cnxk_gpio: support reading queue count Tomasz Duszynski @ 2022-01-05 14:00 ` Tomasz Duszynski 2022-01-05 14:00 ` [PATCH v4 05/11] raw/cnxk_gpio: support queue release Tomasz Duszynski ` (8 subsequent siblings) 12 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-01-05 14:00 UTC (permalink / raw) To: dev; +Cc: thomas, jerinj, Tomasz Duszynski Add support for queue setup. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 80 +++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 3455d6258c..c1e74fad43 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -138,6 +138,85 @@ cnxk_gpio_read_attr_int(char *attr, int *val) return 0; } +static int +cnxk_gpio_write_attr(const char *attr, const char *val) +{ + FILE *fp; + int ret; + + if (!val) + return -EINVAL; + + fp = fopen(attr, "w"); + if (!fp) + return -errno; + + ret = fprintf(fp, "%s", val); + if (ret < 0) { + fclose(fp); + return ret; + } + + ret = fclose(fp); + if (ret) + return -errno; + + return 0; +} + +static int +cnxk_gpio_write_attr_int(const char *attr, int val) +{ + char buf[CNXK_GPIO_BUFSZ]; + + snprintf(buf, sizeof(buf), "%d", val); + + return cnxk_gpio_write_attr(attr, buf); +} + +static struct cnxk_gpio * +cnxk_gpio_lookup(struct cnxk_gpiochip *gpiochip, uint16_t queue) +{ + if (queue >= gpiochip->num_gpios) + return NULL; + + return gpiochip->gpios[queue]; +} + +static int +cnxk_gpio_queue_setup(struct rte_rawdev *dev, uint16_t queue_id, + rte_rawdev_obj_t queue_conf, size_t queue_conf_size) +{ + struct cnxk_gpiochip *gpiochip = dev->dev_private; + char buf[CNXK_GPIO_BUFSZ]; + struct cnxk_gpio *gpio; + int ret; + + RTE_SET_USED(queue_conf); + RTE_SET_USED(queue_conf_size); + + gpio = cnxk_gpio_lookup(gpiochip, queue_id); + if (gpio) + return -EEXIST; + + gpio = rte_zmalloc(NULL, sizeof(*gpio), 0); + if (!gpio) + return -ENOMEM; + gpio->num = queue_id + gpiochip->base; + gpio->gpiochip = gpiochip; + + snprintf(buf, sizeof(buf), "%s/export", CNXK_GPIO_CLASS_PATH); + ret = cnxk_gpio_write_attr_int(buf, gpio->num); + if (ret) { + rte_free(gpio); + return ret; + } + + gpiochip->gpios[queue_id] = gpio; + + return 0; +} + static int cnxk_gpio_queue_def_conf(struct rte_rawdev *dev, uint16_t queue_id, rte_rawdev_obj_t queue_conf, size_t queue_conf_size) @@ -167,6 +246,7 @@ cnxk_gpio_queue_count(struct rte_rawdev *dev) static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { .queue_def_conf = cnxk_gpio_queue_def_conf, .queue_count = cnxk_gpio_queue_count, + .queue_setup = cnxk_gpio_queue_setup, }; static int -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v4 05/11] raw/cnxk_gpio: support queue release 2022-01-05 14:00 ` [PATCH v4 00/11] Add cnxk_gpio Tomasz Duszynski ` (3 preceding siblings ...) 2022-01-05 14:00 ` [PATCH v4 04/11] raw/cnxk_gpio: support queue setup Tomasz Duszynski @ 2022-01-05 14:00 ` Tomasz Duszynski 2022-01-05 14:00 ` [PATCH v4 06/11] raw/cnxk_gpio: support enqueuing buffers Tomasz Duszynski ` (7 subsequent siblings) 12 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-01-05 14:00 UTC (permalink / raw) To: dev; +Cc: thomas, jerinj, Tomasz Duszynski Add support for queue release. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index c1e74fad43..71f568f7a4 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -217,6 +217,29 @@ cnxk_gpio_queue_setup(struct rte_rawdev *dev, uint16_t queue_id, return 0; } +static int +cnxk_gpio_queue_release(struct rte_rawdev *dev, uint16_t queue_id) +{ + struct cnxk_gpiochip *gpiochip = dev->dev_private; + char buf[CNXK_GPIO_BUFSZ]; + struct cnxk_gpio *gpio; + int ret; + + gpio = cnxk_gpio_lookup(gpiochip, queue_id); + if (!gpio) + return -ENODEV; + + snprintf(buf, sizeof(buf), "%s/unexport", CNXK_GPIO_CLASS_PATH); + ret = cnxk_gpio_write_attr_int(buf, gpiochip->base + queue_id); + if (ret) + return ret; + + gpiochip->gpios[queue_id] = NULL; + rte_free(gpio); + + return 0; +} + static int cnxk_gpio_queue_def_conf(struct rte_rawdev *dev, uint16_t queue_id, rte_rawdev_obj_t queue_conf, size_t queue_conf_size) @@ -247,6 +270,7 @@ static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { .queue_def_conf = cnxk_gpio_queue_def_conf, .queue_count = cnxk_gpio_queue_count, .queue_setup = cnxk_gpio_queue_setup, + .queue_release = cnxk_gpio_queue_release, }; static int @@ -320,6 +344,8 @@ cnxk_gpio_remove(struct rte_vdev_device *dev) char name[RTE_RAWDEV_NAME_MAX_LEN]; struct cnxk_gpiochip *gpiochip; struct rte_rawdev *rawdev; + struct cnxk_gpio *gpio; + int i; RTE_SET_USED(dev); @@ -332,6 +358,14 @@ cnxk_gpio_remove(struct rte_vdev_device *dev) return -ENODEV; gpiochip = rawdev->dev_private; + for (i = 0; i < gpiochip->num_gpios; i++) { + gpio = gpiochip->gpios[i]; + if (!gpio) + continue; + + cnxk_gpio_queue_release(rawdev, gpio->num); + } + rte_free(gpiochip->gpios); rte_rawdev_pmd_release(rawdev); -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v4 06/11] raw/cnxk_gpio: support enqueuing buffers 2022-01-05 14:00 ` [PATCH v4 00/11] Add cnxk_gpio Tomasz Duszynski ` (4 preceding siblings ...) 2022-01-05 14:00 ` [PATCH v4 05/11] raw/cnxk_gpio: support queue release Tomasz Duszynski @ 2022-01-05 14:00 ` Tomasz Duszynski 2022-01-05 14:00 ` [PATCH v4 07/11] raw/cnxk_gpio: support dequeuing buffers Tomasz Duszynski ` (6 subsequent siblings) 12 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-01-05 14:00 UTC (permalink / raw) To: dev; +Cc: thomas, jerinj, Tomasz Duszynski Add dummy support for enqueuing buffers. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 47 +++++++++++++++++++++++ drivers/raw/cnxk_gpio/cnxk_gpio.h | 1 + drivers/raw/cnxk_gpio/meson.build | 1 + drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h | 38 ++++++++++++++++++ 4 files changed, 87 insertions(+) create mode 100644 drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 71f568f7a4..235f0d6f7a 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -14,6 +14,7 @@ #include <roc_api.h> #include "cnxk_gpio.h" +#include "rte_pmd_cnxk_gpio.h" #define CNXK_GPIO_BUFSZ 128 #define CNXK_GPIO_CLASS_PATH "/sys/class/gpio" @@ -266,7 +267,53 @@ cnxk_gpio_queue_count(struct rte_rawdev *dev) return gpiochip->num_gpios; } +static int +cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf) +{ + struct cnxk_gpio_msg *msg = rbuf->buf_addr; + void *rsp = NULL; + int ret; + + switch (msg->type) { + default: + return -EINVAL; + } + + /* get rid of last response if any */ + if (gpio->rsp) { + RTE_LOG(WARNING, PMD, "previous response got overwritten\n"); + rte_free(gpio->rsp); + } + gpio->rsp = rsp; + + return ret; +} + +static int +cnxk_gpio_enqueue_bufs(struct rte_rawdev *dev, struct rte_rawdev_buf **buffers, + unsigned int count, rte_rawdev_obj_t context) +{ + struct cnxk_gpiochip *gpiochip = dev->dev_private; + unsigned int queue = (size_t)context; + struct cnxk_gpio *gpio; + int ret; + + if (count == 0) + return 0; + + gpio = cnxk_gpio_lookup(gpiochip, queue); + if (!gpio) + return -ENODEV; + + ret = cnxk_gpio_process_buf(gpio, buffers[0]); + if (ret) + return ret; + + return 1; +} + static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { + .enqueue_bufs = cnxk_gpio_enqueue_bufs, .queue_def_conf = cnxk_gpio_queue_def_conf, .queue_count = cnxk_gpio_queue_count, .queue_setup = cnxk_gpio_queue_setup, diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.h b/drivers/raw/cnxk_gpio/cnxk_gpio.h index 4dae8316ba..6b54ebe6e6 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.h +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.h @@ -9,6 +9,7 @@ struct cnxk_gpiochip; struct cnxk_gpio { struct cnxk_gpiochip *gpiochip; + void *rsp; int num; }; diff --git a/drivers/raw/cnxk_gpio/meson.build b/drivers/raw/cnxk_gpio/meson.build index 9a7e716c1e..3fbfdd838c 100644 --- a/drivers/raw/cnxk_gpio/meson.build +++ b/drivers/raw/cnxk_gpio/meson.build @@ -6,3 +6,4 @@ deps += ['bus_vdev', 'common_cnxk', 'rawdev', 'kvargs'] sources = files( 'cnxk_gpio.c', ) +headers = files('rte_pmd_cnxk_gpio.h') diff --git a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h new file mode 100644 index 0000000000..c71065e10c --- /dev/null +++ b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2021 Marvell. + */ + +#ifndef _RTE_PMD_CNXK_GPIO_H_ +#define _RTE_PMD_CNXK_GPIO_H_ + +/** + * @file rte_pmd_cnxk_gpio.h + * + * Marvell GPIO PMD specific structures and interface + * + * This API allows applications to manage GPIOs in user space along with + * installing interrupt handlers for low latency signal processing. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** Available message types */ +enum cnxk_gpio_msg_type { + /** Invalid message type */ + CNXK_GPIO_MSG_TYPE_INVALID, +}; + +struct cnxk_gpio_msg { + /** Message type */ + enum cnxk_gpio_msg_type type; + /** Message data passed to PMD or received from PMD */ + void *data; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _RTE_PMD_CNXK_GPIO_H_ */ -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v4 07/11] raw/cnxk_gpio: support dequeuing buffers 2022-01-05 14:00 ` [PATCH v4 00/11] Add cnxk_gpio Tomasz Duszynski ` (5 preceding siblings ...) 2022-01-05 14:00 ` [PATCH v4 06/11] raw/cnxk_gpio: support enqueuing buffers Tomasz Duszynski @ 2022-01-05 14:00 ` Tomasz Duszynski 2022-01-05 14:00 ` [PATCH v4 08/11] raw/cnxk_gpio: support standard GPIO operations Tomasz Duszynski ` (5 subsequent siblings) 12 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-01-05 14:00 UTC (permalink / raw) To: dev; +Cc: thomas, jerinj, Tomasz Duszynski Add support for dequeuing buffers. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 235f0d6f7a..909fa9d390 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -312,8 +312,34 @@ cnxk_gpio_enqueue_bufs(struct rte_rawdev *dev, struct rte_rawdev_buf **buffers, return 1; } +static int +cnxk_gpio_dequeue_bufs(struct rte_rawdev *dev, struct rte_rawdev_buf **buffers, + unsigned int count, rte_rawdev_obj_t context) +{ + struct cnxk_gpiochip *gpiochip = dev->dev_private; + unsigned int queue = (size_t)context; + struct cnxk_gpio *gpio; + + if (count == 0) + return 0; + + gpio = cnxk_gpio_lookup(gpiochip, queue); + if (!gpio) + return -ENODEV; + + if (gpio->rsp) { + buffers[0]->buf_addr = gpio->rsp; + gpio->rsp = NULL; + + return 1; + } + + return 0; +} + static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { .enqueue_bufs = cnxk_gpio_enqueue_bufs, + .dequeue_bufs = cnxk_gpio_dequeue_bufs, .queue_def_conf = cnxk_gpio_queue_def_conf, .queue_count = cnxk_gpio_queue_count, .queue_setup = cnxk_gpio_queue_setup, -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v4 08/11] raw/cnxk_gpio: support standard GPIO operations 2022-01-05 14:00 ` [PATCH v4 00/11] Add cnxk_gpio Tomasz Duszynski ` (6 preceding siblings ...) 2022-01-05 14:00 ` [PATCH v4 07/11] raw/cnxk_gpio: support dequeuing buffers Tomasz Duszynski @ 2022-01-05 14:00 ` Tomasz Duszynski 2022-01-05 14:00 ` [PATCH v4 09/11] raw/cnxk_gpio: support custom irq handlers Tomasz Duszynski ` (4 subsequent siblings) 12 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-01-05 14:00 UTC (permalink / raw) To: dev; +Cc: thomas, jerinj, Tomasz Duszynski Add support for standard GPIO operations i.e ones normally provided by GPIO sysfs interface. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- doc/guides/rawdevs/cnxk_gpio.rst | 98 ++++++++ drivers/raw/cnxk_gpio/cnxk_gpio.c | 147 +++++++++++- drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h | 279 +++++++++++++++++++++- 3 files changed, 521 insertions(+), 3 deletions(-) diff --git a/doc/guides/rawdevs/cnxk_gpio.rst b/doc/guides/rawdevs/cnxk_gpio.rst index 868302d07f..f6c3c942c5 100644 --- a/doc/guides/rawdevs/cnxk_gpio.rst +++ b/doc/guides/rawdevs/cnxk_gpio.rst @@ -63,3 +63,101 @@ call barely exports GPIO to userspace. To perform actual data transfer use standard ``rte_rawdev_enqueue_buffers()`` and ``rte_rawdev_dequeue_buffers()`` APIs. Not all messages produce sensible responses hence dequeueing is not always necessary. + +CNXK GPIO PMD +------------- + +PMD accepts ``struct cnxk_gpio_msg`` messages which differ by type and payload. +Message types along with description are listed below. As for the usage examples +please refer to ``cnxk_gpio_selftest()``. There's a set of convenient wrappers +available, one for each existing command. + +Set GPIO value +~~~~~~~~~~~~~~ + +Message is used to set output to low or high. This does not work for GPIOs +configured as input. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_SET_PIN_VALUE``. + +Payload must be an integer set to 0 (low) or 1 (high). + +Consider using ``rte_pmd_gpio_set_pin_value()`` wrapper. + +Set GPIO edge +~~~~~~~~~~~~~ + +Message is used to set edge that triggers interrupt. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_SET_PIN_EDGE``. + +Payload must be `enum cnxk_gpio_pin_edge`. + +Consider using ``rte_pmd_gpio_set_pin_edge()`` wrapper. + +Set GPIO direction +~~~~~~~~~~~~~~~~~~ + +Message is used to change GPIO direction to either input or output. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_SET_PIN_DIR``. + +Payload must be `enum cnxk_gpio_pin_dir`. + +Consider using ``rte_pmd_gpio_set_pin_dir()`` wrapper. + +Set GPIO active low +~~~~~~~~~~~~~~~~~~~ + +Message is used to set whether pin is active low. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_SET_PIN_ACTIVE_LOW``. + +Payload must be an integer set to 0 or 1. The latter activates inversion. + +Consider using ``rte_pmd_gpio_set_pin_active_low()`` wrapper. + +Get GPIO value +~~~~~~~~~~~~~~ + +Message is used to read GPIO value. Value can be 0 (low) or 1 (high). + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_GET_PIN_VALUE``. + +Payload contains integer set to either 0 or 1. + +Consider using ``rte_pmd_gpio_get_pin_value()`` wrapper. + +Get GPIO edge +~~~~~~~~~~~~~ + +Message is used to read GPIO edge. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_GET_PIN_EDGE``. + +Payload contains `enum cnxk_gpio_pin_edge`. + +Consider using ``rte_pmd_gpio_get_pin_edge()`` wrapper. + +Get GPIO direction +~~~~~~~~~~~~~~~~~~ + +Message is used to read GPIO direction. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_GET_PIN_DIR``. + +Payload contains `enum cnxk_gpio_pin_dir`. + +Consider using ``rte_pmd_gpio_get_pin_dir()`` wrapper. + +Get GPIO active low +~~~~~~~~~~~~~~~~~~~ + +Message is used check whether inverted logic is active. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW``. + +Payload contains an integer set to 0 or 1. The latter means inverted logic +is turned on. + +Consider using ``rte_pmd_gpio_get_pin_active_low()`` wrapper. diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 909fa9d390..e24d8c1b6e 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -267,14 +267,159 @@ cnxk_gpio_queue_count(struct rte_rawdev *dev) return gpiochip->num_gpios; } +static const struct { + enum cnxk_gpio_pin_edge edge; + const char *name; +} cnxk_gpio_edge_name[] = { + { CNXK_GPIO_PIN_EDGE_NONE, "none" }, + { CNXK_GPIO_PIN_EDGE_FALLING, "falling" }, + { CNXK_GPIO_PIN_EDGE_RISING, "rising" }, + { CNXK_GPIO_PIN_EDGE_BOTH, "both" }, +}; + +static const char * +cnxk_gpio_edge_to_name(enum cnxk_gpio_pin_edge edge) +{ + unsigned int i; + + for (i = 0; i < RTE_DIM(cnxk_gpio_edge_name); i++) { + if (cnxk_gpio_edge_name[i].edge == edge) + return cnxk_gpio_edge_name[i].name; + } + + return NULL; +} + +static enum cnxk_gpio_pin_edge +cnxk_gpio_name_to_edge(const char *name) +{ + unsigned int i; + + for (i = 0; i < RTE_DIM(cnxk_gpio_edge_name); i++) { + if (!strcmp(cnxk_gpio_edge_name[i].name, name)) + break; + } + + return cnxk_gpio_edge_name[i].edge; +} + +static const struct { + enum cnxk_gpio_pin_dir dir; + const char *name; +} cnxk_gpio_dir_name[] = { + { CNXK_GPIO_PIN_DIR_IN, "in" }, + { CNXK_GPIO_PIN_DIR_OUT, "out" }, + { CNXK_GPIO_PIN_DIR_HIGH, "high" }, + { CNXK_GPIO_PIN_DIR_LOW, "low" }, +}; + +static const char * +cnxk_gpio_dir_to_name(enum cnxk_gpio_pin_dir dir) +{ + unsigned int i; + + for (i = 0; i < RTE_DIM(cnxk_gpio_dir_name); i++) { + if (cnxk_gpio_dir_name[i].dir == dir) + return cnxk_gpio_dir_name[i].name; + } + + return NULL; +} + +static enum cnxk_gpio_pin_dir +cnxk_gpio_name_to_dir(const char *name) +{ + unsigned int i; + + for (i = 0; i < RTE_DIM(cnxk_gpio_dir_name); i++) { + if (!strcmp(cnxk_gpio_dir_name[i].name, name)) + break; + } + + return cnxk_gpio_dir_name[i].dir; +} + static int cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf) { struct cnxk_gpio_msg *msg = rbuf->buf_addr; + enum cnxk_gpio_pin_edge edge; + enum cnxk_gpio_pin_dir dir; + char buf[CNXK_GPIO_BUFSZ]; void *rsp = NULL; - int ret; + int ret, val, n; + + n = snprintf(buf, sizeof(buf), "%s/gpio%d", CNXK_GPIO_CLASS_PATH, + gpio->num); switch (msg->type) { + case CNXK_GPIO_MSG_TYPE_SET_PIN_VALUE: + snprintf(buf + n, sizeof(buf) - n, "/value"); + ret = cnxk_gpio_write_attr_int(buf, !!*(int *)msg->data); + break; + case CNXK_GPIO_MSG_TYPE_SET_PIN_EDGE: + snprintf(buf + n, sizeof(buf) - n, "/edge"); + edge = *(enum cnxk_gpio_pin_edge *)msg->data; + ret = cnxk_gpio_write_attr(buf, cnxk_gpio_edge_to_name(edge)); + break; + case CNXK_GPIO_MSG_TYPE_SET_PIN_DIR: + snprintf(buf + n, sizeof(buf) - n, "/direction"); + dir = *(enum cnxk_gpio_pin_dir *)msg->data; + ret = cnxk_gpio_write_attr(buf, cnxk_gpio_dir_to_name(dir)); + break; + case CNXK_GPIO_MSG_TYPE_SET_PIN_ACTIVE_LOW: + snprintf(buf + n, sizeof(buf) - n, "/active_low"); + val = *(int *)msg->data; + ret = cnxk_gpio_write_attr_int(buf, val); + break; + case CNXK_GPIO_MSG_TYPE_GET_PIN_VALUE: + snprintf(buf + n, sizeof(buf) - n, "/value"); + ret = cnxk_gpio_read_attr_int(buf, &val); + if (ret) + break; + + rsp = rte_zmalloc(NULL, sizeof(int), 0); + if (!rsp) + return -ENOMEM; + + *(int *)rsp = val; + break; + case CNXK_GPIO_MSG_TYPE_GET_PIN_EDGE: + snprintf(buf + n, sizeof(buf) - n, "/edge"); + ret = cnxk_gpio_read_attr(buf, buf); + if (ret) + break; + + rsp = rte_zmalloc(NULL, sizeof(enum cnxk_gpio_pin_edge), 0); + if (!rsp) + return -ENOMEM; + + *(enum cnxk_gpio_pin_edge *)rsp = cnxk_gpio_name_to_edge(buf); + break; + case CNXK_GPIO_MSG_TYPE_GET_PIN_DIR: + snprintf(buf + n, sizeof(buf) - n, "/direction"); + ret = cnxk_gpio_read_attr(buf, buf); + if (ret) + break; + + rsp = rte_zmalloc(NULL, sizeof(enum cnxk_gpio_pin_dir), 0); + if (!rsp) + return -ENOMEM; + + *(enum cnxk_gpio_pin_dir *)rsp = cnxk_gpio_name_to_dir(buf); + break; + case CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW: + snprintf(buf + n, sizeof(buf) - n, "/active_low"); + ret = cnxk_gpio_read_attr_int(buf, &val); + if (ret) + break; + + rsp = rte_zmalloc(NULL, sizeof(int), 0); + if (!rsp) + return -ENOMEM; + + *(int *)rsp = val; + break; default: return -EINVAL; } diff --git a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h index c71065e10c..7c3dc225ca 100644 --- a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h +++ b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h @@ -5,6 +5,10 @@ #ifndef _RTE_PMD_CNXK_GPIO_H_ #define _RTE_PMD_CNXK_GPIO_H_ +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_rawdev.h> + /** * @file rte_pmd_cnxk_gpio.h * @@ -20,8 +24,46 @@ extern "C" { /** Available message types */ enum cnxk_gpio_msg_type { - /** Invalid message type */ - CNXK_GPIO_MSG_TYPE_INVALID, + /** Type used to set output value */ + CNXK_GPIO_MSG_TYPE_SET_PIN_VALUE, + /** Type used to set edge */ + CNXK_GPIO_MSG_TYPE_SET_PIN_EDGE, + /** Type used to set direction */ + CNXK_GPIO_MSG_TYPE_SET_PIN_DIR, + /** Type used to set inverted logic */ + CNXK_GPIO_MSG_TYPE_SET_PIN_ACTIVE_LOW, + /** Type used to read value */ + CNXK_GPIO_MSG_TYPE_GET_PIN_VALUE, + /** Type used to read edge */ + CNXK_GPIO_MSG_TYPE_GET_PIN_EDGE, + /** Type used to read direction */ + CNXK_GPIO_MSG_TYPE_GET_PIN_DIR, + /** Type used to read inverted logic state */ + CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW, +}; + +/** Available edges */ +enum cnxk_gpio_pin_edge { + /** Set edge to none */ + CNXK_GPIO_PIN_EDGE_NONE, + /** Set edge to falling */ + CNXK_GPIO_PIN_EDGE_FALLING, + /** Set edge to rising */ + CNXK_GPIO_PIN_EDGE_RISING, + /** Set edge to both rising and falling */ + CNXK_GPIO_PIN_EDGE_BOTH, +}; + +/** Available directions */ +enum cnxk_gpio_pin_dir { + /** Set direction to input */ + CNXK_GPIO_PIN_DIR_IN, + /** Set direction to output */ + CNXK_GPIO_PIN_DIR_OUT, + /** Set direction to output and value to 1 */ + CNXK_GPIO_PIN_DIR_HIGH, + /* Set direction to output and value to 0 */ + CNXK_GPIO_PIN_DIR_LOW, }; struct cnxk_gpio_msg { @@ -31,6 +73,239 @@ struct cnxk_gpio_msg { void *data; }; +/** @internal helper routine for enqueuing/dequeuing messages */ +static __rte_always_inline int +__rte_pmd_gpio_enq_deq(uint16_t dev_id, int gpio, void *req, void *rsp, + size_t rsp_size) +{ + struct rte_rawdev_buf *bufs[1]; + struct rte_rawdev_buf buf; + void *q; + int ret; + + q = (void *)(size_t)gpio; + buf.buf_addr = req; + bufs[0] = &buf; + + ret = rte_rawdev_enqueue_buffers(dev_id, bufs, RTE_DIM(bufs), q); + if (ret < 0) + return ret; + if (ret != RTE_DIM(bufs)) + return -EIO; + + if (!rsp) + return 0; + + ret = rte_rawdev_dequeue_buffers(dev_id, bufs, RTE_DIM(bufs), q); + if (ret < 0) + return ret; + if (ret != RTE_DIM(bufs)) + return -EIO; + + rte_memcpy(rsp, buf.buf_addr, rsp_size); + rte_free(buf.buf_addr); + + return 0; +} + +/** + * Set output to specific value + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param val + * Value output will be set to. 0 represents low state while + * 1 high state + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_set_pin_value(uint16_t dev_id, int gpio, int val) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_SET_PIN_VALUE, + .data = &val, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Select signal edge that triggers interrupt + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param edge + * Signal edge that triggers interrupt + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_set_pin_edge(uint16_t dev_id, int gpio, + enum cnxk_gpio_pin_edge edge) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_SET_PIN_EDGE, + .data = &edge + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Configure GPIO as input or output + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param dir + * Direction of the GPIO + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_set_pin_dir(uint16_t dev_id, int gpio, enum cnxk_gpio_pin_dir dir) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_SET_PIN_DIR, + .data = &dir, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Enable or disable inverted logic + * + * If GPIO is configured as output then writing 1 or 0 will result in setting + * output to respectively low or high + * + * If GPIO is configured as input then logic inversion applies to edges. Both + * current and future settings are affected + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param val + * 0 to disable, 1 to enable inverted logic + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_set_pin_active_low(uint16_t dev_id, int gpio, int val) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_SET_PIN_ACTIVE_LOW, + .data = &val, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Read GPIO value + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param val + * Where to store read logical signal value i.e 0 or 1 + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_get_pin_value(uint16_t dev_id, int gpio, int *val) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_GET_PIN_VALUE, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, val, sizeof(*val)); +} + +/** + * Read GPIO edge + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param edge + * Where to store edge + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_get_pin_edge(uint16_t dev_id, int gpio, + enum cnxk_gpio_pin_edge *edge) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_GET_PIN_EDGE, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, edge, sizeof(*edge)); +} + +/** + * Read GPIO direction + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param dir + * Where to store direction + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_get_pin_dir(uint16_t dev_id, int gpio, enum cnxk_gpio_pin_dir *dir) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_GET_PIN_DIR, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, dir, sizeof(*dir)); +} + +/** + * Read whether GPIO is active low + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param val + * Where to store active low state + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_get_pin_active_low(uint16_t dev_id, int gpio, int *val) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW, + .data = &val, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, val, sizeof(*val)); +} + #ifdef __cplusplus } #endif -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v4 09/11] raw/cnxk_gpio: support custom irq handlers 2022-01-05 14:00 ` [PATCH v4 00/11] Add cnxk_gpio Tomasz Duszynski ` (7 preceding siblings ...) 2022-01-05 14:00 ` [PATCH v4 08/11] raw/cnxk_gpio: support standard GPIO operations Tomasz Duszynski @ 2022-01-05 14:00 ` Tomasz Duszynski 2022-01-05 14:00 ` [PATCH v4 10/11] raw/cnxk_gpio: support selftest Tomasz Duszynski ` (3 subsequent siblings) 12 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-01-05 14:00 UTC (permalink / raw) To: dev; +Cc: thomas, jerinj, Tomasz Duszynski Add support for custom interrupt handlers. Custom interrupt handlers bypass kernel completely and are meant for fast and low latency access to GPIO state. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- doc/guides/rawdevs/cnxk_gpio.rst | 21 +++ drivers/raw/cnxk_gpio/cnxk_gpio.c | 37 ++++ drivers/raw/cnxk_gpio/cnxk_gpio.h | 8 + drivers/raw/cnxk_gpio/cnxk_gpio_irq.c | 216 ++++++++++++++++++++++ drivers/raw/cnxk_gpio/meson.build | 1 + drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h | 116 ++++++++++++ 6 files changed, 399 insertions(+) create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio_irq.c diff --git a/doc/guides/rawdevs/cnxk_gpio.rst b/doc/guides/rawdevs/cnxk_gpio.rst index f6c3c942c5..ad93ec0d44 100644 --- a/doc/guides/rawdevs/cnxk_gpio.rst +++ b/doc/guides/rawdevs/cnxk_gpio.rst @@ -161,3 +161,24 @@ Payload contains an integer set to 0 or 1. The latter means inverted logic is turned on. Consider using ``rte_pmd_gpio_get_pin_active_low()`` wrapper. + +Request interrupt +~~~~~~~~~~~~~~~~~ + +Message is used to install custom interrupt handler. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_REGISTER_IRQ``. + +Payload needs to be set to ``struct cnxk_gpio_irq`` which describes interrupt +being requested. + +Consider using ``rte_pmd_gpio_register_gpio()`` wrapper. + +Free interrupt +~~~~~~~~~~~~~~ + +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. diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index e24d8c1b6e..b30427c01c 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -339,6 +339,28 @@ cnxk_gpio_name_to_dir(const char *name) return cnxk_gpio_dir_name[i].dir; } +static int +cnxk_gpio_register_irq(struct cnxk_gpio *gpio, struct cnxk_gpio_irq *irq) +{ + int ret; + + ret = cnxk_gpio_irq_request(gpio->num - gpio->gpiochip->base, irq->cpu); + if (ret) + return ret; + + gpio->handler = irq->handler; + gpio->data = irq->data; + gpio->cpu = irq->cpu; + + return 0; +} + +static int +cnxk_gpio_unregister_irq(struct cnxk_gpio *gpio) +{ + return cnxk_gpio_irq_free(gpio->num - gpio->gpiochip->base); +} + static int cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf) { @@ -420,6 +442,13 @@ cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf) *(int *)rsp = val; break; + case CNXK_GPIO_MSG_TYPE_REGISTER_IRQ: + ret = cnxk_gpio_register_irq(gpio, + (struct cnxk_gpio_irq *)msg->data); + break; + case CNXK_GPIO_MSG_TYPE_UNREGISTER_IRQ: + ret = cnxk_gpio_unregister_irq(gpio); + break; default: return -EINVAL; } @@ -523,6 +552,10 @@ cnxk_gpio_probe(struct rte_vdev_device *dev) if (ret) goto out; + ret = cnxk_gpio_irq_init(gpiochip); + if (ret) + goto out; + /* read gpio base */ snprintf(buf, sizeof(buf), "%s/gpiochip%d/base", CNXK_GPIO_CLASS_PATH, gpiochip->num); @@ -581,10 +614,14 @@ cnxk_gpio_remove(struct rte_vdev_device *dev) if (!gpio) continue; + if (gpio->handler) + cnxk_gpio_unregister_irq(gpio); + cnxk_gpio_queue_release(rawdev, gpio->num); } rte_free(gpiochip->gpios); + cnxk_gpio_irq_fini(); rte_rawdev_pmd_release(rawdev); return 0; diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.h b/drivers/raw/cnxk_gpio/cnxk_gpio.h index 6b54ebe6e6..c052ca5735 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.h +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.h @@ -11,6 +11,9 @@ struct cnxk_gpio { struct cnxk_gpiochip *gpiochip; void *rsp; int num; + void (*handler)(int gpio, void *data); + void *data; + int cpu; }; struct cnxk_gpiochip { @@ -20,4 +23,9 @@ struct cnxk_gpiochip { struct cnxk_gpio **gpios; }; +int cnxk_gpio_irq_init(struct cnxk_gpiochip *gpiochip); +void cnxk_gpio_irq_fini(void); +int cnxk_gpio_irq_request(int gpio, int cpu); +int cnxk_gpio_irq_free(int gpio); + #endif /* _CNXK_GPIO_H_ */ diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio_irq.c b/drivers/raw/cnxk_gpio/cnxk_gpio_irq.c new file mode 100644 index 0000000000..2fa8e69899 --- /dev/null +++ b/drivers/raw/cnxk_gpio/cnxk_gpio_irq.c @@ -0,0 +1,216 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2021 Marvell. + */ + +#include <fcntl.h> +#include <pthread.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/queue.h> +#include <unistd.h> + +#include <rte_rawdev_pmd.h> + +#include <roc_api.h> + +#include "cnxk_gpio.h" + +#define OTX_IOC_MAGIC 0xF2 +#define OTX_IOC_SET_GPIO_HANDLER \ + _IOW(OTX_IOC_MAGIC, 1, struct otx_gpio_usr_data) +#define OTX_IOC_CLR_GPIO_HANDLER \ + _IO(OTX_IOC_MAGIC, 2) + +struct otx_gpio_usr_data { + uint64_t isr_base; + uint64_t sp; + uint64_t cpu; + uint64_t gpio_num; +}; + +struct cnxk_gpio_irq_stack { + LIST_ENTRY(cnxk_gpio_irq_stack) next; + void *sp_buffer; + int cpu; + int inuse; +}; + +struct cnxk_gpio_irqchip { + int fd; + /* serialize access to this struct */ + pthread_mutex_t lock; + LIST_HEAD(, cnxk_gpio_irq_stack) stacks; + + struct cnxk_gpiochip *gpiochip; +}; + +static struct cnxk_gpio_irqchip *irqchip; + +static void +cnxk_gpio_irq_stack_free(int cpu) +{ + struct cnxk_gpio_irq_stack *stack; + + LIST_FOREACH(stack, &irqchip->stacks, next) { + if (stack->cpu == cpu) + break; + } + + if (!stack) + return; + + if (stack->inuse) + stack->inuse--; + + if (stack->inuse == 0) { + LIST_REMOVE(stack, next); + rte_free(stack->sp_buffer); + rte_free(stack); + } +} + +static void * +cnxk_gpio_irq_stack_alloc(int cpu) +{ +#define ARM_STACK_ALIGNMENT (2 * sizeof(void *)) +#define IRQ_STACK_SIZE 0x200000 + + struct cnxk_gpio_irq_stack *stack; + + LIST_FOREACH(stack, &irqchip->stacks, next) { + if (stack->cpu == cpu) + break; + } + + if (stack) { + stack->inuse++; + return (char *)stack->sp_buffer + IRQ_STACK_SIZE; + } + + stack = rte_malloc(NULL, sizeof(*stack), 0); + if (!stack) + return NULL; + + stack->sp_buffer = + rte_zmalloc(NULL, IRQ_STACK_SIZE * 2, ARM_STACK_ALIGNMENT); + if (!stack->sp_buffer) { + rte_free(stack); + return NULL; + } + + stack->cpu = cpu; + stack->inuse = 1; + LIST_INSERT_HEAD(&irqchip->stacks, stack, next); + + return (char *)stack->sp_buffer + IRQ_STACK_SIZE; +} + +static void +cnxk_gpio_irq_handler(int gpio_num) +{ + struct cnxk_gpiochip *gpiochip = irqchip->gpiochip; + struct cnxk_gpio *gpio; + + if (gpio_num >= gpiochip->num_gpios) + goto out; + + gpio = gpiochip->gpios[gpio_num]; + if (likely(gpio->handler)) + gpio->handler(gpio_num, gpio->data); + +out: + roc_atf_ret(); +} + +int +cnxk_gpio_irq_init(struct cnxk_gpiochip *gpiochip) +{ + if (irqchip) + return 0; + + irqchip = rte_zmalloc(NULL, sizeof(*irqchip), 0); + if (!irqchip) + return -ENOMEM; + + irqchip->fd = open("/dev/otx-gpio-ctr", O_RDWR | O_SYNC); + if (irqchip->fd < 0) { + rte_free(irqchip); + return -errno; + } + + pthread_mutex_init(&irqchip->lock, NULL); + LIST_INIT(&irqchip->stacks); + irqchip->gpiochip = gpiochip; + + return 0; +} + +void +cnxk_gpio_irq_fini(void) +{ + if (!irqchip) + return; + + close(irqchip->fd); + rte_free(irqchip); + irqchip = NULL; +} + +int +cnxk_gpio_irq_request(int gpio, int cpu) +{ + struct otx_gpio_usr_data data; + void *sp; + int ret; + + pthread_mutex_lock(&irqchip->lock); + + sp = cnxk_gpio_irq_stack_alloc(cpu); + if (!sp) { + ret = -ENOMEM; + goto out_unlock; + } + + data.isr_base = (uint64_t)cnxk_gpio_irq_handler; + data.sp = (uint64_t)sp; + data.cpu = (uint64_t)cpu; + data.gpio_num = (uint64_t)gpio; + + mlockall(MCL_CURRENT | MCL_FUTURE); + ret = ioctl(irqchip->fd, OTX_IOC_SET_GPIO_HANDLER, &data); + if (ret) { + ret = -errno; + goto out_free_stack; + } + + pthread_mutex_unlock(&irqchip->lock); + + return 0; + +out_free_stack: + cnxk_gpio_irq_stack_free(cpu); +out_unlock: + pthread_mutex_unlock(&irqchip->lock); + + return ret; +} + +int +cnxk_gpio_irq_free(int gpio) +{ + int ret; + + pthread_mutex_lock(&irqchip->lock); + + ret = ioctl(irqchip->fd, OTX_IOC_CLR_GPIO_HANDLER, gpio); + if (ret) { + pthread_mutex_unlock(&irqchip->lock); + return -errno; + } + + cnxk_gpio_irq_stack_free(irqchip->gpiochip->gpios[gpio]->cpu); + + pthread_mutex_unlock(&irqchip->lock); + + return 0; +} diff --git a/drivers/raw/cnxk_gpio/meson.build b/drivers/raw/cnxk_gpio/meson.build index 3fbfdd838c..9b55f029c7 100644 --- a/drivers/raw/cnxk_gpio/meson.build +++ b/drivers/raw/cnxk_gpio/meson.build @@ -5,5 +5,6 @@ deps += ['bus_vdev', 'common_cnxk', 'rawdev', 'kvargs'] sources = files( 'cnxk_gpio.c', + 'cnxk_gpio_irq.c', ) headers = files('rte_pmd_cnxk_gpio.h') diff --git a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h index 7c3dc225ca..e3096dc14f 100644 --- a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h +++ b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h @@ -40,6 +40,10 @@ enum cnxk_gpio_msg_type { CNXK_GPIO_MSG_TYPE_GET_PIN_DIR, /** Type used to read inverted logic state */ CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW, + /** Type used to register interrupt handler */ + CNXK_GPIO_MSG_TYPE_REGISTER_IRQ, + /** Type used to remove interrupt handler */ + CNXK_GPIO_MSG_TYPE_UNREGISTER_IRQ, }; /** Available edges */ @@ -66,6 +70,25 @@ enum cnxk_gpio_pin_dir { CNXK_GPIO_PIN_DIR_LOW, }; +/** + * GPIO interrupt handler + * + * @param gpio + * Zero-based GPIO number + * @param data + * Cookie passed to interrupt handler + */ +typedef void (*cnxk_gpio_irq_handler_t)(int gpio, void *data); + +struct cnxk_gpio_irq { + /** Interrupt handler */ + cnxk_gpio_irq_handler_t handler; + /** User data passed to irq handler */ + void *data; + /** CPU which will run irq handler */ + int cpu; +}; + struct cnxk_gpio_msg { /** Message type */ enum cnxk_gpio_msg_type type; @@ -306,6 +329,99 @@ rte_pmd_gpio_get_pin_active_low(uint16_t dev_id, int gpio, int *val) return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, val, sizeof(*val)); } +/** + * Attach interrupt handler to GPIO + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param cpu + * CPU which will be handling interrupt + * @param handler + * Interrupt handler to be executed + * @param data + * Data to be passed to interrupt handler + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_register_irq(uint16_t dev_id, int gpio, int cpu, + cnxk_gpio_irq_handler_t handler, void *data) +{ + struct cnxk_gpio_irq irq = { + .handler = handler, + .data = data, + .cpu = cpu, + }; + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_REGISTER_IRQ, + .data = &irq, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Detach interrupt handler from GPIO + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_unregister_irq(uint16_t dev_id, int gpio) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_UNREGISTER_IRQ, + .data = &gpio, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Enable interrupt + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param edge + * Edge that should trigger interrupt + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_enable_interrupt(uint16_t dev_id, int gpio, + enum cnxk_gpio_pin_edge edge) +{ + return rte_pmd_gpio_set_pin_edge(dev_id, gpio, edge); +} + +/** + * Disable interrupt + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_disable_interrupt(uint16_t dev_id, int gpio) +{ + return rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_NONE); +} + #ifdef __cplusplus } #endif -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v4 10/11] raw/cnxk_gpio: support selftest 2022-01-05 14:00 ` [PATCH v4 00/11] Add cnxk_gpio Tomasz Duszynski ` (8 preceding siblings ...) 2022-01-05 14:00 ` [PATCH v4 09/11] raw/cnxk_gpio: support custom irq handlers Tomasz Duszynski @ 2022-01-05 14:00 ` Tomasz Duszynski 2022-01-05 14:00 ` [PATCH v4 11/11] raw/cnxk_gpio: add option to allow using subset of GPIOs Tomasz Duszynski ` (2 subsequent siblings) 12 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-01-05 14:00 UTC (permalink / raw) To: dev; +Cc: thomas, jerinj, Tomasz Duszynski Add support for performing selftest. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- 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=<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. diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index b30427c01c..16a886b693 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -518,6 +518,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 <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; +} 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') -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v4 11/11] raw/cnxk_gpio: add option to allow using subset of GPIOs 2022-01-05 14:00 ` [PATCH v4 00/11] Add cnxk_gpio Tomasz Duszynski ` (9 preceding siblings ...) 2022-01-05 14:00 ` [PATCH v4 10/11] raw/cnxk_gpio: support selftest Tomasz Duszynski @ 2022-01-05 14:00 ` Tomasz Duszynski 2022-01-06 9:43 ` [PATCH v4 00/11] Add cnxk_gpio Jerin Jacob 2022-01-18 13:24 ` [PATCH v5 " Tomasz Duszynski 12 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-01-05 14:00 UTC (permalink / raw) To: dev; +Cc: thomas, jerinj, Tomasz Duszynski Add PMD parameter that allows one to select only subset of available GPIOs. This might be useful in cases where some GPIOs are already reserved yet still available for userspace access but particular app should not touch them. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> Reviewed-by: Jerin Jacob Kollanukkaran <jerinj@marvell.com> --- doc/guides/rawdevs/cnxk_gpio.rst | 5 + drivers/raw/cnxk_gpio/cnxk_gpio.c | 173 +++++++++++++++++---- drivers/raw/cnxk_gpio/cnxk_gpio.h | 2 + drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c | 20 +-- drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h | 8 + 5 files changed, 170 insertions(+), 38 deletions(-) diff --git a/doc/guides/rawdevs/cnxk_gpio.rst b/doc/guides/rawdevs/cnxk_gpio.rst index c03a5b937c..adff535a77 100644 --- a/doc/guides/rawdevs/cnxk_gpio.rst +++ b/doc/guides/rawdevs/cnxk_gpio.rst @@ -57,6 +57,11 @@ former returns number GPIOs available in the system irrespective of GPIOs being controllable or not. Thus it is user responsibility to pick the proper ones. The latter call simply returns queue capacity. +In order to allow using only subset of available GPIOs `allowlist` PMD param may +be used. For example passing `--vdev=cnxk_gpio,gpiochip=448,allowlist=[0,1,2,3]` +to EAL will deny using all GPIOs except those specified explicitly in the +`allowlist`. + Respective queue needs to be configured with ``rte_rawdev_queue_setup()``. This call barely exports GPIO to userspace. diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 16a886b693..0951899a45 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -22,9 +22,13 @@ static const char *const cnxk_gpio_args[] = { #define CNXK_GPIO_ARG_GPIOCHIP "gpiochip" CNXK_GPIO_ARG_GPIOCHIP, +#define CNXK_GPIO_ARG_ALLOWLIST "allowlist" + CNXK_GPIO_ARG_ALLOWLIST, NULL }; +static char *allowlist; + static void cnxk_gpio_format_name(char *name, size_t len) { @@ -73,13 +77,23 @@ cnxk_gpio_parse_arg_gpiochip(const char *key __rte_unused, const char *value, } static int -cnxk_gpio_parse_args(struct cnxk_gpiochip *gpiochip, - struct rte_devargs *devargs) +cnxk_gpio_parse_arg_allowlist(const char *key __rte_unused, const char *value, + void *extra_args __rte_unused) +{ + allowlist = strdup(value); + if (!allowlist) + return -ENOMEM; + + return 0; +} + +static int +cnxk_gpio_parse_args(struct cnxk_gpiochip *gpiochip, const char *args) { struct rte_kvargs *kvlist; int ret; - kvlist = rte_kvargs_parse(devargs->args, cnxk_gpio_args); + kvlist = rte_kvargs_parse(args, cnxk_gpio_args); if (!kvlist) return 0; @@ -92,6 +106,14 @@ cnxk_gpio_parse_args(struct cnxk_gpiochip *gpiochip, goto out; } + ret = rte_kvargs_count(kvlist, CNXK_GPIO_ARG_ALLOWLIST); + if (ret == 1) { + ret = rte_kvargs_process(kvlist, CNXK_GPIO_ARG_ALLOWLIST, + cnxk_gpio_parse_arg_allowlist, NULL); + if (ret) + goto out; + } + ret = 0; out: rte_kvargs_free(kvlist); @@ -99,6 +121,60 @@ cnxk_gpio_parse_args(struct cnxk_gpiochip *gpiochip, return ret; } +static int +cnxk_gpio_parse_allowlist(struct cnxk_gpiochip *gpiochip) +{ + int i, ret, val, queue = 0; + char *token; + int *list; + + list = rte_calloc(NULL, gpiochip->num_gpios, sizeof(*list), 0); + if (!list) + return -ENOMEM; + + /* replace brackets with something meaningless for strtol() */ + allowlist[0] = ' '; + allowlist[strlen(allowlist) - 1] = ' '; + + /* quiesce -Wcast-qual */ + token = strtok((char *)(uintptr_t)allowlist, ","); + do { + errno = 0; + val = strtol(token, NULL, 10); + if (errno) { + RTE_LOG(ERR, PMD, "failed to parse %s\n", token); + ret = -errno; + goto out; + } + + if (val < 0 || val >= gpiochip->num_gpios) { + RTE_LOG(ERR, PMD, "gpio%d out of 0-%d range\n", val, + gpiochip->num_gpios - 1); + ret = -EINVAL; + goto out; + } + + for (i = 0; i < queue; i++) { + if (list[i] != val) + continue; + + RTE_LOG(WARNING, PMD, "gpio%d already allowed\n", val); + break; + } + if (i == queue) + list[queue++] = val; + } while ((token = strtok(NULL, ","))); + + gpiochip->allowlist = list; + gpiochip->num_queues = queue; + + return 0; +out: + rte_free(list); + + return ret; +} + static int cnxk_gpio_read_attr(char *attr, char *val) { @@ -175,13 +251,24 @@ cnxk_gpio_write_attr_int(const char *attr, int val) return cnxk_gpio_write_attr(attr, buf); } +static bool +cnxk_gpio_queue_valid(struct cnxk_gpiochip *gpiochip, uint16_t queue) +{ + return queue < gpiochip->num_queues; +} + +static int +cnxk_queue_to_gpio(struct cnxk_gpiochip *gpiochip, uint16_t queue) +{ + return gpiochip->allowlist ? gpiochip->allowlist[queue] : queue; +} + static struct cnxk_gpio * cnxk_gpio_lookup(struct cnxk_gpiochip *gpiochip, uint16_t queue) { - if (queue >= gpiochip->num_gpios) - return NULL; + int gpio = cnxk_queue_to_gpio(gpiochip, queue); - return gpiochip->gpios[queue]; + return gpiochip->gpios[gpio]; } static int @@ -191,11 +278,14 @@ cnxk_gpio_queue_setup(struct rte_rawdev *dev, uint16_t queue_id, struct cnxk_gpiochip *gpiochip = dev->dev_private; char buf[CNXK_GPIO_BUFSZ]; struct cnxk_gpio *gpio; - int ret; + int num, ret; RTE_SET_USED(queue_conf); RTE_SET_USED(queue_conf_size); + if (!cnxk_gpio_queue_valid(gpiochip, queue_id)) + return -EINVAL; + gpio = cnxk_gpio_lookup(gpiochip, queue_id); if (gpio) return -EEXIST; @@ -203,7 +293,9 @@ cnxk_gpio_queue_setup(struct rte_rawdev *dev, uint16_t queue_id, gpio = rte_zmalloc(NULL, sizeof(*gpio), 0); if (!gpio) return -ENOMEM; - gpio->num = queue_id + gpiochip->base; + + num = cnxk_queue_to_gpio(gpiochip, queue_id); + gpio->num = num + gpiochip->base; gpio->gpiochip = gpiochip; snprintf(buf, sizeof(buf), "%s/export", CNXK_GPIO_CLASS_PATH); @@ -213,7 +305,7 @@ cnxk_gpio_queue_setup(struct rte_rawdev *dev, uint16_t queue_id, return ret; } - gpiochip->gpios[queue_id] = gpio; + gpiochip->gpios[num] = gpio; return 0; } @@ -224,18 +316,22 @@ cnxk_gpio_queue_release(struct rte_rawdev *dev, uint16_t queue_id) struct cnxk_gpiochip *gpiochip = dev->dev_private; char buf[CNXK_GPIO_BUFSZ]; struct cnxk_gpio *gpio; - int ret; + int num, ret; + + if (!cnxk_gpio_queue_valid(gpiochip, queue_id)) + return -EINVAL; gpio = cnxk_gpio_lookup(gpiochip, queue_id); if (!gpio) return -ENODEV; snprintf(buf, sizeof(buf), "%s/unexport", CNXK_GPIO_CLASS_PATH); - ret = cnxk_gpio_write_attr_int(buf, gpiochip->base + queue_id); + ret = cnxk_gpio_write_attr_int(buf, gpio->num); if (ret) return ret; - gpiochip->gpios[queue_id] = NULL; + num = cnxk_queue_to_gpio(gpiochip, queue_id); + gpiochip->gpios[num] = NULL; rte_free(gpio); return 0; @@ -245,16 +341,17 @@ static int cnxk_gpio_queue_def_conf(struct rte_rawdev *dev, uint16_t queue_id, rte_rawdev_obj_t queue_conf, size_t queue_conf_size) { - unsigned int *conf; + struct cnxk_gpiochip *gpiochip = dev->dev_private; + struct cnxk_gpio_queue_conf *conf = queue_conf; - RTE_SET_USED(dev); - RTE_SET_USED(queue_id); + if (!cnxk_gpio_queue_valid(gpiochip, queue_id)) + return -EINVAL; if (queue_conf_size != sizeof(*conf)) return -EINVAL; - conf = (unsigned int *)queue_conf; - *conf = 1; + conf->size = 1; + conf->gpio = cnxk_queue_to_gpio(gpiochip, queue_id); return 0; } @@ -264,7 +361,7 @@ cnxk_gpio_queue_count(struct rte_rawdev *dev) { struct cnxk_gpiochip *gpiochip = dev->dev_private; - return gpiochip->num_gpios; + return gpiochip->num_queues; } static const struct { @@ -463,21 +560,27 @@ cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf) return ret; } +static bool +cnxk_gpio_valid(struct cnxk_gpiochip *gpiochip, int gpio) +{ + return gpio < gpiochip->num_gpios && gpiochip->gpios[gpio]; +} + static int cnxk_gpio_enqueue_bufs(struct rte_rawdev *dev, struct rte_rawdev_buf **buffers, unsigned int count, rte_rawdev_obj_t context) { struct cnxk_gpiochip *gpiochip = dev->dev_private; - unsigned int queue = (size_t)context; + unsigned int gpio_num = (size_t)context; struct cnxk_gpio *gpio; int ret; if (count == 0) return 0; - gpio = cnxk_gpio_lookup(gpiochip, queue); - if (!gpio) - return -ENODEV; + if (!cnxk_gpio_valid(gpiochip, gpio_num)) + return -EINVAL; + gpio = gpiochip->gpios[gpio_num]; ret = cnxk_gpio_process_buf(gpio, buffers[0]); if (ret) @@ -491,15 +594,15 @@ cnxk_gpio_dequeue_bufs(struct rte_rawdev *dev, struct rte_rawdev_buf **buffers, unsigned int count, rte_rawdev_obj_t context) { struct cnxk_gpiochip *gpiochip = dev->dev_private; - unsigned int queue = (size_t)context; + unsigned int gpio_num = (size_t)context; struct cnxk_gpio *gpio; if (count == 0) return 0; - gpio = cnxk_gpio_lookup(gpiochip, queue); - if (!gpio) - return -ENODEV; + if (!cnxk_gpio_valid(gpiochip, gpio_num)) + return -EINVAL; + gpio = gpiochip->gpios[gpio_num]; if (gpio->rsp) { buffers[0]->buf_addr = gpio->rsp; @@ -549,7 +652,7 @@ cnxk_gpio_probe(struct rte_vdev_device *dev) cnxk_gpio_set_defaults(gpiochip); /* defaults may be overwritten by this call */ - ret = cnxk_gpio_parse_args(gpiochip, dev->device.devargs); + ret = cnxk_gpio_parse_args(gpiochip, rte_vdev_device_args(dev)); if (ret) goto out; @@ -574,6 +677,15 @@ cnxk_gpio_probe(struct rte_vdev_device *dev) RTE_LOG(ERR, PMD, "failed to read %s", buf); goto out; } + gpiochip->num_queues = gpiochip->num_gpios; + + if (allowlist) { + ret = cnxk_gpio_parse_allowlist(gpiochip); + free(allowlist); + allowlist = NULL; + if (ret) + goto out; + } gpiochip->gpios = rte_calloc(NULL, gpiochip->num_gpios, sizeof(struct cnxk_gpio *), 0); @@ -585,6 +697,8 @@ cnxk_gpio_probe(struct rte_vdev_device *dev) return 0; out: + free(allowlist); + rte_free(gpiochip->allowlist); rte_rawdev_pmd_release(rawdev); return ret; @@ -621,6 +735,7 @@ cnxk_gpio_remove(struct rte_vdev_device *dev) cnxk_gpio_queue_release(rawdev, gpio->num); } + rte_free(gpiochip->allowlist); rte_free(gpiochip->gpios); cnxk_gpio_irq_fini(); rte_rawdev_pmd_release(rawdev); @@ -634,4 +749,6 @@ static struct rte_vdev_driver cnxk_gpio_drv = { }; RTE_PMD_REGISTER_VDEV(cnxk_gpio, cnxk_gpio_drv); -RTE_PMD_REGISTER_PARAM_STRING(cnxk_gpio, "gpiochip=<int>"); +RTE_PMD_REGISTER_PARAM_STRING(cnxk_gpio, + "gpiochip=<int> " + "allowlist=<list>"); diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.h b/drivers/raw/cnxk_gpio/cnxk_gpio.h index 1b31b5a486..e62f78a760 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.h +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.h @@ -20,7 +20,9 @@ struct cnxk_gpiochip { int num; int base; int num_gpios; + int num_queues; struct cnxk_gpio **gpios; + int *allowlist; }; int cnxk_gpio_selftest(uint16_t dev_id); diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c b/drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c index 6502902f86..303931fa0f 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c @@ -302,10 +302,11 @@ cnxk_gpio_test_output(uint16_t dev_id, int base, int gpio) int cnxk_gpio_selftest(uint16_t dev_id) { + struct cnxk_gpio_queue_conf conf; struct cnxk_gpiochip *gpiochip; - unsigned int queues, i, size; char buf[CNXK_GPIO_BUFSZ]; struct rte_rawdev *rawdev; + unsigned int queues, i; struct cnxk_gpio *gpio; int ret; @@ -325,10 +326,7 @@ cnxk_gpio_selftest(uint16_t dev_id) 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)); + ret = rte_rawdev_queue_conf_get(dev_id, i, &conf, sizeof(conf)); if (ret) { RTE_LOG(ERR, PMD, "failed to read queue configuration (%d)\n", @@ -336,7 +334,9 @@ cnxk_gpio_selftest(uint16_t dev_id) continue; } - if (size != 1) { + RTE_LOG(INFO, PMD, "testing queue%d (gpio%d)\n", i, conf.gpio); + + if (conf.size != 1) { RTE_LOG(ERR, PMD, "wrong queue size received\n"); continue; } @@ -347,22 +347,22 @@ cnxk_gpio_selftest(uint16_t dev_id) continue; } - gpio = gpiochip->gpios[i]; + gpio = gpiochip->gpios[conf.gpio]; 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); + ret = cnxk_gpio_test_input(dev_id, gpiochip->base, conf.gpio); if (ret) goto release; - ret = cnxk_gpio_test_irq(dev_id, i); + ret = cnxk_gpio_test_irq(dev_id, conf.gpio); if (ret) goto release; - ret = cnxk_gpio_test_output(dev_id, gpiochip->base, i); + ret = cnxk_gpio_test_output(dev_id, gpiochip->base, conf.gpio); if (ret) goto release; diff --git a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h index e3096dc14f..80a37be9c7 100644 --- a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h +++ b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h @@ -22,6 +22,14 @@ extern "C" { #endif +/* Queue default configuration */ +struct cnxk_gpio_queue_conf { + /** Queue size */ + int size; + /** GPIO number as seen by hardware */ + int gpio; +}; + /** Available message types */ enum cnxk_gpio_msg_type { /** Type used to set output value */ -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v4 00/11] Add cnxk_gpio 2022-01-05 14:00 ` [PATCH v4 00/11] Add cnxk_gpio Tomasz Duszynski ` (10 preceding siblings ...) 2022-01-05 14:00 ` [PATCH v4 11/11] raw/cnxk_gpio: add option to allow using subset of GPIOs Tomasz Duszynski @ 2022-01-06 9:43 ` Jerin Jacob 2022-01-18 13:24 ` [PATCH v5 " Tomasz Duszynski 12 siblings, 0 replies; 75+ messages in thread From: Jerin Jacob @ 2022-01-06 9:43 UTC (permalink / raw) To: Tomasz Duszynski; +Cc: dpdk-dev, Thomas Monjalon, Jerin Jacob On Wed, Jan 5, 2022 at 7:30 PM Tomasz Duszynski <tduszynski@marvell.com> wrote: > > This series introduces a new rawdevice PMD which allows > to manage userspace GPIOs and install custom GPIO interrupt > handlers which bypass kernel. This is especially useful for > applications that, besides providing standard dataplane functionality, > want to have fast and low latency access to GPIO pin state. > > It'd be great to have that merged during 22.02 merge window. > > v4: > - free kvargs after parsing arguments > - add support for allowing only subset of available GPIOs > > v3: > - fix meson formatting > - fix cnxk_gpio_process_buf() return value > > v2: > - do not trigger irq by writing to /dev/mem, use ioctl() instead > > Tomasz Duszynski (11): > raw/cnxk_gpio: add GPIO driver skeleton > raw/cnxk_gpio: support reading default queue conf > raw/cnxk_gpio: support reading queue count > raw/cnxk_gpio: support queue setup > raw/cnxk_gpio: support queue release > raw/cnxk_gpio: support enqueuing buffers > raw/cnxk_gpio: support dequeuing buffers > raw/cnxk_gpio: support standard GPIO operations > raw/cnxk_gpio: support custom irq handlers > raw/cnxk_gpio: support selftest > raw/cnxk_gpio: add option to allow using subset of GPIOs > > doc/guides/rawdevs/cnxk_gpio.rst | 200 ++++++ 1) Could you update doc/guides/platform/cnxk.rst file with _table_cnxk_rvu_dpdk_mapping table and "HW Offload Drivers" secion. 2) Please update doc/guides/rel_notes/release_21_11.rst release note. ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v5 00/11] Add cnxk_gpio 2022-01-05 14:00 ` [PATCH v4 00/11] Add cnxk_gpio Tomasz Duszynski ` (11 preceding siblings ...) 2022-01-06 9:43 ` [PATCH v4 00/11] Add cnxk_gpio Jerin Jacob @ 2022-01-18 13:24 ` Tomasz Duszynski 2022-01-18 13:24 ` [PATCH v5 01/11] raw/cnxk_gpio: add GPIO driver skeleton Tomasz Duszynski ` (12 more replies) 12 siblings, 13 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-01-18 13:24 UTC (permalink / raw) To: dev; +Cc: thomas, jerin, Tomasz Duszynski This series introduces a new rawdevice PMD which allows to manage userspace GPIOs and install custom GPIO interrupt handlers which bypass kernel. This is especially useful for applications that, besides providing standard dataplane functionality, want to have fast and low latency access to GPIO pin state. It'd be great to have that merged during 22.03 merge window. v5: - added close callback to make sure PMD resources are freed properly - updated selftest to return early on error - doc fixes v4: - free kvargs after parsing arguments - add support for allowing only subset of available GPIOs v3: - fix meson formatting - fix cnxk_gpio_process_buf() return value v2: - do not trigger irq by writing to /dev/mem, use ioctl() instead Tomasz Duszynski (11): raw/cnxk_gpio: add GPIO driver skeleton raw/cnxk_gpio: support reading default queue conf raw/cnxk_gpio: support reading queue count raw/cnxk_gpio: support queue setup raw/cnxk_gpio: support queue release raw/cnxk_gpio: support enqueuing buffers raw/cnxk_gpio: support dequeuing buffers raw/cnxk_gpio: support standard GPIO operations raw/cnxk_gpio: support custom irq handlers raw/cnxk_gpio: support selftest raw/cnxk_gpio: add option to allow using subset of GPIOs doc/guides/platform/cnxk.rst | 2 + doc/guides/rawdevs/cnxk_gpio.rst | 200 ++++++ doc/guides/rawdevs/index.rst | 1 + doc/guides/rel_notes/release_22_03.rst | 9 + drivers/raw/cnxk_gpio/cnxk_gpio.c | 763 +++++++++++++++++++++ drivers/raw/cnxk_gpio/cnxk_gpio.h | 35 + drivers/raw/cnxk_gpio/cnxk_gpio_irq.c | 216 ++++++ drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c | 393 +++++++++++ drivers/raw/cnxk_gpio/meson.build | 11 + drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h | 437 ++++++++++++ drivers/raw/cnxk_gpio/version.map | 3 + drivers/raw/meson.build | 1 + 12 files changed, 2071 insertions(+) create mode 100644 doc/guides/rawdevs/cnxk_gpio.rst create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio.c create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio.h create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio_irq.c create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c create mode 100644 drivers/raw/cnxk_gpio/meson.build create mode 100644 drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h create mode 100644 drivers/raw/cnxk_gpio/version.map -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v5 01/11] raw/cnxk_gpio: add GPIO driver skeleton 2022-01-18 13:24 ` [PATCH v5 " Tomasz Duszynski @ 2022-01-18 13:24 ` Tomasz Duszynski 2022-01-18 13:24 ` [PATCH v5 02/11] raw/cnxk_gpio: support reading default queue conf Tomasz Duszynski ` (11 subsequent siblings) 12 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-01-18 13:24 UTC (permalink / raw) To: dev; +Cc: thomas, jerin, Tomasz Duszynski Add initial support for PMD that allows to control particular pins form userspace. Moreover PMD allows to attach custom interrupt handlers to controllable GPIOs. Main users of this PMD are dataplain applications requiring fast and low latency access to pin state. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- doc/guides/platform/cnxk.rst | 2 + doc/guides/rawdevs/cnxk_gpio.rst | 65 +++++++ doc/guides/rawdevs/index.rst | 1 + doc/guides/rel_notes/release_22_03.rst | 9 + drivers/raw/cnxk_gpio/cnxk_gpio.c | 248 +++++++++++++++++++++++++ drivers/raw/cnxk_gpio/cnxk_gpio.h | 22 +++ drivers/raw/cnxk_gpio/meson.build | 8 + drivers/raw/cnxk_gpio/version.map | 3 + drivers/raw/meson.build | 1 + 9 files changed, 359 insertions(+) create mode 100644 doc/guides/rawdevs/cnxk_gpio.rst create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio.c create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio.h create mode 100644 drivers/raw/cnxk_gpio/meson.build create mode 100644 drivers/raw/cnxk_gpio/version.map diff --git a/doc/guides/platform/cnxk.rst b/doc/guides/platform/cnxk.rst index 97e38c868c..3dee725ac5 100644 --- a/doc/guides/platform/cnxk.rst +++ b/doc/guides/platform/cnxk.rst @@ -73,6 +73,8 @@ DPDK subsystem. +---+-----+--------------------------------------------------------------+ | 11| BPHY| rte_rawdev | +---+-----+--------------------------------------------------------------+ + | 12| GPIO| rte_rawdev | + +---+-----+--------------------------------------------------------------+ PF0 is called the administrative / admin function (AF) and has exclusive privileges to provision RVU functional block's LFs to each of the PF/VF. diff --git a/doc/guides/rawdevs/cnxk_gpio.rst b/doc/guides/rawdevs/cnxk_gpio.rst new file mode 100644 index 0000000000..868302d07f --- /dev/null +++ b/doc/guides/rawdevs/cnxk_gpio.rst @@ -0,0 +1,65 @@ +.. SPDX-License-Identifier: BSD-3-Clause + Copyright(c) 2021 Marvell. + +Marvell CNXK GPIO Driver +======================== + +CNXK GPIO PMD configures and manages GPIOs available on the system using +standard enqueue/dequeue mechanism offered by raw device abstraction. PMD relies +both on standard sysfs GPIO interface provided by the Linux kernel and GPIO +kernel driver custom interface allowing one to install userspace interrupt +handlers. + +Features +-------- + +Following features are available: + +- export/unexport a GPIO +- read/write specific value from/to exported GPIO +- set GPIO direction +- set GPIO edge that triggers interrupt +- set GPIO active low +- register interrupt handler for specific GPIO + +Requirements +------------ + +PMD relies on modified kernel GPIO driver which exposes ``ioctl()`` interface +for installing interrupt handlers for low latency signal processing. + +Driver is shipped with Marvell SDK. + +Device Setup +------------ + +CNXK GPIO PMD binds to virtual device which gets created by passing +`--vdev=cnxk_gpio,gpiochip=<number>` command line to EAL. `gpiochip` parameter +tells PMD which GPIO controller should be used. Available controllers are +available under `/sys/class/gpio`. For further details on how Linux represents +GPIOs in userspace please refer to +`sysfs.txt <https://www.kernel.org/doc/Documentation/gpio/sysfs.txt>`_. + +If `gpiochip=<number>` was omitted then first gpiochip from the alphabetically +sort list of available gpiochips is used. + +.. code-block:: console + + $ ls /sys/class/gpio + export gpiochip448 unexport + +In above scenario only one GPIO controller is present hence +`--vdev=cnxk_gpio,gpiochip=448` should be passed to EAL. + +Before performing actual data transfer one needs to call +``rte_rawdev_queue_count()`` followed by ``rte_rawdev_queue_conf_get()``. The +former returns number GPIOs available in the system irrespective of GPIOs +being controllable or not. Thus it is user responsibility to pick the proper +ones. The latter call simply returns queue capacity. + +Respective queue needs to be configured with ``rte_rawdev_queue_setup()``. This +call barely exports GPIO to userspace. + +To perform actual data transfer use standard ``rte_rawdev_enqueue_buffers()`` +and ``rte_rawdev_dequeue_buffers()`` APIs. Not all messages produce sensible +responses hence dequeueing is not always necessary. diff --git a/doc/guides/rawdevs/index.rst b/doc/guides/rawdevs/index.rst index b6cf917443..0c02da6e90 100644 --- a/doc/guides/rawdevs/index.rst +++ b/doc/guides/rawdevs/index.rst @@ -12,6 +12,7 @@ application through rawdev API. :numbered: cnxk_bphy + cnxk_gpio dpaa2_cmdif dpaa2_qdma ifpga diff --git a/doc/guides/rel_notes/release_22_03.rst b/doc/guides/rel_notes/release_22_03.rst index 16c66c0641..09039899e8 100644 --- a/doc/guides/rel_notes/release_22_03.rst +++ b/doc/guides/rel_notes/release_22_03.rst @@ -55,6 +55,15 @@ New Features Also, make sure to start the actual text at the margin. ======================================================= +* **Added CNXK GPIO PMD.** + + Added a new rawdevice PMD which allows to manage userspace GPIOs and install + custom GPIO interrupt handlers which bypass kernel. This is especially useful + for applications, that besides providing standard dataplane functionality + want to have fast and low latency access to GPIO pin state. + + See the :doc:`../rawdevs/cnxk_gpio` rawdev guide for more details on this + driver. Removed Items ------------- diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c new file mode 100644 index 0000000000..61069b2185 --- /dev/null +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -0,0 +1,248 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2021 Marvell. + */ + +#include <dirent.h> +#include <string.h> + +#include <rte_bus_vdev.h> +#include <rte_eal.h> +#include <rte_kvargs.h> +#include <rte_lcore.h> +#include <rte_rawdev_pmd.h> + +#include <roc_api.h> + +#include "cnxk_gpio.h" + +#define CNXK_GPIO_BUFSZ 128 +#define CNXK_GPIO_CLASS_PATH "/sys/class/gpio" + +static const char *const cnxk_gpio_args[] = { +#define CNXK_GPIO_ARG_GPIOCHIP "gpiochip" + CNXK_GPIO_ARG_GPIOCHIP, + NULL +}; + +static void +cnxk_gpio_format_name(char *name, size_t len) +{ + snprintf(name, len, "cnxk_gpio"); +} + +static int +cnxk_gpio_filter_gpiochip(const struct dirent *dirent) +{ + const char *pattern = "gpiochip"; + + return !strncmp(dirent->d_name, pattern, strlen(pattern)); +} + +static void +cnxk_gpio_set_defaults(struct cnxk_gpiochip *gpiochip) +{ + struct dirent **namelist; + int n; + + n = scandir(CNXK_GPIO_CLASS_PATH, &namelist, cnxk_gpio_filter_gpiochip, + alphasort); + if (n < 0 || n == 0) + return; + + sscanf(namelist[0]->d_name, "gpiochip%d", &gpiochip->num); + while (n--) + free(namelist[n]); + free(namelist); +} + +static int +cnxk_gpio_parse_arg_gpiochip(const char *key __rte_unused, const char *value, + void *extra_args) +{ + long val; + + errno = 0; + val = strtol(value, NULL, 10); + if (errno) + return -errno; + + *(int *)extra_args = (int)val; + + return 0; +} + +static int +cnxk_gpio_parse_args(struct cnxk_gpiochip *gpiochip, + struct rte_devargs *devargs) +{ + struct rte_kvargs *kvlist; + int ret; + + kvlist = rte_kvargs_parse(devargs->args, cnxk_gpio_args); + if (!kvlist) + return 0; + + ret = rte_kvargs_count(kvlist, CNXK_GPIO_ARG_GPIOCHIP); + if (ret == 1) { + ret = rte_kvargs_process(kvlist, CNXK_GPIO_ARG_GPIOCHIP, + cnxk_gpio_parse_arg_gpiochip, + &gpiochip->num); + if (ret) + goto out; + } + + ret = 0; +out: + rte_kvargs_free(kvlist); + + return ret; +} + +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; +} + +static int +cnxk_gpio_read_attr_int(char *attr, int *val) +{ + char buf[CNXK_GPIO_BUFSZ]; + int ret; + + ret = cnxk_gpio_read_attr(attr, buf); + if (ret) + return ret; + + ret = sscanf(buf, "%d", val); + if (ret < 0) + return -errno; + + return 0; +} + +static int +cnxk_gpio_dev_close(struct rte_rawdev *dev) +{ + RTE_SET_USED(dev); + + return 0; +} + +static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { + .dev_close = cnxk_gpio_dev_close, +}; + +static int +cnxk_gpio_probe(struct rte_vdev_device *dev) +{ + char name[RTE_RAWDEV_NAME_MAX_LEN]; + struct cnxk_gpiochip *gpiochip; + struct rte_rawdev *rawdev; + char buf[CNXK_GPIO_BUFSZ]; + int ret; + + if (rte_eal_process_type() != RTE_PROC_PRIMARY) + return 0; + + cnxk_gpio_format_name(name, sizeof(name)); + rawdev = rte_rawdev_pmd_allocate(name, sizeof(*gpiochip), + rte_socket_id()); + if (!rawdev) { + RTE_LOG(ERR, PMD, "failed to allocate %s rawdev", name); + return -ENOMEM; + } + + rawdev->dev_ops = &cnxk_gpio_rawdev_ops; + rawdev->device = &dev->device; + rawdev->driver_name = dev->device.name; + + gpiochip = rawdev->dev_private; + cnxk_gpio_set_defaults(gpiochip); + + /* defaults may be overwritten by this call */ + ret = cnxk_gpio_parse_args(gpiochip, dev->device.devargs); + if (ret) + goto out; + + /* read gpio base */ + snprintf(buf, sizeof(buf), "%s/gpiochip%d/base", CNXK_GPIO_CLASS_PATH, + gpiochip->num); + ret = cnxk_gpio_read_attr_int(buf, &gpiochip->base); + if (ret) { + RTE_LOG(ERR, PMD, "failed to read %s", buf); + goto out; + } + + /* read number of available gpios */ + snprintf(buf, sizeof(buf), "%s/gpiochip%d/ngpio", CNXK_GPIO_CLASS_PATH, + gpiochip->num); + ret = cnxk_gpio_read_attr_int(buf, &gpiochip->num_gpios); + if (ret) { + RTE_LOG(ERR, PMD, "failed to read %s", buf); + goto out; + } + + gpiochip->gpios = rte_calloc(NULL, gpiochip->num_gpios, + sizeof(struct cnxk_gpio *), 0); + if (!gpiochip->gpios) { + RTE_LOG(ERR, PMD, "failed to allocate gpios memory"); + ret = -ENOMEM; + goto out; + } + + return 0; +out: + rte_rawdev_pmd_release(rawdev); + + return ret; +} + +static int +cnxk_gpio_remove(struct rte_vdev_device *dev) +{ + char name[RTE_RAWDEV_NAME_MAX_LEN]; + struct cnxk_gpiochip *gpiochip; + struct rte_rawdev *rawdev; + + RTE_SET_USED(dev); + + if (rte_eal_process_type() != RTE_PROC_PRIMARY) + return 0; + + cnxk_gpio_format_name(name, sizeof(name)); + rawdev = rte_rawdev_pmd_get_named_dev(name); + if (!rawdev) + return -ENODEV; + + gpiochip = rawdev->dev_private; + rte_free(gpiochip->gpios); + rte_rawdev_pmd_release(rawdev); + + return 0; +} + +static struct rte_vdev_driver cnxk_gpio_drv = { + .probe = cnxk_gpio_probe, + .remove = cnxk_gpio_remove, +}; + +RTE_PMD_REGISTER_VDEV(cnxk_gpio, cnxk_gpio_drv); +RTE_PMD_REGISTER_PARAM_STRING(cnxk_gpio, "gpiochip=<int>"); diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.h b/drivers/raw/cnxk_gpio/cnxk_gpio.h new file mode 100644 index 0000000000..4dae8316ba --- /dev/null +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2021 Marvell. + */ + +#ifndef _CNXK_GPIO_H_ +#define _CNXK_GPIO_H_ + +struct cnxk_gpiochip; + +struct cnxk_gpio { + struct cnxk_gpiochip *gpiochip; + int num; +}; + +struct cnxk_gpiochip { + int num; + int base; + int num_gpios; + struct cnxk_gpio **gpios; +}; + +#endif /* _CNXK_GPIO_H_ */ diff --git a/drivers/raw/cnxk_gpio/meson.build b/drivers/raw/cnxk_gpio/meson.build new file mode 100644 index 0000000000..9a7e716c1e --- /dev/null +++ b/drivers/raw/cnxk_gpio/meson.build @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(C) 2021 Marvell. +# + +deps += ['bus_vdev', 'common_cnxk', 'rawdev', 'kvargs'] +sources = files( + 'cnxk_gpio.c', +) diff --git a/drivers/raw/cnxk_gpio/version.map b/drivers/raw/cnxk_gpio/version.map new file mode 100644 index 0000000000..4a76d1d52d --- /dev/null +++ b/drivers/raw/cnxk_gpio/version.map @@ -0,0 +1,3 @@ +DPDK_21 { + local: *; +}; diff --git a/drivers/raw/meson.build b/drivers/raw/meson.build index 87694a758e..05e7de1bfe 100644 --- a/drivers/raw/meson.build +++ b/drivers/raw/meson.build @@ -7,6 +7,7 @@ endif drivers = [ 'cnxk_bphy', + 'cnxk_gpio', 'dpaa2_cmdif', 'dpaa2_qdma', 'ifpga', -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v5 02/11] raw/cnxk_gpio: support reading default queue conf 2022-01-18 13:24 ` [PATCH v5 " Tomasz Duszynski 2022-01-18 13:24 ` [PATCH v5 01/11] raw/cnxk_gpio: add GPIO driver skeleton Tomasz Duszynski @ 2022-01-18 13:24 ` Tomasz Duszynski 2022-01-18 13:24 ` [PATCH v5 03/11] raw/cnxk_gpio: support reading queue count Tomasz Duszynski ` (10 subsequent siblings) 12 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-01-18 13:24 UTC (permalink / raw) To: dev; +Cc: thomas, jerin, Tomasz Duszynski Add support for reading default queue configuration. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 61069b2185..1f36f6e22c 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -146,8 +146,27 @@ cnxk_gpio_dev_close(struct rte_rawdev *dev) return 0; } +static int +cnxk_gpio_queue_def_conf(struct rte_rawdev *dev, uint16_t queue_id, + rte_rawdev_obj_t queue_conf, size_t queue_conf_size) +{ + unsigned int *conf; + + RTE_SET_USED(dev); + RTE_SET_USED(queue_id); + + if (queue_conf_size != sizeof(*conf)) + return -EINVAL; + + conf = (unsigned int *)queue_conf; + *conf = 1; + + return 0; +} + static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { .dev_close = cnxk_gpio_dev_close, + .queue_def_conf = cnxk_gpio_queue_def_conf, }; static int -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v5 03/11] raw/cnxk_gpio: support reading queue count 2022-01-18 13:24 ` [PATCH v5 " Tomasz Duszynski 2022-01-18 13:24 ` [PATCH v5 01/11] raw/cnxk_gpio: add GPIO driver skeleton Tomasz Duszynski 2022-01-18 13:24 ` [PATCH v5 02/11] raw/cnxk_gpio: support reading default queue conf Tomasz Duszynski @ 2022-01-18 13:24 ` Tomasz Duszynski 2022-01-18 13:24 ` [PATCH v5 04/11] raw/cnxk_gpio: support queue setup Tomasz Duszynski ` (9 subsequent siblings) 12 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-01-18 13:24 UTC (permalink / raw) To: dev; +Cc: thomas, jerin, Tomasz Duszynski Add support for reading number of available queues. Single queue corresponds to GPIO. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 1f36f6e22c..cee75e389a 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -164,9 +164,18 @@ cnxk_gpio_queue_def_conf(struct rte_rawdev *dev, uint16_t queue_id, return 0; } +static uint16_t +cnxk_gpio_queue_count(struct rte_rawdev *dev) +{ + struct cnxk_gpiochip *gpiochip = dev->dev_private; + + return gpiochip->num_gpios; +} + static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { .dev_close = cnxk_gpio_dev_close, .queue_def_conf = cnxk_gpio_queue_def_conf, + .queue_count = cnxk_gpio_queue_count, }; static int -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v5 04/11] raw/cnxk_gpio: support queue setup 2022-01-18 13:24 ` [PATCH v5 " Tomasz Duszynski ` (2 preceding siblings ...) 2022-01-18 13:24 ` [PATCH v5 03/11] raw/cnxk_gpio: support reading queue count Tomasz Duszynski @ 2022-01-18 13:24 ` Tomasz Duszynski 2022-01-18 13:24 ` [PATCH v5 05/11] raw/cnxk_gpio: support queue release Tomasz Duszynski ` (8 subsequent siblings) 12 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-01-18 13:24 UTC (permalink / raw) To: dev; +Cc: thomas, jerin, Tomasz Duszynski Add support for queue setup. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 84 ++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 2 deletions(-) diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index cee75e389a..9baa43187e 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -139,9 +139,80 @@ cnxk_gpio_read_attr_int(char *attr, int *val) } static int -cnxk_gpio_dev_close(struct rte_rawdev *dev) +cnxk_gpio_write_attr(const char *attr, const char *val) { - RTE_SET_USED(dev); + FILE *fp; + int ret; + + if (!val) + return -EINVAL; + + fp = fopen(attr, "w"); + if (!fp) + return -errno; + + ret = fprintf(fp, "%s", val); + if (ret < 0) { + fclose(fp); + return ret; + } + + ret = fclose(fp); + if (ret) + return -errno; + + return 0; +} + +static int +cnxk_gpio_write_attr_int(const char *attr, int val) +{ + char buf[CNXK_GPIO_BUFSZ]; + + snprintf(buf, sizeof(buf), "%d", val); + + return cnxk_gpio_write_attr(attr, buf); +} + +static struct cnxk_gpio * +cnxk_gpio_lookup(struct cnxk_gpiochip *gpiochip, uint16_t queue) +{ + if (queue >= gpiochip->num_gpios) + return NULL; + + return gpiochip->gpios[queue]; +} + +static int +cnxk_gpio_queue_setup(struct rte_rawdev *dev, uint16_t queue_id, + rte_rawdev_obj_t queue_conf, size_t queue_conf_size) +{ + struct cnxk_gpiochip *gpiochip = dev->dev_private; + char buf[CNXK_GPIO_BUFSZ]; + struct cnxk_gpio *gpio; + int ret; + + RTE_SET_USED(queue_conf); + RTE_SET_USED(queue_conf_size); + + gpio = cnxk_gpio_lookup(gpiochip, queue_id); + if (gpio) + return -EEXIST; + + gpio = rte_zmalloc(NULL, sizeof(*gpio), 0); + if (!gpio) + return -ENOMEM; + gpio->num = queue_id + gpiochip->base; + gpio->gpiochip = gpiochip; + + snprintf(buf, sizeof(buf), "%s/export", CNXK_GPIO_CLASS_PATH); + ret = cnxk_gpio_write_attr_int(buf, gpio->num); + if (ret) { + rte_free(gpio); + return ret; + } + + gpiochip->gpios[queue_id] = gpio; return 0; } @@ -172,10 +243,19 @@ cnxk_gpio_queue_count(struct rte_rawdev *dev) return gpiochip->num_gpios; } +static int +cnxk_gpio_dev_close(struct rte_rawdev *dev) +{ + RTE_SET_USED(dev); + + return 0; +} + static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { .dev_close = cnxk_gpio_dev_close, .queue_def_conf = cnxk_gpio_queue_def_conf, .queue_count = cnxk_gpio_queue_count, + .queue_setup = cnxk_gpio_queue_setup, }; static int -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v5 05/11] raw/cnxk_gpio: support queue release 2022-01-18 13:24 ` [PATCH v5 " Tomasz Duszynski ` (3 preceding siblings ...) 2022-01-18 13:24 ` [PATCH v5 04/11] raw/cnxk_gpio: support queue setup Tomasz Duszynski @ 2022-01-18 13:24 ` Tomasz Duszynski 2022-01-18 13:24 ` [PATCH v5 06/11] raw/cnxk_gpio: support enqueuing buffers Tomasz Duszynski ` (7 subsequent siblings) 12 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-01-18 13:24 UTC (permalink / raw) To: dev; +Cc: thomas, jerin, Tomasz Duszynski Add support for queue release. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 9baa43187e..ec82a55918 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -217,6 +217,29 @@ cnxk_gpio_queue_setup(struct rte_rawdev *dev, uint16_t queue_id, return 0; } +static int +cnxk_gpio_queue_release(struct rte_rawdev *dev, uint16_t queue_id) +{ + struct cnxk_gpiochip *gpiochip = dev->dev_private; + char buf[CNXK_GPIO_BUFSZ]; + struct cnxk_gpio *gpio; + int ret; + + gpio = cnxk_gpio_lookup(gpiochip, queue_id); + if (!gpio) + return -ENODEV; + + snprintf(buf, sizeof(buf), "%s/unexport", CNXK_GPIO_CLASS_PATH); + ret = cnxk_gpio_write_attr_int(buf, gpiochip->base + queue_id); + if (ret) + return ret; + + gpiochip->gpios[queue_id] = NULL; + rte_free(gpio); + + return 0; +} + static int cnxk_gpio_queue_def_conf(struct rte_rawdev *dev, uint16_t queue_id, rte_rawdev_obj_t queue_conf, size_t queue_conf_size) @@ -256,6 +279,7 @@ static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { .queue_def_conf = cnxk_gpio_queue_def_conf, .queue_count = cnxk_gpio_queue_count, .queue_setup = cnxk_gpio_queue_setup, + .queue_release = cnxk_gpio_queue_release, }; static int @@ -329,6 +353,8 @@ cnxk_gpio_remove(struct rte_vdev_device *dev) char name[RTE_RAWDEV_NAME_MAX_LEN]; struct cnxk_gpiochip *gpiochip; struct rte_rawdev *rawdev; + struct cnxk_gpio *gpio; + int i; RTE_SET_USED(dev); @@ -341,6 +367,14 @@ cnxk_gpio_remove(struct rte_vdev_device *dev) return -ENODEV; gpiochip = rawdev->dev_private; + for (i = 0; i < gpiochip->num_gpios; i++) { + gpio = gpiochip->gpios[i]; + if (!gpio) + continue; + + cnxk_gpio_queue_release(rawdev, gpio->num); + } + rte_free(gpiochip->gpios); rte_rawdev_pmd_release(rawdev); -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v5 06/11] raw/cnxk_gpio: support enqueuing buffers 2022-01-18 13:24 ` [PATCH v5 " Tomasz Duszynski ` (4 preceding siblings ...) 2022-01-18 13:24 ` [PATCH v5 05/11] raw/cnxk_gpio: support queue release Tomasz Duszynski @ 2022-01-18 13:24 ` Tomasz Duszynski 2022-01-18 13:24 ` [PATCH v5 07/11] raw/cnxk_gpio: support dequeuing buffers Tomasz Duszynski ` (6 subsequent siblings) 12 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-01-18 13:24 UTC (permalink / raw) To: dev; +Cc: thomas, jerin, Tomasz Duszynski Add dummy support for enqueuing buffers. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 47 +++++++++++++++++++++++ drivers/raw/cnxk_gpio/cnxk_gpio.h | 1 + drivers/raw/cnxk_gpio/meson.build | 1 + drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h | 38 ++++++++++++++++++ 4 files changed, 87 insertions(+) create mode 100644 drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index ec82a55918..6f3795df41 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -14,6 +14,7 @@ #include <roc_api.h> #include "cnxk_gpio.h" +#include "rte_pmd_cnxk_gpio.h" #define CNXK_GPIO_BUFSZ 128 #define CNXK_GPIO_CLASS_PATH "/sys/class/gpio" @@ -274,8 +275,54 @@ cnxk_gpio_dev_close(struct rte_rawdev *dev) return 0; } +static int +cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf) +{ + struct cnxk_gpio_msg *msg = rbuf->buf_addr; + void *rsp = NULL; + int ret; + + switch (msg->type) { + default: + return -EINVAL; + } + + /* get rid of last response if any */ + if (gpio->rsp) { + RTE_LOG(WARNING, PMD, "previous response got overwritten\n"); + rte_free(gpio->rsp); + } + gpio->rsp = rsp; + + return ret; +} + +static int +cnxk_gpio_enqueue_bufs(struct rte_rawdev *dev, struct rte_rawdev_buf **buffers, + unsigned int count, rte_rawdev_obj_t context) +{ + struct cnxk_gpiochip *gpiochip = dev->dev_private; + unsigned int queue = (size_t)context; + struct cnxk_gpio *gpio; + int ret; + + if (count == 0) + return 0; + + gpio = cnxk_gpio_lookup(gpiochip, queue); + if (!gpio) + return -ENODEV; + + ret = cnxk_gpio_process_buf(gpio, buffers[0]); + if (ret) + return ret; + + return 1; +} + static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { .dev_close = cnxk_gpio_dev_close, + .enqueue_bufs = cnxk_gpio_enqueue_bufs, .queue_def_conf = cnxk_gpio_queue_def_conf, .queue_count = cnxk_gpio_queue_count, .queue_setup = cnxk_gpio_queue_setup, diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.h b/drivers/raw/cnxk_gpio/cnxk_gpio.h index 4dae8316ba..6b54ebe6e6 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.h +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.h @@ -9,6 +9,7 @@ struct cnxk_gpiochip; struct cnxk_gpio { struct cnxk_gpiochip *gpiochip; + void *rsp; int num; }; diff --git a/drivers/raw/cnxk_gpio/meson.build b/drivers/raw/cnxk_gpio/meson.build index 9a7e716c1e..3fbfdd838c 100644 --- a/drivers/raw/cnxk_gpio/meson.build +++ b/drivers/raw/cnxk_gpio/meson.build @@ -6,3 +6,4 @@ deps += ['bus_vdev', 'common_cnxk', 'rawdev', 'kvargs'] sources = files( 'cnxk_gpio.c', ) +headers = files('rte_pmd_cnxk_gpio.h') diff --git a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h new file mode 100644 index 0000000000..c71065e10c --- /dev/null +++ b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2021 Marvell. + */ + +#ifndef _RTE_PMD_CNXK_GPIO_H_ +#define _RTE_PMD_CNXK_GPIO_H_ + +/** + * @file rte_pmd_cnxk_gpio.h + * + * Marvell GPIO PMD specific structures and interface + * + * This API allows applications to manage GPIOs in user space along with + * installing interrupt handlers for low latency signal processing. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** Available message types */ +enum cnxk_gpio_msg_type { + /** Invalid message type */ + CNXK_GPIO_MSG_TYPE_INVALID, +}; + +struct cnxk_gpio_msg { + /** Message type */ + enum cnxk_gpio_msg_type type; + /** Message data passed to PMD or received from PMD */ + void *data; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _RTE_PMD_CNXK_GPIO_H_ */ -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v5 07/11] raw/cnxk_gpio: support dequeuing buffers 2022-01-18 13:24 ` [PATCH v5 " Tomasz Duszynski ` (5 preceding siblings ...) 2022-01-18 13:24 ` [PATCH v5 06/11] raw/cnxk_gpio: support enqueuing buffers Tomasz Duszynski @ 2022-01-18 13:24 ` Tomasz Duszynski 2022-01-18 13:24 ` [PATCH v5 08/11] raw/cnxk_gpio: support standard GPIO operations Tomasz Duszynski ` (5 subsequent siblings) 12 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-01-18 13:24 UTC (permalink / raw) To: dev; +Cc: thomas, jerin, Tomasz Duszynski Add support for dequeuing buffers. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 6f3795df41..46f9e63dd7 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -320,9 +320,35 @@ cnxk_gpio_enqueue_bufs(struct rte_rawdev *dev, struct rte_rawdev_buf **buffers, return 1; } +static int +cnxk_gpio_dequeue_bufs(struct rte_rawdev *dev, struct rte_rawdev_buf **buffers, + unsigned int count, rte_rawdev_obj_t context) +{ + struct cnxk_gpiochip *gpiochip = dev->dev_private; + unsigned int queue = (size_t)context; + struct cnxk_gpio *gpio; + + if (count == 0) + return 0; + + gpio = cnxk_gpio_lookup(gpiochip, queue); + if (!gpio) + return -ENODEV; + + if (gpio->rsp) { + buffers[0]->buf_addr = gpio->rsp; + gpio->rsp = NULL; + + return 1; + } + + return 0; +} + static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { .dev_close = cnxk_gpio_dev_close, .enqueue_bufs = cnxk_gpio_enqueue_bufs, + .dequeue_bufs = cnxk_gpio_dequeue_bufs, .queue_def_conf = cnxk_gpio_queue_def_conf, .queue_count = cnxk_gpio_queue_count, .queue_setup = cnxk_gpio_queue_setup, -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v5 08/11] raw/cnxk_gpio: support standard GPIO operations 2022-01-18 13:24 ` [PATCH v5 " Tomasz Duszynski ` (6 preceding siblings ...) 2022-01-18 13:24 ` [PATCH v5 07/11] raw/cnxk_gpio: support dequeuing buffers Tomasz Duszynski @ 2022-01-18 13:24 ` Tomasz Duszynski 2022-01-18 13:24 ` [PATCH v5 09/11] raw/cnxk_gpio: support custom irq handlers Tomasz Duszynski ` (4 subsequent siblings) 12 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-01-18 13:24 UTC (permalink / raw) To: dev; +Cc: thomas, jerin, Tomasz Duszynski Add support for standard GPIO operations i.e ones normally provided by GPIO sysfs interface. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- doc/guides/rawdevs/cnxk_gpio.rst | 98 ++++++++ drivers/raw/cnxk_gpio/cnxk_gpio.c | 147 +++++++++++- drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h | 279 +++++++++++++++++++++- 3 files changed, 521 insertions(+), 3 deletions(-) diff --git a/doc/guides/rawdevs/cnxk_gpio.rst b/doc/guides/rawdevs/cnxk_gpio.rst index 868302d07f..f6c3c942c5 100644 --- a/doc/guides/rawdevs/cnxk_gpio.rst +++ b/doc/guides/rawdevs/cnxk_gpio.rst @@ -63,3 +63,101 @@ call barely exports GPIO to userspace. To perform actual data transfer use standard ``rte_rawdev_enqueue_buffers()`` and ``rte_rawdev_dequeue_buffers()`` APIs. Not all messages produce sensible responses hence dequeueing is not always necessary. + +CNXK GPIO PMD +------------- + +PMD accepts ``struct cnxk_gpio_msg`` messages which differ by type and payload. +Message types along with description are listed below. As for the usage examples +please refer to ``cnxk_gpio_selftest()``. There's a set of convenient wrappers +available, one for each existing command. + +Set GPIO value +~~~~~~~~~~~~~~ + +Message is used to set output to low or high. This does not work for GPIOs +configured as input. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_SET_PIN_VALUE``. + +Payload must be an integer set to 0 (low) or 1 (high). + +Consider using ``rte_pmd_gpio_set_pin_value()`` wrapper. + +Set GPIO edge +~~~~~~~~~~~~~ + +Message is used to set edge that triggers interrupt. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_SET_PIN_EDGE``. + +Payload must be `enum cnxk_gpio_pin_edge`. + +Consider using ``rte_pmd_gpio_set_pin_edge()`` wrapper. + +Set GPIO direction +~~~~~~~~~~~~~~~~~~ + +Message is used to change GPIO direction to either input or output. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_SET_PIN_DIR``. + +Payload must be `enum cnxk_gpio_pin_dir`. + +Consider using ``rte_pmd_gpio_set_pin_dir()`` wrapper. + +Set GPIO active low +~~~~~~~~~~~~~~~~~~~ + +Message is used to set whether pin is active low. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_SET_PIN_ACTIVE_LOW``. + +Payload must be an integer set to 0 or 1. The latter activates inversion. + +Consider using ``rte_pmd_gpio_set_pin_active_low()`` wrapper. + +Get GPIO value +~~~~~~~~~~~~~~ + +Message is used to read GPIO value. Value can be 0 (low) or 1 (high). + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_GET_PIN_VALUE``. + +Payload contains integer set to either 0 or 1. + +Consider using ``rte_pmd_gpio_get_pin_value()`` wrapper. + +Get GPIO edge +~~~~~~~~~~~~~ + +Message is used to read GPIO edge. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_GET_PIN_EDGE``. + +Payload contains `enum cnxk_gpio_pin_edge`. + +Consider using ``rte_pmd_gpio_get_pin_edge()`` wrapper. + +Get GPIO direction +~~~~~~~~~~~~~~~~~~ + +Message is used to read GPIO direction. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_GET_PIN_DIR``. + +Payload contains `enum cnxk_gpio_pin_dir`. + +Consider using ``rte_pmd_gpio_get_pin_dir()`` wrapper. + +Get GPIO active low +~~~~~~~~~~~~~~~~~~~ + +Message is used check whether inverted logic is active. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW``. + +Payload contains an integer set to 0 or 1. The latter means inverted logic +is turned on. + +Consider using ``rte_pmd_gpio_get_pin_active_low()`` wrapper. diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 46f9e63dd7..67c6d4813c 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -267,6 +267,78 @@ cnxk_gpio_queue_count(struct rte_rawdev *dev) return gpiochip->num_gpios; } +static const struct { + enum cnxk_gpio_pin_edge edge; + const char *name; +} cnxk_gpio_edge_name[] = { + { CNXK_GPIO_PIN_EDGE_NONE, "none" }, + { CNXK_GPIO_PIN_EDGE_FALLING, "falling" }, + { CNXK_GPIO_PIN_EDGE_RISING, "rising" }, + { CNXK_GPIO_PIN_EDGE_BOTH, "both" }, +}; + +static const char * +cnxk_gpio_edge_to_name(enum cnxk_gpio_pin_edge edge) +{ + unsigned int i; + + for (i = 0; i < RTE_DIM(cnxk_gpio_edge_name); i++) { + if (cnxk_gpio_edge_name[i].edge == edge) + return cnxk_gpio_edge_name[i].name; + } + + return NULL; +} + +static enum cnxk_gpio_pin_edge +cnxk_gpio_name_to_edge(const char *name) +{ + unsigned int i; + + for (i = 0; i < RTE_DIM(cnxk_gpio_edge_name); i++) { + if (!strcmp(cnxk_gpio_edge_name[i].name, name)) + break; + } + + return cnxk_gpio_edge_name[i].edge; +} + +static const struct { + enum cnxk_gpio_pin_dir dir; + const char *name; +} cnxk_gpio_dir_name[] = { + { CNXK_GPIO_PIN_DIR_IN, "in" }, + { CNXK_GPIO_PIN_DIR_OUT, "out" }, + { CNXK_GPIO_PIN_DIR_HIGH, "high" }, + { CNXK_GPIO_PIN_DIR_LOW, "low" }, +}; + +static const char * +cnxk_gpio_dir_to_name(enum cnxk_gpio_pin_dir dir) +{ + unsigned int i; + + for (i = 0; i < RTE_DIM(cnxk_gpio_dir_name); i++) { + if (cnxk_gpio_dir_name[i].dir == dir) + return cnxk_gpio_dir_name[i].name; + } + + return NULL; +} + +static enum cnxk_gpio_pin_dir +cnxk_gpio_name_to_dir(const char *name) +{ + unsigned int i; + + for (i = 0; i < RTE_DIM(cnxk_gpio_dir_name); i++) { + if (!strcmp(cnxk_gpio_dir_name[i].name, name)) + break; + } + + return cnxk_gpio_dir_name[i].dir; +} + static int cnxk_gpio_dev_close(struct rte_rawdev *dev) { @@ -279,10 +351,83 @@ static int cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf) { struct cnxk_gpio_msg *msg = rbuf->buf_addr; + enum cnxk_gpio_pin_edge edge; + enum cnxk_gpio_pin_dir dir; + char buf[CNXK_GPIO_BUFSZ]; void *rsp = NULL; - int ret; + int ret, val, n; + + n = snprintf(buf, sizeof(buf), "%s/gpio%d", CNXK_GPIO_CLASS_PATH, + gpio->num); switch (msg->type) { + case CNXK_GPIO_MSG_TYPE_SET_PIN_VALUE: + snprintf(buf + n, sizeof(buf) - n, "/value"); + ret = cnxk_gpio_write_attr_int(buf, !!*(int *)msg->data); + break; + case CNXK_GPIO_MSG_TYPE_SET_PIN_EDGE: + snprintf(buf + n, sizeof(buf) - n, "/edge"); + edge = *(enum cnxk_gpio_pin_edge *)msg->data; + ret = cnxk_gpio_write_attr(buf, cnxk_gpio_edge_to_name(edge)); + break; + case CNXK_GPIO_MSG_TYPE_SET_PIN_DIR: + snprintf(buf + n, sizeof(buf) - n, "/direction"); + dir = *(enum cnxk_gpio_pin_dir *)msg->data; + ret = cnxk_gpio_write_attr(buf, cnxk_gpio_dir_to_name(dir)); + break; + case CNXK_GPIO_MSG_TYPE_SET_PIN_ACTIVE_LOW: + snprintf(buf + n, sizeof(buf) - n, "/active_low"); + val = *(int *)msg->data; + ret = cnxk_gpio_write_attr_int(buf, val); + break; + case CNXK_GPIO_MSG_TYPE_GET_PIN_VALUE: + snprintf(buf + n, sizeof(buf) - n, "/value"); + ret = cnxk_gpio_read_attr_int(buf, &val); + if (ret) + break; + + rsp = rte_zmalloc(NULL, sizeof(int), 0); + if (!rsp) + return -ENOMEM; + + *(int *)rsp = val; + break; + case CNXK_GPIO_MSG_TYPE_GET_PIN_EDGE: + snprintf(buf + n, sizeof(buf) - n, "/edge"); + ret = cnxk_gpio_read_attr(buf, buf); + if (ret) + break; + + rsp = rte_zmalloc(NULL, sizeof(enum cnxk_gpio_pin_edge), 0); + if (!rsp) + return -ENOMEM; + + *(enum cnxk_gpio_pin_edge *)rsp = cnxk_gpio_name_to_edge(buf); + break; + case CNXK_GPIO_MSG_TYPE_GET_PIN_DIR: + snprintf(buf + n, sizeof(buf) - n, "/direction"); + ret = cnxk_gpio_read_attr(buf, buf); + if (ret) + break; + + rsp = rte_zmalloc(NULL, sizeof(enum cnxk_gpio_pin_dir), 0); + if (!rsp) + return -ENOMEM; + + *(enum cnxk_gpio_pin_dir *)rsp = cnxk_gpio_name_to_dir(buf); + break; + case CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW: + snprintf(buf + n, sizeof(buf) - n, "/active_low"); + ret = cnxk_gpio_read_attr_int(buf, &val); + if (ret) + break; + + rsp = rte_zmalloc(NULL, sizeof(int), 0); + if (!rsp) + return -ENOMEM; + + *(int *)rsp = val; + break; default: return -EINVAL; } diff --git a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h index c71065e10c..7c3dc225ca 100644 --- a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h +++ b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h @@ -5,6 +5,10 @@ #ifndef _RTE_PMD_CNXK_GPIO_H_ #define _RTE_PMD_CNXK_GPIO_H_ +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_rawdev.h> + /** * @file rte_pmd_cnxk_gpio.h * @@ -20,8 +24,46 @@ extern "C" { /** Available message types */ enum cnxk_gpio_msg_type { - /** Invalid message type */ - CNXK_GPIO_MSG_TYPE_INVALID, + /** Type used to set output value */ + CNXK_GPIO_MSG_TYPE_SET_PIN_VALUE, + /** Type used to set edge */ + CNXK_GPIO_MSG_TYPE_SET_PIN_EDGE, + /** Type used to set direction */ + CNXK_GPIO_MSG_TYPE_SET_PIN_DIR, + /** Type used to set inverted logic */ + CNXK_GPIO_MSG_TYPE_SET_PIN_ACTIVE_LOW, + /** Type used to read value */ + CNXK_GPIO_MSG_TYPE_GET_PIN_VALUE, + /** Type used to read edge */ + CNXK_GPIO_MSG_TYPE_GET_PIN_EDGE, + /** Type used to read direction */ + CNXK_GPIO_MSG_TYPE_GET_PIN_DIR, + /** Type used to read inverted logic state */ + CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW, +}; + +/** Available edges */ +enum cnxk_gpio_pin_edge { + /** Set edge to none */ + CNXK_GPIO_PIN_EDGE_NONE, + /** Set edge to falling */ + CNXK_GPIO_PIN_EDGE_FALLING, + /** Set edge to rising */ + CNXK_GPIO_PIN_EDGE_RISING, + /** Set edge to both rising and falling */ + CNXK_GPIO_PIN_EDGE_BOTH, +}; + +/** Available directions */ +enum cnxk_gpio_pin_dir { + /** Set direction to input */ + CNXK_GPIO_PIN_DIR_IN, + /** Set direction to output */ + CNXK_GPIO_PIN_DIR_OUT, + /** Set direction to output and value to 1 */ + CNXK_GPIO_PIN_DIR_HIGH, + /* Set direction to output and value to 0 */ + CNXK_GPIO_PIN_DIR_LOW, }; struct cnxk_gpio_msg { @@ -31,6 +73,239 @@ struct cnxk_gpio_msg { void *data; }; +/** @internal helper routine for enqueuing/dequeuing messages */ +static __rte_always_inline int +__rte_pmd_gpio_enq_deq(uint16_t dev_id, int gpio, void *req, void *rsp, + size_t rsp_size) +{ + struct rte_rawdev_buf *bufs[1]; + struct rte_rawdev_buf buf; + void *q; + int ret; + + q = (void *)(size_t)gpio; + buf.buf_addr = req; + bufs[0] = &buf; + + ret = rte_rawdev_enqueue_buffers(dev_id, bufs, RTE_DIM(bufs), q); + if (ret < 0) + return ret; + if (ret != RTE_DIM(bufs)) + return -EIO; + + if (!rsp) + return 0; + + ret = rte_rawdev_dequeue_buffers(dev_id, bufs, RTE_DIM(bufs), q); + if (ret < 0) + return ret; + if (ret != RTE_DIM(bufs)) + return -EIO; + + rte_memcpy(rsp, buf.buf_addr, rsp_size); + rte_free(buf.buf_addr); + + return 0; +} + +/** + * Set output to specific value + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param val + * Value output will be set to. 0 represents low state while + * 1 high state + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_set_pin_value(uint16_t dev_id, int gpio, int val) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_SET_PIN_VALUE, + .data = &val, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Select signal edge that triggers interrupt + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param edge + * Signal edge that triggers interrupt + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_set_pin_edge(uint16_t dev_id, int gpio, + enum cnxk_gpio_pin_edge edge) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_SET_PIN_EDGE, + .data = &edge + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Configure GPIO as input or output + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param dir + * Direction of the GPIO + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_set_pin_dir(uint16_t dev_id, int gpio, enum cnxk_gpio_pin_dir dir) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_SET_PIN_DIR, + .data = &dir, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Enable or disable inverted logic + * + * If GPIO is configured as output then writing 1 or 0 will result in setting + * output to respectively low or high + * + * If GPIO is configured as input then logic inversion applies to edges. Both + * current and future settings are affected + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param val + * 0 to disable, 1 to enable inverted logic + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_set_pin_active_low(uint16_t dev_id, int gpio, int val) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_SET_PIN_ACTIVE_LOW, + .data = &val, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Read GPIO value + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param val + * Where to store read logical signal value i.e 0 or 1 + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_get_pin_value(uint16_t dev_id, int gpio, int *val) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_GET_PIN_VALUE, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, val, sizeof(*val)); +} + +/** + * Read GPIO edge + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param edge + * Where to store edge + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_get_pin_edge(uint16_t dev_id, int gpio, + enum cnxk_gpio_pin_edge *edge) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_GET_PIN_EDGE, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, edge, sizeof(*edge)); +} + +/** + * Read GPIO direction + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param dir + * Where to store direction + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_get_pin_dir(uint16_t dev_id, int gpio, enum cnxk_gpio_pin_dir *dir) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_GET_PIN_DIR, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, dir, sizeof(*dir)); +} + +/** + * Read whether GPIO is active low + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param val + * Where to store active low state + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_get_pin_active_low(uint16_t dev_id, int gpio, int *val) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW, + .data = &val, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, val, sizeof(*val)); +} + #ifdef __cplusplus } #endif -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v5 09/11] raw/cnxk_gpio: support custom irq handlers 2022-01-18 13:24 ` [PATCH v5 " Tomasz Duszynski ` (7 preceding siblings ...) 2022-01-18 13:24 ` [PATCH v5 08/11] raw/cnxk_gpio: support standard GPIO operations Tomasz Duszynski @ 2022-01-18 13:24 ` Tomasz Duszynski 2022-01-18 13:24 ` [PATCH v5 10/11] raw/cnxk_gpio: support selftest Tomasz Duszynski ` (3 subsequent siblings) 12 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-01-18 13:24 UTC (permalink / raw) To: dev; +Cc: thomas, jerin, Tomasz Duszynski Add support for custom interrupt handlers. Custom interrupt handlers bypass kernel completely and are meant for fast and low latency access to GPIO state. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- doc/guides/rawdevs/cnxk_gpio.rst | 21 +++ drivers/raw/cnxk_gpio/cnxk_gpio.c | 41 +++- drivers/raw/cnxk_gpio/cnxk_gpio.h | 8 + drivers/raw/cnxk_gpio/cnxk_gpio_irq.c | 216 ++++++++++++++++++++++ drivers/raw/cnxk_gpio/meson.build | 1 + drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h | 116 ++++++++++++ 6 files changed, 401 insertions(+), 2 deletions(-) create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio_irq.c diff --git a/doc/guides/rawdevs/cnxk_gpio.rst b/doc/guides/rawdevs/cnxk_gpio.rst index f6c3c942c5..ad93ec0d44 100644 --- a/doc/guides/rawdevs/cnxk_gpio.rst +++ b/doc/guides/rawdevs/cnxk_gpio.rst @@ -161,3 +161,24 @@ Payload contains an integer set to 0 or 1. The latter means inverted logic is turned on. Consider using ``rte_pmd_gpio_get_pin_active_low()`` wrapper. + +Request interrupt +~~~~~~~~~~~~~~~~~ + +Message is used to install custom interrupt handler. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_REGISTER_IRQ``. + +Payload needs to be set to ``struct cnxk_gpio_irq`` which describes interrupt +being requested. + +Consider using ``rte_pmd_gpio_register_gpio()`` wrapper. + +Free interrupt +~~~~~~~~~~~~~~ + +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. diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 67c6d4813c..58553ad3d9 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -340,13 +340,27 @@ cnxk_gpio_name_to_dir(const char *name) } static int -cnxk_gpio_dev_close(struct rte_rawdev *dev) +cnxk_gpio_register_irq(struct cnxk_gpio *gpio, struct cnxk_gpio_irq *irq) { - RTE_SET_USED(dev); + int ret; + + ret = cnxk_gpio_irq_request(gpio->num - gpio->gpiochip->base, irq->cpu); + if (ret) + return ret; + + gpio->handler = irq->handler; + gpio->data = irq->data; + gpio->cpu = irq->cpu; return 0; } +static int +cnxk_gpio_unregister_irq(struct cnxk_gpio *gpio) +{ + return cnxk_gpio_irq_free(gpio->num - gpio->gpiochip->base); +} + static int cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf) { @@ -428,6 +442,13 @@ cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf) *(int *)rsp = val; break; + case CNXK_GPIO_MSG_TYPE_REGISTER_IRQ: + ret = cnxk_gpio_register_irq(gpio, + (struct cnxk_gpio_irq *)msg->data); + break; + case CNXK_GPIO_MSG_TYPE_UNREGISTER_IRQ: + ret = cnxk_gpio_unregister_irq(gpio); + break; default: return -EINVAL; } @@ -490,6 +511,14 @@ cnxk_gpio_dequeue_bufs(struct rte_rawdev *dev, struct rte_rawdev_buf **buffers, return 0; } +static int +cnxk_gpio_dev_close(struct rte_rawdev *dev) +{ + RTE_SET_USED(dev); + + return 0; +} + static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { .dev_close = cnxk_gpio_dev_close, .enqueue_bufs = cnxk_gpio_enqueue_bufs, @@ -532,6 +561,10 @@ cnxk_gpio_probe(struct rte_vdev_device *dev) if (ret) goto out; + ret = cnxk_gpio_irq_init(gpiochip); + if (ret) + goto out; + /* read gpio base */ snprintf(buf, sizeof(buf), "%s/gpiochip%d/base", CNXK_GPIO_CLASS_PATH, gpiochip->num); @@ -590,10 +623,14 @@ cnxk_gpio_remove(struct rte_vdev_device *dev) if (!gpio) continue; + if (gpio->handler) + cnxk_gpio_unregister_irq(gpio); + cnxk_gpio_queue_release(rawdev, gpio->num); } rte_free(gpiochip->gpios); + cnxk_gpio_irq_fini(); rte_rawdev_pmd_release(rawdev); return 0; diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.h b/drivers/raw/cnxk_gpio/cnxk_gpio.h index 6b54ebe6e6..c052ca5735 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.h +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.h @@ -11,6 +11,9 @@ struct cnxk_gpio { struct cnxk_gpiochip *gpiochip; void *rsp; int num; + void (*handler)(int gpio, void *data); + void *data; + int cpu; }; struct cnxk_gpiochip { @@ -20,4 +23,9 @@ struct cnxk_gpiochip { struct cnxk_gpio **gpios; }; +int cnxk_gpio_irq_init(struct cnxk_gpiochip *gpiochip); +void cnxk_gpio_irq_fini(void); +int cnxk_gpio_irq_request(int gpio, int cpu); +int cnxk_gpio_irq_free(int gpio); + #endif /* _CNXK_GPIO_H_ */ diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio_irq.c b/drivers/raw/cnxk_gpio/cnxk_gpio_irq.c new file mode 100644 index 0000000000..2fa8e69899 --- /dev/null +++ b/drivers/raw/cnxk_gpio/cnxk_gpio_irq.c @@ -0,0 +1,216 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2021 Marvell. + */ + +#include <fcntl.h> +#include <pthread.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/queue.h> +#include <unistd.h> + +#include <rte_rawdev_pmd.h> + +#include <roc_api.h> + +#include "cnxk_gpio.h" + +#define OTX_IOC_MAGIC 0xF2 +#define OTX_IOC_SET_GPIO_HANDLER \ + _IOW(OTX_IOC_MAGIC, 1, struct otx_gpio_usr_data) +#define OTX_IOC_CLR_GPIO_HANDLER \ + _IO(OTX_IOC_MAGIC, 2) + +struct otx_gpio_usr_data { + uint64_t isr_base; + uint64_t sp; + uint64_t cpu; + uint64_t gpio_num; +}; + +struct cnxk_gpio_irq_stack { + LIST_ENTRY(cnxk_gpio_irq_stack) next; + void *sp_buffer; + int cpu; + int inuse; +}; + +struct cnxk_gpio_irqchip { + int fd; + /* serialize access to this struct */ + pthread_mutex_t lock; + LIST_HEAD(, cnxk_gpio_irq_stack) stacks; + + struct cnxk_gpiochip *gpiochip; +}; + +static struct cnxk_gpio_irqchip *irqchip; + +static void +cnxk_gpio_irq_stack_free(int cpu) +{ + struct cnxk_gpio_irq_stack *stack; + + LIST_FOREACH(stack, &irqchip->stacks, next) { + if (stack->cpu == cpu) + break; + } + + if (!stack) + return; + + if (stack->inuse) + stack->inuse--; + + if (stack->inuse == 0) { + LIST_REMOVE(stack, next); + rte_free(stack->sp_buffer); + rte_free(stack); + } +} + +static void * +cnxk_gpio_irq_stack_alloc(int cpu) +{ +#define ARM_STACK_ALIGNMENT (2 * sizeof(void *)) +#define IRQ_STACK_SIZE 0x200000 + + struct cnxk_gpio_irq_stack *stack; + + LIST_FOREACH(stack, &irqchip->stacks, next) { + if (stack->cpu == cpu) + break; + } + + if (stack) { + stack->inuse++; + return (char *)stack->sp_buffer + IRQ_STACK_SIZE; + } + + stack = rte_malloc(NULL, sizeof(*stack), 0); + if (!stack) + return NULL; + + stack->sp_buffer = + rte_zmalloc(NULL, IRQ_STACK_SIZE * 2, ARM_STACK_ALIGNMENT); + if (!stack->sp_buffer) { + rte_free(stack); + return NULL; + } + + stack->cpu = cpu; + stack->inuse = 1; + LIST_INSERT_HEAD(&irqchip->stacks, stack, next); + + return (char *)stack->sp_buffer + IRQ_STACK_SIZE; +} + +static void +cnxk_gpio_irq_handler(int gpio_num) +{ + struct cnxk_gpiochip *gpiochip = irqchip->gpiochip; + struct cnxk_gpio *gpio; + + if (gpio_num >= gpiochip->num_gpios) + goto out; + + gpio = gpiochip->gpios[gpio_num]; + if (likely(gpio->handler)) + gpio->handler(gpio_num, gpio->data); + +out: + roc_atf_ret(); +} + +int +cnxk_gpio_irq_init(struct cnxk_gpiochip *gpiochip) +{ + if (irqchip) + return 0; + + irqchip = rte_zmalloc(NULL, sizeof(*irqchip), 0); + if (!irqchip) + return -ENOMEM; + + irqchip->fd = open("/dev/otx-gpio-ctr", O_RDWR | O_SYNC); + if (irqchip->fd < 0) { + rte_free(irqchip); + return -errno; + } + + pthread_mutex_init(&irqchip->lock, NULL); + LIST_INIT(&irqchip->stacks); + irqchip->gpiochip = gpiochip; + + return 0; +} + +void +cnxk_gpio_irq_fini(void) +{ + if (!irqchip) + return; + + close(irqchip->fd); + rte_free(irqchip); + irqchip = NULL; +} + +int +cnxk_gpio_irq_request(int gpio, int cpu) +{ + struct otx_gpio_usr_data data; + void *sp; + int ret; + + pthread_mutex_lock(&irqchip->lock); + + sp = cnxk_gpio_irq_stack_alloc(cpu); + if (!sp) { + ret = -ENOMEM; + goto out_unlock; + } + + data.isr_base = (uint64_t)cnxk_gpio_irq_handler; + data.sp = (uint64_t)sp; + data.cpu = (uint64_t)cpu; + data.gpio_num = (uint64_t)gpio; + + mlockall(MCL_CURRENT | MCL_FUTURE); + ret = ioctl(irqchip->fd, OTX_IOC_SET_GPIO_HANDLER, &data); + if (ret) { + ret = -errno; + goto out_free_stack; + } + + pthread_mutex_unlock(&irqchip->lock); + + return 0; + +out_free_stack: + cnxk_gpio_irq_stack_free(cpu); +out_unlock: + pthread_mutex_unlock(&irqchip->lock); + + return ret; +} + +int +cnxk_gpio_irq_free(int gpio) +{ + int ret; + + pthread_mutex_lock(&irqchip->lock); + + ret = ioctl(irqchip->fd, OTX_IOC_CLR_GPIO_HANDLER, gpio); + if (ret) { + pthread_mutex_unlock(&irqchip->lock); + return -errno; + } + + cnxk_gpio_irq_stack_free(irqchip->gpiochip->gpios[gpio]->cpu); + + pthread_mutex_unlock(&irqchip->lock); + + return 0; +} diff --git a/drivers/raw/cnxk_gpio/meson.build b/drivers/raw/cnxk_gpio/meson.build index 3fbfdd838c..9b55f029c7 100644 --- a/drivers/raw/cnxk_gpio/meson.build +++ b/drivers/raw/cnxk_gpio/meson.build @@ -5,5 +5,6 @@ deps += ['bus_vdev', 'common_cnxk', 'rawdev', 'kvargs'] sources = files( 'cnxk_gpio.c', + 'cnxk_gpio_irq.c', ) headers = files('rte_pmd_cnxk_gpio.h') diff --git a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h index 7c3dc225ca..e3096dc14f 100644 --- a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h +++ b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h @@ -40,6 +40,10 @@ enum cnxk_gpio_msg_type { CNXK_GPIO_MSG_TYPE_GET_PIN_DIR, /** Type used to read inverted logic state */ CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW, + /** Type used to register interrupt handler */ + CNXK_GPIO_MSG_TYPE_REGISTER_IRQ, + /** Type used to remove interrupt handler */ + CNXK_GPIO_MSG_TYPE_UNREGISTER_IRQ, }; /** Available edges */ @@ -66,6 +70,25 @@ enum cnxk_gpio_pin_dir { CNXK_GPIO_PIN_DIR_LOW, }; +/** + * GPIO interrupt handler + * + * @param gpio + * Zero-based GPIO number + * @param data + * Cookie passed to interrupt handler + */ +typedef void (*cnxk_gpio_irq_handler_t)(int gpio, void *data); + +struct cnxk_gpio_irq { + /** Interrupt handler */ + cnxk_gpio_irq_handler_t handler; + /** User data passed to irq handler */ + void *data; + /** CPU which will run irq handler */ + int cpu; +}; + struct cnxk_gpio_msg { /** Message type */ enum cnxk_gpio_msg_type type; @@ -306,6 +329,99 @@ rte_pmd_gpio_get_pin_active_low(uint16_t dev_id, int gpio, int *val) return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, val, sizeof(*val)); } +/** + * Attach interrupt handler to GPIO + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param cpu + * CPU which will be handling interrupt + * @param handler + * Interrupt handler to be executed + * @param data + * Data to be passed to interrupt handler + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_register_irq(uint16_t dev_id, int gpio, int cpu, + cnxk_gpio_irq_handler_t handler, void *data) +{ + struct cnxk_gpio_irq irq = { + .handler = handler, + .data = data, + .cpu = cpu, + }; + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_REGISTER_IRQ, + .data = &irq, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Detach interrupt handler from GPIO + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_unregister_irq(uint16_t dev_id, int gpio) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_UNREGISTER_IRQ, + .data = &gpio, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Enable interrupt + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param edge + * Edge that should trigger interrupt + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_enable_interrupt(uint16_t dev_id, int gpio, + enum cnxk_gpio_pin_edge edge) +{ + return rte_pmd_gpio_set_pin_edge(dev_id, gpio, edge); +} + +/** + * Disable interrupt + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_disable_interrupt(uint16_t dev_id, int gpio) +{ + return rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_NONE); +} + #ifdef __cplusplus } #endif -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v5 10/11] raw/cnxk_gpio: support selftest 2022-01-18 13:24 ` [PATCH v5 " Tomasz Duszynski ` (8 preceding siblings ...) 2022-01-18 13:24 ` [PATCH v5 09/11] raw/cnxk_gpio: support custom irq handlers Tomasz Duszynski @ 2022-01-18 13:24 ` Tomasz Duszynski 2022-01-18 13:24 ` [PATCH v5 11/11] raw/cnxk_gpio: add option to allow using subset of GPIOs Tomasz Duszynski ` (2 subsequent siblings) 12 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-01-18 13:24 UTC (permalink / raw) To: dev; +Cc: thomas, jerin, Tomasz Duszynski Add support for performing selftest. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- 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=<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. 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 <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; +} 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') -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v5 11/11] raw/cnxk_gpio: add option to allow using subset of GPIOs 2022-01-18 13:24 ` [PATCH v5 " Tomasz Duszynski ` (9 preceding siblings ...) 2022-01-18 13:24 ` [PATCH v5 10/11] raw/cnxk_gpio: support selftest Tomasz Duszynski @ 2022-01-18 13:24 ` Tomasz Duszynski 2022-02-09 13:24 ` [PATCH v5 00/11] Add cnxk_gpio Thomas Monjalon 2022-02-17 11:09 ` [PATCH v6 " Tomasz Duszynski 12 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-01-18 13:24 UTC (permalink / raw) To: dev; +Cc: thomas, jerin, Tomasz Duszynski, Jerin Jacob Kollanukkaran Add PMD parameter that allows one to select only subset of available GPIOs. This might be useful in cases where some GPIOs are already reserved yet still available for userspace access but particular app should not touch them. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> Reviewed-by: Jerin Jacob Kollanukkaran <jerinj@marvell.com> --- doc/guides/rawdevs/cnxk_gpio.rst | 5 + drivers/raw/cnxk_gpio/cnxk_gpio.c | 173 +++++++++++++++++---- drivers/raw/cnxk_gpio/cnxk_gpio.h | 2 + drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c | 49 +++--- drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h | 8 + 5 files changed, 188 insertions(+), 49 deletions(-) diff --git a/doc/guides/rawdevs/cnxk_gpio.rst b/doc/guides/rawdevs/cnxk_gpio.rst index c03a5b937c..adff535a77 100644 --- a/doc/guides/rawdevs/cnxk_gpio.rst +++ b/doc/guides/rawdevs/cnxk_gpio.rst @@ -57,6 +57,11 @@ former returns number GPIOs available in the system irrespective of GPIOs being controllable or not. Thus it is user responsibility to pick the proper ones. The latter call simply returns queue capacity. +In order to allow using only subset of available GPIOs `allowlist` PMD param may +be used. For example passing `--vdev=cnxk_gpio,gpiochip=448,allowlist=[0,1,2,3]` +to EAL will deny using all GPIOs except those specified explicitly in the +`allowlist`. + Respective queue needs to be configured with ``rte_rawdev_queue_setup()``. This call barely exports GPIO to userspace. diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 230016b078..4ff132861d 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -22,9 +22,13 @@ static const char *const cnxk_gpio_args[] = { #define CNXK_GPIO_ARG_GPIOCHIP "gpiochip" CNXK_GPIO_ARG_GPIOCHIP, +#define CNXK_GPIO_ARG_ALLOWLIST "allowlist" + CNXK_GPIO_ARG_ALLOWLIST, NULL }; +static char *allowlist; + static void cnxk_gpio_format_name(char *name, size_t len) { @@ -73,13 +77,23 @@ cnxk_gpio_parse_arg_gpiochip(const char *key __rte_unused, const char *value, } static int -cnxk_gpio_parse_args(struct cnxk_gpiochip *gpiochip, - struct rte_devargs *devargs) +cnxk_gpio_parse_arg_allowlist(const char *key __rte_unused, const char *value, + void *extra_args __rte_unused) +{ + allowlist = strdup(value); + if (!allowlist) + return -ENOMEM; + + return 0; +} + +static int +cnxk_gpio_parse_args(struct cnxk_gpiochip *gpiochip, const char *args) { struct rte_kvargs *kvlist; int ret; - kvlist = rte_kvargs_parse(devargs->args, cnxk_gpio_args); + kvlist = rte_kvargs_parse(args, cnxk_gpio_args); if (!kvlist) return 0; @@ -92,6 +106,14 @@ cnxk_gpio_parse_args(struct cnxk_gpiochip *gpiochip, goto out; } + ret = rte_kvargs_count(kvlist, CNXK_GPIO_ARG_ALLOWLIST); + if (ret == 1) { + ret = rte_kvargs_process(kvlist, CNXK_GPIO_ARG_ALLOWLIST, + cnxk_gpio_parse_arg_allowlist, NULL); + if (ret) + goto out; + } + ret = 0; out: rte_kvargs_free(kvlist); @@ -99,6 +121,60 @@ cnxk_gpio_parse_args(struct cnxk_gpiochip *gpiochip, return ret; } +static int +cnxk_gpio_parse_allowlist(struct cnxk_gpiochip *gpiochip) +{ + int i, ret, val, queue = 0; + char *token; + int *list; + + list = rte_calloc(NULL, gpiochip->num_gpios, sizeof(*list), 0); + if (!list) + return -ENOMEM; + + /* replace brackets with something meaningless for strtol() */ + allowlist[0] = ' '; + allowlist[strlen(allowlist) - 1] = ' '; + + /* quiesce -Wcast-qual */ + token = strtok((char *)(uintptr_t)allowlist, ","); + do { + errno = 0; + val = strtol(token, NULL, 10); + if (errno) { + RTE_LOG(ERR, PMD, "failed to parse %s\n", token); + ret = -errno; + goto out; + } + + if (val < 0 || val >= gpiochip->num_gpios) { + RTE_LOG(ERR, PMD, "gpio%d out of 0-%d range\n", val, + gpiochip->num_gpios - 1); + ret = -EINVAL; + goto out; + } + + for (i = 0; i < queue; i++) { + if (list[i] != val) + continue; + + RTE_LOG(WARNING, PMD, "gpio%d already allowed\n", val); + break; + } + if (i == queue) + list[queue++] = val; + } while ((token = strtok(NULL, ","))); + + gpiochip->allowlist = list; + gpiochip->num_queues = queue; + + return 0; +out: + rte_free(list); + + return ret; +} + static int cnxk_gpio_read_attr(char *attr, char *val) { @@ -175,13 +251,24 @@ cnxk_gpio_write_attr_int(const char *attr, int val) return cnxk_gpio_write_attr(attr, buf); } +static bool +cnxk_gpio_queue_valid(struct cnxk_gpiochip *gpiochip, uint16_t queue) +{ + return queue < gpiochip->num_queues; +} + +static int +cnxk_queue_to_gpio(struct cnxk_gpiochip *gpiochip, uint16_t queue) +{ + return gpiochip->allowlist ? gpiochip->allowlist[queue] : queue; +} + static struct cnxk_gpio * cnxk_gpio_lookup(struct cnxk_gpiochip *gpiochip, uint16_t queue) { - if (queue >= gpiochip->num_gpios) - return NULL; + int gpio = cnxk_queue_to_gpio(gpiochip, queue); - return gpiochip->gpios[queue]; + return gpiochip->gpios[gpio]; } static int @@ -191,11 +278,14 @@ cnxk_gpio_queue_setup(struct rte_rawdev *dev, uint16_t queue_id, struct cnxk_gpiochip *gpiochip = dev->dev_private; char buf[CNXK_GPIO_BUFSZ]; struct cnxk_gpio *gpio; - int ret; + int num, ret; RTE_SET_USED(queue_conf); RTE_SET_USED(queue_conf_size); + if (!cnxk_gpio_queue_valid(gpiochip, queue_id)) + return -EINVAL; + gpio = cnxk_gpio_lookup(gpiochip, queue_id); if (gpio) return -EEXIST; @@ -203,7 +293,9 @@ cnxk_gpio_queue_setup(struct rte_rawdev *dev, uint16_t queue_id, gpio = rte_zmalloc(NULL, sizeof(*gpio), 0); if (!gpio) return -ENOMEM; - gpio->num = queue_id + gpiochip->base; + + num = cnxk_queue_to_gpio(gpiochip, queue_id); + gpio->num = num + gpiochip->base; gpio->gpiochip = gpiochip; snprintf(buf, sizeof(buf), "%s/export", CNXK_GPIO_CLASS_PATH); @@ -213,7 +305,7 @@ cnxk_gpio_queue_setup(struct rte_rawdev *dev, uint16_t queue_id, return ret; } - gpiochip->gpios[queue_id] = gpio; + gpiochip->gpios[num] = gpio; return 0; } @@ -224,18 +316,22 @@ cnxk_gpio_queue_release(struct rte_rawdev *dev, uint16_t queue_id) struct cnxk_gpiochip *gpiochip = dev->dev_private; char buf[CNXK_GPIO_BUFSZ]; struct cnxk_gpio *gpio; - int ret; + int num, ret; + + if (!cnxk_gpio_queue_valid(gpiochip, queue_id)) + return -EINVAL; gpio = cnxk_gpio_lookup(gpiochip, queue_id); if (!gpio) return -ENODEV; snprintf(buf, sizeof(buf), "%s/unexport", CNXK_GPIO_CLASS_PATH); - ret = cnxk_gpio_write_attr_int(buf, gpiochip->base + queue_id); + ret = cnxk_gpio_write_attr_int(buf, gpio->num); if (ret) return ret; - gpiochip->gpios[queue_id] = NULL; + num = cnxk_queue_to_gpio(gpiochip, queue_id); + gpiochip->gpios[num] = NULL; rte_free(gpio); return 0; @@ -245,16 +341,17 @@ static int cnxk_gpio_queue_def_conf(struct rte_rawdev *dev, uint16_t queue_id, rte_rawdev_obj_t queue_conf, size_t queue_conf_size) { - unsigned int *conf; + struct cnxk_gpiochip *gpiochip = dev->dev_private; + struct cnxk_gpio_queue_conf *conf = queue_conf; - RTE_SET_USED(dev); - RTE_SET_USED(queue_id); + if (!cnxk_gpio_queue_valid(gpiochip, queue_id)) + return -EINVAL; if (queue_conf_size != sizeof(*conf)) return -EINVAL; - conf = (unsigned int *)queue_conf; - *conf = 1; + conf->size = 1; + conf->gpio = cnxk_queue_to_gpio(gpiochip, queue_id); return 0; } @@ -264,7 +361,7 @@ cnxk_gpio_queue_count(struct rte_rawdev *dev) { struct cnxk_gpiochip *gpiochip = dev->dev_private; - return gpiochip->num_gpios; + return gpiochip->num_queues; } static const struct { @@ -463,21 +560,27 @@ cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf) return ret; } +static bool +cnxk_gpio_valid(struct cnxk_gpiochip *gpiochip, int gpio) +{ + return gpio < gpiochip->num_gpios && gpiochip->gpios[gpio]; +} + static int cnxk_gpio_enqueue_bufs(struct rte_rawdev *dev, struct rte_rawdev_buf **buffers, unsigned int count, rte_rawdev_obj_t context) { struct cnxk_gpiochip *gpiochip = dev->dev_private; - unsigned int queue = (size_t)context; + unsigned int gpio_num = (size_t)context; struct cnxk_gpio *gpio; int ret; if (count == 0) return 0; - gpio = cnxk_gpio_lookup(gpiochip, queue); - if (!gpio) - return -ENODEV; + if (!cnxk_gpio_valid(gpiochip, gpio_num)) + return -EINVAL; + gpio = gpiochip->gpios[gpio_num]; ret = cnxk_gpio_process_buf(gpio, buffers[0]); if (ret) @@ -491,15 +594,15 @@ cnxk_gpio_dequeue_bufs(struct rte_rawdev *dev, struct rte_rawdev_buf **buffers, unsigned int count, rte_rawdev_obj_t context) { struct cnxk_gpiochip *gpiochip = dev->dev_private; - unsigned int queue = (size_t)context; + unsigned int gpio_num = (size_t)context; struct cnxk_gpio *gpio; if (count == 0) return 0; - gpio = cnxk_gpio_lookup(gpiochip, queue); - if (!gpio) - return -ENODEV; + if (!cnxk_gpio_valid(gpiochip, gpio_num)) + return -EINVAL; + gpio = gpiochip->gpios[gpio_num]; if (gpio->rsp) { buffers[0]->buf_addr = gpio->rsp; @@ -558,7 +661,7 @@ cnxk_gpio_probe(struct rte_vdev_device *dev) cnxk_gpio_set_defaults(gpiochip); /* defaults may be overwritten by this call */ - ret = cnxk_gpio_parse_args(gpiochip, dev->device.devargs); + ret = cnxk_gpio_parse_args(gpiochip, rte_vdev_device_args(dev)); if (ret) goto out; @@ -583,6 +686,15 @@ cnxk_gpio_probe(struct rte_vdev_device *dev) RTE_LOG(ERR, PMD, "failed to read %s", buf); goto out; } + gpiochip->num_queues = gpiochip->num_gpios; + + if (allowlist) { + ret = cnxk_gpio_parse_allowlist(gpiochip); + free(allowlist); + allowlist = NULL; + if (ret) + goto out; + } gpiochip->gpios = rte_calloc(NULL, gpiochip->num_gpios, sizeof(struct cnxk_gpio *), 0); @@ -594,6 +706,8 @@ cnxk_gpio_probe(struct rte_vdev_device *dev) return 0; out: + free(allowlist); + rte_free(gpiochip->allowlist); rte_rawdev_pmd_release(rawdev); return ret; @@ -630,6 +744,7 @@ cnxk_gpio_remove(struct rte_vdev_device *dev) cnxk_gpio_queue_release(rawdev, gpio->num); } + rte_free(gpiochip->allowlist); rte_free(gpiochip->gpios); cnxk_gpio_irq_fini(); rte_rawdev_pmd_release(rawdev); @@ -643,4 +758,6 @@ static struct rte_vdev_driver cnxk_gpio_drv = { }; RTE_PMD_REGISTER_VDEV(cnxk_gpio, cnxk_gpio_drv); -RTE_PMD_REGISTER_PARAM_STRING(cnxk_gpio, "gpiochip=<int>"); +RTE_PMD_REGISTER_PARAM_STRING(cnxk_gpio, + "gpiochip=<int> " + "allowlist=<list>"); diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.h b/drivers/raw/cnxk_gpio/cnxk_gpio.h index 1b31b5a486..e62f78a760 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.h +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.h @@ -20,7 +20,9 @@ struct cnxk_gpiochip { int num; int base; int num_gpios; + int num_queues; struct cnxk_gpio **gpios; + int *allowlist; }; int cnxk_gpio_selftest(uint16_t dev_id); diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c b/drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c index 6502902f86..7fccc48f30 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c @@ -302,12 +302,13 @@ cnxk_gpio_test_output(uint16_t dev_id, int base, int gpio) int cnxk_gpio_selftest(uint16_t dev_id) { + struct cnxk_gpio_queue_conf conf; struct cnxk_gpiochip *gpiochip; - unsigned int queues, i, size; char buf[CNXK_GPIO_BUFSZ]; struct rte_rawdev *rawdev; + unsigned int queues, i; struct cnxk_gpio *gpio; - int ret; + int ret, ret2; rawdev = rte_rawdev_pmd_get_named_dev("cnxk_gpio"); gpiochip = rawdev->dev_private; @@ -325,62 +326,68 @@ cnxk_gpio_selftest(uint16_t dev_id) 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)); + ret = rte_rawdev_queue_conf_get(dev_id, i, &conf, sizeof(conf)); if (ret) { RTE_LOG(ERR, PMD, "failed to read queue configuration (%d)\n", ret); - continue; + goto out; } - if (size != 1) { + RTE_LOG(INFO, PMD, "testing queue%d (gpio%d)\n", i, conf.gpio); + + if (conf.size != 1) { RTE_LOG(ERR, PMD, "wrong queue size received\n"); - continue; + ret = -EIO; + goto out; } ret = rte_rawdev_queue_setup(dev_id, i, NULL, 0); if (ret) { RTE_LOG(ERR, PMD, "failed to setup queue (%d)\n", ret); - continue; + goto out; } - gpio = gpiochip->gpios[i]; + gpio = gpiochip->gpios[conf.gpio]; 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) + ret = -ENOENT; goto release; + } - ret = cnxk_gpio_test_irq(dev_id, i); + ret = cnxk_gpio_test_input(dev_id, gpiochip->base, conf.gpio); if (ret) goto release; - ret = cnxk_gpio_test_output(dev_id, gpiochip->base, i); + ret = cnxk_gpio_test_irq(dev_id, conf.gpio); if (ret) goto release; + ret = cnxk_gpio_test_output(dev_id, gpiochip->base, conf.gpio); release: + ret2 = ret; ret = rte_rawdev_queue_release(dev_id, i); if (ret) { RTE_LOG(ERR, PMD, "failed to release queue (%d)\n", ret); - continue; + break; } if (cnxk_gpio_attr_exists(buf)) { RTE_LOG(ERR, PMD, "%s still exists\n", buf); - continue; + ret = -EIO; + break; + } + + if (ret2) { + ret = ret2; + break; } } +out: close(fd); - return 0; + return ret; } diff --git a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h index e3096dc14f..80a37be9c7 100644 --- a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h +++ b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h @@ -22,6 +22,14 @@ extern "C" { #endif +/* Queue default configuration */ +struct cnxk_gpio_queue_conf { + /** Queue size */ + int size; + /** GPIO number as seen by hardware */ + int gpio; +}; + /** Available message types */ enum cnxk_gpio_msg_type { /** Type used to set output value */ -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v5 00/11] Add cnxk_gpio 2022-01-18 13:24 ` [PATCH v5 " Tomasz Duszynski ` (10 preceding siblings ...) 2022-01-18 13:24 ` [PATCH v5 11/11] raw/cnxk_gpio: add option to allow using subset of GPIOs Tomasz Duszynski @ 2022-02-09 13:24 ` Thomas Monjalon 2022-02-17 11:09 ` [PATCH v6 " Tomasz Duszynski 12 siblings, 0 replies; 75+ messages in thread From: Thomas Monjalon @ 2022-02-09 13:24 UTC (permalink / raw) To: Tomasz Duszynski; +Cc: dev, jerin 18/01/2022 14:24, Tomasz Duszynski: > doc/guides/platform/cnxk.rst | 2 + > doc/guides/rawdevs/cnxk_gpio.rst | 200 ++++++ > doc/guides/rawdevs/index.rst | 1 + > doc/guides/rel_notes/release_22_03.rst | 9 + > drivers/raw/cnxk_gpio/cnxk_gpio.c | 763 +++++++++++++++++++++ > drivers/raw/cnxk_gpio/cnxk_gpio.h | 35 + > drivers/raw/cnxk_gpio/cnxk_gpio_irq.c | 216 ++++++ > drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c | 393 +++++++++++ > drivers/raw/cnxk_gpio/meson.build | 11 + > drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h | 437 ++++++++++++ > drivers/raw/cnxk_gpio/version.map | 3 + > drivers/raw/meson.build | 1 + > 12 files changed, 2071 insertions(+) An update of the file MAINTAINERS is missing. ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v6 00/11] Add cnxk_gpio 2022-01-18 13:24 ` [PATCH v5 " Tomasz Duszynski ` (11 preceding siblings ...) 2022-02-09 13:24 ` [PATCH v5 00/11] Add cnxk_gpio Thomas Monjalon @ 2022-02-17 11:09 ` Tomasz Duszynski 2022-02-17 11:09 ` [PATCH v6 01/11] raw/cnxk_gpio: add GPIO driver skeleton Tomasz Duszynski ` (11 more replies) 12 siblings, 12 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-02-17 11:09 UTC (permalink / raw) To: dev; +Cc: jerinj, thomas, Tomasz Duszynski This series introduces a new rawdevice PMD which allows to manage userspace GPIOs and install custom GPIO interrupt handlers which bypass kernel. This is especially useful for applications that, besides providing standard dataplane functionality, want to have fast and low latency access to GPIO pin state. It'd be great to have that merged during 22.03 merge window. v6: - update MAINTAINERS v5: - added close callback to make sure PMD resources are freed properly - updated selftest to return early on error - doc fixes v4: - free kvargs after parsing arguments - add support for allowing only subset of available GPIOs v3: - fix meson formatting - fix cnxk_gpio_process_buf() return value v2: - do not trigger irq by writing to /dev/mem, use ioctl() instead Tomasz Duszynski (11): raw/cnxk_gpio: add GPIO driver skeleton raw/cnxk_gpio: support reading default queue conf raw/cnxk_gpio: support reading queue count raw/cnxk_gpio: support queue setup raw/cnxk_gpio: support queue release raw/cnxk_gpio: support enqueuing buffers raw/cnxk_gpio: support dequeuing buffers raw/cnxk_gpio: support standard GPIO operations raw/cnxk_gpio: support custom irq handlers raw/cnxk_gpio: support selftest raw/cnxk_gpio: add option to allow using subset of GPIOs MAINTAINERS | 6 + doc/guides/platform/cnxk.rst | 2 + doc/guides/rawdevs/cnxk_gpio.rst | 200 ++++++ doc/guides/rawdevs/index.rst | 1 + doc/guides/rel_notes/release_22_03.rst | 10 + drivers/raw/cnxk_gpio/cnxk_gpio.c | 763 +++++++++++++++++++++ drivers/raw/cnxk_gpio/cnxk_gpio.h | 35 + drivers/raw/cnxk_gpio/cnxk_gpio_irq.c | 216 ++++++ drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c | 393 +++++++++++ drivers/raw/cnxk_gpio/meson.build | 11 + drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h | 437 ++++++++++++ drivers/raw/cnxk_gpio/version.map | 3 + drivers/raw/meson.build | 1 + 13 files changed, 2078 insertions(+) create mode 100644 doc/guides/rawdevs/cnxk_gpio.rst create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio.c create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio.h create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio_irq.c create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c create mode 100644 drivers/raw/cnxk_gpio/meson.build create mode 100644 drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h create mode 100644 drivers/raw/cnxk_gpio/version.map -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v6 01/11] raw/cnxk_gpio: add GPIO driver skeleton 2022-02-17 11:09 ` [PATCH v6 " Tomasz Duszynski @ 2022-02-17 11:09 ` Tomasz Duszynski 2022-02-18 11:47 ` Thomas Monjalon 2022-02-22 14:18 ` Ferruh Yigit 2022-02-17 11:09 ` [PATCH v6 02/11] raw/cnxk_gpio: support reading default queue conf Tomasz Duszynski ` (10 subsequent siblings) 11 siblings, 2 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-02-17 11:09 UTC (permalink / raw) To: dev, Thomas Monjalon, Nithin Dabilpuram, Kiran Kumar K, Sunil Kumar Kori, Satha Rao, Jakub Palider, Tomasz Duszynski, Ray Kinsella, Anatoly Burakov Cc: jerinj Add initial support for PMD that allows to control particular pins form userspace. Moreover PMD allows to attach custom interrupt handlers to controllable GPIOs. Main users of this PMD are dataplain applications requiring fast and low latency access to pin state. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- MAINTAINERS | 6 + doc/guides/platform/cnxk.rst | 2 + doc/guides/rawdevs/cnxk_gpio.rst | 65 +++++++ doc/guides/rawdevs/index.rst | 1 + doc/guides/rel_notes/release_22_03.rst | 10 + drivers/raw/cnxk_gpio/cnxk_gpio.c | 248 +++++++++++++++++++++++++ drivers/raw/cnxk_gpio/cnxk_gpio.h | 22 +++ drivers/raw/cnxk_gpio/meson.build | 8 + drivers/raw/cnxk_gpio/version.map | 3 + drivers/raw/meson.build | 1 + 10 files changed, 366 insertions(+) create mode 100644 doc/guides/rawdevs/cnxk_gpio.rst create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio.c create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio.h create mode 100644 drivers/raw/cnxk_gpio/meson.build create mode 100644 drivers/raw/cnxk_gpio/version.map diff --git a/MAINTAINERS b/MAINTAINERS index 153c3ff4de..3ce78e6896 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1338,6 +1338,12 @@ M: Tomasz Duszynski <tduszynski@marvell.com> F: doc/guides/rawdevs/cnxk_bphy.rst F: drivers/raw/cnxk_bphy/ +Marvell CNXK GPIO +M: Jakub Palider <jpalider@marvell.com> +M: Tomasz Duszynski <tduszynski@marvell.com> +F: doc/guides/rawdevs/cnxk_gpio.rst +F: drivers/raw/cnxk_gpio/ + NTB M: Jingjing Wu <jingjing.wu@intel.com> M: Junfeng Guo <junfeng.guo@intel.com> diff --git a/doc/guides/platform/cnxk.rst b/doc/guides/platform/cnxk.rst index 97e38c868c..3dee725ac5 100644 --- a/doc/guides/platform/cnxk.rst +++ b/doc/guides/platform/cnxk.rst @@ -73,6 +73,8 @@ DPDK subsystem. +---+-----+--------------------------------------------------------------+ | 11| BPHY| rte_rawdev | +---+-----+--------------------------------------------------------------+ + | 12| GPIO| rte_rawdev | + +---+-----+--------------------------------------------------------------+ PF0 is called the administrative / admin function (AF) and has exclusive privileges to provision RVU functional block's LFs to each of the PF/VF. diff --git a/doc/guides/rawdevs/cnxk_gpio.rst b/doc/guides/rawdevs/cnxk_gpio.rst new file mode 100644 index 0000000000..868302d07f --- /dev/null +++ b/doc/guides/rawdevs/cnxk_gpio.rst @@ -0,0 +1,65 @@ +.. SPDX-License-Identifier: BSD-3-Clause + Copyright(c) 2021 Marvell. + +Marvell CNXK GPIO Driver +======================== + +CNXK GPIO PMD configures and manages GPIOs available on the system using +standard enqueue/dequeue mechanism offered by raw device abstraction. PMD relies +both on standard sysfs GPIO interface provided by the Linux kernel and GPIO +kernel driver custom interface allowing one to install userspace interrupt +handlers. + +Features +-------- + +Following features are available: + +- export/unexport a GPIO +- read/write specific value from/to exported GPIO +- set GPIO direction +- set GPIO edge that triggers interrupt +- set GPIO active low +- register interrupt handler for specific GPIO + +Requirements +------------ + +PMD relies on modified kernel GPIO driver which exposes ``ioctl()`` interface +for installing interrupt handlers for low latency signal processing. + +Driver is shipped with Marvell SDK. + +Device Setup +------------ + +CNXK GPIO PMD binds to virtual device which gets created by passing +`--vdev=cnxk_gpio,gpiochip=<number>` command line to EAL. `gpiochip` parameter +tells PMD which GPIO controller should be used. Available controllers are +available under `/sys/class/gpio`. For further details on how Linux represents +GPIOs in userspace please refer to +`sysfs.txt <https://www.kernel.org/doc/Documentation/gpio/sysfs.txt>`_. + +If `gpiochip=<number>` was omitted then first gpiochip from the alphabetically +sort list of available gpiochips is used. + +.. code-block:: console + + $ ls /sys/class/gpio + export gpiochip448 unexport + +In above scenario only one GPIO controller is present hence +`--vdev=cnxk_gpio,gpiochip=448` should be passed to EAL. + +Before performing actual data transfer one needs to call +``rte_rawdev_queue_count()`` followed by ``rte_rawdev_queue_conf_get()``. The +former returns number GPIOs available in the system irrespective of GPIOs +being controllable or not. Thus it is user responsibility to pick the proper +ones. The latter call simply returns queue capacity. + +Respective queue needs to be configured with ``rte_rawdev_queue_setup()``. This +call barely exports GPIO to userspace. + +To perform actual data transfer use standard ``rte_rawdev_enqueue_buffers()`` +and ``rte_rawdev_dequeue_buffers()`` APIs. Not all messages produce sensible +responses hence dequeueing is not always necessary. diff --git a/doc/guides/rawdevs/index.rst b/doc/guides/rawdevs/index.rst index b6cf917443..0c02da6e90 100644 --- a/doc/guides/rawdevs/index.rst +++ b/doc/guides/rawdevs/index.rst @@ -12,6 +12,7 @@ application through rawdev API. :numbered: cnxk_bphy + cnxk_gpio dpaa2_cmdif dpaa2_qdma ifpga diff --git a/doc/guides/rel_notes/release_22_03.rst b/doc/guides/rel_notes/release_22_03.rst index 41923f50e6..714b7c8e5f 100644 --- a/doc/guides/rel_notes/release_22_03.rst +++ b/doc/guides/rel_notes/release_22_03.rst @@ -55,6 +55,7 @@ New Features Also, make sure to start the actual text at the margin. ======================================================= +<<<<<<< HEAD * **Added ability to reuse hugepages in Linux.** It is possible to reuse files in hugetlbfs to speed up hugepage mapping, @@ -158,6 +159,15 @@ New Features * Called ``rte_ipv4/6_udptcp_cksum_mbuf()`` functions in testpmd csum mode to support software UDP/TCP checksum over multiple segments. +* **Added CNXK GPIO PMD.** + + Added a new rawdevice PMD which allows to manage userspace GPIOs and install + custom GPIO interrupt handlers which bypass kernel. This is especially useful + for applications, that besides providing standard dataplane functionality + want to have fast and low latency access to GPIO pin state. + + See the :doc:`../rawdevs/cnxk_gpio` rawdev guide for more details on this + driver. Removed Items ------------- diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c new file mode 100644 index 0000000000..61069b2185 --- /dev/null +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -0,0 +1,248 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2021 Marvell. + */ + +#include <dirent.h> +#include <string.h> + +#include <rte_bus_vdev.h> +#include <rte_eal.h> +#include <rte_kvargs.h> +#include <rte_lcore.h> +#include <rte_rawdev_pmd.h> + +#include <roc_api.h> + +#include "cnxk_gpio.h" + +#define CNXK_GPIO_BUFSZ 128 +#define CNXK_GPIO_CLASS_PATH "/sys/class/gpio" + +static const char *const cnxk_gpio_args[] = { +#define CNXK_GPIO_ARG_GPIOCHIP "gpiochip" + CNXK_GPIO_ARG_GPIOCHIP, + NULL +}; + +static void +cnxk_gpio_format_name(char *name, size_t len) +{ + snprintf(name, len, "cnxk_gpio"); +} + +static int +cnxk_gpio_filter_gpiochip(const struct dirent *dirent) +{ + const char *pattern = "gpiochip"; + + return !strncmp(dirent->d_name, pattern, strlen(pattern)); +} + +static void +cnxk_gpio_set_defaults(struct cnxk_gpiochip *gpiochip) +{ + struct dirent **namelist; + int n; + + n = scandir(CNXK_GPIO_CLASS_PATH, &namelist, cnxk_gpio_filter_gpiochip, + alphasort); + if (n < 0 || n == 0) + return; + + sscanf(namelist[0]->d_name, "gpiochip%d", &gpiochip->num); + while (n--) + free(namelist[n]); + free(namelist); +} + +static int +cnxk_gpio_parse_arg_gpiochip(const char *key __rte_unused, const char *value, + void *extra_args) +{ + long val; + + errno = 0; + val = strtol(value, NULL, 10); + if (errno) + return -errno; + + *(int *)extra_args = (int)val; + + return 0; +} + +static int +cnxk_gpio_parse_args(struct cnxk_gpiochip *gpiochip, + struct rte_devargs *devargs) +{ + struct rte_kvargs *kvlist; + int ret; + + kvlist = rte_kvargs_parse(devargs->args, cnxk_gpio_args); + if (!kvlist) + return 0; + + ret = rte_kvargs_count(kvlist, CNXK_GPIO_ARG_GPIOCHIP); + if (ret == 1) { + ret = rte_kvargs_process(kvlist, CNXK_GPIO_ARG_GPIOCHIP, + cnxk_gpio_parse_arg_gpiochip, + &gpiochip->num); + if (ret) + goto out; + } + + ret = 0; +out: + rte_kvargs_free(kvlist); + + return ret; +} + +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; +} + +static int +cnxk_gpio_read_attr_int(char *attr, int *val) +{ + char buf[CNXK_GPIO_BUFSZ]; + int ret; + + ret = cnxk_gpio_read_attr(attr, buf); + if (ret) + return ret; + + ret = sscanf(buf, "%d", val); + if (ret < 0) + return -errno; + + return 0; +} + +static int +cnxk_gpio_dev_close(struct rte_rawdev *dev) +{ + RTE_SET_USED(dev); + + return 0; +} + +static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { + .dev_close = cnxk_gpio_dev_close, +}; + +static int +cnxk_gpio_probe(struct rte_vdev_device *dev) +{ + char name[RTE_RAWDEV_NAME_MAX_LEN]; + struct cnxk_gpiochip *gpiochip; + struct rte_rawdev *rawdev; + char buf[CNXK_GPIO_BUFSZ]; + int ret; + + if (rte_eal_process_type() != RTE_PROC_PRIMARY) + return 0; + + cnxk_gpio_format_name(name, sizeof(name)); + rawdev = rte_rawdev_pmd_allocate(name, sizeof(*gpiochip), + rte_socket_id()); + if (!rawdev) { + RTE_LOG(ERR, PMD, "failed to allocate %s rawdev", name); + return -ENOMEM; + } + + rawdev->dev_ops = &cnxk_gpio_rawdev_ops; + rawdev->device = &dev->device; + rawdev->driver_name = dev->device.name; + + gpiochip = rawdev->dev_private; + cnxk_gpio_set_defaults(gpiochip); + + /* defaults may be overwritten by this call */ + ret = cnxk_gpio_parse_args(gpiochip, dev->device.devargs); + if (ret) + goto out; + + /* read gpio base */ + snprintf(buf, sizeof(buf), "%s/gpiochip%d/base", CNXK_GPIO_CLASS_PATH, + gpiochip->num); + ret = cnxk_gpio_read_attr_int(buf, &gpiochip->base); + if (ret) { + RTE_LOG(ERR, PMD, "failed to read %s", buf); + goto out; + } + + /* read number of available gpios */ + snprintf(buf, sizeof(buf), "%s/gpiochip%d/ngpio", CNXK_GPIO_CLASS_PATH, + gpiochip->num); + ret = cnxk_gpio_read_attr_int(buf, &gpiochip->num_gpios); + if (ret) { + RTE_LOG(ERR, PMD, "failed to read %s", buf); + goto out; + } + + gpiochip->gpios = rte_calloc(NULL, gpiochip->num_gpios, + sizeof(struct cnxk_gpio *), 0); + if (!gpiochip->gpios) { + RTE_LOG(ERR, PMD, "failed to allocate gpios memory"); + ret = -ENOMEM; + goto out; + } + + return 0; +out: + rte_rawdev_pmd_release(rawdev); + + return ret; +} + +static int +cnxk_gpio_remove(struct rte_vdev_device *dev) +{ + char name[RTE_RAWDEV_NAME_MAX_LEN]; + struct cnxk_gpiochip *gpiochip; + struct rte_rawdev *rawdev; + + RTE_SET_USED(dev); + + if (rte_eal_process_type() != RTE_PROC_PRIMARY) + return 0; + + cnxk_gpio_format_name(name, sizeof(name)); + rawdev = rte_rawdev_pmd_get_named_dev(name); + if (!rawdev) + return -ENODEV; + + gpiochip = rawdev->dev_private; + rte_free(gpiochip->gpios); + rte_rawdev_pmd_release(rawdev); + + return 0; +} + +static struct rte_vdev_driver cnxk_gpio_drv = { + .probe = cnxk_gpio_probe, + .remove = cnxk_gpio_remove, +}; + +RTE_PMD_REGISTER_VDEV(cnxk_gpio, cnxk_gpio_drv); +RTE_PMD_REGISTER_PARAM_STRING(cnxk_gpio, "gpiochip=<int>"); diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.h b/drivers/raw/cnxk_gpio/cnxk_gpio.h new file mode 100644 index 0000000000..4dae8316ba --- /dev/null +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2021 Marvell. + */ + +#ifndef _CNXK_GPIO_H_ +#define _CNXK_GPIO_H_ + +struct cnxk_gpiochip; + +struct cnxk_gpio { + struct cnxk_gpiochip *gpiochip; + int num; +}; + +struct cnxk_gpiochip { + int num; + int base; + int num_gpios; + struct cnxk_gpio **gpios; +}; + +#endif /* _CNXK_GPIO_H_ */ diff --git a/drivers/raw/cnxk_gpio/meson.build b/drivers/raw/cnxk_gpio/meson.build new file mode 100644 index 0000000000..9a7e716c1e --- /dev/null +++ b/drivers/raw/cnxk_gpio/meson.build @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(C) 2021 Marvell. +# + +deps += ['bus_vdev', 'common_cnxk', 'rawdev', 'kvargs'] +sources = files( + 'cnxk_gpio.c', +) diff --git a/drivers/raw/cnxk_gpio/version.map b/drivers/raw/cnxk_gpio/version.map new file mode 100644 index 0000000000..4a76d1d52d --- /dev/null +++ b/drivers/raw/cnxk_gpio/version.map @@ -0,0 +1,3 @@ +DPDK_21 { + local: *; +}; diff --git a/drivers/raw/meson.build b/drivers/raw/meson.build index 87694a758e..05e7de1bfe 100644 --- a/drivers/raw/meson.build +++ b/drivers/raw/meson.build @@ -7,6 +7,7 @@ endif drivers = [ 'cnxk_bphy', + 'cnxk_gpio', 'dpaa2_cmdif', 'dpaa2_qdma', 'ifpga', -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v6 01/11] raw/cnxk_gpio: add GPIO driver skeleton 2022-02-17 11:09 ` [PATCH v6 01/11] raw/cnxk_gpio: add GPIO driver skeleton Tomasz Duszynski @ 2022-02-18 11:47 ` Thomas Monjalon 2022-02-22 14:18 ` Ferruh Yigit 1 sibling, 0 replies; 75+ messages in thread From: Thomas Monjalon @ 2022-02-18 11:47 UTC (permalink / raw) To: Tomasz Duszynski, Tomasz Duszynski Cc: dev, Nithin Dabilpuram, Kiran Kumar K, Sunil Kumar Kori, Satha Rao, Jakub Palider, Ray Kinsella, Anatoly Burakov, jerinj 17/02/2022 12:09, Tomasz Duszynski: > --- a/doc/guides/rel_notes/release_22_03.rst > +++ b/doc/guides/rel_notes/release_22_03.rst > @@ -55,6 +55,7 @@ New Features > Also, make sure to start the actual text at the margin. > ======================================================= > > +<<<<<<< HEAD > * **Added ability to reuse hugepages in Linux.** Will remove this rebase trace while merging. ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v6 01/11] raw/cnxk_gpio: add GPIO driver skeleton 2022-02-17 11:09 ` [PATCH v6 01/11] raw/cnxk_gpio: add GPIO driver skeleton Tomasz Duszynski 2022-02-18 11:47 ` Thomas Monjalon @ 2022-02-22 14:18 ` Ferruh Yigit 1 sibling, 0 replies; 75+ messages in thread From: Ferruh Yigit @ 2022-02-22 14:18 UTC (permalink / raw) To: Tomasz Duszynski, dev, Thomas Monjalon, Nithin Dabilpuram, Kiran Kumar K, Sunil Kumar Kori, Satha Rao, Jakub Palider, Ray Kinsella, Anatoly Burakov Cc: jerinj On 2/17/2022 11:09 AM, Tomasz Duszynski wrote: > diff --git a/drivers/raw/cnxk_gpio/version.map b/drivers/raw/cnxk_gpio/version.map > new file mode 100644 > index 0000000000..4a76d1d52d > --- /dev/null > +++ b/drivers/raw/cnxk_gpio/version.map > @@ -0,0 +1,3 @@ > +DPDK_21 { > + local: *; > +}; This should be 'DPDK_22' now, since patch is merged can you please send an incremental fix? ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v6 02/11] raw/cnxk_gpio: support reading default queue conf 2022-02-17 11:09 ` [PATCH v6 " Tomasz Duszynski 2022-02-17 11:09 ` [PATCH v6 01/11] raw/cnxk_gpio: add GPIO driver skeleton Tomasz Duszynski @ 2022-02-17 11:09 ` Tomasz Duszynski 2022-02-17 11:09 ` [PATCH v6 03/11] raw/cnxk_gpio: support reading queue count Tomasz Duszynski ` (9 subsequent siblings) 11 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-02-17 11:09 UTC (permalink / raw) To: dev, Jakub Palider, Tomasz Duszynski; +Cc: jerinj, thomas Add support for reading default queue configuration. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 61069b2185..1f36f6e22c 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -146,8 +146,27 @@ cnxk_gpio_dev_close(struct rte_rawdev *dev) return 0; } +static int +cnxk_gpio_queue_def_conf(struct rte_rawdev *dev, uint16_t queue_id, + rte_rawdev_obj_t queue_conf, size_t queue_conf_size) +{ + unsigned int *conf; + + RTE_SET_USED(dev); + RTE_SET_USED(queue_id); + + if (queue_conf_size != sizeof(*conf)) + return -EINVAL; + + conf = (unsigned int *)queue_conf; + *conf = 1; + + return 0; +} + static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { .dev_close = cnxk_gpio_dev_close, + .queue_def_conf = cnxk_gpio_queue_def_conf, }; static int -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v6 03/11] raw/cnxk_gpio: support reading queue count 2022-02-17 11:09 ` [PATCH v6 " Tomasz Duszynski 2022-02-17 11:09 ` [PATCH v6 01/11] raw/cnxk_gpio: add GPIO driver skeleton Tomasz Duszynski 2022-02-17 11:09 ` [PATCH v6 02/11] raw/cnxk_gpio: support reading default queue conf Tomasz Duszynski @ 2022-02-17 11:09 ` Tomasz Duszynski 2022-02-17 11:09 ` [PATCH v6 04/11] raw/cnxk_gpio: support queue setup Tomasz Duszynski ` (8 subsequent siblings) 11 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-02-17 11:09 UTC (permalink / raw) To: dev, Jakub Palider, Tomasz Duszynski; +Cc: jerinj, thomas Add support for reading number of available queues. Single queue corresponds to GPIO. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 1f36f6e22c..cee75e389a 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -164,9 +164,18 @@ cnxk_gpio_queue_def_conf(struct rte_rawdev *dev, uint16_t queue_id, return 0; } +static uint16_t +cnxk_gpio_queue_count(struct rte_rawdev *dev) +{ + struct cnxk_gpiochip *gpiochip = dev->dev_private; + + return gpiochip->num_gpios; +} + static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { .dev_close = cnxk_gpio_dev_close, .queue_def_conf = cnxk_gpio_queue_def_conf, + .queue_count = cnxk_gpio_queue_count, }; static int -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v6 04/11] raw/cnxk_gpio: support queue setup 2022-02-17 11:09 ` [PATCH v6 " Tomasz Duszynski ` (2 preceding siblings ...) 2022-02-17 11:09 ` [PATCH v6 03/11] raw/cnxk_gpio: support reading queue count Tomasz Duszynski @ 2022-02-17 11:09 ` Tomasz Duszynski 2022-02-17 11:09 ` [PATCH v6 05/11] raw/cnxk_gpio: support queue release Tomasz Duszynski ` (7 subsequent siblings) 11 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-02-17 11:09 UTC (permalink / raw) To: dev, Jakub Palider, Tomasz Duszynski; +Cc: jerinj, thomas Add support for queue setup. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 84 ++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 2 deletions(-) diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index cee75e389a..9baa43187e 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -139,9 +139,80 @@ cnxk_gpio_read_attr_int(char *attr, int *val) } static int -cnxk_gpio_dev_close(struct rte_rawdev *dev) +cnxk_gpio_write_attr(const char *attr, const char *val) { - RTE_SET_USED(dev); + FILE *fp; + int ret; + + if (!val) + return -EINVAL; + + fp = fopen(attr, "w"); + if (!fp) + return -errno; + + ret = fprintf(fp, "%s", val); + if (ret < 0) { + fclose(fp); + return ret; + } + + ret = fclose(fp); + if (ret) + return -errno; + + return 0; +} + +static int +cnxk_gpio_write_attr_int(const char *attr, int val) +{ + char buf[CNXK_GPIO_BUFSZ]; + + snprintf(buf, sizeof(buf), "%d", val); + + return cnxk_gpio_write_attr(attr, buf); +} + +static struct cnxk_gpio * +cnxk_gpio_lookup(struct cnxk_gpiochip *gpiochip, uint16_t queue) +{ + if (queue >= gpiochip->num_gpios) + return NULL; + + return gpiochip->gpios[queue]; +} + +static int +cnxk_gpio_queue_setup(struct rte_rawdev *dev, uint16_t queue_id, + rte_rawdev_obj_t queue_conf, size_t queue_conf_size) +{ + struct cnxk_gpiochip *gpiochip = dev->dev_private; + char buf[CNXK_GPIO_BUFSZ]; + struct cnxk_gpio *gpio; + int ret; + + RTE_SET_USED(queue_conf); + RTE_SET_USED(queue_conf_size); + + gpio = cnxk_gpio_lookup(gpiochip, queue_id); + if (gpio) + return -EEXIST; + + gpio = rte_zmalloc(NULL, sizeof(*gpio), 0); + if (!gpio) + return -ENOMEM; + gpio->num = queue_id + gpiochip->base; + gpio->gpiochip = gpiochip; + + snprintf(buf, sizeof(buf), "%s/export", CNXK_GPIO_CLASS_PATH); + ret = cnxk_gpio_write_attr_int(buf, gpio->num); + if (ret) { + rte_free(gpio); + return ret; + } + + gpiochip->gpios[queue_id] = gpio; return 0; } @@ -172,10 +243,19 @@ cnxk_gpio_queue_count(struct rte_rawdev *dev) return gpiochip->num_gpios; } +static int +cnxk_gpio_dev_close(struct rte_rawdev *dev) +{ + RTE_SET_USED(dev); + + return 0; +} + static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { .dev_close = cnxk_gpio_dev_close, .queue_def_conf = cnxk_gpio_queue_def_conf, .queue_count = cnxk_gpio_queue_count, + .queue_setup = cnxk_gpio_queue_setup, }; static int -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v6 05/11] raw/cnxk_gpio: support queue release 2022-02-17 11:09 ` [PATCH v6 " Tomasz Duszynski ` (3 preceding siblings ...) 2022-02-17 11:09 ` [PATCH v6 04/11] raw/cnxk_gpio: support queue setup Tomasz Duszynski @ 2022-02-17 11:09 ` Tomasz Duszynski 2022-02-17 11:09 ` [PATCH v6 06/11] raw/cnxk_gpio: support enqueuing buffers Tomasz Duszynski ` (6 subsequent siblings) 11 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-02-17 11:09 UTC (permalink / raw) To: dev, Jakub Palider, Tomasz Duszynski; +Cc: jerinj, thomas Add support for queue release. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 9baa43187e..ec82a55918 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -217,6 +217,29 @@ cnxk_gpio_queue_setup(struct rte_rawdev *dev, uint16_t queue_id, return 0; } +static int +cnxk_gpio_queue_release(struct rte_rawdev *dev, uint16_t queue_id) +{ + struct cnxk_gpiochip *gpiochip = dev->dev_private; + char buf[CNXK_GPIO_BUFSZ]; + struct cnxk_gpio *gpio; + int ret; + + gpio = cnxk_gpio_lookup(gpiochip, queue_id); + if (!gpio) + return -ENODEV; + + snprintf(buf, sizeof(buf), "%s/unexport", CNXK_GPIO_CLASS_PATH); + ret = cnxk_gpio_write_attr_int(buf, gpiochip->base + queue_id); + if (ret) + return ret; + + gpiochip->gpios[queue_id] = NULL; + rte_free(gpio); + + return 0; +} + static int cnxk_gpio_queue_def_conf(struct rte_rawdev *dev, uint16_t queue_id, rte_rawdev_obj_t queue_conf, size_t queue_conf_size) @@ -256,6 +279,7 @@ static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { .queue_def_conf = cnxk_gpio_queue_def_conf, .queue_count = cnxk_gpio_queue_count, .queue_setup = cnxk_gpio_queue_setup, + .queue_release = cnxk_gpio_queue_release, }; static int @@ -329,6 +353,8 @@ cnxk_gpio_remove(struct rte_vdev_device *dev) char name[RTE_RAWDEV_NAME_MAX_LEN]; struct cnxk_gpiochip *gpiochip; struct rte_rawdev *rawdev; + struct cnxk_gpio *gpio; + int i; RTE_SET_USED(dev); @@ -341,6 +367,14 @@ cnxk_gpio_remove(struct rte_vdev_device *dev) return -ENODEV; gpiochip = rawdev->dev_private; + for (i = 0; i < gpiochip->num_gpios; i++) { + gpio = gpiochip->gpios[i]; + if (!gpio) + continue; + + cnxk_gpio_queue_release(rawdev, gpio->num); + } + rte_free(gpiochip->gpios); rte_rawdev_pmd_release(rawdev); -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v6 06/11] raw/cnxk_gpio: support enqueuing buffers 2022-02-17 11:09 ` [PATCH v6 " Tomasz Duszynski ` (4 preceding siblings ...) 2022-02-17 11:09 ` [PATCH v6 05/11] raw/cnxk_gpio: support queue release Tomasz Duszynski @ 2022-02-17 11:09 ` Tomasz Duszynski 2022-02-17 11:09 ` [PATCH v6 07/11] raw/cnxk_gpio: support dequeuing buffers Tomasz Duszynski ` (5 subsequent siblings) 11 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-02-17 11:09 UTC (permalink / raw) To: dev, Jakub Palider, Tomasz Duszynski; +Cc: jerinj, thomas Add dummy support for enqueuing buffers. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 47 +++++++++++++++++++++++ drivers/raw/cnxk_gpio/cnxk_gpio.h | 1 + drivers/raw/cnxk_gpio/meson.build | 1 + drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h | 38 ++++++++++++++++++ 4 files changed, 87 insertions(+) create mode 100644 drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index ec82a55918..6f3795df41 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -14,6 +14,7 @@ #include <roc_api.h> #include "cnxk_gpio.h" +#include "rte_pmd_cnxk_gpio.h" #define CNXK_GPIO_BUFSZ 128 #define CNXK_GPIO_CLASS_PATH "/sys/class/gpio" @@ -274,8 +275,54 @@ cnxk_gpio_dev_close(struct rte_rawdev *dev) return 0; } +static int +cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf) +{ + struct cnxk_gpio_msg *msg = rbuf->buf_addr; + void *rsp = NULL; + int ret; + + switch (msg->type) { + default: + return -EINVAL; + } + + /* get rid of last response if any */ + if (gpio->rsp) { + RTE_LOG(WARNING, PMD, "previous response got overwritten\n"); + rte_free(gpio->rsp); + } + gpio->rsp = rsp; + + return ret; +} + +static int +cnxk_gpio_enqueue_bufs(struct rte_rawdev *dev, struct rte_rawdev_buf **buffers, + unsigned int count, rte_rawdev_obj_t context) +{ + struct cnxk_gpiochip *gpiochip = dev->dev_private; + unsigned int queue = (size_t)context; + struct cnxk_gpio *gpio; + int ret; + + if (count == 0) + return 0; + + gpio = cnxk_gpio_lookup(gpiochip, queue); + if (!gpio) + return -ENODEV; + + ret = cnxk_gpio_process_buf(gpio, buffers[0]); + if (ret) + return ret; + + return 1; +} + static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { .dev_close = cnxk_gpio_dev_close, + .enqueue_bufs = cnxk_gpio_enqueue_bufs, .queue_def_conf = cnxk_gpio_queue_def_conf, .queue_count = cnxk_gpio_queue_count, .queue_setup = cnxk_gpio_queue_setup, diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.h b/drivers/raw/cnxk_gpio/cnxk_gpio.h index 4dae8316ba..6b54ebe6e6 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.h +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.h @@ -9,6 +9,7 @@ struct cnxk_gpiochip; struct cnxk_gpio { struct cnxk_gpiochip *gpiochip; + void *rsp; int num; }; diff --git a/drivers/raw/cnxk_gpio/meson.build b/drivers/raw/cnxk_gpio/meson.build index 9a7e716c1e..3fbfdd838c 100644 --- a/drivers/raw/cnxk_gpio/meson.build +++ b/drivers/raw/cnxk_gpio/meson.build @@ -6,3 +6,4 @@ deps += ['bus_vdev', 'common_cnxk', 'rawdev', 'kvargs'] sources = files( 'cnxk_gpio.c', ) +headers = files('rte_pmd_cnxk_gpio.h') diff --git a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h new file mode 100644 index 0000000000..c71065e10c --- /dev/null +++ b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2021 Marvell. + */ + +#ifndef _RTE_PMD_CNXK_GPIO_H_ +#define _RTE_PMD_CNXK_GPIO_H_ + +/** + * @file rte_pmd_cnxk_gpio.h + * + * Marvell GPIO PMD specific structures and interface + * + * This API allows applications to manage GPIOs in user space along with + * installing interrupt handlers for low latency signal processing. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** Available message types */ +enum cnxk_gpio_msg_type { + /** Invalid message type */ + CNXK_GPIO_MSG_TYPE_INVALID, +}; + +struct cnxk_gpio_msg { + /** Message type */ + enum cnxk_gpio_msg_type type; + /** Message data passed to PMD or received from PMD */ + void *data; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _RTE_PMD_CNXK_GPIO_H_ */ -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v6 07/11] raw/cnxk_gpio: support dequeuing buffers 2022-02-17 11:09 ` [PATCH v6 " Tomasz Duszynski ` (5 preceding siblings ...) 2022-02-17 11:09 ` [PATCH v6 06/11] raw/cnxk_gpio: support enqueuing buffers Tomasz Duszynski @ 2022-02-17 11:09 ` Tomasz Duszynski 2022-02-17 11:09 ` [PATCH v6 08/11] raw/cnxk_gpio: support standard GPIO operations Tomasz Duszynski ` (4 subsequent siblings) 11 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-02-17 11:09 UTC (permalink / raw) To: dev, Jakub Palider, Tomasz Duszynski; +Cc: jerinj, thomas Add support for dequeuing buffers. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- drivers/raw/cnxk_gpio/cnxk_gpio.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 6f3795df41..46f9e63dd7 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -320,9 +320,35 @@ cnxk_gpio_enqueue_bufs(struct rte_rawdev *dev, struct rte_rawdev_buf **buffers, return 1; } +static int +cnxk_gpio_dequeue_bufs(struct rte_rawdev *dev, struct rte_rawdev_buf **buffers, + unsigned int count, rte_rawdev_obj_t context) +{ + struct cnxk_gpiochip *gpiochip = dev->dev_private; + unsigned int queue = (size_t)context; + struct cnxk_gpio *gpio; + + if (count == 0) + return 0; + + gpio = cnxk_gpio_lookup(gpiochip, queue); + if (!gpio) + return -ENODEV; + + if (gpio->rsp) { + buffers[0]->buf_addr = gpio->rsp; + gpio->rsp = NULL; + + return 1; + } + + return 0; +} + static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { .dev_close = cnxk_gpio_dev_close, .enqueue_bufs = cnxk_gpio_enqueue_bufs, + .dequeue_bufs = cnxk_gpio_dequeue_bufs, .queue_def_conf = cnxk_gpio_queue_def_conf, .queue_count = cnxk_gpio_queue_count, .queue_setup = cnxk_gpio_queue_setup, -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v6 08/11] raw/cnxk_gpio: support standard GPIO operations 2022-02-17 11:09 ` [PATCH v6 " Tomasz Duszynski ` (6 preceding siblings ...) 2022-02-17 11:09 ` [PATCH v6 07/11] raw/cnxk_gpio: support dequeuing buffers Tomasz Duszynski @ 2022-02-17 11:09 ` Tomasz Duszynski 2022-02-17 11:09 ` [PATCH v6 09/11] raw/cnxk_gpio: support custom irq handlers Tomasz Duszynski ` (3 subsequent siblings) 11 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-02-17 11:09 UTC (permalink / raw) To: dev, Jakub Palider, Tomasz Duszynski; +Cc: jerinj, thomas Add support for standard GPIO operations i.e ones normally provided by GPIO sysfs interface. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- doc/guides/rawdevs/cnxk_gpio.rst | 98 ++++++++ drivers/raw/cnxk_gpio/cnxk_gpio.c | 147 +++++++++++- drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h | 279 +++++++++++++++++++++- 3 files changed, 521 insertions(+), 3 deletions(-) diff --git a/doc/guides/rawdevs/cnxk_gpio.rst b/doc/guides/rawdevs/cnxk_gpio.rst index 868302d07f..f6c3c942c5 100644 --- a/doc/guides/rawdevs/cnxk_gpio.rst +++ b/doc/guides/rawdevs/cnxk_gpio.rst @@ -63,3 +63,101 @@ call barely exports GPIO to userspace. To perform actual data transfer use standard ``rte_rawdev_enqueue_buffers()`` and ``rte_rawdev_dequeue_buffers()`` APIs. Not all messages produce sensible responses hence dequeueing is not always necessary. + +CNXK GPIO PMD +------------- + +PMD accepts ``struct cnxk_gpio_msg`` messages which differ by type and payload. +Message types along with description are listed below. As for the usage examples +please refer to ``cnxk_gpio_selftest()``. There's a set of convenient wrappers +available, one for each existing command. + +Set GPIO value +~~~~~~~~~~~~~~ + +Message is used to set output to low or high. This does not work for GPIOs +configured as input. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_SET_PIN_VALUE``. + +Payload must be an integer set to 0 (low) or 1 (high). + +Consider using ``rte_pmd_gpio_set_pin_value()`` wrapper. + +Set GPIO edge +~~~~~~~~~~~~~ + +Message is used to set edge that triggers interrupt. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_SET_PIN_EDGE``. + +Payload must be `enum cnxk_gpio_pin_edge`. + +Consider using ``rte_pmd_gpio_set_pin_edge()`` wrapper. + +Set GPIO direction +~~~~~~~~~~~~~~~~~~ + +Message is used to change GPIO direction to either input or output. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_SET_PIN_DIR``. + +Payload must be `enum cnxk_gpio_pin_dir`. + +Consider using ``rte_pmd_gpio_set_pin_dir()`` wrapper. + +Set GPIO active low +~~~~~~~~~~~~~~~~~~~ + +Message is used to set whether pin is active low. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_SET_PIN_ACTIVE_LOW``. + +Payload must be an integer set to 0 or 1. The latter activates inversion. + +Consider using ``rte_pmd_gpio_set_pin_active_low()`` wrapper. + +Get GPIO value +~~~~~~~~~~~~~~ + +Message is used to read GPIO value. Value can be 0 (low) or 1 (high). + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_GET_PIN_VALUE``. + +Payload contains integer set to either 0 or 1. + +Consider using ``rte_pmd_gpio_get_pin_value()`` wrapper. + +Get GPIO edge +~~~~~~~~~~~~~ + +Message is used to read GPIO edge. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_GET_PIN_EDGE``. + +Payload contains `enum cnxk_gpio_pin_edge`. + +Consider using ``rte_pmd_gpio_get_pin_edge()`` wrapper. + +Get GPIO direction +~~~~~~~~~~~~~~~~~~ + +Message is used to read GPIO direction. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_GET_PIN_DIR``. + +Payload contains `enum cnxk_gpio_pin_dir`. + +Consider using ``rte_pmd_gpio_get_pin_dir()`` wrapper. + +Get GPIO active low +~~~~~~~~~~~~~~~~~~~ + +Message is used check whether inverted logic is active. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW``. + +Payload contains an integer set to 0 or 1. The latter means inverted logic +is turned on. + +Consider using ``rte_pmd_gpio_get_pin_active_low()`` wrapper. diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 46f9e63dd7..67c6d4813c 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -267,6 +267,78 @@ cnxk_gpio_queue_count(struct rte_rawdev *dev) return gpiochip->num_gpios; } +static const struct { + enum cnxk_gpio_pin_edge edge; + const char *name; +} cnxk_gpio_edge_name[] = { + { CNXK_GPIO_PIN_EDGE_NONE, "none" }, + { CNXK_GPIO_PIN_EDGE_FALLING, "falling" }, + { CNXK_GPIO_PIN_EDGE_RISING, "rising" }, + { CNXK_GPIO_PIN_EDGE_BOTH, "both" }, +}; + +static const char * +cnxk_gpio_edge_to_name(enum cnxk_gpio_pin_edge edge) +{ + unsigned int i; + + for (i = 0; i < RTE_DIM(cnxk_gpio_edge_name); i++) { + if (cnxk_gpio_edge_name[i].edge == edge) + return cnxk_gpio_edge_name[i].name; + } + + return NULL; +} + +static enum cnxk_gpio_pin_edge +cnxk_gpio_name_to_edge(const char *name) +{ + unsigned int i; + + for (i = 0; i < RTE_DIM(cnxk_gpio_edge_name); i++) { + if (!strcmp(cnxk_gpio_edge_name[i].name, name)) + break; + } + + return cnxk_gpio_edge_name[i].edge; +} + +static const struct { + enum cnxk_gpio_pin_dir dir; + const char *name; +} cnxk_gpio_dir_name[] = { + { CNXK_GPIO_PIN_DIR_IN, "in" }, + { CNXK_GPIO_PIN_DIR_OUT, "out" }, + { CNXK_GPIO_PIN_DIR_HIGH, "high" }, + { CNXK_GPIO_PIN_DIR_LOW, "low" }, +}; + +static const char * +cnxk_gpio_dir_to_name(enum cnxk_gpio_pin_dir dir) +{ + unsigned int i; + + for (i = 0; i < RTE_DIM(cnxk_gpio_dir_name); i++) { + if (cnxk_gpio_dir_name[i].dir == dir) + return cnxk_gpio_dir_name[i].name; + } + + return NULL; +} + +static enum cnxk_gpio_pin_dir +cnxk_gpio_name_to_dir(const char *name) +{ + unsigned int i; + + for (i = 0; i < RTE_DIM(cnxk_gpio_dir_name); i++) { + if (!strcmp(cnxk_gpio_dir_name[i].name, name)) + break; + } + + return cnxk_gpio_dir_name[i].dir; +} + static int cnxk_gpio_dev_close(struct rte_rawdev *dev) { @@ -279,10 +351,83 @@ static int cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf) { struct cnxk_gpio_msg *msg = rbuf->buf_addr; + enum cnxk_gpio_pin_edge edge; + enum cnxk_gpio_pin_dir dir; + char buf[CNXK_GPIO_BUFSZ]; void *rsp = NULL; - int ret; + int ret, val, n; + + n = snprintf(buf, sizeof(buf), "%s/gpio%d", CNXK_GPIO_CLASS_PATH, + gpio->num); switch (msg->type) { + case CNXK_GPIO_MSG_TYPE_SET_PIN_VALUE: + snprintf(buf + n, sizeof(buf) - n, "/value"); + ret = cnxk_gpio_write_attr_int(buf, !!*(int *)msg->data); + break; + case CNXK_GPIO_MSG_TYPE_SET_PIN_EDGE: + snprintf(buf + n, sizeof(buf) - n, "/edge"); + edge = *(enum cnxk_gpio_pin_edge *)msg->data; + ret = cnxk_gpio_write_attr(buf, cnxk_gpio_edge_to_name(edge)); + break; + case CNXK_GPIO_MSG_TYPE_SET_PIN_DIR: + snprintf(buf + n, sizeof(buf) - n, "/direction"); + dir = *(enum cnxk_gpio_pin_dir *)msg->data; + ret = cnxk_gpio_write_attr(buf, cnxk_gpio_dir_to_name(dir)); + break; + case CNXK_GPIO_MSG_TYPE_SET_PIN_ACTIVE_LOW: + snprintf(buf + n, sizeof(buf) - n, "/active_low"); + val = *(int *)msg->data; + ret = cnxk_gpio_write_attr_int(buf, val); + break; + case CNXK_GPIO_MSG_TYPE_GET_PIN_VALUE: + snprintf(buf + n, sizeof(buf) - n, "/value"); + ret = cnxk_gpio_read_attr_int(buf, &val); + if (ret) + break; + + rsp = rte_zmalloc(NULL, sizeof(int), 0); + if (!rsp) + return -ENOMEM; + + *(int *)rsp = val; + break; + case CNXK_GPIO_MSG_TYPE_GET_PIN_EDGE: + snprintf(buf + n, sizeof(buf) - n, "/edge"); + ret = cnxk_gpio_read_attr(buf, buf); + if (ret) + break; + + rsp = rte_zmalloc(NULL, sizeof(enum cnxk_gpio_pin_edge), 0); + if (!rsp) + return -ENOMEM; + + *(enum cnxk_gpio_pin_edge *)rsp = cnxk_gpio_name_to_edge(buf); + break; + case CNXK_GPIO_MSG_TYPE_GET_PIN_DIR: + snprintf(buf + n, sizeof(buf) - n, "/direction"); + ret = cnxk_gpio_read_attr(buf, buf); + if (ret) + break; + + rsp = rte_zmalloc(NULL, sizeof(enum cnxk_gpio_pin_dir), 0); + if (!rsp) + return -ENOMEM; + + *(enum cnxk_gpio_pin_dir *)rsp = cnxk_gpio_name_to_dir(buf); + break; + case CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW: + snprintf(buf + n, sizeof(buf) - n, "/active_low"); + ret = cnxk_gpio_read_attr_int(buf, &val); + if (ret) + break; + + rsp = rte_zmalloc(NULL, sizeof(int), 0); + if (!rsp) + return -ENOMEM; + + *(int *)rsp = val; + break; default: return -EINVAL; } diff --git a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h index c71065e10c..7c3dc225ca 100644 --- a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h +++ b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h @@ -5,6 +5,10 @@ #ifndef _RTE_PMD_CNXK_GPIO_H_ #define _RTE_PMD_CNXK_GPIO_H_ +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_rawdev.h> + /** * @file rte_pmd_cnxk_gpio.h * @@ -20,8 +24,46 @@ extern "C" { /** Available message types */ enum cnxk_gpio_msg_type { - /** Invalid message type */ - CNXK_GPIO_MSG_TYPE_INVALID, + /** Type used to set output value */ + CNXK_GPIO_MSG_TYPE_SET_PIN_VALUE, + /** Type used to set edge */ + CNXK_GPIO_MSG_TYPE_SET_PIN_EDGE, + /** Type used to set direction */ + CNXK_GPIO_MSG_TYPE_SET_PIN_DIR, + /** Type used to set inverted logic */ + CNXK_GPIO_MSG_TYPE_SET_PIN_ACTIVE_LOW, + /** Type used to read value */ + CNXK_GPIO_MSG_TYPE_GET_PIN_VALUE, + /** Type used to read edge */ + CNXK_GPIO_MSG_TYPE_GET_PIN_EDGE, + /** Type used to read direction */ + CNXK_GPIO_MSG_TYPE_GET_PIN_DIR, + /** Type used to read inverted logic state */ + CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW, +}; + +/** Available edges */ +enum cnxk_gpio_pin_edge { + /** Set edge to none */ + CNXK_GPIO_PIN_EDGE_NONE, + /** Set edge to falling */ + CNXK_GPIO_PIN_EDGE_FALLING, + /** Set edge to rising */ + CNXK_GPIO_PIN_EDGE_RISING, + /** Set edge to both rising and falling */ + CNXK_GPIO_PIN_EDGE_BOTH, +}; + +/** Available directions */ +enum cnxk_gpio_pin_dir { + /** Set direction to input */ + CNXK_GPIO_PIN_DIR_IN, + /** Set direction to output */ + CNXK_GPIO_PIN_DIR_OUT, + /** Set direction to output and value to 1 */ + CNXK_GPIO_PIN_DIR_HIGH, + /* Set direction to output and value to 0 */ + CNXK_GPIO_PIN_DIR_LOW, }; struct cnxk_gpio_msg { @@ -31,6 +73,239 @@ struct cnxk_gpio_msg { void *data; }; +/** @internal helper routine for enqueuing/dequeuing messages */ +static __rte_always_inline int +__rte_pmd_gpio_enq_deq(uint16_t dev_id, int gpio, void *req, void *rsp, + size_t rsp_size) +{ + struct rte_rawdev_buf *bufs[1]; + struct rte_rawdev_buf buf; + void *q; + int ret; + + q = (void *)(size_t)gpio; + buf.buf_addr = req; + bufs[0] = &buf; + + ret = rte_rawdev_enqueue_buffers(dev_id, bufs, RTE_DIM(bufs), q); + if (ret < 0) + return ret; + if (ret != RTE_DIM(bufs)) + return -EIO; + + if (!rsp) + return 0; + + ret = rte_rawdev_dequeue_buffers(dev_id, bufs, RTE_DIM(bufs), q); + if (ret < 0) + return ret; + if (ret != RTE_DIM(bufs)) + return -EIO; + + rte_memcpy(rsp, buf.buf_addr, rsp_size); + rte_free(buf.buf_addr); + + return 0; +} + +/** + * Set output to specific value + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param val + * Value output will be set to. 0 represents low state while + * 1 high state + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_set_pin_value(uint16_t dev_id, int gpio, int val) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_SET_PIN_VALUE, + .data = &val, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Select signal edge that triggers interrupt + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param edge + * Signal edge that triggers interrupt + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_set_pin_edge(uint16_t dev_id, int gpio, + enum cnxk_gpio_pin_edge edge) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_SET_PIN_EDGE, + .data = &edge + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Configure GPIO as input or output + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param dir + * Direction of the GPIO + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_set_pin_dir(uint16_t dev_id, int gpio, enum cnxk_gpio_pin_dir dir) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_SET_PIN_DIR, + .data = &dir, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Enable or disable inverted logic + * + * If GPIO is configured as output then writing 1 or 0 will result in setting + * output to respectively low or high + * + * If GPIO is configured as input then logic inversion applies to edges. Both + * current and future settings are affected + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param val + * 0 to disable, 1 to enable inverted logic + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_set_pin_active_low(uint16_t dev_id, int gpio, int val) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_SET_PIN_ACTIVE_LOW, + .data = &val, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Read GPIO value + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param val + * Where to store read logical signal value i.e 0 or 1 + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_get_pin_value(uint16_t dev_id, int gpio, int *val) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_GET_PIN_VALUE, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, val, sizeof(*val)); +} + +/** + * Read GPIO edge + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param edge + * Where to store edge + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_get_pin_edge(uint16_t dev_id, int gpio, + enum cnxk_gpio_pin_edge *edge) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_GET_PIN_EDGE, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, edge, sizeof(*edge)); +} + +/** + * Read GPIO direction + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param dir + * Where to store direction + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_get_pin_dir(uint16_t dev_id, int gpio, enum cnxk_gpio_pin_dir *dir) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_GET_PIN_DIR, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, dir, sizeof(*dir)); +} + +/** + * Read whether GPIO is active low + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param val + * Where to store active low state + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_get_pin_active_low(uint16_t dev_id, int gpio, int *val) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW, + .data = &val, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, val, sizeof(*val)); +} + #ifdef __cplusplus } #endif -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v6 09/11] raw/cnxk_gpio: support custom irq handlers 2022-02-17 11:09 ` [PATCH v6 " Tomasz Duszynski ` (7 preceding siblings ...) 2022-02-17 11:09 ` [PATCH v6 08/11] raw/cnxk_gpio: support standard GPIO operations Tomasz Duszynski @ 2022-02-17 11:09 ` Tomasz Duszynski 2022-02-17 11:09 ` [PATCH v6 10/11] raw/cnxk_gpio: support selftest Tomasz Duszynski ` (2 subsequent siblings) 11 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-02-17 11:09 UTC (permalink / raw) To: dev, Jakub Palider, Tomasz Duszynski; +Cc: jerinj, thomas Add support for custom interrupt handlers. Custom interrupt handlers bypass kernel completely and are meant for fast and low latency access to GPIO state. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- doc/guides/rawdevs/cnxk_gpio.rst | 21 +++ drivers/raw/cnxk_gpio/cnxk_gpio.c | 41 +++- drivers/raw/cnxk_gpio/cnxk_gpio.h | 8 + drivers/raw/cnxk_gpio/cnxk_gpio_irq.c | 216 ++++++++++++++++++++++ drivers/raw/cnxk_gpio/meson.build | 1 + drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h | 116 ++++++++++++ 6 files changed, 401 insertions(+), 2 deletions(-) create mode 100644 drivers/raw/cnxk_gpio/cnxk_gpio_irq.c diff --git a/doc/guides/rawdevs/cnxk_gpio.rst b/doc/guides/rawdevs/cnxk_gpio.rst index f6c3c942c5..ad93ec0d44 100644 --- a/doc/guides/rawdevs/cnxk_gpio.rst +++ b/doc/guides/rawdevs/cnxk_gpio.rst @@ -161,3 +161,24 @@ Payload contains an integer set to 0 or 1. The latter means inverted logic is turned on. Consider using ``rte_pmd_gpio_get_pin_active_low()`` wrapper. + +Request interrupt +~~~~~~~~~~~~~~~~~ + +Message is used to install custom interrupt handler. + +Message must have type set to ``CNXK_GPIO_MSG_TYPE_REGISTER_IRQ``. + +Payload needs to be set to ``struct cnxk_gpio_irq`` which describes interrupt +being requested. + +Consider using ``rte_pmd_gpio_register_gpio()`` wrapper. + +Free interrupt +~~~~~~~~~~~~~~ + +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. diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 67c6d4813c..58553ad3d9 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -340,13 +340,27 @@ cnxk_gpio_name_to_dir(const char *name) } static int -cnxk_gpio_dev_close(struct rte_rawdev *dev) +cnxk_gpio_register_irq(struct cnxk_gpio *gpio, struct cnxk_gpio_irq *irq) { - RTE_SET_USED(dev); + int ret; + + ret = cnxk_gpio_irq_request(gpio->num - gpio->gpiochip->base, irq->cpu); + if (ret) + return ret; + + gpio->handler = irq->handler; + gpio->data = irq->data; + gpio->cpu = irq->cpu; return 0; } +static int +cnxk_gpio_unregister_irq(struct cnxk_gpio *gpio) +{ + return cnxk_gpio_irq_free(gpio->num - gpio->gpiochip->base); +} + static int cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf) { @@ -428,6 +442,13 @@ cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf) *(int *)rsp = val; break; + case CNXK_GPIO_MSG_TYPE_REGISTER_IRQ: + ret = cnxk_gpio_register_irq(gpio, + (struct cnxk_gpio_irq *)msg->data); + break; + case CNXK_GPIO_MSG_TYPE_UNREGISTER_IRQ: + ret = cnxk_gpio_unregister_irq(gpio); + break; default: return -EINVAL; } @@ -490,6 +511,14 @@ cnxk_gpio_dequeue_bufs(struct rte_rawdev *dev, struct rte_rawdev_buf **buffers, return 0; } +static int +cnxk_gpio_dev_close(struct rte_rawdev *dev) +{ + RTE_SET_USED(dev); + + return 0; +} + static const struct rte_rawdev_ops cnxk_gpio_rawdev_ops = { .dev_close = cnxk_gpio_dev_close, .enqueue_bufs = cnxk_gpio_enqueue_bufs, @@ -532,6 +561,10 @@ cnxk_gpio_probe(struct rte_vdev_device *dev) if (ret) goto out; + ret = cnxk_gpio_irq_init(gpiochip); + if (ret) + goto out; + /* read gpio base */ snprintf(buf, sizeof(buf), "%s/gpiochip%d/base", CNXK_GPIO_CLASS_PATH, gpiochip->num); @@ -590,10 +623,14 @@ cnxk_gpio_remove(struct rte_vdev_device *dev) if (!gpio) continue; + if (gpio->handler) + cnxk_gpio_unregister_irq(gpio); + cnxk_gpio_queue_release(rawdev, gpio->num); } rte_free(gpiochip->gpios); + cnxk_gpio_irq_fini(); rte_rawdev_pmd_release(rawdev); return 0; diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.h b/drivers/raw/cnxk_gpio/cnxk_gpio.h index 6b54ebe6e6..c052ca5735 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.h +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.h @@ -11,6 +11,9 @@ struct cnxk_gpio { struct cnxk_gpiochip *gpiochip; void *rsp; int num; + void (*handler)(int gpio, void *data); + void *data; + int cpu; }; struct cnxk_gpiochip { @@ -20,4 +23,9 @@ struct cnxk_gpiochip { struct cnxk_gpio **gpios; }; +int cnxk_gpio_irq_init(struct cnxk_gpiochip *gpiochip); +void cnxk_gpio_irq_fini(void); +int cnxk_gpio_irq_request(int gpio, int cpu); +int cnxk_gpio_irq_free(int gpio); + #endif /* _CNXK_GPIO_H_ */ diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio_irq.c b/drivers/raw/cnxk_gpio/cnxk_gpio_irq.c new file mode 100644 index 0000000000..2fa8e69899 --- /dev/null +++ b/drivers/raw/cnxk_gpio/cnxk_gpio_irq.c @@ -0,0 +1,216 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2021 Marvell. + */ + +#include <fcntl.h> +#include <pthread.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/queue.h> +#include <unistd.h> + +#include <rte_rawdev_pmd.h> + +#include <roc_api.h> + +#include "cnxk_gpio.h" + +#define OTX_IOC_MAGIC 0xF2 +#define OTX_IOC_SET_GPIO_HANDLER \ + _IOW(OTX_IOC_MAGIC, 1, struct otx_gpio_usr_data) +#define OTX_IOC_CLR_GPIO_HANDLER \ + _IO(OTX_IOC_MAGIC, 2) + +struct otx_gpio_usr_data { + uint64_t isr_base; + uint64_t sp; + uint64_t cpu; + uint64_t gpio_num; +}; + +struct cnxk_gpio_irq_stack { + LIST_ENTRY(cnxk_gpio_irq_stack) next; + void *sp_buffer; + int cpu; + int inuse; +}; + +struct cnxk_gpio_irqchip { + int fd; + /* serialize access to this struct */ + pthread_mutex_t lock; + LIST_HEAD(, cnxk_gpio_irq_stack) stacks; + + struct cnxk_gpiochip *gpiochip; +}; + +static struct cnxk_gpio_irqchip *irqchip; + +static void +cnxk_gpio_irq_stack_free(int cpu) +{ + struct cnxk_gpio_irq_stack *stack; + + LIST_FOREACH(stack, &irqchip->stacks, next) { + if (stack->cpu == cpu) + break; + } + + if (!stack) + return; + + if (stack->inuse) + stack->inuse--; + + if (stack->inuse == 0) { + LIST_REMOVE(stack, next); + rte_free(stack->sp_buffer); + rte_free(stack); + } +} + +static void * +cnxk_gpio_irq_stack_alloc(int cpu) +{ +#define ARM_STACK_ALIGNMENT (2 * sizeof(void *)) +#define IRQ_STACK_SIZE 0x200000 + + struct cnxk_gpio_irq_stack *stack; + + LIST_FOREACH(stack, &irqchip->stacks, next) { + if (stack->cpu == cpu) + break; + } + + if (stack) { + stack->inuse++; + return (char *)stack->sp_buffer + IRQ_STACK_SIZE; + } + + stack = rte_malloc(NULL, sizeof(*stack), 0); + if (!stack) + return NULL; + + stack->sp_buffer = + rte_zmalloc(NULL, IRQ_STACK_SIZE * 2, ARM_STACK_ALIGNMENT); + if (!stack->sp_buffer) { + rte_free(stack); + return NULL; + } + + stack->cpu = cpu; + stack->inuse = 1; + LIST_INSERT_HEAD(&irqchip->stacks, stack, next); + + return (char *)stack->sp_buffer + IRQ_STACK_SIZE; +} + +static void +cnxk_gpio_irq_handler(int gpio_num) +{ + struct cnxk_gpiochip *gpiochip = irqchip->gpiochip; + struct cnxk_gpio *gpio; + + if (gpio_num >= gpiochip->num_gpios) + goto out; + + gpio = gpiochip->gpios[gpio_num]; + if (likely(gpio->handler)) + gpio->handler(gpio_num, gpio->data); + +out: + roc_atf_ret(); +} + +int +cnxk_gpio_irq_init(struct cnxk_gpiochip *gpiochip) +{ + if (irqchip) + return 0; + + irqchip = rte_zmalloc(NULL, sizeof(*irqchip), 0); + if (!irqchip) + return -ENOMEM; + + irqchip->fd = open("/dev/otx-gpio-ctr", O_RDWR | O_SYNC); + if (irqchip->fd < 0) { + rte_free(irqchip); + return -errno; + } + + pthread_mutex_init(&irqchip->lock, NULL); + LIST_INIT(&irqchip->stacks); + irqchip->gpiochip = gpiochip; + + return 0; +} + +void +cnxk_gpio_irq_fini(void) +{ + if (!irqchip) + return; + + close(irqchip->fd); + rte_free(irqchip); + irqchip = NULL; +} + +int +cnxk_gpio_irq_request(int gpio, int cpu) +{ + struct otx_gpio_usr_data data; + void *sp; + int ret; + + pthread_mutex_lock(&irqchip->lock); + + sp = cnxk_gpio_irq_stack_alloc(cpu); + if (!sp) { + ret = -ENOMEM; + goto out_unlock; + } + + data.isr_base = (uint64_t)cnxk_gpio_irq_handler; + data.sp = (uint64_t)sp; + data.cpu = (uint64_t)cpu; + data.gpio_num = (uint64_t)gpio; + + mlockall(MCL_CURRENT | MCL_FUTURE); + ret = ioctl(irqchip->fd, OTX_IOC_SET_GPIO_HANDLER, &data); + if (ret) { + ret = -errno; + goto out_free_stack; + } + + pthread_mutex_unlock(&irqchip->lock); + + return 0; + +out_free_stack: + cnxk_gpio_irq_stack_free(cpu); +out_unlock: + pthread_mutex_unlock(&irqchip->lock); + + return ret; +} + +int +cnxk_gpio_irq_free(int gpio) +{ + int ret; + + pthread_mutex_lock(&irqchip->lock); + + ret = ioctl(irqchip->fd, OTX_IOC_CLR_GPIO_HANDLER, gpio); + if (ret) { + pthread_mutex_unlock(&irqchip->lock); + return -errno; + } + + cnxk_gpio_irq_stack_free(irqchip->gpiochip->gpios[gpio]->cpu); + + pthread_mutex_unlock(&irqchip->lock); + + return 0; +} diff --git a/drivers/raw/cnxk_gpio/meson.build b/drivers/raw/cnxk_gpio/meson.build index 3fbfdd838c..9b55f029c7 100644 --- a/drivers/raw/cnxk_gpio/meson.build +++ b/drivers/raw/cnxk_gpio/meson.build @@ -5,5 +5,6 @@ deps += ['bus_vdev', 'common_cnxk', 'rawdev', 'kvargs'] sources = files( 'cnxk_gpio.c', + 'cnxk_gpio_irq.c', ) headers = files('rte_pmd_cnxk_gpio.h') diff --git a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h index 7c3dc225ca..e3096dc14f 100644 --- a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h +++ b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h @@ -40,6 +40,10 @@ enum cnxk_gpio_msg_type { CNXK_GPIO_MSG_TYPE_GET_PIN_DIR, /** Type used to read inverted logic state */ CNXK_GPIO_MSG_TYPE_GET_PIN_ACTIVE_LOW, + /** Type used to register interrupt handler */ + CNXK_GPIO_MSG_TYPE_REGISTER_IRQ, + /** Type used to remove interrupt handler */ + CNXK_GPIO_MSG_TYPE_UNREGISTER_IRQ, }; /** Available edges */ @@ -66,6 +70,25 @@ enum cnxk_gpio_pin_dir { CNXK_GPIO_PIN_DIR_LOW, }; +/** + * GPIO interrupt handler + * + * @param gpio + * Zero-based GPIO number + * @param data + * Cookie passed to interrupt handler + */ +typedef void (*cnxk_gpio_irq_handler_t)(int gpio, void *data); + +struct cnxk_gpio_irq { + /** Interrupt handler */ + cnxk_gpio_irq_handler_t handler; + /** User data passed to irq handler */ + void *data; + /** CPU which will run irq handler */ + int cpu; +}; + struct cnxk_gpio_msg { /** Message type */ enum cnxk_gpio_msg_type type; @@ -306,6 +329,99 @@ rte_pmd_gpio_get_pin_active_low(uint16_t dev_id, int gpio, int *val) return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, val, sizeof(*val)); } +/** + * Attach interrupt handler to GPIO + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param cpu + * CPU which will be handling interrupt + * @param handler + * Interrupt handler to be executed + * @param data + * Data to be passed to interrupt handler + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_register_irq(uint16_t dev_id, int gpio, int cpu, + cnxk_gpio_irq_handler_t handler, void *data) +{ + struct cnxk_gpio_irq irq = { + .handler = handler, + .data = data, + .cpu = cpu, + }; + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_REGISTER_IRQ, + .data = &irq, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Detach interrupt handler from GPIO + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_unregister_irq(uint16_t dev_id, int gpio) +{ + struct cnxk_gpio_msg msg = { + .type = CNXK_GPIO_MSG_TYPE_UNREGISTER_IRQ, + .data = &gpio, + }; + + return __rte_pmd_gpio_enq_deq(dev_id, gpio, &msg, NULL, 0); +} + +/** + * Enable interrupt + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * @param edge + * Edge that should trigger interrupt + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_enable_interrupt(uint16_t dev_id, int gpio, + enum cnxk_gpio_pin_edge edge) +{ + return rte_pmd_gpio_set_pin_edge(dev_id, gpio, edge); +} + +/** + * Disable interrupt + * + * @param dev_id + * The identifier of the device + * @param gpio + * Zero-based GPIO number + * + * @return + * Returns 0 on success, negative error code otherwise + */ +static __rte_always_inline int +rte_pmd_gpio_disable_interrupt(uint16_t dev_id, int gpio) +{ + return rte_pmd_gpio_set_pin_edge(dev_id, gpio, CNXK_GPIO_PIN_EDGE_NONE); +} + #ifdef __cplusplus } #endif -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v6 10/11] raw/cnxk_gpio: support selftest 2022-02-17 11:09 ` [PATCH v6 " Tomasz Duszynski ` (8 preceding siblings ...) 2022-02-17 11:09 ` [PATCH v6 09/11] raw/cnxk_gpio: support custom irq handlers Tomasz Duszynski @ 2022-02-17 11:09 ` Tomasz Duszynski 2022-02-17 11:09 ` [PATCH v6 11/11] raw/cnxk_gpio: add option to allow using subset of GPIOs Tomasz Duszynski 2022-02-18 17:19 ` [PATCH v6 00/11] Add cnxk_gpio Thomas Monjalon 11 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-02-17 11:09 UTC (permalink / raw) To: dev, Jakub Palider, Tomasz Duszynski; +Cc: jerinj, thomas Add support for performing selftest. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> --- 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=<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. 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 <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; +} 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') -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* [PATCH v6 11/11] raw/cnxk_gpio: add option to allow using subset of GPIOs 2022-02-17 11:09 ` [PATCH v6 " Tomasz Duszynski ` (9 preceding siblings ...) 2022-02-17 11:09 ` [PATCH v6 10/11] raw/cnxk_gpio: support selftest Tomasz Duszynski @ 2022-02-17 11:09 ` Tomasz Duszynski 2022-02-18 17:19 ` [PATCH v6 00/11] Add cnxk_gpio Thomas Monjalon 11 siblings, 0 replies; 75+ messages in thread From: Tomasz Duszynski @ 2022-02-17 11:09 UTC (permalink / raw) To: dev, Jakub Palider, Tomasz Duszynski; +Cc: jerinj, thomas Add PMD parameter that allows one to select only subset of available GPIOs. This might be useful in cases where some GPIOs are already reserved yet still available for userspace access but particular app should not touch them. Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com> Reviewed-by: Jerin Jacob Kollanukkaran <jerinj@marvell.com> --- doc/guides/rawdevs/cnxk_gpio.rst | 5 + drivers/raw/cnxk_gpio/cnxk_gpio.c | 173 +++++++++++++++++---- drivers/raw/cnxk_gpio/cnxk_gpio.h | 2 + drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c | 49 +++--- drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h | 8 + 5 files changed, 188 insertions(+), 49 deletions(-) diff --git a/doc/guides/rawdevs/cnxk_gpio.rst b/doc/guides/rawdevs/cnxk_gpio.rst index c03a5b937c..adff535a77 100644 --- a/doc/guides/rawdevs/cnxk_gpio.rst +++ b/doc/guides/rawdevs/cnxk_gpio.rst @@ -57,6 +57,11 @@ former returns number GPIOs available in the system irrespective of GPIOs being controllable or not. Thus it is user responsibility to pick the proper ones. The latter call simply returns queue capacity. +In order to allow using only subset of available GPIOs `allowlist` PMD param may +be used. For example passing `--vdev=cnxk_gpio,gpiochip=448,allowlist=[0,1,2,3]` +to EAL will deny using all GPIOs except those specified explicitly in the +`allowlist`. + Respective queue needs to be configured with ``rte_rawdev_queue_setup()``. This call barely exports GPIO to userspace. diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c index 230016b078..4ff132861d 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c @@ -22,9 +22,13 @@ static const char *const cnxk_gpio_args[] = { #define CNXK_GPIO_ARG_GPIOCHIP "gpiochip" CNXK_GPIO_ARG_GPIOCHIP, +#define CNXK_GPIO_ARG_ALLOWLIST "allowlist" + CNXK_GPIO_ARG_ALLOWLIST, NULL }; +static char *allowlist; + static void cnxk_gpio_format_name(char *name, size_t len) { @@ -73,13 +77,23 @@ cnxk_gpio_parse_arg_gpiochip(const char *key __rte_unused, const char *value, } static int -cnxk_gpio_parse_args(struct cnxk_gpiochip *gpiochip, - struct rte_devargs *devargs) +cnxk_gpio_parse_arg_allowlist(const char *key __rte_unused, const char *value, + void *extra_args __rte_unused) +{ + allowlist = strdup(value); + if (!allowlist) + return -ENOMEM; + + return 0; +} + +static int +cnxk_gpio_parse_args(struct cnxk_gpiochip *gpiochip, const char *args) { struct rte_kvargs *kvlist; int ret; - kvlist = rte_kvargs_parse(devargs->args, cnxk_gpio_args); + kvlist = rte_kvargs_parse(args, cnxk_gpio_args); if (!kvlist) return 0; @@ -92,6 +106,14 @@ cnxk_gpio_parse_args(struct cnxk_gpiochip *gpiochip, goto out; } + ret = rte_kvargs_count(kvlist, CNXK_GPIO_ARG_ALLOWLIST); + if (ret == 1) { + ret = rte_kvargs_process(kvlist, CNXK_GPIO_ARG_ALLOWLIST, + cnxk_gpio_parse_arg_allowlist, NULL); + if (ret) + goto out; + } + ret = 0; out: rte_kvargs_free(kvlist); @@ -99,6 +121,60 @@ cnxk_gpio_parse_args(struct cnxk_gpiochip *gpiochip, return ret; } +static int +cnxk_gpio_parse_allowlist(struct cnxk_gpiochip *gpiochip) +{ + int i, ret, val, queue = 0; + char *token; + int *list; + + list = rte_calloc(NULL, gpiochip->num_gpios, sizeof(*list), 0); + if (!list) + return -ENOMEM; + + /* replace brackets with something meaningless for strtol() */ + allowlist[0] = ' '; + allowlist[strlen(allowlist) - 1] = ' '; + + /* quiesce -Wcast-qual */ + token = strtok((char *)(uintptr_t)allowlist, ","); + do { + errno = 0; + val = strtol(token, NULL, 10); + if (errno) { + RTE_LOG(ERR, PMD, "failed to parse %s\n", token); + ret = -errno; + goto out; + } + + if (val < 0 || val >= gpiochip->num_gpios) { + RTE_LOG(ERR, PMD, "gpio%d out of 0-%d range\n", val, + gpiochip->num_gpios - 1); + ret = -EINVAL; + goto out; + } + + for (i = 0; i < queue; i++) { + if (list[i] != val) + continue; + + RTE_LOG(WARNING, PMD, "gpio%d already allowed\n", val); + break; + } + if (i == queue) + list[queue++] = val; + } while ((token = strtok(NULL, ","))); + + gpiochip->allowlist = list; + gpiochip->num_queues = queue; + + return 0; +out: + rte_free(list); + + return ret; +} + static int cnxk_gpio_read_attr(char *attr, char *val) { @@ -175,13 +251,24 @@ cnxk_gpio_write_attr_int(const char *attr, int val) return cnxk_gpio_write_attr(attr, buf); } +static bool +cnxk_gpio_queue_valid(struct cnxk_gpiochip *gpiochip, uint16_t queue) +{ + return queue < gpiochip->num_queues; +} + +static int +cnxk_queue_to_gpio(struct cnxk_gpiochip *gpiochip, uint16_t queue) +{ + return gpiochip->allowlist ? gpiochip->allowlist[queue] : queue; +} + static struct cnxk_gpio * cnxk_gpio_lookup(struct cnxk_gpiochip *gpiochip, uint16_t queue) { - if (queue >= gpiochip->num_gpios) - return NULL; + int gpio = cnxk_queue_to_gpio(gpiochip, queue); - return gpiochip->gpios[queue]; + return gpiochip->gpios[gpio]; } static int @@ -191,11 +278,14 @@ cnxk_gpio_queue_setup(struct rte_rawdev *dev, uint16_t queue_id, struct cnxk_gpiochip *gpiochip = dev->dev_private; char buf[CNXK_GPIO_BUFSZ]; struct cnxk_gpio *gpio; - int ret; + int num, ret; RTE_SET_USED(queue_conf); RTE_SET_USED(queue_conf_size); + if (!cnxk_gpio_queue_valid(gpiochip, queue_id)) + return -EINVAL; + gpio = cnxk_gpio_lookup(gpiochip, queue_id); if (gpio) return -EEXIST; @@ -203,7 +293,9 @@ cnxk_gpio_queue_setup(struct rte_rawdev *dev, uint16_t queue_id, gpio = rte_zmalloc(NULL, sizeof(*gpio), 0); if (!gpio) return -ENOMEM; - gpio->num = queue_id + gpiochip->base; + + num = cnxk_queue_to_gpio(gpiochip, queue_id); + gpio->num = num + gpiochip->base; gpio->gpiochip = gpiochip; snprintf(buf, sizeof(buf), "%s/export", CNXK_GPIO_CLASS_PATH); @@ -213,7 +305,7 @@ cnxk_gpio_queue_setup(struct rte_rawdev *dev, uint16_t queue_id, return ret; } - gpiochip->gpios[queue_id] = gpio; + gpiochip->gpios[num] = gpio; return 0; } @@ -224,18 +316,22 @@ cnxk_gpio_queue_release(struct rte_rawdev *dev, uint16_t queue_id) struct cnxk_gpiochip *gpiochip = dev->dev_private; char buf[CNXK_GPIO_BUFSZ]; struct cnxk_gpio *gpio; - int ret; + int num, ret; + + if (!cnxk_gpio_queue_valid(gpiochip, queue_id)) + return -EINVAL; gpio = cnxk_gpio_lookup(gpiochip, queue_id); if (!gpio) return -ENODEV; snprintf(buf, sizeof(buf), "%s/unexport", CNXK_GPIO_CLASS_PATH); - ret = cnxk_gpio_write_attr_int(buf, gpiochip->base + queue_id); + ret = cnxk_gpio_write_attr_int(buf, gpio->num); if (ret) return ret; - gpiochip->gpios[queue_id] = NULL; + num = cnxk_queue_to_gpio(gpiochip, queue_id); + gpiochip->gpios[num] = NULL; rte_free(gpio); return 0; @@ -245,16 +341,17 @@ static int cnxk_gpio_queue_def_conf(struct rte_rawdev *dev, uint16_t queue_id, rte_rawdev_obj_t queue_conf, size_t queue_conf_size) { - unsigned int *conf; + struct cnxk_gpiochip *gpiochip = dev->dev_private; + struct cnxk_gpio_queue_conf *conf = queue_conf; - RTE_SET_USED(dev); - RTE_SET_USED(queue_id); + if (!cnxk_gpio_queue_valid(gpiochip, queue_id)) + return -EINVAL; if (queue_conf_size != sizeof(*conf)) return -EINVAL; - conf = (unsigned int *)queue_conf; - *conf = 1; + conf->size = 1; + conf->gpio = cnxk_queue_to_gpio(gpiochip, queue_id); return 0; } @@ -264,7 +361,7 @@ cnxk_gpio_queue_count(struct rte_rawdev *dev) { struct cnxk_gpiochip *gpiochip = dev->dev_private; - return gpiochip->num_gpios; + return gpiochip->num_queues; } static const struct { @@ -463,21 +560,27 @@ cnxk_gpio_process_buf(struct cnxk_gpio *gpio, struct rte_rawdev_buf *rbuf) return ret; } +static bool +cnxk_gpio_valid(struct cnxk_gpiochip *gpiochip, int gpio) +{ + return gpio < gpiochip->num_gpios && gpiochip->gpios[gpio]; +} + static int cnxk_gpio_enqueue_bufs(struct rte_rawdev *dev, struct rte_rawdev_buf **buffers, unsigned int count, rte_rawdev_obj_t context) { struct cnxk_gpiochip *gpiochip = dev->dev_private; - unsigned int queue = (size_t)context; + unsigned int gpio_num = (size_t)context; struct cnxk_gpio *gpio; int ret; if (count == 0) return 0; - gpio = cnxk_gpio_lookup(gpiochip, queue); - if (!gpio) - return -ENODEV; + if (!cnxk_gpio_valid(gpiochip, gpio_num)) + return -EINVAL; + gpio = gpiochip->gpios[gpio_num]; ret = cnxk_gpio_process_buf(gpio, buffers[0]); if (ret) @@ -491,15 +594,15 @@ cnxk_gpio_dequeue_bufs(struct rte_rawdev *dev, struct rte_rawdev_buf **buffers, unsigned int count, rte_rawdev_obj_t context) { struct cnxk_gpiochip *gpiochip = dev->dev_private; - unsigned int queue = (size_t)context; + unsigned int gpio_num = (size_t)context; struct cnxk_gpio *gpio; if (count == 0) return 0; - gpio = cnxk_gpio_lookup(gpiochip, queue); - if (!gpio) - return -ENODEV; + if (!cnxk_gpio_valid(gpiochip, gpio_num)) + return -EINVAL; + gpio = gpiochip->gpios[gpio_num]; if (gpio->rsp) { buffers[0]->buf_addr = gpio->rsp; @@ -558,7 +661,7 @@ cnxk_gpio_probe(struct rte_vdev_device *dev) cnxk_gpio_set_defaults(gpiochip); /* defaults may be overwritten by this call */ - ret = cnxk_gpio_parse_args(gpiochip, dev->device.devargs); + ret = cnxk_gpio_parse_args(gpiochip, rte_vdev_device_args(dev)); if (ret) goto out; @@ -583,6 +686,15 @@ cnxk_gpio_probe(struct rte_vdev_device *dev) RTE_LOG(ERR, PMD, "failed to read %s", buf); goto out; } + gpiochip->num_queues = gpiochip->num_gpios; + + if (allowlist) { + ret = cnxk_gpio_parse_allowlist(gpiochip); + free(allowlist); + allowlist = NULL; + if (ret) + goto out; + } gpiochip->gpios = rte_calloc(NULL, gpiochip->num_gpios, sizeof(struct cnxk_gpio *), 0); @@ -594,6 +706,8 @@ cnxk_gpio_probe(struct rte_vdev_device *dev) return 0; out: + free(allowlist); + rte_free(gpiochip->allowlist); rte_rawdev_pmd_release(rawdev); return ret; @@ -630,6 +744,7 @@ cnxk_gpio_remove(struct rte_vdev_device *dev) cnxk_gpio_queue_release(rawdev, gpio->num); } + rte_free(gpiochip->allowlist); rte_free(gpiochip->gpios); cnxk_gpio_irq_fini(); rte_rawdev_pmd_release(rawdev); @@ -643,4 +758,6 @@ static struct rte_vdev_driver cnxk_gpio_drv = { }; RTE_PMD_REGISTER_VDEV(cnxk_gpio, cnxk_gpio_drv); -RTE_PMD_REGISTER_PARAM_STRING(cnxk_gpio, "gpiochip=<int>"); +RTE_PMD_REGISTER_PARAM_STRING(cnxk_gpio, + "gpiochip=<int> " + "allowlist=<list>"); diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.h b/drivers/raw/cnxk_gpio/cnxk_gpio.h index 1b31b5a486..e62f78a760 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio.h +++ b/drivers/raw/cnxk_gpio/cnxk_gpio.h @@ -20,7 +20,9 @@ struct cnxk_gpiochip { int num; int base; int num_gpios; + int num_queues; struct cnxk_gpio **gpios; + int *allowlist; }; int cnxk_gpio_selftest(uint16_t dev_id); diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c b/drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c index 6502902f86..7fccc48f30 100644 --- a/drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c +++ b/drivers/raw/cnxk_gpio/cnxk_gpio_selftest.c @@ -302,12 +302,13 @@ cnxk_gpio_test_output(uint16_t dev_id, int base, int gpio) int cnxk_gpio_selftest(uint16_t dev_id) { + struct cnxk_gpio_queue_conf conf; struct cnxk_gpiochip *gpiochip; - unsigned int queues, i, size; char buf[CNXK_GPIO_BUFSZ]; struct rte_rawdev *rawdev; + unsigned int queues, i; struct cnxk_gpio *gpio; - int ret; + int ret, ret2; rawdev = rte_rawdev_pmd_get_named_dev("cnxk_gpio"); gpiochip = rawdev->dev_private; @@ -325,62 +326,68 @@ cnxk_gpio_selftest(uint16_t dev_id) 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)); + ret = rte_rawdev_queue_conf_get(dev_id, i, &conf, sizeof(conf)); if (ret) { RTE_LOG(ERR, PMD, "failed to read queue configuration (%d)\n", ret); - continue; + goto out; } - if (size != 1) { + RTE_LOG(INFO, PMD, "testing queue%d (gpio%d)\n", i, conf.gpio); + + if (conf.size != 1) { RTE_LOG(ERR, PMD, "wrong queue size received\n"); - continue; + ret = -EIO; + goto out; } ret = rte_rawdev_queue_setup(dev_id, i, NULL, 0); if (ret) { RTE_LOG(ERR, PMD, "failed to setup queue (%d)\n", ret); - continue; + goto out; } - gpio = gpiochip->gpios[i]; + gpio = gpiochip->gpios[conf.gpio]; 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) + ret = -ENOENT; goto release; + } - ret = cnxk_gpio_test_irq(dev_id, i); + ret = cnxk_gpio_test_input(dev_id, gpiochip->base, conf.gpio); if (ret) goto release; - ret = cnxk_gpio_test_output(dev_id, gpiochip->base, i); + ret = cnxk_gpio_test_irq(dev_id, conf.gpio); if (ret) goto release; + ret = cnxk_gpio_test_output(dev_id, gpiochip->base, conf.gpio); release: + ret2 = ret; ret = rte_rawdev_queue_release(dev_id, i); if (ret) { RTE_LOG(ERR, PMD, "failed to release queue (%d)\n", ret); - continue; + break; } if (cnxk_gpio_attr_exists(buf)) { RTE_LOG(ERR, PMD, "%s still exists\n", buf); - continue; + ret = -EIO; + break; + } + + if (ret2) { + ret = ret2; + break; } } +out: close(fd); - return 0; + return ret; } diff --git a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h index e3096dc14f..80a37be9c7 100644 --- a/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h +++ b/drivers/raw/cnxk_gpio/rte_pmd_cnxk_gpio.h @@ -22,6 +22,14 @@ extern "C" { #endif +/* Queue default configuration */ +struct cnxk_gpio_queue_conf { + /** Queue size */ + int size; + /** GPIO number as seen by hardware */ + int gpio; +}; + /** Available message types */ enum cnxk_gpio_msg_type { /** Type used to set output value */ -- 2.25.1 ^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [PATCH v6 00/11] Add cnxk_gpio 2022-02-17 11:09 ` [PATCH v6 " Tomasz Duszynski ` (10 preceding siblings ...) 2022-02-17 11:09 ` [PATCH v6 11/11] raw/cnxk_gpio: add option to allow using subset of GPIOs Tomasz Duszynski @ 2022-02-18 17:19 ` Thomas Monjalon 11 siblings, 0 replies; 75+ messages in thread From: Thomas Monjalon @ 2022-02-18 17:19 UTC (permalink / raw) To: Tomasz Duszynski; +Cc: dev, jerinj > Tomasz Duszynski (11): > raw/cnxk_gpio: add GPIO driver skeleton > raw/cnxk_gpio: support reading default queue conf > raw/cnxk_gpio: support reading queue count > raw/cnxk_gpio: support queue setup > raw/cnxk_gpio: support queue release > raw/cnxk_gpio: support enqueuing buffers > raw/cnxk_gpio: support dequeuing buffers > raw/cnxk_gpio: support standard GPIO operations > raw/cnxk_gpio: support custom irq handlers > raw/cnxk_gpio: support selftest > raw/cnxk_gpio: add option to allow using subset of GPIOs Applied, thanks. ^ permalink raw reply [flat|nested] 75+ messages in thread
end of thread, other threads:[~2022-02-22 14:19 UTC | newest] Thread overview: 75+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2021-11-17 0:21 [DPDK 22.02 PATCH 00/10] Add cnxk_gpio PMD Tomasz Duszynski 2021-11-17 0:21 ` [PATCH 01/10] raw/cnxk_gpio: add GPIO driver skeleton Tomasz Duszynski 2021-11-17 0:21 ` [PATCH 02/10] raw/cnxk_gpio: support reading default queue conf Tomasz Duszynski 2021-11-17 0:21 ` [PATCH 03/10] raw/cnxk_gpio: support reading queue count Tomasz Duszynski 2021-11-17 0:21 ` [PATCH 04/10] raw/cnxk_gpio: support queue setup Tomasz Duszynski 2021-11-17 0:21 ` [PATCH 05/10] raw/cnxk_gpio: support queue release Tomasz Duszynski 2021-11-17 0:21 ` [PATCH 06/10] raw/cnxk_gpio: support enqueuing buffers Tomasz Duszynski 2021-11-17 0:21 ` [PATCH 07/10] raw/cnxk_gpio: support dequeuing buffers Tomasz Duszynski 2021-11-17 0:21 ` [PATCH 08/10] raw/cnxk_gpio: support standard GPIO operations Tomasz Duszynski 2021-11-17 0:21 ` [PATCH 09/10] raw/cnxk_gpio: support custom irq handlers Tomasz Duszynski 2021-11-17 0:21 ` [PATCH 10/10] raw/cnxk_gpio: support selftest Tomasz Duszynski 2021-11-17 1:17 ` Stephen Hemminger 2021-11-28 15:44 ` [DPDK 22.02 PATCH v2 00/10] Add cnxk_gpio PMD Tomasz Duszynski 2021-11-28 15:44 ` [PATCH v2 01/10] raw/cnxk_gpio: add GPIO driver skeleton Tomasz Duszynski 2021-11-28 15:44 ` [PATCH v2 02/10] raw/cnxk_gpio: support reading default queue conf Tomasz Duszynski 2021-11-28 15:44 ` [PATCH v2 03/10] raw/cnxk_gpio: support reading queue count Tomasz Duszynski 2021-11-28 15:44 ` [PATCH v2 04/10] raw/cnxk_gpio: support queue setup Tomasz Duszynski 2021-11-28 15:44 ` [PATCH v2 05/10] raw/cnxk_gpio: support queue release Tomasz Duszynski 2021-11-28 15:44 ` [PATCH v2 06/10] raw/cnxk_gpio: support enqueuing buffers Tomasz Duszynski 2021-11-28 15:44 ` [PATCH v2 07/10] raw/cnxk_gpio: support dequeuing buffers Tomasz Duszynski 2021-11-28 15:44 ` [PATCH v2 08/10] raw/cnxk_gpio: support standard GPIO operations Tomasz Duszynski 2021-11-28 15:44 ` [PATCH v2 09/10] raw/cnxk_gpio: support custom irq handlers Tomasz Duszynski 2021-11-28 15:44 ` [PATCH v2 10/10] raw/cnxk_gpio: support selftest Tomasz Duszynski 2021-12-13 8:17 ` [PATCH v3 00/10] Add cnxk_gpio PMD Tomasz Duszynski 2021-12-13 8:17 ` [PATCH v3 01/10] raw/cnxk_gpio: add GPIO driver skeleton Tomasz Duszynski 2021-12-13 8:17 ` [PATCH v3 02/10] raw/cnxk_gpio: support reading default queue conf Tomasz Duszynski 2021-12-13 8:17 ` [PATCH v3 03/10] raw/cnxk_gpio: support reading queue count Tomasz Duszynski 2021-12-13 8:17 ` [PATCH v3 04/10] raw/cnxk_gpio: support queue setup Tomasz Duszynski 2021-12-13 8:17 ` [PATCH v3 05/10] raw/cnxk_gpio: support queue release Tomasz Duszynski 2021-12-13 8:17 ` [PATCH v3 06/10] raw/cnxk_gpio: support enqueuing buffers Tomasz Duszynski 2021-12-13 8:17 ` [PATCH v3 07/10] raw/cnxk_gpio: support dequeuing buffers Tomasz Duszynski 2021-12-13 8:17 ` [PATCH v3 08/10] raw/cnxk_gpio: support standard GPIO operations Tomasz Duszynski 2021-12-13 8:17 ` [PATCH v3 09/10] raw/cnxk_gpio: support custom irq handlers Tomasz Duszynski 2021-12-13 8:17 ` [PATCH v3 10/10] raw/cnxk_gpio: support selftest Tomasz Duszynski 2022-01-05 14:00 ` [PATCH v4 00/11] Add cnxk_gpio Tomasz Duszynski 2022-01-05 14:00 ` [PATCH v4 01/11] raw/cnxk_gpio: add GPIO driver skeleton Tomasz Duszynski 2022-01-05 14:00 ` [PATCH v4 02/11] raw/cnxk_gpio: support reading default queue conf Tomasz Duszynski 2022-01-05 14:00 ` [PATCH v4 03/11] raw/cnxk_gpio: support reading queue count Tomasz Duszynski 2022-01-05 14:00 ` [PATCH v4 04/11] raw/cnxk_gpio: support queue setup Tomasz Duszynski 2022-01-05 14:00 ` [PATCH v4 05/11] raw/cnxk_gpio: support queue release Tomasz Duszynski 2022-01-05 14:00 ` [PATCH v4 06/11] raw/cnxk_gpio: support enqueuing buffers Tomasz Duszynski 2022-01-05 14:00 ` [PATCH v4 07/11] raw/cnxk_gpio: support dequeuing buffers Tomasz Duszynski 2022-01-05 14:00 ` [PATCH v4 08/11] raw/cnxk_gpio: support standard GPIO operations Tomasz Duszynski 2022-01-05 14:00 ` [PATCH v4 09/11] raw/cnxk_gpio: support custom irq handlers Tomasz Duszynski 2022-01-05 14:00 ` [PATCH v4 10/11] raw/cnxk_gpio: support selftest Tomasz Duszynski 2022-01-05 14:00 ` [PATCH v4 11/11] raw/cnxk_gpio: add option to allow using subset of GPIOs Tomasz Duszynski 2022-01-06 9:43 ` [PATCH v4 00/11] Add cnxk_gpio Jerin Jacob 2022-01-18 13:24 ` [PATCH v5 " Tomasz Duszynski 2022-01-18 13:24 ` [PATCH v5 01/11] raw/cnxk_gpio: add GPIO driver skeleton Tomasz Duszynski 2022-01-18 13:24 ` [PATCH v5 02/11] raw/cnxk_gpio: support reading default queue conf Tomasz Duszynski 2022-01-18 13:24 ` [PATCH v5 03/11] raw/cnxk_gpio: support reading queue count Tomasz Duszynski 2022-01-18 13:24 ` [PATCH v5 04/11] raw/cnxk_gpio: support queue setup Tomasz Duszynski 2022-01-18 13:24 ` [PATCH v5 05/11] raw/cnxk_gpio: support queue release Tomasz Duszynski 2022-01-18 13:24 ` [PATCH v5 06/11] raw/cnxk_gpio: support enqueuing buffers Tomasz Duszynski 2022-01-18 13:24 ` [PATCH v5 07/11] raw/cnxk_gpio: support dequeuing buffers Tomasz Duszynski 2022-01-18 13:24 ` [PATCH v5 08/11] raw/cnxk_gpio: support standard GPIO operations Tomasz Duszynski 2022-01-18 13:24 ` [PATCH v5 09/11] raw/cnxk_gpio: support custom irq handlers Tomasz Duszynski 2022-01-18 13:24 ` [PATCH v5 10/11] raw/cnxk_gpio: support selftest Tomasz Duszynski 2022-01-18 13:24 ` [PATCH v5 11/11] raw/cnxk_gpio: add option to allow using subset of GPIOs Tomasz Duszynski 2022-02-09 13:24 ` [PATCH v5 00/11] Add cnxk_gpio Thomas Monjalon 2022-02-17 11:09 ` [PATCH v6 " Tomasz Duszynski 2022-02-17 11:09 ` [PATCH v6 01/11] raw/cnxk_gpio: add GPIO driver skeleton Tomasz Duszynski 2022-02-18 11:47 ` Thomas Monjalon 2022-02-22 14:18 ` Ferruh Yigit 2022-02-17 11:09 ` [PATCH v6 02/11] raw/cnxk_gpio: support reading default queue conf Tomasz Duszynski 2022-02-17 11:09 ` [PATCH v6 03/11] raw/cnxk_gpio: support reading queue count Tomasz Duszynski 2022-02-17 11:09 ` [PATCH v6 04/11] raw/cnxk_gpio: support queue setup Tomasz Duszynski 2022-02-17 11:09 ` [PATCH v6 05/11] raw/cnxk_gpio: support queue release Tomasz Duszynski 2022-02-17 11:09 ` [PATCH v6 06/11] raw/cnxk_gpio: support enqueuing buffers Tomasz Duszynski 2022-02-17 11:09 ` [PATCH v6 07/11] raw/cnxk_gpio: support dequeuing buffers Tomasz Duszynski 2022-02-17 11:09 ` [PATCH v6 08/11] raw/cnxk_gpio: support standard GPIO operations Tomasz Duszynski 2022-02-17 11:09 ` [PATCH v6 09/11] raw/cnxk_gpio: support custom irq handlers Tomasz Duszynski 2022-02-17 11:09 ` [PATCH v6 10/11] raw/cnxk_gpio: support selftest Tomasz Duszynski 2022-02-17 11:09 ` [PATCH v6 11/11] raw/cnxk_gpio: add option to allow using subset of GPIOs Tomasz Duszynski 2022-02-18 17:19 ` [PATCH v6 00/11] Add cnxk_gpio Thomas Monjalon
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).