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 22 DECLARE_GLOBAL_DATA_PTR; 23 24 #define SUNXI_GPIOS_PER_BANK SUNXI_GPIO_A_NR 25 26 struct sunxi_gpio_platdata { 27 struct sunxi_gpio *regs; 28 const char *bank_name; /* Name of bank, e.g. "B" */ 29 int gpio_count; 30 }; 31 32 #ifndef CONFIG_DM_GPIO 33 static int sunxi_gpio_output(u32 pin, u32 val) 34 { 35 u32 dat; 36 u32 bank = GPIO_BANK(pin); 37 u32 num = GPIO_NUM(pin); 38 struct sunxi_gpio *pio = BANK_TO_GPIO(bank); 39 40 dat = readl(&pio->dat); 41 if (val) 42 dat |= 0x1 << num; 43 else 44 dat &= ~(0x1 << num); 45 46 writel(dat, &pio->dat); 47 48 return 0; 49 } 50 51 static int sunxi_gpio_input(u32 pin) 52 { 53 u32 dat; 54 u32 bank = GPIO_BANK(pin); 55 u32 num = GPIO_NUM(pin); 56 struct sunxi_gpio *pio = BANK_TO_GPIO(bank); 57 58 dat = readl(&pio->dat); 59 dat >>= num; 60 61 return dat & 0x1; 62 } 63 64 int gpio_request(unsigned gpio, const char *label) 65 { 66 return 0; 67 } 68 69 int gpio_free(unsigned gpio) 70 { 71 return 0; 72 } 73 74 int gpio_direction_input(unsigned gpio) 75 { 76 sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_INPUT); 77 78 return sunxi_gpio_input(gpio); 79 } 80 81 int gpio_direction_output(unsigned gpio, int value) 82 { 83 sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_OUTPUT); 84 85 return sunxi_gpio_output(gpio, value); 86 } 87 88 int gpio_get_value(unsigned gpio) 89 { 90 return sunxi_gpio_input(gpio); 91 } 92 93 int gpio_set_value(unsigned gpio, int value) 94 { 95 return sunxi_gpio_output(gpio, value); 96 } 97 98 int sunxi_name_to_gpio(const char *name) 99 { 100 int group = 0; 101 int groupsize = 9 * 32; 102 long pin; 103 char *eptr; 104 if (*name == 'P' || *name == 'p') 105 name++; 106 if (*name >= 'A') { 107 group = *name - (*name > 'a' ? 'a' : 'A'); 108 groupsize = 32; 109 name++; 110 } 111 112 pin = simple_strtol(name, &eptr, 10); 113 if (!*name || *eptr) 114 return -1; 115 if (pin < 0 || pin > groupsize || group >= 9) 116 return -1; 117 return group * 32 + pin; 118 } 119 #endif 120 121 #ifdef CONFIG_DM_GPIO 122 static int sunxi_gpio_direction_input(struct udevice *dev, unsigned offset) 123 { 124 struct sunxi_gpio_platdata *plat = dev_get_platdata(dev); 125 126 sunxi_gpio_set_cfgbank(plat->regs, offset, SUNXI_GPIO_INPUT); 127 128 return 0; 129 } 130 131 static int sunxi_gpio_direction_output(struct udevice *dev, unsigned offset, 132 int value) 133 { 134 struct sunxi_gpio_platdata *plat = dev_get_platdata(dev); 135 u32 num = GPIO_NUM(offset); 136 137 sunxi_gpio_set_cfgbank(plat->regs, offset, SUNXI_GPIO_OUTPUT); 138 clrsetbits_le32(&plat->regs->dat, 1 << num, value ? (1 << num) : 0); 139 140 return 0; 141 } 142 143 static int sunxi_gpio_get_value(struct udevice *dev, unsigned offset) 144 { 145 struct sunxi_gpio_platdata *plat = dev_get_platdata(dev); 146 u32 num = GPIO_NUM(offset); 147 unsigned dat; 148 149 dat = readl(&plat->regs->dat); 150 dat >>= num; 151 152 return dat & 0x1; 153 } 154 155 static int sunxi_gpio_set_value(struct udevice *dev, unsigned offset, 156 int value) 157 { 158 struct sunxi_gpio_platdata *plat = dev_get_platdata(dev); 159 u32 num = GPIO_NUM(offset); 160 161 clrsetbits_le32(&plat->regs->dat, 1 << num, value ? (1 << num) : 0); 162 return 0; 163 } 164 165 static int sunxi_gpio_get_function(struct udevice *dev, unsigned offset) 166 { 167 struct sunxi_gpio_platdata *plat = dev_get_platdata(dev); 168 int func; 169 170 func = sunxi_gpio_get_cfgbank(plat->regs, offset); 171 if (func == SUNXI_GPIO_OUTPUT) 172 return GPIOF_OUTPUT; 173 else if (func == SUNXI_GPIO_INPUT) 174 return GPIOF_INPUT; 175 else 176 return GPIOF_FUNC; 177 } 178 179 static const struct dm_gpio_ops gpio_sunxi_ops = { 180 .direction_input = sunxi_gpio_direction_input, 181 .direction_output = sunxi_gpio_direction_output, 182 .get_value = sunxi_gpio_get_value, 183 .set_value = sunxi_gpio_set_value, 184 .get_function = sunxi_gpio_get_function, 185 }; 186 187 /** 188 * Returns the name of a GPIO bank 189 * 190 * GPIO banks are named A, B, C, ... 191 * 192 * @bank: Bank number (0, 1..n-1) 193 * @return allocated string containing the name 194 */ 195 static char *gpio_bank_name(int bank) 196 { 197 char *name; 198 199 name = malloc(2); 200 if (name) { 201 name[0] = 'A' + bank; 202 name[1] = '\0'; 203 } 204 205 return name; 206 } 207 208 static int gpio_sunxi_probe(struct udevice *dev) 209 { 210 struct sunxi_gpio_platdata *plat = dev_get_platdata(dev); 211 struct gpio_dev_priv *uc_priv = dev->uclass_priv; 212 213 /* Tell the uclass how many GPIOs we have */ 214 if (plat) { 215 uc_priv->gpio_count = plat->gpio_count; 216 uc_priv->bank_name = plat->bank_name; 217 } 218 219 return 0; 220 } 221 /** 222 * We have a top-level GPIO device with no actual GPIOs. It has a child 223 * device for each Sunxi bank. 224 */ 225 static int gpio_sunxi_bind(struct udevice *parent) 226 { 227 struct sunxi_gpio_platdata *plat = parent->platdata; 228 struct sunxi_gpio_reg *ctlr; 229 int bank; 230 int ret; 231 232 /* If this is a child device, there is nothing to do here */ 233 if (plat) 234 return 0; 235 236 ctlr = (struct sunxi_gpio_reg *)fdtdec_get_addr(gd->fdt_blob, 237 parent->of_offset, "reg"); 238 for (bank = 0; bank < SUNXI_GPIO_BANKS; bank++) { 239 struct sunxi_gpio_platdata *plat; 240 struct udevice *dev; 241 242 plat = calloc(1, sizeof(*plat)); 243 if (!plat) 244 return -ENOMEM; 245 plat->regs = &ctlr->gpio_bank[bank]; 246 plat->bank_name = gpio_bank_name(bank); 247 plat->gpio_count = SUNXI_GPIOS_PER_BANK; 248 249 ret = device_bind(parent, parent->driver, 250 plat->bank_name, plat, -1, &dev); 251 if (ret) 252 return ret; 253 dev->of_offset = parent->of_offset; 254 } 255 256 return 0; 257 } 258 259 static const struct udevice_id sunxi_gpio_ids[] = { 260 { .compatible = "allwinner,sun7i-a20-pinctrl" }, 261 { } 262 }; 263 264 U_BOOT_DRIVER(gpio_sunxi) = { 265 .name = "gpio_sunxi", 266 .id = UCLASS_GPIO, 267 .ops = &gpio_sunxi_ops, 268 .of_match = sunxi_gpio_ids, 269 .bind = gpio_sunxi_bind, 270 .probe = gpio_sunxi_probe, 271 }; 272 #endif 273