From mboxrd@z Thu Jan  1 00:00:00 1970
Return-Path: <dev-bounces@dpdk.org>
Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124])
	by inbox.dpdk.org (Postfix) with ESMTP id 51799A00BE;
	Mon, 13 Dec 2021 09:19:25 +0100 (CET)
Received: from [217.70.189.124] (localhost [127.0.0.1])
	by mails.dpdk.org (Postfix) with ESMTP id 1BA87411C9;
	Mon, 13 Dec 2021 09:18:38 +0100 (CET)
Received: from mx0b-0016f401.pphosted.com (mx0a-0016f401.pphosted.com
 [67.231.148.174])
 by mails.dpdk.org (Postfix) with ESMTP id D418D411AE
 for <dev@dpdk.org>; Mon, 13 Dec 2021 09:18:35 +0100 (CET)
Received: from pps.filterd (m0045849.ppops.net [127.0.0.1])
 by mx0a-0016f401.pphosted.com (8.16.1.2/8.16.1.2) with ESMTP id 1BD1Z3CR027207;
 Mon, 13 Dec 2021 00:18:35 -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=2GsUaHA13oXR8LSXQblVbmLoyEAJ0/CSsnxrR9Gqd7k=;
 b=Wwr9Vocvb9XgO87wZgf6cf/tArE7XlDaXdQQpCcalj0+YRfsBQyz4cI+T53JqkESyEu7
 V+4M3PV/tB0qv4jW0kQyZRbVmzyVoc8dzpZXm9BoQAxQ73ySXQt3ZjUgYwyPxOPzkAE7
 Bbeq4L9WOXMNqQvuqC5IKbMCe6xIDF+46B1UjG35OHd3FmHBmVUf9y9aHe4VxbS//Ki0
 nBb+jRj8apHM6QiwqbdFtc+haiCDPzoNhK2n8C2oUnudDh9Y0XvTbx7EXWbtIzaKvIyg
 IK5Q8vAQ5CEhp0+4ZPHVLM/FzgicxYcItiOY2Zq0y571ytQTyEaEKko55XPxAzoljBkz zw== 
Received: from dc5-exch02.marvell.com ([199.233.59.182])
 by mx0a-0016f401.pphosted.com (PPS) with ESMTPS id 3cwvmys385-1
 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-SHA384 bits=256 verify=NOT);
 Mon, 13 Dec 2021 00:18:34 -0800
Received: from DC5-EXCH01.marvell.com (10.69.176.38) by DC5-EXCH02.marvell.com
 (10.69.176.39) with Microsoft SMTP Server (TLS) id 15.0.1497.18;
 Mon, 13 Dec 2021 00:18:34 -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; Mon, 13 Dec 2021 00:18:33 -0800
Received: from localhost.localdomain (unknown [10.28.34.39])
 by maili.marvell.com (Postfix) with ESMTP id 7FA2E3F7045;
 Mon, 13 Dec 2021 00:18:32 -0800 (PST)
From: Tomasz Duszynski <tduszynski@marvell.com>
To: <dev@dpdk.org>
CC: <jerinj@marvell.com>, <stephen@networkplumber.org>, Tomasz Duszynski
 <tduszynski@marvell.com>
Subject: [PATCH v3 09/10] raw/cnxk_gpio: support custom irq handlers
Date: Mon, 13 Dec 2021 09:17:31 +0100
Message-ID: <20211213081732.2096334-10-tduszynski@marvell.com>
X-Mailer: git-send-email 2.25.1
In-Reply-To: <20211213081732.2096334-1-tduszynski@marvell.com>
References: <20211128154442.4029049-1-tduszynski@marvell.com>
 <20211213081732.2096334-1-tduszynski@marvell.com>
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
Content-Type: text/plain
X-Proofpoint-GUID: gjBjDExJWt7k7nUmh4TjmxIJkG54KzUj
X-Proofpoint-ORIG-GUID: gjBjDExJWt7k7nUmh4TjmxIJkG54KzUj
X-Proofpoint-Virus-Version: vendor=baseguard
 engine=ICAP:2.0.205,Aquarius:18.0.790,Hydra:6.0.425,FMLib:17.11.62.513
 definitions=2021-12-13_03,2021-12-10_01,2021-12-02_01
X-BeenThere: dev@dpdk.org
X-Mailman-Version: 2.1.29
Precedence: list
List-Id: DPDK patches and discussions <dev.dpdk.org>
List-Unsubscribe: <https://mails.dpdk.org/options/dev>,
 <mailto:dev-request@dpdk.org?subject=unsubscribe>
List-Archive: <http://mails.dpdk.org/archives/dev/>
List-Post: <mailto:dev@dpdk.org>
List-Help: <mailto:dev-request@dpdk.org?subject=help>
List-Subscribe: <https://mails.dpdk.org/listinfo/dev>,
 <mailto:dev-request@dpdk.org?subject=subscribe>
Errors-To: dev-bounces@dpdk.org

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