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>
3515cf75ecSSimon Glass #include <asm/cpu.h>
3655ae10f8SBill Richardson #include <asm/gpio.h>
3755ae10f8SBill Richardson #include <asm/io.h>
381b4f25ffSSimon Glass #include <asm/pci.h>
3955ae10f8SBill Richardson
408b097916SSimon Glass DECLARE_GLOBAL_DATA_PTR;
418b097916SSimon Glass
427414112dSSimon Glass #define GPIO_PER_BANK 32
437414112dSSimon Glass
447414112dSSimon Glass struct ich6_bank_priv {
457414112dSSimon Glass /* These are I/O addresses */
46b71eec31SBin Meng uint16_t use_sel;
47b71eec31SBin Meng uint16_t io_sel;
48b71eec31SBin Meng uint16_t lvl;
49770ee017SBin Meng u32 lvl_write_cache;
50770ee017SBin Meng bool use_lvl_write_cache;
5157be9172SBill Richardson };
5255ae10f8SBill Richardson
535318f18dSGabriel Huau #define GPIO_USESEL_OFFSET(x) (x)
545318f18dSGabriel Huau #define GPIO_IOSEL_OFFSET(x) (x + 4)
555318f18dSGabriel Huau #define GPIO_LVL_OFFSET(x) (x + 8)
565318f18dSGabriel Huau
_ich6_gpio_set_value(struct ich6_bank_priv * bank,unsigned offset,int value)57770ee017SBin Meng static int _ich6_gpio_set_value(struct ich6_bank_priv *bank, unsigned offset,
58770ee017SBin Meng int value)
595318f18dSGabriel Huau {
605318f18dSGabriel Huau u32 val;
615318f18dSGabriel Huau
62770ee017SBin Meng if (bank->use_lvl_write_cache)
63770ee017SBin Meng val = bank->lvl_write_cache;
64770ee017SBin Meng else
65770ee017SBin Meng val = inl(bank->lvl);
66770ee017SBin Meng
675318f18dSGabriel Huau if (value)
685318f18dSGabriel Huau val |= (1UL << offset);
695318f18dSGabriel Huau else
705318f18dSGabriel Huau val &= ~(1UL << offset);
71770ee017SBin Meng outl(val, bank->lvl);
72770ee017SBin Meng if (bank->use_lvl_write_cache)
73770ee017SBin Meng bank->lvl_write_cache = val;
745318f18dSGabriel Huau
755318f18dSGabriel Huau return 0;
765318f18dSGabriel Huau }
775318f18dSGabriel Huau
_ich6_gpio_set_direction(uint16_t base,unsigned offset,int dir)785318f18dSGabriel Huau static int _ich6_gpio_set_direction(uint16_t base, unsigned offset, int dir)
795318f18dSGabriel Huau {
805318f18dSGabriel Huau u32 val;
815318f18dSGabriel Huau
825318f18dSGabriel Huau if (!dir) {
835318f18dSGabriel Huau val = inl(base);
845318f18dSGabriel Huau val |= (1UL << offset);
855318f18dSGabriel Huau outl(val, base);
865318f18dSGabriel Huau } else {
875318f18dSGabriel Huau val = inl(base);
885318f18dSGabriel Huau val &= ~(1UL << offset);
895318f18dSGabriel Huau outl(val, base);
905318f18dSGabriel Huau }
915318f18dSGabriel Huau
925318f18dSGabriel Huau return 0;
935318f18dSGabriel Huau }
945318f18dSGabriel Huau
gpio_ich6_ofdata_to_platdata(struct udevice * dev)955318f18dSGabriel Huau static int gpio_ich6_ofdata_to_platdata(struct udevice *dev)
965318f18dSGabriel Huau {
975318f18dSGabriel Huau struct ich6_bank_platdata *plat = dev_get_platdata(dev);
983ddc1c7bSBin Meng u32 gpiobase;
995318f18dSGabriel Huau int offset;
1003ddc1c7bSBin Meng int ret;
1015318f18dSGabriel Huau
1023ddc1c7bSBin Meng ret = pch_get_gpio_base(dev->parent, &gpiobase);
1033ddc1c7bSBin Meng if (ret)
1043ddc1c7bSBin Meng return ret;
1053ddc1c7bSBin Meng
106e160f7d4SSimon Glass offset = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "reg", -1);
1077414112dSSimon Glass if (offset == -1) {
1087414112dSSimon Glass debug("%s: Invalid register offset %d\n", __func__, offset);
1097414112dSSimon Glass return -EINVAL;
1107414112dSSimon Glass }
111d6d50db8SSimon Glass plat->offset = offset;
1127414112dSSimon Glass plat->base_addr = gpiobase + offset;
113e160f7d4SSimon Glass plat->bank_name = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
1147414112dSSimon Glass "bank-name", NULL);
11555ae10f8SBill Richardson
11655ae10f8SBill Richardson return 0;
11755ae10f8SBill Richardson }
11855ae10f8SBill Richardson
ich6_gpio_probe(struct udevice * dev)1191b4f25ffSSimon Glass static int ich6_gpio_probe(struct udevice *dev)
12055ae10f8SBill Richardson {
1217414112dSSimon Glass struct ich6_bank_platdata *plat = dev_get_platdata(dev);
122e564f054SSimon Glass struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
1237414112dSSimon Glass struct ich6_bank_priv *bank = dev_get_priv(dev);
124770ee017SBin Meng const void *prop;
1252795573aSBin Meng
1267414112dSSimon Glass uc_priv->gpio_count = GPIO_PER_BANK;
1277414112dSSimon Glass uc_priv->bank_name = plat->bank_name;
1287414112dSSimon Glass bank->use_sel = plat->base_addr;
1297414112dSSimon Glass bank->io_sel = plat->base_addr + 4;
1307414112dSSimon Glass bank->lvl = plat->base_addr + 8;
1317414112dSSimon Glass
132*da409cccSSimon Glass prop = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
133770ee017SBin Meng "use-lvl-write-cache", NULL);
134770ee017SBin Meng if (prop)
135770ee017SBin Meng bank->use_lvl_write_cache = true;
136770ee017SBin Meng else
137770ee017SBin Meng bank->use_lvl_write_cache = false;
138770ee017SBin Meng bank->lvl_write_cache = 0;
139770ee017SBin Meng
1407414112dSSimon Glass return 0;
1417414112dSSimon Glass }
1427414112dSSimon Glass
ich6_gpio_request(struct udevice * dev,unsigned offset,const char * label)1431b4f25ffSSimon Glass static int ich6_gpio_request(struct udevice *dev, unsigned offset,
1441b4f25ffSSimon Glass const char *label)
1457414112dSSimon Glass {
1467414112dSSimon Glass struct ich6_bank_priv *bank = dev_get_priv(dev);
14755ae10f8SBill Richardson u32 tmplong;
14855ae10f8SBill Richardson
14955ae10f8SBill Richardson /*
15055ae10f8SBill Richardson * Make sure that the GPIO pin we want isn't already in use for some
15155ae10f8SBill Richardson * built-in hardware function. We have to check this for every
15255ae10f8SBill Richardson * requested pin.
15355ae10f8SBill Richardson */
1547414112dSSimon Glass tmplong = inl(bank->use_sel);
1557414112dSSimon Glass if (!(tmplong & (1UL << offset))) {
15657be9172SBill Richardson debug("%s: gpio %d is reserved for internal use\n", __func__,
1577414112dSSimon Glass offset);
1587414112dSSimon Glass return -EPERM;
15955ae10f8SBill Richardson }
16055ae10f8SBill Richardson
16155ae10f8SBill Richardson return 0;
16255ae10f8SBill Richardson }
16355ae10f8SBill Richardson
ich6_gpio_direction_input(struct udevice * dev,unsigned offset)1647414112dSSimon Glass static int ich6_gpio_direction_input(struct udevice *dev, unsigned offset)
16555ae10f8SBill Richardson {
1667414112dSSimon Glass struct ich6_bank_priv *bank = dev_get_priv(dev);
16757be9172SBill Richardson
168e7cc0b6fSSimon Glass return _ich6_gpio_set_direction(bank->io_sel, offset, 0);
16955ae10f8SBill Richardson }
17055ae10f8SBill Richardson
ich6_gpio_direction_output(struct udevice * dev,unsigned offset,int value)1717414112dSSimon Glass static int ich6_gpio_direction_output(struct udevice *dev, unsigned offset,
1727414112dSSimon Glass int value)
17355ae10f8SBill Richardson {
1745318f18dSGabriel Huau int ret;
1757414112dSSimon Glass struct ich6_bank_priv *bank = dev_get_priv(dev);
17655ae10f8SBill Richardson
177e7cc0b6fSSimon Glass ret = _ich6_gpio_set_direction(bank->io_sel, offset, 1);
1785318f18dSGabriel Huau if (ret)
1795318f18dSGabriel Huau return ret;
1800a54745fSAxel Lin
181770ee017SBin Meng return _ich6_gpio_set_value(bank, offset, value);
18255ae10f8SBill Richardson }
18355ae10f8SBill Richardson
ich6_gpio_get_value(struct udevice * dev,unsigned offset)1847414112dSSimon Glass static int ich6_gpio_get_value(struct udevice *dev, unsigned offset)
18555ae10f8SBill Richardson {
1867414112dSSimon Glass struct ich6_bank_priv *bank = dev_get_priv(dev);
18755ae10f8SBill Richardson u32 tmplong;
18857be9172SBill Richardson int r;
18955ae10f8SBill Richardson
1907414112dSSimon Glass tmplong = inl(bank->lvl);
191770ee017SBin Meng if (bank->use_lvl_write_cache)
192770ee017SBin Meng tmplong |= bank->lvl_write_cache;
1937414112dSSimon Glass r = (tmplong & (1UL << offset)) ? 1 : 0;
19457be9172SBill Richardson return r;
19555ae10f8SBill Richardson }
19655ae10f8SBill Richardson
ich6_gpio_set_value(struct udevice * dev,unsigned offset,int value)1977414112dSSimon Glass static int ich6_gpio_set_value(struct udevice *dev, unsigned offset,
1987414112dSSimon Glass int value)
19955ae10f8SBill Richardson {
2007414112dSSimon Glass struct ich6_bank_priv *bank = dev_get_priv(dev);
201770ee017SBin Meng return _ich6_gpio_set_value(bank, offset, value);
20255ae10f8SBill Richardson }
2037414112dSSimon Glass
ich6_gpio_get_function(struct udevice * dev,unsigned offset)2047414112dSSimon Glass static int ich6_gpio_get_function(struct udevice *dev, unsigned offset)
2057414112dSSimon Glass {
2067414112dSSimon Glass struct ich6_bank_priv *bank = dev_get_priv(dev);
2077414112dSSimon Glass u32 mask = 1UL << offset;
2087414112dSSimon Glass
2097414112dSSimon Glass if (!(inl(bank->use_sel) & mask))
2107414112dSSimon Glass return GPIOF_FUNC;
2117414112dSSimon Glass if (inl(bank->io_sel) & mask)
2127414112dSSimon Glass return GPIOF_INPUT;
2137414112dSSimon Glass else
2147414112dSSimon Glass return GPIOF_OUTPUT;
2157414112dSSimon Glass }
2167414112dSSimon Glass
2177414112dSSimon Glass static const struct dm_gpio_ops gpio_ich6_ops = {
2187414112dSSimon Glass .request = ich6_gpio_request,
2197414112dSSimon Glass .direction_input = ich6_gpio_direction_input,
2207414112dSSimon Glass .direction_output = ich6_gpio_direction_output,
2217414112dSSimon Glass .get_value = ich6_gpio_get_value,
2227414112dSSimon Glass .set_value = ich6_gpio_set_value,
2237414112dSSimon Glass .get_function = ich6_gpio_get_function,
2247414112dSSimon Glass };
2257414112dSSimon Glass
2267414112dSSimon Glass static const struct udevice_id intel_ich6_gpio_ids[] = {
2277414112dSSimon Glass { .compatible = "intel,ich6-gpio" },
2287414112dSSimon Glass { }
2297414112dSSimon Glass };
2307414112dSSimon Glass
2317414112dSSimon Glass U_BOOT_DRIVER(gpio_ich6) = {
2327414112dSSimon Glass .name = "gpio_ich6",
2337414112dSSimon Glass .id = UCLASS_GPIO,
2347414112dSSimon Glass .of_match = intel_ich6_gpio_ids,
2357414112dSSimon Glass .ops = &gpio_ich6_ops,
2367414112dSSimon Glass .ofdata_to_platdata = gpio_ich6_ofdata_to_platdata,
2377414112dSSimon Glass .probe = ich6_gpio_probe,
2387414112dSSimon Glass .priv_auto_alloc_size = sizeof(struct ich6_bank_priv),
2397414112dSSimon Glass .platdata_auto_alloc_size = sizeof(struct ich6_bank_platdata),
2407414112dSSimon Glass };
241