/* * Copyright (c) 2013 Xilinx, Michal Simek * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #include #include static LIST_HEAD(gpio_list); enum gpio_direction { GPIO_DIRECTION_OUT = 0, GPIO_DIRECTION_IN = 1, }; /* Gpio simple map */ struct gpio_regs { u32 gpiodata; u32 gpiodir; }; #define GPIO_NAME_SIZE 10 struct gpio_names { char name[GPIO_NAME_SIZE]; }; /* Initialized, rxbd_current, rx_first_buf must be 0 after init */ struct xilinx_gpio_priv { struct gpio_regs *regs; u32 gpio_min; u32 gpio_max; u32 gpiodata_store; char name[GPIO_NAME_SIZE]; struct list_head list; struct gpio_names *gpio_name; }; /* Store number of allocated gpio pins */ static u32 xilinx_gpio_max; /* Get associated gpio controller */ static struct xilinx_gpio_priv *gpio_get_controller(unsigned gpio) { struct list_head *entry; struct xilinx_gpio_priv *priv = NULL; list_for_each(entry, &gpio_list) { priv = list_entry(entry, struct xilinx_gpio_priv, list); if (gpio >= priv->gpio_min && gpio <= priv->gpio_max) { debug("%s: reg: %x, min-max: %d-%d\n", __func__, (u32)priv->regs, priv->gpio_min, priv->gpio_max); return priv; } } puts("!!!Can't get gpio controller!!!\n"); return NULL; } /* Get gpio pin name if used/setup */ static char *get_name(unsigned gpio) { u32 gpio_priv; struct xilinx_gpio_priv *priv; debug("%s\n", __func__); priv = gpio_get_controller(gpio); if (priv) { gpio_priv = gpio - priv->gpio_min; return *priv->gpio_name[gpio_priv].name ? priv->gpio_name[gpio_priv].name : "UNKNOWN"; } return "UNKNOWN"; } /* Get output value */ static int gpio_get_output_value(unsigned gpio) { u32 val, gpio_priv; struct xilinx_gpio_priv *priv = gpio_get_controller(gpio); if (priv) { gpio_priv = gpio - priv->gpio_min; val = !!(priv->gpiodata_store & (1 << gpio_priv)); debug("%s: reg: %x, gpio_no: %d, dir: %d\n", __func__, (u32)priv->regs, gpio_priv, val); return val; } return -1; } /* Get input value */ static int gpio_get_input_value(unsigned gpio) { u32 val, gpio_priv; struct gpio_regs *regs; struct xilinx_gpio_priv *priv = gpio_get_controller(gpio); if (priv) { regs = priv->regs; gpio_priv = gpio - priv->gpio_min; val = readl(®s->gpiodata); val = !!(val & (1 << gpio_priv)); debug("%s: reg: %x, gpio_no: %d, dir: %d\n", __func__, (u32)priv->regs, gpio_priv, val); return val; } return -1; } /* Set gpio direction */ static int gpio_set_direction(unsigned gpio, enum gpio_direction direction) { u32 val, gpio_priv; struct gpio_regs *regs; struct xilinx_gpio_priv *priv = gpio_get_controller(gpio); if (priv) { regs = priv->regs; val = readl(®s->gpiodir); gpio_priv = gpio - priv->gpio_min; if (direction == GPIO_DIRECTION_OUT) val &= ~(1 << gpio_priv); else val |= 1 << gpio_priv; writel(val, ®s->gpiodir); debug("%s: reg: %x, gpio_no: %d, dir: %d\n", __func__, (u32)priv->regs, gpio_priv, val); return 0; } return -1; } /* Get gpio direction */ static int gpio_get_direction(unsigned gpio) { u32 val, gpio_priv; struct gpio_regs *regs; struct xilinx_gpio_priv *priv = gpio_get_controller(gpio); if (priv) { regs = priv->regs; gpio_priv = gpio - priv->gpio_min; val = readl(®s->gpiodir); val = !!(val & (1 << gpio_priv)); debug("%s: reg: %x, gpio_no: %d, dir: %d\n", __func__, (u32)priv->regs, gpio_priv, val); return val; } return -1; } /* * Get input value * for example gpio setup to output only can't get input value * which is breaking gpio toggle command */ int gpio_get_value(unsigned gpio) { u32 val; if (gpio_get_direction(gpio) == GPIO_DIRECTION_OUT) val = gpio_get_output_value(gpio); else val = gpio_get_input_value(gpio); return val; } /* Set output value */ static int gpio_set_output_value(unsigned gpio, int value) { u32 val, gpio_priv; struct gpio_regs *regs; struct xilinx_gpio_priv *priv = gpio_get_controller(gpio); if (priv) { regs = priv->regs; gpio_priv = gpio - priv->gpio_min; val = priv->gpiodata_store; if (value) val |= 1 << gpio_priv; else val &= ~(1 << gpio_priv); writel(val, ®s->gpiodata); debug("%s: reg: %x, gpio_no: %d, output_val: %d\n", __func__, (u32)priv->regs, gpio_priv, val); priv->gpiodata_store = val; return 0; } return -1; } int gpio_set_value(unsigned gpio, int value) { if (gpio_get_direction(gpio) == GPIO_DIRECTION_OUT) return gpio_set_output_value(gpio, value); return -1; } /* Set GPIO as input */ int gpio_direction_input(unsigned gpio) { debug("%s\n", __func__); return gpio_set_direction(gpio, GPIO_DIRECTION_IN); } /* Setup GPIO as output and set output value */ int gpio_direction_output(unsigned gpio, int value) { int ret = gpio_set_direction(gpio, GPIO_DIRECTION_OUT); debug("%s\n", __func__); if (ret < 0) return ret; return gpio_set_output_value(gpio, value); } /* Show gpio status */ void gpio_info(void) { unsigned gpio; struct list_head *entry; struct xilinx_gpio_priv *priv = NULL; list_for_each(entry, &gpio_list) { priv = list_entry(entry, struct xilinx_gpio_priv, list); printf("\n%s: %s/%x (%d-%d)\n", __func__, priv->name, (u32)priv->regs, priv->gpio_min, priv->gpio_max); for (gpio = priv->gpio_min; gpio <= priv->gpio_max; gpio++) { printf("GPIO_%d:\t%s is an ", gpio, get_name(gpio)); if (gpio_get_direction(gpio) == GPIO_DIRECTION_OUT) printf("OUTPUT value = %d\n", gpio_get_output_value(gpio)); else printf("INPUT value = %d\n", gpio_get_input_value(gpio)); } } } int gpio_request(unsigned gpio, const char *label) { u32 gpio_priv; struct xilinx_gpio_priv *priv; if (gpio >= xilinx_gpio_max) return -EINVAL; priv = gpio_get_controller(gpio); if (priv) { gpio_priv = gpio - priv->gpio_min; if (label != NULL) { strncpy(priv->gpio_name[gpio_priv].name, label, GPIO_NAME_SIZE); priv->gpio_name[gpio_priv].name[GPIO_NAME_SIZE - 1] = '\0'; } return 0; } return -1; } int gpio_free(unsigned gpio) { u32 gpio_priv; struct xilinx_gpio_priv *priv; if (gpio >= xilinx_gpio_max) return -EINVAL; priv = gpio_get_controller(gpio); if (priv) { gpio_priv = gpio - priv->gpio_min; priv->gpio_name[gpio_priv].name[0] = '\0'; /* Do nothing here */ return 0; } return -1; } int gpio_alloc(u32 baseaddr, const char *name, u32 gpio_no) { struct xilinx_gpio_priv *priv; priv = calloc(1, sizeof(struct xilinx_gpio_priv)); /* Setup gpio name */ if (name != NULL) { strncpy(priv->name, name, GPIO_NAME_SIZE); priv->name[GPIO_NAME_SIZE - 1] = '\0'; } priv->regs = (struct gpio_regs *)baseaddr; priv->gpio_min = xilinx_gpio_max; xilinx_gpio_max = priv->gpio_min + gpio_no; priv->gpio_max = xilinx_gpio_max - 1; priv->gpio_name = calloc(gpio_no, sizeof(struct gpio_names)); INIT_LIST_HEAD(&priv->list); list_add_tail(&priv->list, &gpio_list); printf("%s: Add %s (%d-%d)\n", __func__, name, priv->gpio_min, priv->gpio_max); /* Return the first gpio allocated for this device */ return priv->gpio_min; } /* Dual channel gpio is one IP with two independent channels */ int gpio_alloc_dual(u32 baseaddr, const char *name, u32 gpio_no0, u32 gpio_no1) { int ret; ret = gpio_alloc(baseaddr, name, gpio_no0); gpio_alloc(baseaddr + 8, strcat((char *)name, "_1"), gpio_no1); /* Return the first gpio allocated for this device */ return ret; }