1 /* 2 * (C) Copyright 2012 Henrik Nordstrom <henrik@henriknordstrom.net> 3 * 4 * Based on earlier arch/arm/cpu/armv7/sunxi/gpio.c: 5 * 6 * (C) Copyright 2007-2011 7 * Allwinner Technology Co., Ltd. <www.allwinnertech.com> 8 * Tom Cubie <tangliang@allwinnertech.com> 9 * 10 * SPDX-License-Identifier: GPL-2.0+ 11 */ 12 13 #include <common.h> 14 #include <dm.h> 15 #include <errno.h> 16 #include <fdtdec.h> 17 #include <malloc.h> 18 #include <asm/io.h> 19 #include <asm/gpio.h> 20 #include <dm/device-internal.h> 21 #ifdef CONFIG_AXP209_POWER 22 #include <axp209.h> 23 #endif 24 #ifdef CONFIG_AXP221_POWER 25 #include <axp221.h> 26 #endif 27 28 DECLARE_GLOBAL_DATA_PTR; 29 30 #define SUNXI_GPIOS_PER_BANK SUNXI_GPIO_A_NR 31 32 struct sunxi_gpio_platdata { 33 struct sunxi_gpio *regs; 34 const char *bank_name; /* Name of bank, e.g. "B" */ 35 int gpio_count; 36 }; 37 38 #ifndef CONFIG_DM_GPIO 39 static int sunxi_gpio_output(u32 pin, u32 val) 40 { 41 u32 dat; 42 u32 bank = GPIO_BANK(pin); 43 u32 num = GPIO_NUM(pin); 44 struct sunxi_gpio *pio = BANK_TO_GPIO(bank); 45 46 dat = readl(&pio->dat); 47 if (val) 48 dat |= 0x1 << num; 49 else 50 dat &= ~(0x1 << num); 51 52 writel(dat, &pio->dat); 53 54 return 0; 55 } 56 57 static int sunxi_gpio_input(u32 pin) 58 { 59 u32 dat; 60 u32 bank = GPIO_BANK(pin); 61 u32 num = GPIO_NUM(pin); 62 struct sunxi_gpio *pio = BANK_TO_GPIO(bank); 63 64 dat = readl(&pio->dat); 65 dat >>= num; 66 67 return dat & 0x1; 68 } 69 70 int gpio_request(unsigned gpio, const char *label) 71 { 72 return 0; 73 } 74 75 int gpio_free(unsigned gpio) 76 { 77 return 0; 78 } 79 80 int gpio_direction_input(unsigned gpio) 81 { 82 #ifdef AXP_GPIO 83 if (gpio >= SUNXI_GPIO_AXP0_START) 84 return axp_gpio_direction_input(NULL, gpio - SUNXI_GPIO_AXP0_START); 85 #endif 86 sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_INPUT); 87 88 return 0; 89 } 90 91 int gpio_direction_output(unsigned gpio, int value) 92 { 93 #ifdef AXP_GPIO 94 if (gpio >= SUNXI_GPIO_AXP0_START) 95 return axp_gpio_direction_output(NULL, gpio - SUNXI_GPIO_AXP0_START, 96 value); 97 #endif 98 sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_OUTPUT); 99 100 return sunxi_gpio_output(gpio, value); 101 } 102 103 int gpio_get_value(unsigned gpio) 104 { 105 #ifdef AXP_GPIO 106 if (gpio >= SUNXI_GPIO_AXP0_START) 107 return axp_gpio_get_value(NULL, gpio - SUNXI_GPIO_AXP0_START); 108 #endif 109 return sunxi_gpio_input(gpio); 110 } 111 112 int gpio_set_value(unsigned gpio, int value) 113 { 114 #ifdef AXP_GPIO 115 if (gpio >= SUNXI_GPIO_AXP0_START) 116 return axp_gpio_set_value(NULL, gpio - SUNXI_GPIO_AXP0_START, value); 117 #endif 118 return sunxi_gpio_output(gpio, value); 119 } 120 121 int sunxi_name_to_gpio(const char *name) 122 { 123 int group = 0; 124 int groupsize = 9 * 32; 125 long pin; 126 char *eptr; 127 128 #ifdef AXP_GPIO 129 if (strncasecmp(name, "AXP0-", 5) == 0) { 130 name += 5; 131 if (strcmp(name, "VBUS-DETECT") == 0) 132 return SUNXI_GPIO_AXP0_START + 133 SUNXI_GPIO_AXP0_VBUS_DETECT; 134 if (strcmp(name, "VBUS-ENABLE") == 0) 135 return SUNXI_GPIO_AXP0_START + 136 SUNXI_GPIO_AXP0_VBUS_ENABLE; 137 pin = simple_strtol(name, &eptr, 10); 138 if (!*name || *eptr) 139 return -1; 140 return SUNXI_GPIO_AXP0_START + pin; 141 } 142 #endif 143 if (*name == 'P' || *name == 'p') 144 name++; 145 if (*name >= 'A') { 146 group = *name - (*name > 'a' ? 'a' : 'A'); 147 groupsize = 32; 148 name++; 149 } 150 151 pin = simple_strtol(name, &eptr, 10); 152 if (!*name || *eptr) 153 return -1; 154 if (pin < 0 || pin > groupsize || group >= 9) 155 return -1; 156 return group * 32 + pin; 157 } 158 #endif 159 160 int sunxi_name_to_gpio_bank(const char *name) 161 { 162 int group = 0; 163 164 if (*name == 'P' || *name == 'p') 165 name++; 166 if (*name >= 'A') { 167 group = *name - (*name > 'a' ? 'a' : 'A'); 168 return group; 169 } 170 171 return -1; 172 } 173 174 #ifdef CONFIG_DM_GPIO 175 /* TODO(sjg@chromium.org): Remove this function and use device tree */ 176 int sunxi_name_to_gpio(const char *name) 177 { 178 unsigned int gpio; 179 int ret; 180 181 ret = gpio_lookup_name(name, NULL, NULL, &gpio); 182 183 return ret ? ret : gpio; 184 } 185 186 static int sunxi_gpio_direction_input(struct udevice *dev, unsigned offset) 187 { 188 struct sunxi_gpio_platdata *plat = dev_get_platdata(dev); 189 190 sunxi_gpio_set_cfgbank(plat->regs, offset, SUNXI_GPIO_INPUT); 191 192 return 0; 193 } 194 195 static int sunxi_gpio_direction_output(struct udevice *dev, unsigned offset, 196 int value) 197 { 198 struct sunxi_gpio_platdata *plat = dev_get_platdata(dev); 199 u32 num = GPIO_NUM(offset); 200 201 sunxi_gpio_set_cfgbank(plat->regs, offset, SUNXI_GPIO_OUTPUT); 202 clrsetbits_le32(&plat->regs->dat, 1 << num, value ? (1 << num) : 0); 203 204 return 0; 205 } 206 207 static int sunxi_gpio_get_value(struct udevice *dev, unsigned offset) 208 { 209 struct sunxi_gpio_platdata *plat = dev_get_platdata(dev); 210 u32 num = GPIO_NUM(offset); 211 unsigned dat; 212 213 dat = readl(&plat->regs->dat); 214 dat >>= num; 215 216 return dat & 0x1; 217 } 218 219 static int sunxi_gpio_set_value(struct udevice *dev, unsigned offset, 220 int value) 221 { 222 struct sunxi_gpio_platdata *plat = dev_get_platdata(dev); 223 u32 num = GPIO_NUM(offset); 224 225 clrsetbits_le32(&plat->regs->dat, 1 << num, value ? (1 << num) : 0); 226 return 0; 227 } 228 229 static int sunxi_gpio_get_function(struct udevice *dev, unsigned offset) 230 { 231 struct sunxi_gpio_platdata *plat = dev_get_platdata(dev); 232 int func; 233 234 func = sunxi_gpio_get_cfgbank(plat->regs, offset); 235 if (func == SUNXI_GPIO_OUTPUT) 236 return GPIOF_OUTPUT; 237 else if (func == SUNXI_GPIO_INPUT) 238 return GPIOF_INPUT; 239 else 240 return GPIOF_FUNC; 241 } 242 243 static const struct dm_gpio_ops gpio_sunxi_ops = { 244 .direction_input = sunxi_gpio_direction_input, 245 .direction_output = sunxi_gpio_direction_output, 246 .get_value = sunxi_gpio_get_value, 247 .set_value = sunxi_gpio_set_value, 248 .get_function = sunxi_gpio_get_function, 249 }; 250 251 /** 252 * Returns the name of a GPIO bank 253 * 254 * GPIO banks are named A, B, C, ... 255 * 256 * @bank: Bank number (0, 1..n-1) 257 * @return allocated string containing the name 258 */ 259 static char *gpio_bank_name(int bank) 260 { 261 char *name; 262 263 name = malloc(3); 264 if (name) { 265 name[0] = 'P'; 266 name[1] = 'A' + bank; 267 name[2] = '\0'; 268 } 269 270 return name; 271 } 272 273 static int gpio_sunxi_probe(struct udevice *dev) 274 { 275 struct sunxi_gpio_platdata *plat = dev_get_platdata(dev); 276 struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); 277 278 /* Tell the uclass how many GPIOs we have */ 279 if (plat) { 280 uc_priv->gpio_count = plat->gpio_count; 281 uc_priv->bank_name = plat->bank_name; 282 } 283 284 return 0; 285 } 286 /** 287 * We have a top-level GPIO device with no actual GPIOs. It has a child 288 * device for each Sunxi bank. 289 */ 290 static int gpio_sunxi_bind(struct udevice *parent) 291 { 292 struct sunxi_gpio_platdata *plat = parent->platdata; 293 struct sunxi_gpio_reg *ctlr; 294 int bank; 295 int ret; 296 297 /* If this is a child device, there is nothing to do here */ 298 if (plat) 299 return 0; 300 301 ctlr = (struct sunxi_gpio_reg *)fdtdec_get_addr(gd->fdt_blob, 302 parent->of_offset, "reg"); 303 for (bank = 0; bank < SUNXI_GPIO_BANKS; bank++) { 304 struct sunxi_gpio_platdata *plat; 305 struct udevice *dev; 306 307 plat = calloc(1, sizeof(*plat)); 308 if (!plat) 309 return -ENOMEM; 310 plat->regs = &ctlr->gpio_bank[bank]; 311 plat->bank_name = gpio_bank_name(bank); 312 plat->gpio_count = SUNXI_GPIOS_PER_BANK; 313 314 ret = device_bind(parent, parent->driver, 315 plat->bank_name, plat, -1, &dev); 316 if (ret) 317 return ret; 318 dev->of_offset = parent->of_offset; 319 } 320 321 return 0; 322 } 323 324 static const struct udevice_id sunxi_gpio_ids[] = { 325 { .compatible = "allwinner,sun4i-a10-pinctrl" }, 326 { .compatible = "allwinner,sun5i-a10s-pinctrl" }, 327 { .compatible = "allwinner,sun5i-a13-pinctrl" }, 328 { .compatible = "allwinner,sun6i-a31-pinctrl" }, 329 { .compatible = "allwinner,sun6i-a31s-pinctrl" }, 330 { .compatible = "allwinner,sun7i-a20-pinctrl" }, 331 { .compatible = "allwinner,sun8i-a23-pinctrl" }, 332 { .compatible = "allwinner,sun9i-a80-pinctrl" }, 333 { } 334 }; 335 336 U_BOOT_DRIVER(gpio_sunxi) = { 337 .name = "gpio_sunxi", 338 .id = UCLASS_GPIO, 339 .ops = &gpio_sunxi_ops, 340 .of_match = sunxi_gpio_ids, 341 .bind = gpio_sunxi_bind, 342 .probe = gpio_sunxi_probe, 343 }; 344 #endif 345