#define pr_fmt(fmt) "gpiolib: " fmt #include #include #include #include #include #include #include #include #include static LIST_HEAD(chip_list); struct gpio_info { struct gpio_chip *chip; bool requested; bool active_low; char *label; char *name; }; static struct gpio_info *gpio_desc; static int gpio_desc_alloc(void) { gpio_desc = xzalloc(sizeof(struct gpio_info) * ARCH_NR_GPIOS); return 0; } pure_initcall(gpio_desc_alloc); static int gpio_ensure_requested(struct gpio_info *gi, int gpio) { if (gi->requested) return 0; return gpio_request(gpio, "gpio"); } static struct gpio_info *gpio_to_desc(unsigned gpio) { if (gpio_is_valid(gpio)) if (gpio_desc[gpio].chip) return &gpio_desc[gpio]; pr_warning("invalid GPIO %d\n", gpio); return NULL; } static unsigned gpioinfo_chip_offset(struct gpio_info *gi) { return (gi - gpio_desc) - gi->chip->base; } static int gpio_adjust_value(struct gpio_info *gi, int value) { if (value < 0) return value; return !!value ^ gi->active_low; } static int gpioinfo_request(struct gpio_info *gi, const char *label) { int ret; if (gi->requested) { ret = -EBUSY; goto done; } ret = 0; if (gi->chip->ops->request) { ret = gi->chip->ops->request(gi->chip, gpioinfo_chip_offset(gi)); if (ret) goto done; } gi->requested = true; gi->active_low = false; gi->label = xstrdup(label); done: if (ret) pr_err("_gpio_request: gpio-%td (%s) status %d\n", gi - gpio_desc, label ? : "?", ret); return ret; } int gpio_find_by_label(const char *label) { int i; for (i = 0; i < ARCH_NR_GPIOS; i++) { struct gpio_info *info = &gpio_desc[i]; if (!info->requested || !info->chip || !info->label) continue; if (!strcmp(info->label, label)) return i; } return -ENOENT; } int gpio_find_by_name(const char *name) { int i; for (i = 0; i < ARCH_NR_GPIOS; i++) { struct gpio_info *info = &gpio_desc[i]; if (!info->chip || !info->name) continue; if (!strcmp(info->name, name)) return i; } return -ENOENT; } int gpio_request(unsigned gpio, const char *label) { struct gpio_info *gi = gpio_to_desc(gpio); if (!gi) { pr_err("_gpio_request: gpio-%d (%s) status %d\n", gpio, label ? : "?", -ENODEV); return -ENODEV; } return gpioinfo_request(gi, label); } static void gpioinfo_free(struct gpio_info *gi) { if (!gi->requested) return; if (gi->chip->ops->free) gi->chip->ops->free(gi->chip, gpioinfo_chip_offset(gi)); gi->requested = false; gi->active_low = false; free(gi->label); gi->label = NULL; } void gpio_free(unsigned gpio) { struct gpio_info *gi = gpio_to_desc(gpio); if (!gi) return; gpioinfo_free(gi); } static void gpioinfo_set_value(struct gpio_info *gi, int value) { if (gi->chip->ops->set) gi->chip->ops->set(gi->chip, gpioinfo_chip_offset(gi), value); } void gpio_set_value(unsigned gpio, int value) { struct gpio_info *gi = gpio_to_desc(gpio); if (!gi) return; if (gpio_ensure_requested(gi, gpio)) return; gpioinfo_set_value(gi, value); } EXPORT_SYMBOL(gpio_set_value); void gpio_set_active(unsigned gpio, bool value) { struct gpio_info *gi = gpio_to_desc(gpio); if (!gi) return; gpio_set_value(gpio, gpio_adjust_value(gi, value)); } EXPORT_SYMBOL(gpio_set_active); static int gpioinfo_get_value(struct gpio_info *gi) { if (!gi->chip->ops->get) return -ENOSYS; return gi->chip->ops->get(gi->chip, gpioinfo_chip_offset(gi)); } int gpio_get_value(unsigned gpio) { struct gpio_info *gi = gpio_to_desc(gpio); int ret; if (!gi) return -ENODEV; ret = gpio_ensure_requested(gi, gpio); if (ret) return ret; return gpioinfo_get_value(gi); } EXPORT_SYMBOL(gpio_get_value); int gpio_is_active(unsigned gpio) { struct gpio_info *gi = gpio_to_desc(gpio); if (!gi) return -ENODEV; return gpio_adjust_value(gi, gpio_get_value(gpio)); } EXPORT_SYMBOL(gpio_is_active); static int gpioinfo_direction_output(struct gpio_info *gi, int value) { if (!gi->chip->ops->direction_output) return -ENOSYS; return gi->chip->ops->direction_output(gi->chip, gpioinfo_chip_offset(gi), value); } int gpio_direction_output(unsigned gpio, int value) { struct gpio_info *gi = gpio_to_desc(gpio); int ret; if (!gi) return -ENODEV; ret = gpio_ensure_requested(gi, gpio); if (ret) return ret; return gpioinfo_direction_output(gi, value); } EXPORT_SYMBOL(gpio_direction_output); static int gpioinfo_direction_active(struct gpio_info *gi, bool value) { return gpioinfo_direction_output(gi, gpio_adjust_value(gi, value)); } int gpio_direction_active(unsigned gpio, bool value) { struct gpio_info *gi = gpio_to_desc(gpio); if (!gi) return -ENODEV; return gpioinfo_direction_active(gi, value); } EXPORT_SYMBOL(gpio_direction_active); static int gpioinfo_direction_input(struct gpio_info *gi) { if (!gi->chip->ops->direction_input) return -ENOSYS; return gi->chip->ops->direction_input(gi->chip, gpioinfo_chip_offset(gi)); } int gpio_direction_input(unsigned gpio) { struct gpio_info *gi = gpio_to_desc(gpio); int ret; if (!gi) return -ENODEV; ret = gpio_ensure_requested(gi, gpio); if (ret) return ret; return gpioinfo_direction_input(gi); } EXPORT_SYMBOL(gpio_direction_input); static int gpioinfo_request_one(struct gpio_info *gi, unsigned long flags, const char *label) { int err; /* * Not all of the flags below are mulit-bit, but, for the sake * of consistency, the code is written as if all of them were. */ const bool active_low = (flags & GPIOF_ACTIVE_LOW) == GPIOF_ACTIVE_LOW; const bool dir_in = (flags & GPIOF_DIR_IN) == GPIOF_DIR_IN; const bool logical = (flags & GPIOF_LOGICAL) == GPIOF_LOGICAL; const bool init_active = (flags & GPIOF_INIT_ACTIVE) == GPIOF_INIT_ACTIVE; const bool init_high = (flags & GPIOF_INIT_HIGH) == GPIOF_INIT_HIGH; err = gpioinfo_request(gi, label); if (err) return err; gi->active_low = active_low; if (dir_in) err = gpioinfo_direction_input(gi); else if (logical) err = gpioinfo_direction_active(gi, init_active); else err = gpioinfo_direction_output(gi, init_high); if (err) gpioinfo_free(gi); return err; } /** * gpio_request_one - request a single GPIO with initial configuration * @gpio: the GPIO number * @flags: GPIO configuration as specified by GPIOF_* * @label: a literal description string of this GPIO */ int gpio_request_one(unsigned gpio, unsigned long flags, const char *label) { struct gpio_info *gi = gpio_to_desc(gpio); if (!gi) return -ENODEV; return gpioinfo_request_one(gi, flags, label); } EXPORT_SYMBOL_GPL(gpio_request_one); /** * gpio_request_array - request multiple GPIOs in a single call * @array: array of the 'struct gpio' * @num: how many GPIOs in the array */ int gpio_request_array(const struct gpio *array, size_t num) { int i, err; for (i = 0; i < num; i++, array++) { err = gpio_request_one(array->gpio, array->flags, array->label); if (err) goto err_free; } return 0; err_free: while (i--) gpio_free((--array)->gpio); return err; } EXPORT_SYMBOL_GPL(gpio_request_array); /** * gpio_free_array - release multiple GPIOs in a single call * @array: array of the 'struct gpio' * @num: how many GPIOs in the array */ void gpio_free_array(const struct gpio *array, size_t num) { while (num--) gpio_free((array++)->gpio); } EXPORT_SYMBOL_GPL(gpio_free_array); int gpio_array_to_id(const struct gpio *array, size_t num, u32 *val) { u32 id = 0; int ret, i; if (num > 32) return -EOVERFLOW; ret = gpio_request_array(array, num); if (ret) return ret; /* Wait until logic level will be stable */ udelay(5); for (i = 0; i < num; i++) { ret = gpio_is_active(array[i].gpio); if (ret < 0) goto free_array; if (ret) id |= 1UL << i; } *val = id; ret = 0; free_array: gpio_free_array(array, num); return ret; } EXPORT_SYMBOL(gpio_array_to_id); static int gpiochip_find_base(int start, int ngpio) { int i; int spare = 0; int base = -ENOSPC; if (start < 0) start = 0; for (i = start; i < ARCH_NR_GPIOS; i++) { struct gpio_chip *chip = gpio_desc[i].chip; if (!chip) { spare++; if (spare == ngpio) { base = i + 1 - ngpio; break; } } else { spare = 0; i += chip->ngpio - 1; } } if (gpio_is_valid(base)) debug("%s: found new base at %d\n", __func__, base); return base; } static int of_hog_gpio(struct device_node *np, struct gpio_chip *chip, unsigned int idx) { struct device_node *chip_np = chip->dev->device_node; unsigned long flags = 0; u32 gpio_cells, gpio_num, gpio_flags; int ret, gpio; const char *name = NULL; ret = of_property_read_u32(chip_np, "#gpio-cells", &gpio_cells); if (ret) return ret; /* * Support for GPIOs that don't have #gpio-cells set to 2 is * not implemented */ if (WARN_ON(gpio_cells != 2)) return -ENOTSUPP; ret = of_property_read_u32_index(np, "gpios", idx * gpio_cells, &gpio_num); if (ret) return ret; ret = of_property_read_u32_index(np, "gpios", idx * gpio_cells + 1, &gpio_flags); if (ret) return ret; if (gpio_flags & OF_GPIO_ACTIVE_LOW) flags |= GPIOF_ACTIVE_LOW; gpio = gpio_get_num(chip->dev, gpio_num); if (gpio == -EPROBE_DEFER) return gpio; if (gpio < 0) { dev_err(chip->dev, "unable to get gpio %u\n", gpio_num); return gpio; } /* * Note that, in order to be compatible with Linux, the code * below interprets 'output-high' as to mean 'output-active'. * That is, when processed for active-low GPIO, it will result * in output being asserted logically 'active', but physically * 'low'. * * Conversely it means that specifying 'output-low' for * 'active-low' GPIO would result in 'high' level observed on * the corresponding pin * */ if (of_property_read_bool(np, "input")) flags |= GPIOF_DIR_IN; else if (of_property_read_bool(np, "output-low")) flags |= GPIOF_OUT_INIT_INACTIVE; else if (of_property_read_bool(np, "output-high")) flags |= GPIOF_OUT_INIT_ACTIVE; else return -EINVAL; /* The line-name is optional and if not present the node name is used */ ret = of_property_read_string(np, "line-name", &name); if (ret < 0) name = np->name; return gpio_request_one(gpio, flags, name); } static int of_gpiochip_scan_hogs(struct gpio_chip *chip) { struct device_node *np; int ret, i, count; if (!IS_ENABLED(CONFIG_OFDEVICE) || !chip->dev->device_node) return 0; count = of_property_count_strings(chip->dev->device_node, "gpio-line-names"); if (count > 0) { const char **arr = xzalloc(count * sizeof(char *)); of_property_read_string_array(chip->dev->device_node, "gpio-line-names", arr, count); for (i = 0; i < chip->ngpio && i < count; i++) gpio_desc[chip->base + i].name = xstrdup(arr[i]); free(arr); } for_each_available_child_of_node(chip->dev->device_node, np) { if (!of_property_read_bool(np, "gpio-hog")) continue; for (ret = 0, i = 0; !ret; ret = of_hog_gpio(np, chip, i), i++) ; /* * We ignore -EOVERFLOW because it serves us as an * indicator that there's no more GPIOs to handle. */ if (ret < 0 && ret != -EOVERFLOW) return ret; } return 0; } static const char *gpio_suffixes[] = { "gpios", "gpio", }; /* Linux compatibility helper: Get a GPIO descriptor from device tree */ int gpiod_get(struct device_d *dev, const char *_con_id, enum gpiod_flags flags) { struct device_node *np = dev->device_node; enum of_gpio_flags of_flags; const char *label = dev_name(dev); char *buf = NULL, *con_id; int gpio; int ret, i; if (!IS_ENABLED(CONFIG_OFDEVICE) || !dev->device_node) return -ENODEV; for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { if (_con_id) con_id = basprintf("%s-%s", _con_id, gpio_suffixes[i]); else con_id = basprintf("%s", gpio_suffixes[i]); if (!con_id) return -ENOMEM; gpio = of_get_named_gpio_flags(np, con_id, 0, &of_flags); free(con_id); if (gpio_is_valid(gpio)) break; } if (!gpio_is_valid(gpio)) return gpio < 0 ? gpio : -EINVAL; if (of_flags & OF_GPIO_ACTIVE_LOW) flags |= GPIOF_ACTIVE_LOW; buf = NULL; if (_con_id) { label = buf = basprintf("%s-%s", dev_name(dev), _con_id); if (!label) return -ENOMEM; } ret = gpio_request_one(gpio, flags, label); free(buf); return ret ?: gpio; } int gpiochip_add(struct gpio_chip *chip) { int base, i; base = gpiochip_find_base(chip->base, chip->ngpio); if (base < 0) return base; if (chip->base >= 0 && chip->base != base) return -EBUSY; chip->base = base; list_add_tail(&chip->list, &chip_list); for (i = chip->base; i < chip->base + chip->ngpio; i++) gpio_desc[i].chip = chip; return of_gpiochip_scan_hogs(chip); } void gpiochip_remove(struct gpio_chip *chip) { list_del(&chip->list); } int gpio_get_num(struct device_d *dev, int gpio) { struct gpio_chip *chip; if (!dev) return -ENODEV; list_for_each_entry(chip, &chip_list, list) { if (chip->dev == dev) return chip->base + gpio; } return -EPROBE_DEFER; } struct gpio_chip *gpio_get_chip(int gpio) { struct gpio_info *gi = gpio_to_desc(gpio); return gi ? gi->chip : NULL; } #ifdef CONFIG_CMD_GPIO static int do_gpiolib(int argc, char *argv[]) { int i; for (i = 0; i < ARCH_NR_GPIOS; i++) { struct gpio_info *gi = &gpio_desc[i]; int val = -1, dir = -1; if (!gi->chip) continue; /* print chip information and header on first gpio */ if (gi->chip->base == i) { printf("\nGPIOs %u-%u, chip %s:\n", gi->chip->base, gi->chip->base + gi->chip->ngpio - 1, gi->chip->dev->name); printf(" %-3s %-3s %-9s %-20s %-20s\n", "dir", "val", "requested", "name", "label"); } if (gi->chip->ops->get_direction) dir = gi->chip->ops->get_direction(gi->chip, i - gi->chip->base); if (gi->chip->ops->get) val = gi->chip->ops->get(gi->chip, i - gi->chip->base); printf(" GPIO %4d: %-3s %-3s %-9s %-20s %-20s\n", i, (dir < 0) ? "unk" : ((dir == GPIOF_DIR_IN) ? "in" : "out"), (val < 0) ? "unk" : ((val == 0) ? "lo" : "hi"), gi->requested ? (gi->active_low ? "active low" : "true") : "false", gi->name ? gi->name : "", gi->label ? gi->label : ""); } return 0; } BAREBOX_CMD_START(gpioinfo) .cmd = do_gpiolib, BAREBOX_CMD_DESC("list registered GPIOs") BAREBOX_CMD_GROUP(CMD_GRP_INFO) BAREBOX_CMD_COMPLETE(empty_complete) BAREBOX_CMD_END #endif