/* * Google LWIS GPIO Interface * * Copyright (c) 2018 Google, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #define pr_fmt(fmt) KBUILD_MODNAME "-gpio: " fmt #include #include #include "lwis_gpio.h" #include "lwis_interrupt.h" #define SHARED_STRING "shared-" #define PULSE_STRING "pulse-" /* debug function */ void lwis_gpio_list_print(char *name, struct gpio_descs *gpios) { int i; if (IS_ERR_OR_NULL(gpios)) { pr_info("name: %s error: %ld\n", name, PTR_ERR(gpios)); } else { pr_info("name: %s, count: %d\n", name, gpios->ndescs); for (i = 0; i < gpios->ndescs; i++) { pr_info("gpio number: %d\n", desc_to_gpio(gpios->desc[i])); } } } struct gpio_descs *lwis_gpio_list_get(struct device *dev, const char *name) { /* By default, the GPIO pins are acquired but uninitialized */ return devm_gpiod_get_array(dev, name, GPIOD_ASIS); } void lwis_gpio_list_put(struct gpio_descs *gpios, struct device *dev) { devm_gpiod_put_array(dev, gpios); } int lwis_gpio_list_set_output_value(struct gpio_descs *gpios, int value) { int i; int ret; if (!gpios) { return -EINVAL; } for (i = 0; i < gpios->ndescs; ++i) { ret = gpiod_direction_output(gpios->desc[i], value); if (ret) { pr_err("Failed to set value for GPIO %d\n", i); return ret; } } return 0; } int lwis_gpio_list_set_output_value_raw(struct gpio_descs *gpios, int value) { int i; int ret; if (!gpios) { return -EINVAL; } for (i = 0; i < gpios->ndescs; ++i) { ret = gpiod_direction_output_raw(gpios->desc[i], value); if (ret) { pr_err("Failed to set value for GPIO %d\n", i); return ret; } } return 0; } int lwis_gpio_list_set_input(struct gpio_descs *gpios) { int i; int ret; if (!gpios) { return -EINVAL; } for (i = 0; i < gpios->ndescs; ++i) { ret = gpiod_direction_input(gpios->desc[i]); if (ret) { pr_err("Failed to set GPIO %d to input\n", i); return ret; } } return 0; } int lwis_gpios_list_add_info_by_name(struct device *dev, struct list_head *list, const char *name) { struct lwis_gpios_info *gpios_info; struct gpio_descs *descs; /* Check gpio already exist or not */ gpios_info = lwis_gpios_get_info_by_name(list, name); if (!IS_ERR_OR_NULL(gpios_info)) { return 0; } gpios_info = kmalloc(sizeof(struct lwis_gpios_info), GFP_KERNEL); if (IS_ERR_OR_NULL(gpios_info)) { pr_err("Allocate lwis_gpios_info failed\n"); return -ENOMEM; } descs = lwis_gpio_list_get(dev, name); if (IS_ERR_OR_NULL(descs)) { pr_err("Error parsing GPIO list %s (%ld)\n", name, PTR_ERR(descs)); kfree(gpios_info); return PTR_ERR(descs); } gpios_info->id = desc_to_gpio(descs->desc[0]); gpios_info->hold_dev = dev; /* * The GPIO pins are valid, release the list as we do not need to hold * on to the pins yet */ lwis_gpio_list_put(descs, dev); gpios_info->gpios = NULL; gpios_info->irq_list = NULL; strscpy(gpios_info->name, name, LWIS_MAX_NAME_STRING_LEN); if (strncmp(SHARED_STRING, name, strlen(SHARED_STRING)) == 0) { gpios_info->is_shared = true; } else { gpios_info->is_shared = false; } if (strncmp(PULSE_STRING, name, strlen(PULSE_STRING)) == 0) { gpios_info->is_pulse = true; } else { gpios_info->is_pulse = false; } list_add(&gpios_info->node, list); return 0; } void lwis_gpios_list_free(struct list_head *list) { struct lwis_gpios_info *gpio_node; struct list_head *it_node, *it_tmp; if (!list || list_empty(list)) { return; } list_for_each_safe (it_node, it_tmp, list) { gpio_node = list_entry(it_node, struct lwis_gpios_info, node); list_del(&gpio_node->node); if (gpio_node->irq_list) { lwis_interrupt_list_free(gpio_node->irq_list); } kfree(gpio_node); } } struct lwis_gpios_info *lwis_gpios_get_info_by_name(struct list_head *list, const char *name) { struct lwis_gpios_info *gpio_node; struct list_head *it_node, *it_tmp; if (!list || !name || list_empty(list)) { return ERR_PTR(-EINVAL); } list_for_each_safe (it_node, it_tmp, list) { gpio_node = list_entry(it_node, struct lwis_gpios_info, node); if (!strcmp(gpio_node->name, name)) { return gpio_node; } } return ERR_PTR(-EINVAL); }