1 /* 2 * Copyright (C) 2016 Masahiro Yamada <yamada.masahiro@socionext.com> 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 7 #include <common.h> 8 #include <dm/device.h> 9 #include <mapmem.h> 10 #include <linux/bitops.h> 11 #include <linux/io.h> 12 #include <asm/errno.h> 13 #include <asm/gpio.h> 14 15 #define UNIPHIER_GPIO_PORTS_PER_BANK 8 16 17 #define UNIPHIER_GPIO_REG_DATA 0 /* data */ 18 #define UNIPHIER_GPIO_REG_DIR 4 /* direction (1:in, 0:out) */ 19 20 struct uniphier_gpio_priv { 21 void __iomem *base; 22 char bank_name[16]; 23 }; 24 25 static void uniphier_gpio_offset_write(struct udevice *dev, unsigned offset, 26 unsigned reg, int value) 27 { 28 struct uniphier_gpio_priv *priv = dev_get_priv(dev); 29 u32 tmp; 30 31 tmp = readl(priv->base + reg); 32 if (value) 33 tmp |= BIT(offset); 34 else 35 tmp &= ~BIT(offset); 36 writel(tmp, priv->base + reg); 37 } 38 39 static int uniphier_gpio_offset_read(struct udevice *dev, unsigned offset, 40 unsigned reg) 41 { 42 struct uniphier_gpio_priv *priv = dev_get_priv(dev); 43 44 return !!(readl(priv->base + reg) & BIT(offset)); 45 } 46 47 static int uniphier_gpio_direction_input(struct udevice *dev, unsigned offset) 48 { 49 uniphier_gpio_offset_write(dev, offset, UNIPHIER_GPIO_REG_DIR, 1); 50 51 return 0; 52 } 53 54 static int uniphier_gpio_direction_output(struct udevice *dev, unsigned offset, 55 int value) 56 { 57 uniphier_gpio_offset_write(dev, offset, UNIPHIER_GPIO_REG_DATA, value); 58 uniphier_gpio_offset_write(dev, offset, UNIPHIER_GPIO_REG_DIR, 0); 59 60 return 0; 61 } 62 63 static int uniphier_gpio_get_value(struct udevice *dev, unsigned offset) 64 { 65 return uniphier_gpio_offset_read(dev, offset, UNIPHIER_GPIO_REG_DATA); 66 } 67 68 static int uniphier_gpio_set_value(struct udevice *dev, unsigned offset, 69 int value) 70 { 71 uniphier_gpio_offset_write(dev, offset, UNIPHIER_GPIO_REG_DATA, value); 72 73 return 0; 74 } 75 76 static int uniphier_gpio_get_function(struct udevice *dev, unsigned offset) 77 { 78 return uniphier_gpio_offset_read(dev, offset, UNIPHIER_GPIO_REG_DIR) ? 79 GPIOF_INPUT : GPIOF_OUTPUT; 80 } 81 82 static const struct dm_gpio_ops uniphier_gpio_ops = { 83 .direction_input = uniphier_gpio_direction_input, 84 .direction_output = uniphier_gpio_direction_output, 85 .get_value = uniphier_gpio_get_value, 86 .set_value = uniphier_gpio_set_value, 87 .get_function = uniphier_gpio_get_function, 88 }; 89 90 static int uniphier_gpio_probe(struct udevice *dev) 91 { 92 struct uniphier_gpio_priv *priv = dev_get_priv(dev); 93 struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); 94 DECLARE_GLOBAL_DATA_PTR; 95 fdt_addr_t addr; 96 fdt_size_t size; 97 unsigned int tmp; 98 99 addr = fdtdec_get_addr_size(gd->fdt_blob, dev->of_offset, "reg", 100 &size); 101 if (addr == FDT_ADDR_T_NONE) 102 return -EINVAL; 103 104 priv->base = map_sysmem(addr, size); 105 if (!priv->base) 106 return -ENOMEM; 107 108 uc_priv->gpio_count = UNIPHIER_GPIO_PORTS_PER_BANK; 109 110 tmp = (addr & 0xfff); 111 112 /* Unfortunately, there is a register hole at offset 0x90-0x9f. */ 113 if (tmp > 0x90) 114 tmp -= 0x10; 115 116 snprintf(priv->bank_name, sizeof(priv->bank_name) - 1, 117 "port%d-", (tmp - 8) / 8); 118 119 uc_priv->bank_name = priv->bank_name; 120 121 return 0; 122 } 123 124 static int uniphier_gpio_remove(struct udevice *dev) 125 { 126 struct uniphier_gpio_priv *priv = dev_get_priv(dev); 127 128 unmap_sysmem(priv->base); 129 130 return 0; 131 } 132 133 /* .data = the number of GPIO banks */ 134 static const struct udevice_id uniphier_gpio_match[] = { 135 { .compatible = "socionext,uniphier-gpio" }, 136 { /* sentinel */ } 137 }; 138 139 U_BOOT_DRIVER(uniphier_gpio) = { 140 .name = "uniphier_gpio", 141 .id = UCLASS_GPIO, 142 .of_match = uniphier_gpio_match, 143 .probe = uniphier_gpio_probe, 144 .remove = uniphier_gpio_remove, 145 .priv_auto_alloc_size = sizeof(struct uniphier_gpio_priv), 146 .ops = &uniphier_gpio_ops, 147 }; 148