raw/cnxk_bphy: add doxygen comments
[dpdk.git] / drivers / raw / cnxk_gpio / cnxk_gpio.c
index 9baa431..d759ed8 100644 (file)
@@ -4,6 +4,7 @@
 
 #include <dirent.h>
 #include <string.h>
+#include <sys/stat.h>
 
 #include <rte_bus_vdev.h>
 #include <rte_eal.h>
@@ -14,6 +15,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"
 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)
 {
@@ -72,13 +78,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;
 
@@ -91,6 +107,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);
@@ -98,27 +122,87 @@ out:
        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)
 {
+       int ret, ret2;
        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;
+       if (ret < 0) {
+               ret = -errno;
+               goto out;
+       }
+       if (ret != 1) {
+               ret = -EIO;
+               goto out;
+       }
 
-       ret = fclose(fp);
-       if (ret)
-               return -errno;
+       ret = 0;
+out:
+       ret2 = fclose(fp);
+       if (!ret)
+               ret = ret2;
 
-       return 0;
+       return ret;
 }
 
 static int
@@ -174,13 +258,35 @@ 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 bool
+cnxk_gpio_exists(int num)
+{
+       char buf[CNXK_GPIO_BUFSZ];
+       struct stat st;
+
+       snprintf(buf, sizeof(buf), "%s/gpio%d", CNXK_GPIO_CLASS_PATH, num);
+
+       return !stat(buf, &st);
 }
 
 static int
@@ -190,11 +296,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;
@@ -202,17 +311,50 @@ 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);
+       if (!cnxk_gpio_exists(gpio->num)) {
+               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;
+               }
+       } else {
+               RTE_LOG(WARNING, PMD, "using existing gpio%d\n", gpio->num);
+       }
+
+       gpiochip->gpios[num] = gpio;
+
+       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 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, gpio->num);
-       if (ret) {
-               rte_free(gpio);
+       if (ret)
                return ret;
-       }
 
-       gpiochip->gpios[queue_id] = gpio;
+       num = cnxk_queue_to_gpio(gpiochip, queue_id);
+       gpiochip->gpios[num] = NULL;
+       rte_free(gpio);
 
        return 0;
 }
@@ -221,16 +363,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;
 }
@@ -240,7 +383,257 @@ 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 {
+       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_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)
+{
+       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;
+       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;
+       }
+
+       /* 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 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 gpio_num = (size_t)context;
+       struct cnxk_gpio *gpio;
+       int ret;
+
+       if (count == 0)
+               return 0;
+
+       if (!cnxk_gpio_valid(gpiochip, gpio_num))
+               return -EINVAL;
+       gpio = gpiochip->gpios[gpio_num];
+
+       ret = cnxk_gpio_process_buf(gpio, buffers[0]);
+       if (ret)
+               return ret;
+
+       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 gpio_num = (size_t)context;
+       struct cnxk_gpio *gpio;
+
+       if (count == 0)
+               return 0;
+
+       if (!cnxk_gpio_valid(gpiochip, gpio_num))
+               return -EINVAL;
+       gpio = gpiochip->gpios[gpio_num];
+
+       if (gpio->rsp) {
+               buffers[0]->buf_addr = gpio->rsp;
+               gpio->rsp = NULL;
+
+               return 1;
+       }
+
+       return 0;
 }
 
 static int
@@ -253,9 +646,13 @@ cnxk_gpio_dev_close(struct rte_rawdev *dev)
 
 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,
+       .queue_release = cnxk_gpio_queue_release,
+       .dev_selftest = cnxk_gpio_selftest,
 };
 
 static int
@@ -286,7 +683,11 @@ 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;
+
+       ret = cnxk_gpio_irq_init(gpiochip);
        if (ret)
                goto out;
 
@@ -307,6 +708,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);
@@ -318,6 +728,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;
@@ -329,6 +741,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,7 +755,20 @@ 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;
+
+               if (gpio->handler)
+                       cnxk_gpio_unregister_irq(gpio);
+
+               cnxk_gpio_queue_release(rawdev, gpio->num);
+       }
+
+       rte_free(gpiochip->allowlist);
        rte_free(gpiochip->gpios);
+       cnxk_gpio_irq_fini();
        rte_rawdev_pmd_release(rawdev);
 
        return 0;
@@ -353,4 +780,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>");