X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=drivers%2Fgpio%2Fintel_ich6_gpio.c;h=ad08b3aa794c504f97a0772f86eb851471cc94b6;hb=9308df81a206e73687a8ba7a1f1b3753e2f2fd79;hp=8a108f3805a0b7929ea69dfdde3cf72784006b34;hpb=5318f18d2c002f505054b90bb95ba7c53532eedf;p=oweals%2Fu-boot.git diff --git a/drivers/gpio/intel_ich6_gpio.c b/drivers/gpio/intel_ich6_gpio.c index 8a108f3805..ad08b3aa79 100644 --- a/drivers/gpio/intel_ich6_gpio.c +++ b/drivers/gpio/intel_ich6_gpio.c @@ -1,6 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2012 The Chromium OS Authors. - * SPDX-License-Identifier: GPL-2.0+ */ /* @@ -30,11 +30,15 @@ #include #include #include +#include #include +#include #include #include #include +DECLARE_GLOBAL_DATA_PTR; + #define GPIO_PER_BANK 32 struct ich6_bank_priv { @@ -42,136 +46,31 @@ struct ich6_bank_priv { uint16_t use_sel; uint16_t io_sel; uint16_t lvl; + u32 lvl_write_cache; + bool use_lvl_write_cache; }; #define GPIO_USESEL_OFFSET(x) (x) #define GPIO_IOSEL_OFFSET(x) (x + 4) #define GPIO_LVL_OFFSET(x) (x + 8) -#define IOPAD_MODE_MASK 0x7 -#define IOPAD_PULL_ASSIGN_SHIFT 7 -#define IOPAD_PULL_ASSIGN_MASK (0x3 << IOPAD_PULL_ASSIGN_SHIFT) -#define IOPAD_PULL_STRENGTH_SHIFT 9 -#define IOPAD_PULL_STRENGTH_MASK (0x3 << IOPAD_PULL_STRENGTH_SHIFT) - -/* TODO: Move this to device tree, or platform data */ -void ich_gpio_set_gpio_map(const struct pch_gpio_map *map) -{ - gd->arch.gpio_map = map; -} - -static int gpio_ich6_get_base(unsigned long base) -{ - pci_dev_t pci_dev; /* handle for 0:1f:0 */ - u8 tmpbyte; - u16 tmpword; - u32 tmplong; - - /* Where should it be? */ - pci_dev = PCI_BDF(0, 0x1f, 0); - - /* Is the device present? */ - tmpword = x86_pci_read_config16(pci_dev, PCI_VENDOR_ID); - if (tmpword != PCI_VENDOR_ID_INTEL) { - debug("%s: wrong VendorID\n", __func__); - return -ENODEV; - } - - tmpword = x86_pci_read_config16(pci_dev, PCI_DEVICE_ID); - debug("Found %04x:%04x\n", PCI_VENDOR_ID_INTEL, tmpword); - /* - * We'd like to validate the Device ID too, but pretty much any - * value is either a) correct with slight differences, or b) - * correct but undocumented. We'll have to check a bunch of other - * things instead... - */ - - /* I/O should already be enabled (it's a RO bit). */ - tmpword = x86_pci_read_config16(pci_dev, PCI_COMMAND); - if (!(tmpword & PCI_COMMAND_IO)) { - debug("%s: device IO not enabled\n", __func__); - return -ENODEV; - } - - /* Header Type must be normal (bits 6-0 only; see spec.) */ - tmpbyte = x86_pci_read_config8(pci_dev, PCI_HEADER_TYPE); - if ((tmpbyte & 0x7f) != PCI_HEADER_TYPE_NORMAL) { - debug("%s: invalid Header type\n", __func__); - return -ENODEV; - } - - /* Base Class must be a bridge device */ - tmpbyte = x86_pci_read_config8(pci_dev, PCI_CLASS_CODE); - if (tmpbyte != PCI_CLASS_CODE_BRIDGE) { - debug("%s: invalid class\n", __func__); - return -ENODEV; - } - /* Sub Class must be ISA */ - tmpbyte = x86_pci_read_config8(pci_dev, PCI_CLASS_SUB_CODE); - if (tmpbyte != PCI_CLASS_SUB_CODE_BRIDGE_ISA) { - debug("%s: invalid subclass\n", __func__); - return -ENODEV; - } - - /* Programming Interface must be 0x00 (no others exist) */ - tmpbyte = x86_pci_read_config8(pci_dev, PCI_CLASS_PROG); - if (tmpbyte != 0x00) { - debug("%s: invalid interface type\n", __func__); - return -ENODEV; - } - - /* - * GPIOBASE moved to its current offset with ICH6, but prior to - * that it was unused (or undocumented). Check that it looks - * okay: not all ones or zeros. - * - * Note we don't need check bit0 here, because the Tunnel Creek - * GPIO base address register bit0 is reserved (read returns 0), - * while on the Ivybridge the bit0 is used to indicate it is an - * I/O space. - */ - tmplong = x86_pci_read_config32(pci_dev, base); - if (tmplong == 0x00000000 || tmplong == 0xffffffff) { - debug("%s: unexpected BASE value\n", __func__); - return -ENODEV; - } - - /* - * Okay, I guess we're looking at the right device. The actual - * GPIO registers are in the PCI device's I/O space, starting - * at the offset that we just read. Bit 0 indicates that it's - * an I/O address, not a memory address, so mask that off. - */ - return tmplong & 0xfffc; -} - -static int _ich6_gpio_set_value(uint16_t base, unsigned offset, int value) +static int _ich6_gpio_set_value(struct ich6_bank_priv *bank, unsigned offset, + int value) { u32 val; - val = inl(base); - if (value) - val |= (1UL << offset); + if (bank->use_lvl_write_cache) + val = bank->lvl_write_cache; else - val &= ~(1UL << offset); - outl(val, base); + val = inl(bank->lvl); - return 0; -} - -static int _ich6_gpio_set_function(uint16_t base, unsigned offset, int func) -{ - u32 val; - - if (func) { - val = inl(base); + if (value) val |= (1UL << offset); - outl(val, base); - } else { - val = inl(base); + else val &= ~(1UL << offset); - outl(val, base); - } + outl(val, bank->lvl); + if (bank->use_lvl_write_cache) + bank->lvl_write_cache = val; return 0; } @@ -193,171 +92,25 @@ static int _ich6_gpio_set_direction(uint16_t base, unsigned offset, int dir) return 0; } -static int _gpio_ich6_pinctrl_cfg_pin(s32 gpiobase, s32 iobase, int pin_node) -{ - u32 gpio_offset[2]; - int pad_offset; - int val; - int ret; - const void *prop; - - /* - * GPIO node is not mandatory, so we only do the - * pinmuxing if the node exist. - */ - ret = fdtdec_get_int_array(gd->fdt_blob, pin_node, "gpio-offset", - gpio_offset, 2); - if (!ret) { - /* Do we want to force the GPIO mode? */ - prop = fdt_getprop(gd->fdt_blob, pin_node, "mode-gpio", - NULL); - if (prop) - _ich6_gpio_set_function(GPIO_USESEL_OFFSET - (gpiobase) + - gpio_offset[0], - gpio_offset[1], 1); - - val = - fdtdec_get_int(gd->fdt_blob, pin_node, "direction", -1); - if (val != -1) - _ich6_gpio_set_direction(GPIO_IOSEL_OFFSET - (gpiobase) + - gpio_offset[0], - gpio_offset[1], val); - - val = - fdtdec_get_int(gd->fdt_blob, pin_node, "output-value", -1); - if (val != -1) - _ich6_gpio_set_value(GPIO_LVL_OFFSET(gpiobase) - + gpio_offset[0], - gpio_offset[1], val); - } - - /* if iobase is present, let's configure the pad */ - if (iobase != -1) { - int iobase_addr; - - /* - * The offset for the same pin for the IOBASE and GPIOBASE are - * different, so instead of maintaining a lookup table, - * the device tree should provide directly the correct - * value for both mapping. - */ - pad_offset = - fdtdec_get_int(gd->fdt_blob, pin_node, "pad-offset", -1); - if (pad_offset == -1) { - debug("%s: Invalid register io offset %d\n", - __func__, pad_offset); - return -EINVAL; - } - - /* compute the absolute pad address */ - iobase_addr = iobase + pad_offset; - - /* - * Do we need to set a specific function mode? - * If someone put also 'mode-gpio', this option will - * be just ignored by the controller - */ - val = fdtdec_get_int(gd->fdt_blob, pin_node, "mode-func", -1); - if (val != -1) - clrsetbits_le32(iobase_addr, IOPAD_MODE_MASK, val); - - /* Configure the pull-up/down if needed */ - val = fdtdec_get_int(gd->fdt_blob, pin_node, "pull-assign", -1); - if (val != -1) - clrsetbits_le32(iobase_addr, - IOPAD_PULL_ASSIGN_MASK, - val << IOPAD_PULL_ASSIGN_SHIFT); - - val = - fdtdec_get_int(gd->fdt_blob, pin_node, "pull-strength", -1); - if (val != -1) - clrsetbits_le32(iobase_addr, - IOPAD_PULL_STRENGTH_MASK, - val << IOPAD_PULL_STRENGTH_SHIFT); - - debug("%s: pad cfg [0x%x]: %08x\n", __func__, pad_offset, - readl(iobase_addr)); - } - - return 0; -} - -int gpio_ich6_pinctrl_init(void) -{ - int pin_node; - int node; - int ret; - int gpiobase; - int iobase_offset; - int iobase = -1; - - /* - * Get the memory/io base address to configure every pins. - * IOBASE is used to configure the mode/pads - * GPIOBASE is used to configure the direction and default value - */ - gpiobase = gpio_ich6_get_base(PCI_CFG_GPIOBASE); - if (gpiobase < 0) { - debug("%s: invalid GPIOBASE address (%08x)\n", __func__, - gpiobase); - return -EINVAL; - } - - /* This is not an error to not have a pinctrl node */ - node = - fdtdec_next_compatible(gd->fdt_blob, 0, COMPAT_INTEL_X86_PINCTRL); - if (node <= 0) { - debug("%s: no pinctrl node\n", __func__); - return 0; - } - - /* - * Get the IOBASE, this is not mandatory as this is not - * supported by all the CPU - */ - iobase_offset = fdtdec_get_int(gd->fdt_blob, node, "io-base", -1); - if (iobase_offset == -1) { - debug("%s: io-base offset not present\n", __func__); - } else { - iobase = gpio_ich6_get_base(iobase_offset); - if (iobase < 0) { - debug("%s: invalid IOBASE address (%08x)\n", __func__, - iobase); - return -EINVAL; - } - } - - for (pin_node = fdt_first_subnode(gd->fdt_blob, node); - pin_node > 0; - pin_node = fdt_next_subnode(gd->fdt_blob, pin_node)) { - /* Configure the pin */ - ret = _gpio_ich6_pinctrl_cfg_pin(gpiobase, iobase, pin_node); - if (ret != 0) { - debug("%s: invalid configuration for the pin %d\n", - __func__, pin_node); - return ret; - } - } - - return 0; -} - static int gpio_ich6_ofdata_to_platdata(struct udevice *dev) { struct ich6_bank_platdata *plat = dev_get_platdata(dev); - u16 gpiobase; + u32 gpiobase; int offset; + int ret; + + ret = pch_get_gpio_base(dev->parent, &gpiobase); + if (ret) + return ret; - gpiobase = gpio_ich6_get_base(PCI_CFG_GPIOBASE); - offset = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "reg", -1); + offset = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "reg", -1); if (offset == -1) { debug("%s: Invalid register offset %d\n", __func__, offset); return -EINVAL; } + plat->offset = offset; plat->base_addr = gpiobase + offset; - plat->bank_name = fdt_getprop(gd->fdt_blob, dev->of_offset, + plat->bank_name = fdt_getprop(gd->fdt_blob, dev_of_offset(dev), "bank-name", NULL); return 0; @@ -368,11 +121,7 @@ static int ich6_gpio_probe(struct udevice *dev) struct ich6_bank_platdata *plat = dev_get_platdata(dev); struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); struct ich6_bank_priv *bank = dev_get_priv(dev); - - if (gd->arch.gpio_map) { - setup_pch_gpios(plat->base_addr, gd->arch.gpio_map); - gd->arch.gpio_map = NULL; - } + const void *prop; uc_priv->gpio_count = GPIO_PER_BANK; uc_priv->bank_name = plat->bank_name; @@ -380,6 +129,14 @@ static int ich6_gpio_probe(struct udevice *dev) bank->io_sel = plat->base_addr + 4; bank->lvl = plat->base_addr + 8; + prop = fdt_getprop(gd->fdt_blob, dev_of_offset(dev), + "use-lvl-write-cache", NULL); + if (prop) + bank->use_lvl_write_cache = true; + else + bank->use_lvl_write_cache = false; + bank->lvl_write_cache = 0; + return 0; } @@ -408,7 +165,7 @@ static int ich6_gpio_direction_input(struct udevice *dev, unsigned offset) { struct ich6_bank_priv *bank = dev_get_priv(dev); - return _ich6_gpio_set_direction(inl(bank->io_sel), offset, 0); + return _ich6_gpio_set_direction(bank->io_sel, offset, 0); } static int ich6_gpio_direction_output(struct udevice *dev, unsigned offset, @@ -417,11 +174,11 @@ static int ich6_gpio_direction_output(struct udevice *dev, unsigned offset, int ret; struct ich6_bank_priv *bank = dev_get_priv(dev); - ret = _ich6_gpio_set_direction(inl(bank->io_sel), offset, 1); + ret = _ich6_gpio_set_direction(bank->io_sel, offset, 1); if (ret) return ret; - return _ich6_gpio_set_value(bank->lvl, offset, value); + return _ich6_gpio_set_value(bank, offset, value); } static int ich6_gpio_get_value(struct udevice *dev, unsigned offset) @@ -431,6 +188,8 @@ static int ich6_gpio_get_value(struct udevice *dev, unsigned offset) int r; tmplong = inl(bank->lvl); + if (bank->use_lvl_write_cache) + tmplong |= bank->lvl_write_cache; r = (tmplong & (1UL << offset)) ? 1 : 0; return r; } @@ -439,7 +198,7 @@ static int ich6_gpio_set_value(struct udevice *dev, unsigned offset, int value) { struct ich6_bank_priv *bank = dev_get_priv(dev); - return _ich6_gpio_set_value(bank->lvl, offset, value); + return _ich6_gpio_set_value(bank, offset, value); } static int ich6_gpio_get_function(struct udevice *dev, unsigned offset)