1 /* 2 * Copyright (c) 2012 The Chromium OS Authors. 3 * SPDX-License-Identifier: GPL-2.0+ 4 */ 5 6 /* 7 * This is a GPIO driver for Intel ICH6 and later. The x86 GPIOs are accessed 8 * through the PCI bus. Each PCI device has 256 bytes of configuration space, 9 * consisting of a standard header and a device-specific set of registers. PCI 10 * bus 0, device 31, function 0 gives us access to the chipset GPIOs (among 11 * other things). Within the PCI configuration space, the GPIOBASE register 12 * tells us where in the device's I/O region we can find more registers to 13 * actually access the GPIOs. 14 * 15 * PCI bus/device/function 0:1f:0 => PCI config registers 16 * PCI config register "GPIOBASE" 17 * PCI I/O space + [GPIOBASE] => start of GPIO registers 18 * GPIO registers => gpio pin function, direction, value 19 * 20 * 21 * Danger Will Robinson! Bank 0 (GPIOs 0-31) seems to be fairly stable. Most 22 * ICH versions have more, but the decoding the matrix that describes them is 23 * absurdly complex and constantly changing. We'll provide Bank 1 and Bank 2, 24 * but they will ONLY work for certain unspecified chipsets because the offset 25 * from GPIOBASE changes randomly. Even then, many GPIOs are unimplemented or 26 * reserved or subject to arcane restrictions. 27 */ 28 29 #include <common.h> 30 #include <dm.h> 31 #include <errno.h> 32 #include <fdtdec.h> 33 #include <pch.h> 34 #include <pci.h> 35 #include <syscon.h> 36 #include <asm/cpu.h> 37 #include <asm/gpio.h> 38 #include <asm/io.h> 39 #include <asm/pci.h> 40 41 DECLARE_GLOBAL_DATA_PTR; 42 43 #define GPIO_PER_BANK 32 44 45 struct ich6_bank_priv { 46 /* These are I/O addresses */ 47 uint16_t use_sel; 48 uint16_t io_sel; 49 uint16_t lvl; 50 }; 51 52 #define GPIO_USESEL_OFFSET(x) (x) 53 #define GPIO_IOSEL_OFFSET(x) (x + 4) 54 #define GPIO_LVL_OFFSET(x) (x + 8) 55 56 static int _ich6_gpio_set_value(uint16_t base, unsigned offset, int value) 57 { 58 u32 val; 59 60 val = inl(base); 61 if (value) 62 val |= (1UL << offset); 63 else 64 val &= ~(1UL << offset); 65 outl(val, base); 66 67 return 0; 68 } 69 70 static int _ich6_gpio_set_direction(uint16_t base, unsigned offset, int dir) 71 { 72 u32 val; 73 74 if (!dir) { 75 val = inl(base); 76 val |= (1UL << offset); 77 outl(val, base); 78 } else { 79 val = inl(base); 80 val &= ~(1UL << offset); 81 outl(val, base); 82 } 83 84 return 0; 85 } 86 87 static int gpio_ich6_ofdata_to_platdata(struct udevice *dev) 88 { 89 struct ich6_bank_platdata *plat = dev_get_platdata(dev); 90 u32 gpiobase; 91 int offset; 92 int ret; 93 94 ret = pch_get_gpio_base(dev->parent, &gpiobase); 95 if (ret) 96 return ret; 97 98 offset = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "reg", -1); 99 if (offset == -1) { 100 debug("%s: Invalid register offset %d\n", __func__, offset); 101 return -EINVAL; 102 } 103 plat->offset = offset; 104 plat->base_addr = gpiobase + offset; 105 plat->bank_name = fdt_getprop(gd->fdt_blob, dev->of_offset, 106 "bank-name", NULL); 107 108 return 0; 109 } 110 111 static int ich6_gpio_probe(struct udevice *dev) 112 { 113 struct ich6_bank_platdata *plat = dev_get_platdata(dev); 114 struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); 115 struct ich6_bank_priv *bank = dev_get_priv(dev); 116 struct udevice *pinctrl; 117 118 /* Set up pin control if available */ 119 syscon_get_by_driver_data(X86_SYSCON_PINCONF, &pinctrl); 120 121 uc_priv->gpio_count = GPIO_PER_BANK; 122 uc_priv->bank_name = plat->bank_name; 123 bank->use_sel = plat->base_addr; 124 bank->io_sel = plat->base_addr + 4; 125 bank->lvl = plat->base_addr + 8; 126 127 return 0; 128 } 129 130 static int ich6_gpio_request(struct udevice *dev, unsigned offset, 131 const char *label) 132 { 133 struct ich6_bank_priv *bank = dev_get_priv(dev); 134 u32 tmplong; 135 136 /* 137 * Make sure that the GPIO pin we want isn't already in use for some 138 * built-in hardware function. We have to check this for every 139 * requested pin. 140 */ 141 tmplong = inl(bank->use_sel); 142 if (!(tmplong & (1UL << offset))) { 143 debug("%s: gpio %d is reserved for internal use\n", __func__, 144 offset); 145 return -EPERM; 146 } 147 148 return 0; 149 } 150 151 static int ich6_gpio_direction_input(struct udevice *dev, unsigned offset) 152 { 153 struct ich6_bank_priv *bank = dev_get_priv(dev); 154 155 return _ich6_gpio_set_direction(bank->io_sel, offset, 0); 156 } 157 158 static int ich6_gpio_direction_output(struct udevice *dev, unsigned offset, 159 int value) 160 { 161 int ret; 162 struct ich6_bank_priv *bank = dev_get_priv(dev); 163 164 ret = _ich6_gpio_set_direction(bank->io_sel, offset, 1); 165 if (ret) 166 return ret; 167 168 return _ich6_gpio_set_value(bank->lvl, offset, value); 169 } 170 171 static int ich6_gpio_get_value(struct udevice *dev, unsigned offset) 172 { 173 struct ich6_bank_priv *bank = dev_get_priv(dev); 174 u32 tmplong; 175 int r; 176 177 tmplong = inl(bank->lvl); 178 r = (tmplong & (1UL << offset)) ? 1 : 0; 179 return r; 180 } 181 182 static int ich6_gpio_set_value(struct udevice *dev, unsigned offset, 183 int value) 184 { 185 struct ich6_bank_priv *bank = dev_get_priv(dev); 186 return _ich6_gpio_set_value(bank->lvl, offset, value); 187 } 188 189 static int ich6_gpio_get_function(struct udevice *dev, unsigned offset) 190 { 191 struct ich6_bank_priv *bank = dev_get_priv(dev); 192 u32 mask = 1UL << offset; 193 194 if (!(inl(bank->use_sel) & mask)) 195 return GPIOF_FUNC; 196 if (inl(bank->io_sel) & mask) 197 return GPIOF_INPUT; 198 else 199 return GPIOF_OUTPUT; 200 } 201 202 static const struct dm_gpio_ops gpio_ich6_ops = { 203 .request = ich6_gpio_request, 204 .direction_input = ich6_gpio_direction_input, 205 .direction_output = ich6_gpio_direction_output, 206 .get_value = ich6_gpio_get_value, 207 .set_value = ich6_gpio_set_value, 208 .get_function = ich6_gpio_get_function, 209 }; 210 211 static const struct udevice_id intel_ich6_gpio_ids[] = { 212 { .compatible = "intel,ich6-gpio" }, 213 { } 214 }; 215 216 U_BOOT_DRIVER(gpio_ich6) = { 217 .name = "gpio_ich6", 218 .id = UCLASS_GPIO, 219 .of_match = intel_ich6_gpio_ids, 220 .ops = &gpio_ich6_ops, 221 .ofdata_to_platdata = gpio_ich6_ofdata_to_platdata, 222 .probe = ich6_gpio_probe, 223 .priv_auto_alloc_size = sizeof(struct ich6_bank_priv), 224 .platdata_auto_alloc_size = sizeof(struct ich6_bank_platdata), 225 }; 226