155ae10f8SBill Richardson /* 255ae10f8SBill Richardson * Copyright (c) 2012 The Chromium OS Authors. 31a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 455ae10f8SBill Richardson */ 555ae10f8SBill Richardson 655ae10f8SBill Richardson /* 755ae10f8SBill Richardson * This is a GPIO driver for Intel ICH6 and later. The x86 GPIOs are accessed 855ae10f8SBill Richardson * through the PCI bus. Each PCI device has 256 bytes of configuration space, 955ae10f8SBill Richardson * consisting of a standard header and a device-specific set of registers. PCI 1055ae10f8SBill Richardson * bus 0, device 31, function 0 gives us access to the chipset GPIOs (among 1155ae10f8SBill Richardson * other things). Within the PCI configuration space, the GPIOBASE register 1255ae10f8SBill Richardson * tells us where in the device's I/O region we can find more registers to 1355ae10f8SBill Richardson * actually access the GPIOs. 1455ae10f8SBill Richardson * 1555ae10f8SBill Richardson * PCI bus/device/function 0:1f:0 => PCI config registers 1655ae10f8SBill Richardson * PCI config register "GPIOBASE" 1755ae10f8SBill Richardson * PCI I/O space + [GPIOBASE] => start of GPIO registers 1855ae10f8SBill Richardson * GPIO registers => gpio pin function, direction, value 1957be9172SBill Richardson * 2057be9172SBill Richardson * 2157be9172SBill Richardson * Danger Will Robinson! Bank 0 (GPIOs 0-31) seems to be fairly stable. Most 2257be9172SBill Richardson * ICH versions have more, but the decoding the matrix that describes them is 2357be9172SBill Richardson * absurdly complex and constantly changing. We'll provide Bank 1 and Bank 2, 2457be9172SBill Richardson * but they will ONLY work for certain unspecified chipsets because the offset 2557be9172SBill Richardson * from GPIOBASE changes randomly. Even then, many GPIOs are unimplemented or 2657be9172SBill Richardson * reserved or subject to arcane restrictions. 2755ae10f8SBill Richardson */ 2855ae10f8SBill Richardson 2955ae10f8SBill Richardson #include <common.h> 307414112dSSimon Glass #include <dm.h> 317414112dSSimon Glass #include <errno.h> 327414112dSSimon Glass #include <fdtdec.h> 333ddc1c7bSBin Meng #include <pch.h> 3455ae10f8SBill Richardson #include <pci.h> 35*15cf75ecSSimon Glass #include <syscon.h> 36*15cf75ecSSimon Glass #include <asm/cpu.h> 3755ae10f8SBill Richardson #include <asm/gpio.h> 3855ae10f8SBill Richardson #include <asm/io.h> 391b4f25ffSSimon Glass #include <asm/pci.h> 4055ae10f8SBill Richardson 418b097916SSimon Glass DECLARE_GLOBAL_DATA_PTR; 428b097916SSimon Glass 437414112dSSimon Glass #define GPIO_PER_BANK 32 447414112dSSimon Glass 457414112dSSimon Glass struct ich6_bank_priv { 467414112dSSimon Glass /* These are I/O addresses */ 47b71eec31SBin Meng uint16_t use_sel; 48b71eec31SBin Meng uint16_t io_sel; 49b71eec31SBin Meng uint16_t lvl; 5057be9172SBill Richardson }; 5155ae10f8SBill Richardson 525318f18dSGabriel Huau #define GPIO_USESEL_OFFSET(x) (x) 535318f18dSGabriel Huau #define GPIO_IOSEL_OFFSET(x) (x + 4) 545318f18dSGabriel Huau #define GPIO_LVL_OFFSET(x) (x + 8) 555318f18dSGabriel Huau 561b4f25ffSSimon Glass /* TODO: Move this to device tree, or platform data */ 571b4f25ffSSimon Glass void ich_gpio_set_gpio_map(const struct pch_gpio_map *map) 581b4f25ffSSimon Glass { 591b4f25ffSSimon Glass gd->arch.gpio_map = map; 601b4f25ffSSimon Glass } 611b4f25ffSSimon Glass 625318f18dSGabriel Huau static int _ich6_gpio_set_value(uint16_t base, unsigned offset, int value) 635318f18dSGabriel Huau { 645318f18dSGabriel Huau u32 val; 655318f18dSGabriel Huau 665318f18dSGabriel Huau val = inl(base); 675318f18dSGabriel Huau if (value) 685318f18dSGabriel Huau val |= (1UL << offset); 695318f18dSGabriel Huau else 705318f18dSGabriel Huau val &= ~(1UL << offset); 715318f18dSGabriel Huau outl(val, base); 725318f18dSGabriel Huau 735318f18dSGabriel Huau return 0; 745318f18dSGabriel Huau } 755318f18dSGabriel Huau 765318f18dSGabriel Huau static int _ich6_gpio_set_direction(uint16_t base, unsigned offset, int dir) 775318f18dSGabriel Huau { 785318f18dSGabriel Huau u32 val; 795318f18dSGabriel Huau 805318f18dSGabriel Huau if (!dir) { 815318f18dSGabriel Huau val = inl(base); 825318f18dSGabriel Huau val |= (1UL << offset); 835318f18dSGabriel Huau outl(val, base); 845318f18dSGabriel Huau } else { 855318f18dSGabriel Huau val = inl(base); 865318f18dSGabriel Huau val &= ~(1UL << offset); 875318f18dSGabriel Huau outl(val, base); 885318f18dSGabriel Huau } 895318f18dSGabriel Huau 905318f18dSGabriel Huau return 0; 915318f18dSGabriel Huau } 925318f18dSGabriel Huau 935318f18dSGabriel Huau int gpio_ich6_pinctrl_init(void) 945318f18dSGabriel Huau { 955318f18dSGabriel Huau return 0; 965318f18dSGabriel Huau } 975318f18dSGabriel Huau 985318f18dSGabriel Huau static int gpio_ich6_ofdata_to_platdata(struct udevice *dev) 995318f18dSGabriel Huau { 1005318f18dSGabriel Huau struct ich6_bank_platdata *plat = dev_get_platdata(dev); 1013ddc1c7bSBin Meng u32 gpiobase; 1025318f18dSGabriel Huau int offset; 1033ddc1c7bSBin Meng int ret; 1045318f18dSGabriel Huau 1053ddc1c7bSBin Meng ret = pch_get_gpio_base(dev->parent, &gpiobase); 1063ddc1c7bSBin Meng if (ret) 1073ddc1c7bSBin Meng return ret; 1083ddc1c7bSBin Meng 1097414112dSSimon Glass offset = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "reg", -1); 1107414112dSSimon Glass if (offset == -1) { 1117414112dSSimon Glass debug("%s: Invalid register offset %d\n", __func__, offset); 1127414112dSSimon Glass return -EINVAL; 1137414112dSSimon Glass } 114d6d50db8SSimon Glass plat->offset = offset; 1157414112dSSimon Glass plat->base_addr = gpiobase + offset; 1167414112dSSimon Glass plat->bank_name = fdt_getprop(gd->fdt_blob, dev->of_offset, 1177414112dSSimon Glass "bank-name", NULL); 11855ae10f8SBill Richardson 11955ae10f8SBill Richardson return 0; 12055ae10f8SBill Richardson } 12155ae10f8SBill Richardson 1221b4f25ffSSimon Glass static int ich6_gpio_probe(struct udevice *dev) 12355ae10f8SBill Richardson { 1247414112dSSimon Glass struct ich6_bank_platdata *plat = dev_get_platdata(dev); 125e564f054SSimon Glass struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); 1267414112dSSimon Glass struct ich6_bank_priv *bank = dev_get_priv(dev); 127*15cf75ecSSimon Glass struct udevice *pinctrl; 1287414112dSSimon Glass 129*15cf75ecSSimon Glass /* Set up pin control if available */ 130*15cf75ecSSimon Glass syscon_get_by_driver_data(X86_SYSCON_PINCONF, &pinctrl); 1312795573aSBin Meng 1327414112dSSimon Glass uc_priv->gpio_count = GPIO_PER_BANK; 1337414112dSSimon Glass uc_priv->bank_name = plat->bank_name; 1347414112dSSimon Glass bank->use_sel = plat->base_addr; 1357414112dSSimon Glass bank->io_sel = plat->base_addr + 4; 1367414112dSSimon Glass bank->lvl = plat->base_addr + 8; 1377414112dSSimon Glass 1387414112dSSimon Glass return 0; 1397414112dSSimon Glass } 1407414112dSSimon Glass 1411b4f25ffSSimon Glass static int ich6_gpio_request(struct udevice *dev, unsigned offset, 1421b4f25ffSSimon Glass const char *label) 1437414112dSSimon Glass { 1447414112dSSimon Glass struct ich6_bank_priv *bank = dev_get_priv(dev); 14555ae10f8SBill Richardson u32 tmplong; 14655ae10f8SBill Richardson 14755ae10f8SBill Richardson /* 14855ae10f8SBill Richardson * Make sure that the GPIO pin we want isn't already in use for some 14955ae10f8SBill Richardson * built-in hardware function. We have to check this for every 15055ae10f8SBill Richardson * requested pin. 15155ae10f8SBill Richardson */ 1527414112dSSimon Glass tmplong = inl(bank->use_sel); 1537414112dSSimon Glass if (!(tmplong & (1UL << offset))) { 15457be9172SBill Richardson debug("%s: gpio %d is reserved for internal use\n", __func__, 1557414112dSSimon Glass offset); 1567414112dSSimon Glass return -EPERM; 15755ae10f8SBill Richardson } 15855ae10f8SBill Richardson 15955ae10f8SBill Richardson return 0; 16055ae10f8SBill Richardson } 16155ae10f8SBill Richardson 1627414112dSSimon Glass static int ich6_gpio_direction_input(struct udevice *dev, unsigned offset) 16355ae10f8SBill Richardson { 1647414112dSSimon Glass struct ich6_bank_priv *bank = dev_get_priv(dev); 16557be9172SBill Richardson 166e7cc0b6fSSimon Glass return _ich6_gpio_set_direction(bank->io_sel, offset, 0); 16755ae10f8SBill Richardson } 16855ae10f8SBill Richardson 1697414112dSSimon Glass static int ich6_gpio_direction_output(struct udevice *dev, unsigned offset, 1707414112dSSimon Glass int value) 17155ae10f8SBill Richardson { 1725318f18dSGabriel Huau int ret; 1737414112dSSimon Glass struct ich6_bank_priv *bank = dev_get_priv(dev); 17455ae10f8SBill Richardson 175e7cc0b6fSSimon Glass ret = _ich6_gpio_set_direction(bank->io_sel, offset, 1); 1765318f18dSGabriel Huau if (ret) 1775318f18dSGabriel Huau return ret; 1780a54745fSAxel Lin 1795318f18dSGabriel Huau return _ich6_gpio_set_value(bank->lvl, offset, value); 18055ae10f8SBill Richardson } 18155ae10f8SBill Richardson 1827414112dSSimon Glass static int ich6_gpio_get_value(struct udevice *dev, unsigned offset) 18355ae10f8SBill Richardson { 1847414112dSSimon Glass struct ich6_bank_priv *bank = dev_get_priv(dev); 18555ae10f8SBill Richardson u32 tmplong; 18657be9172SBill Richardson int r; 18755ae10f8SBill Richardson 1887414112dSSimon Glass tmplong = inl(bank->lvl); 1897414112dSSimon Glass r = (tmplong & (1UL << offset)) ? 1 : 0; 19057be9172SBill Richardson return r; 19155ae10f8SBill Richardson } 19255ae10f8SBill Richardson 1937414112dSSimon Glass static int ich6_gpio_set_value(struct udevice *dev, unsigned offset, 1947414112dSSimon Glass int value) 19555ae10f8SBill Richardson { 1967414112dSSimon Glass struct ich6_bank_priv *bank = dev_get_priv(dev); 1975318f18dSGabriel Huau return _ich6_gpio_set_value(bank->lvl, offset, value); 19855ae10f8SBill Richardson } 1997414112dSSimon Glass 2007414112dSSimon Glass static int ich6_gpio_get_function(struct udevice *dev, unsigned offset) 2017414112dSSimon Glass { 2027414112dSSimon Glass struct ich6_bank_priv *bank = dev_get_priv(dev); 2037414112dSSimon Glass u32 mask = 1UL << offset; 2047414112dSSimon Glass 2057414112dSSimon Glass if (!(inl(bank->use_sel) & mask)) 2067414112dSSimon Glass return GPIOF_FUNC; 2077414112dSSimon Glass if (inl(bank->io_sel) & mask) 2087414112dSSimon Glass return GPIOF_INPUT; 2097414112dSSimon Glass else 2107414112dSSimon Glass return GPIOF_OUTPUT; 2117414112dSSimon Glass } 2127414112dSSimon Glass 2137414112dSSimon Glass static const struct dm_gpio_ops gpio_ich6_ops = { 2147414112dSSimon Glass .request = ich6_gpio_request, 2157414112dSSimon Glass .direction_input = ich6_gpio_direction_input, 2167414112dSSimon Glass .direction_output = ich6_gpio_direction_output, 2177414112dSSimon Glass .get_value = ich6_gpio_get_value, 2187414112dSSimon Glass .set_value = ich6_gpio_set_value, 2197414112dSSimon Glass .get_function = ich6_gpio_get_function, 2207414112dSSimon Glass }; 2217414112dSSimon Glass 2227414112dSSimon Glass static const struct udevice_id intel_ich6_gpio_ids[] = { 2237414112dSSimon Glass { .compatible = "intel,ich6-gpio" }, 2247414112dSSimon Glass { } 2257414112dSSimon Glass }; 2267414112dSSimon Glass 2277414112dSSimon Glass U_BOOT_DRIVER(gpio_ich6) = { 2287414112dSSimon Glass .name = "gpio_ich6", 2297414112dSSimon Glass .id = UCLASS_GPIO, 2307414112dSSimon Glass .of_match = intel_ich6_gpio_ids, 2317414112dSSimon Glass .ops = &gpio_ich6_ops, 2327414112dSSimon Glass .ofdata_to_platdata = gpio_ich6_ofdata_to_platdata, 2337414112dSSimon Glass .probe = ich6_gpio_probe, 2347414112dSSimon Glass .priv_auto_alloc_size = sizeof(struct ich6_bank_priv), 2357414112dSSimon Glass .platdata_auto_alloc_size = sizeof(struct ich6_bank_platdata), 2367414112dSSimon Glass }; 237