From patchwork Tue Jan 18 13:24:21 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomasz Duszynski X-Patchwork-Id: 106023 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 3C2D0A034C; Tue, 18 Jan 2022 14:25:55 +0100 (CET) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 01B2642728; Tue, 18 Jan 2022 14:25:11 +0100 (CET) Received: from mx0b-0016f401.pphosted.com (mx0b-0016f401.pphosted.com [67.231.156.173]) by mails.dpdk.org (Postfix) with ESMTP id 36B3642745 for ; Tue, 18 Jan 2022 14:25:05 +0100 (CET) Received: from pps.filterd (m0045851.ppops.net [127.0.0.1]) by mx0b-0016f401.pphosted.com (8.16.1.2/8.16.1.2) with ESMTP id 20IB9uC5012185; Tue, 18 Jan 2022 05:25:04 -0800 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=marvell.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding : content-type; s=pfpt0220; bh=imtGd8TbLySPrCKIxQNwuwRKtxT1/dkendhi6KKWAn8=; b=eB8vQoNgxqv8zfz+TUKLh75jRTAD4kiyvJtsUMa1HzWj/UnNE2V9bwpo2lljiGcNRA6p QKkEfkf82vjqjma2AcaJSudlZ9MgDbet1GYuzRhe+mka07ra4Ilq9OBIcKTMUF5k5yUE LdaRAh6s2XtYykhAjBgnUrofP0PxWFNVWuU6RD1ydwmNnggEOXU8xe507/Ji8E/rShqA LoBekzNK2gz/EGgttjr/Lg978TUCZ/Sn6qHhSkDcuGUPt1g243DwWyHJN2higzmxhOCu XCGGG9uWapnrgKG6XIK27WG+0B38JuyFvBQqjZtJ9zyGpY3b/BWlcbVFNb3yEL5XZZBH yA== Received: from dc5-exch01.marvell.com ([199.233.59.181]) by mx0b-0016f401.pphosted.com (PPS) with ESMTPS id 3dnvea0c0k-19 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-SHA384 bits=256 verify=NOT); Tue, 18 Jan 2022 05:25:04 -0800 Received: from DC5-EXCH01.marvell.com (10.69.176.38) by DC5-EXCH01.marvell.com (10.69.176.38) with Microsoft SMTP Server (TLS) id 15.0.1497.2; Tue, 18 Jan 2022 05:24:46 -0800 Received: from maili.marvell.com (10.69.176.80) by DC5-EXCH01.marvell.com (10.69.176.38) with Microsoft SMTP Server id 15.0.1497.2 via Frontend Transport; Tue, 18 Jan 2022 05:24:46 -0800 Received: from localhost.localdomain (unknown [10.28.34.39]) by maili.marvell.com (Postfix) with ESMTP id 68D263F705D; Tue, 18 Jan 2022 05:24:44 -0800 (PST) From: Tomasz Duszynski To: CC: , , Tomasz Duszynski Subject: [PATCH v5 08/11] raw/cnxk_gpio: support standard GPIO operations Date: Tue, 18 Jan 2022 14:24:21 +0100 Message-ID: <20220118132424.2573372-9-tduszynski@marvell.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220118132424.2573372-1-tduszynski@marvell.com> References: <20220105140020.1615256-1-tduszynski@marvell.com> <20220118132424.2573372-1-tduszynski@marvell.com> MIME-Version: 1.0 X-Proofpoint-ORIG-GUID: yag2qeYTbu1nKtJcP5U9aUTc0Fs2gkq- X-Proofpoint-GUID: yag2qeYTbu1nKtJcP5U9aUTc0Fs2gkq- X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.205,Aquarius:18.0.816,Hydra:6.0.425,FMLib:17.11.62.513 definitions=2022-01-18_03,2022-01-18_01,2021-12-02_01 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Add support for standard GPIO operations i.e ones normally provided by GPIO sysfs interface. Signed-off-by: Tomasz Duszynski --- 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 +#include +#include + /** * @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