diff options
Diffstat (limited to 'drivers/gpio/zynq_gpio.c')
-rw-r--r-- | drivers/gpio/zynq_gpio.c | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/drivers/gpio/zynq_gpio.c b/drivers/gpio/zynq_gpio.c index 83a2c46..92c9f0e 100644 --- a/drivers/gpio/zynq_gpio.c +++ b/drivers/gpio/zynq_gpio.c @@ -14,6 +14,17 @@ #include <asm/io.h> #include <asm/errno.h> +#ifdef CONFIG_DM_GPIO +#include <dm.h> +#include <fdtdec.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct zynq_gpio_privdata { + phys_addr_t base; +}; +#endif + /** * zynq_gpio_get_bank_pin - Get the bank number and pin number within that bank * for a given pin in the GPIO device @@ -68,6 +79,7 @@ static int check_gpio(unsigned gpio) return 0; } +#ifndef CONFIG_DM_GPIO /** * gpio_get_value - Get the state of the specified pin of GPIO device * @gpio: gpio pin number within the device @@ -218,3 +230,142 @@ int gpio_free(unsigned gpio) { return 0; } +#else +static int zynq_gpio_get_value(struct udevice *dev, unsigned gpio) +{ + u32 data; + unsigned int bank_num, bank_pin_num; + struct zynq_gpio_privdata *priv = dev_get_priv(dev); + + if (check_gpio(gpio) < 0) + return -1; + + zynq_gpio_get_bank_pin(gpio, &bank_num, &bank_pin_num); + + data = readl(priv->base + + ZYNQ_GPIO_DATA_RO_OFFSET(bank_num)); + + return (data >> bank_pin_num) & 1; +} + +static int zynq_gpio_set_value(struct udevice *dev, unsigned gpio, int value) +{ + unsigned int reg_offset, bank_num, bank_pin_num; + struct zynq_gpio_privdata *priv = dev_get_priv(dev); + + if (check_gpio(gpio) < 0) + return -1; + + zynq_gpio_get_bank_pin(gpio, &bank_num, &bank_pin_num); + + if (bank_pin_num >= ZYNQ_GPIO_MID_PIN_NUM) { + /* only 16 data bits in bit maskable reg */ + bank_pin_num -= ZYNQ_GPIO_MID_PIN_NUM; + reg_offset = ZYNQ_GPIO_DATA_MSW_OFFSET(bank_num); + } else { + reg_offset = ZYNQ_GPIO_DATA_LSW_OFFSET(bank_num); + } + + /* + * get the 32 bit value to be written to the mask/data register where + * the upper 16 bits is the mask and lower 16 bits is the data + */ + value = !!value; + value = ~(1 << (bank_pin_num + ZYNQ_GPIO_MID_PIN_NUM)) & + ((value << bank_pin_num) | ZYNQ_GPIO_UPPER_MASK); + + writel(value, priv->base + reg_offset); + + return 0; +} + +static int zynq_gpio_direction_input(struct udevice *dev, unsigned gpio) +{ + u32 reg; + unsigned int bank_num, bank_pin_num; + struct zynq_gpio_privdata *priv = dev_get_priv(dev); + + if (check_gpio(gpio) < 0) + return -1; + + zynq_gpio_get_bank_pin(gpio, &bank_num, &bank_pin_num); + + /* bank 0 pins 7 and 8 are special and cannot be used as inputs */ + if (bank_num == 0 && (bank_pin_num == 7 || bank_pin_num == 8)) + return -1; + + /* clear the bit in direction mode reg to set the pin as input */ + reg = readl(priv->base + ZYNQ_GPIO_DIRM_OFFSET(bank_num)); + reg &= ~BIT(bank_pin_num); + writel(reg, priv->base + ZYNQ_GPIO_DIRM_OFFSET(bank_num)); + + return 0; +} + +static int zynq_gpio_direction_output(struct udevice *dev, unsigned gpio, + int value) +{ + u32 reg; + unsigned int bank_num, bank_pin_num; + struct zynq_gpio_privdata *priv = dev_get_priv(dev); + + if (check_gpio(gpio) < 0) + return -1; + + zynq_gpio_get_bank_pin(gpio, &bank_num, &bank_pin_num); + + /* set the GPIO pin as output */ + reg = readl(priv->base + ZYNQ_GPIO_DIRM_OFFSET(bank_num)); + reg |= BIT(bank_pin_num); + writel(reg, priv->base + ZYNQ_GPIO_DIRM_OFFSET(bank_num)); + + /* configure the output enable reg for the pin */ + reg = readl(priv->base + ZYNQ_GPIO_OUTEN_OFFSET(bank_num)); + reg |= BIT(bank_pin_num); + writel(reg, priv->base + ZYNQ_GPIO_OUTEN_OFFSET(bank_num)); + + /* set the state of the pin */ + gpio_set_value(gpio, value); + return 0; +} + +static const struct dm_gpio_ops gpio_zynq_ops = { + .direction_input = zynq_gpio_direction_input, + .direction_output = zynq_gpio_direction_output, + .get_value = zynq_gpio_get_value, + .set_value = zynq_gpio_set_value, +}; + +static int zynq_gpio_probe(struct udevice *dev) +{ + struct zynq_gpio_privdata *priv = dev_get_priv(dev); + + priv->base = dev_get_addr(dev); + + return 0; +} + +static int zynq_gpio_ofdata_to_platdata(struct udevice *dev) +{ + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + + uc_priv->gpio_count = ZYNQ_GPIO_NR_GPIOS; + + return 0; +} + +static const struct udevice_id zynq_gpio_ids[] = { + { .compatible = "xlnx,zynq-gpio-1.0" }, + { } +}; + +U_BOOT_DRIVER(gpio_zynq) = { + .name = "gpio_zynq", + .id = UCLASS_GPIO, + .ops = &gpio_zynq_ops, + .of_match = zynq_gpio_ids, + .ofdata_to_platdata = zynq_gpio_ofdata_to_platdata, + .probe = zynq_gpio_probe, + .priv_auto_alloc_size = sizeof(struct zynq_gpio_privdata), +}; +#endif |