X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=drivers%2Fgpio%2Fintel_ich6_gpio.c;h=7720cc3dadfbc1d32a4f2a298243e66e7d438cf4;hb=36d68668e33191d55b2a23c9f0ec0c1ffecd6897;hp=7d9fac72337fc329ac12d2c80ad21f03b9e8fe7e;hpb=c2120fbfbc4d1f6953228f86be8bdbf38bacfdab;p=u-boot diff --git a/drivers/gpio/intel_ich6_gpio.c b/drivers/gpio/intel_ich6_gpio.c index 7d9fac7233..7720cc3dad 100644 --- a/drivers/gpio/intel_ich6_gpio.c +++ b/drivers/gpio/intel_ich6_gpio.c @@ -27,88 +27,50 @@ */ #include +#include +#include +#include #include #include #include +#include -/* Where in config space is the register that points to the GPIO registers? */ -#define PCI_CFG_GPIOBASE 0x48 +#define GPIO_PER_BANK 32 -#define NUM_BANKS 3 - -/* Within the I/O space, where are the registers to control the GPIOs? */ -static struct { - u8 use_sel; - u8 io_sel; - u8 lvl; -} gpio_bank[NUM_BANKS] = { - { 0x00, 0x04, 0x0c }, /* Bank 0 */ - { 0x30, 0x34, 0x38 }, /* Bank 1 */ - { 0x40, 0x44, 0x48 } /* Bank 2 */ +struct ich6_bank_priv { + /* These are I/O addresses */ + uint16_t use_sel; + uint16_t io_sel; + uint16_t lvl; }; -static pci_dev_t dev; /* handle for 0:1f:0 */ -static u32 gpiobase; /* offset into I/O space */ -static int found_it_once; /* valid GPIO device? */ -static u32 lock[NUM_BANKS]; /* "lock" for access to pins */ - -static int bad_arg(int num, int *bank, int *bitnum) -{ - int i = num / 32; - int j = num % 32; - - if (num < 0 || i > NUM_BANKS) { - debug("%s: bogus gpio num: %d\n", __func__, num); - return -1; - } - *bank = i; - *bitnum = j; - return 0; -} - -static int mark_gpio(int bank, int bitnum) +/* TODO: Move this to device tree, or platform data */ +void ich_gpio_set_gpio_map(const struct pch_gpio_map *map) { - if (lock[bank] & (1UL << bitnum)) { - debug("%s: %d.%d already marked\n", __func__, bank, bitnum); - return -1; - } - lock[bank] |= (1 << bitnum); - return 0; + gd->arch.gpio_map = map; } -static void clear_gpio(int bank, int bitnum) -{ - lock[bank] &= ~(1 << bitnum); -} - -static int notmine(int num, int *bank, int *bitnum) -{ - if (bad_arg(num, bank, bitnum)) - return -1; - return !(lock[*bank] & (1UL << *bitnum)); -} - -static int gpio_init(void) +static int gpio_ich6_ofdata_to_platdata(struct udevice *dev) { + struct ich6_bank_platdata *plat = dev_get_platdata(dev); + pci_dev_t pci_dev; /* handle for 0:1f:0 */ u8 tmpbyte; u16 tmpword; u32 tmplong; - - /* Have we already done this? */ - if (found_it_once) - return 0; + u16 gpiobase; + int offset; /* Where should it be? */ - dev = PCI_BDF(0, 0x1f, 0); + pci_dev = PCI_BDF(0, 0x1f, 0); /* Is the device present? */ - pci_read_config_word(dev, PCI_VENDOR_ID, &tmpword); + tmpword = pci_read_config16(pci_dev, PCI_VENDOR_ID); if (tmpword != PCI_VENDOR_ID_INTEL) { debug("%s: wrong VendorID\n", __func__); - return -1; + return -ENODEV; } - pci_read_config_word(dev, PCI_DEVICE_ID, &tmpword); + tmpword = 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 @@ -118,49 +80,53 @@ static int gpio_init(void) */ /* I/O should already be enabled (it's a RO bit). */ - pci_read_config_word(dev, PCI_COMMAND, &tmpword); + tmpword = pci_read_config16(pci_dev, PCI_COMMAND); if (!(tmpword & PCI_COMMAND_IO)) { debug("%s: device IO not enabled\n", __func__); - return -1; + return -ENODEV; } /* Header Type must be normal (bits 6-0 only; see spec.) */ - pci_read_config_byte(dev, PCI_HEADER_TYPE, &tmpbyte); + tmpbyte = pci_read_config8(pci_dev, PCI_HEADER_TYPE); if ((tmpbyte & 0x7f) != PCI_HEADER_TYPE_NORMAL) { debug("%s: invalid Header type\n", __func__); - return -1; + return -ENODEV; } /* Base Class must be a bridge device */ - pci_read_config_byte(dev, PCI_CLASS_CODE, &tmpbyte); + tmpbyte = pci_read_config8(pci_dev, PCI_CLASS_CODE); if (tmpbyte != PCI_CLASS_CODE_BRIDGE) { debug("%s: invalid class\n", __func__); - return -1; + return -ENODEV; } /* Sub Class must be ISA */ - pci_read_config_byte(dev, PCI_CLASS_SUB_CODE, &tmpbyte); + tmpbyte = pci_read_config8(pci_dev, PCI_CLASS_SUB_CODE); if (tmpbyte != PCI_CLASS_SUB_CODE_BRIDGE_ISA) { debug("%s: invalid subclass\n", __func__); - return -1; + return -ENODEV; } /* Programming Interface must be 0x00 (no others exist) */ - pci_read_config_byte(dev, PCI_CLASS_PROG, &tmpbyte); + tmpbyte = pci_read_config8(pci_dev, PCI_CLASS_PROG); if (tmpbyte != 0x00) { debug("%s: invalid interface type\n", __func__); - return -1; + 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, and mapped to I/O space (bit 0). + * 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. */ - pci_read_config_dword(dev, PCI_CFG_GPIOBASE, &tmplong); - if (tmplong == 0x00000000 || tmplong == 0xffffffff || - !(tmplong & 0x00000001)) { + tmplong = pci_read_config32(pci_dev, PCI_CFG_GPIOBASE); + if (tmplong == 0x00000000 || tmplong == 0xffffffff) { debug("%s: unexpected GPIOBASE value\n", __func__); - return -1; + return -ENODEV; } /* @@ -169,106 +135,146 @@ static int gpio_init(void) * 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. */ - gpiobase = tmplong & 0xfffffffe; + gpiobase = tmplong & 0xfffe; + offset = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "reg", -1); + if (offset == -1) { + debug("%s: Invalid register offset %d\n", __func__, offset); + return -EINVAL; + } + plat->base_addr = gpiobase + offset; + plat->bank_name = fdt_getprop(gd->fdt_blob, dev->of_offset, + "bank-name", NULL); - /* Finally. These are the droids we're looking for. */ - found_it_once = 1; return 0; } -int gpio_request(unsigned num, const char *label /* UNUSED */) +static int ich6_gpio_probe(struct udevice *dev) { - u32 tmplong; - int i = 0, j = 0; + struct ich6_bank_platdata *plat = dev_get_platdata(dev); + struct gpio_dev_priv *uc_priv = dev->uclass_priv; + 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; + } - /* Is the hardware ready? */ - if (gpio_init()) - return -1; + uc_priv->gpio_count = GPIO_PER_BANK; + uc_priv->bank_name = plat->bank_name; + bank->use_sel = plat->base_addr; + bank->io_sel = plat->base_addr + 4; + bank->lvl = plat->base_addr + 8; + + return 0; +} - if (bad_arg(num, &i, &j)) - return -1; +static int ich6_gpio_request(struct udevice *dev, unsigned offset, + const char *label) +{ + struct ich6_bank_priv *bank = dev_get_priv(dev); + u32 tmplong; /* * Make sure that the GPIO pin we want isn't already in use for some * built-in hardware function. We have to check this for every * requested pin. */ - tmplong = inl(gpiobase + gpio_bank[i].use_sel); - if (!(tmplong & (1UL << j))) { + tmplong = inl(bank->use_sel); + if (!(tmplong & (1UL << offset))) { debug("%s: gpio %d is reserved for internal use\n", __func__, - num); - return -1; + offset); + return -EPERM; } - return mark_gpio(i, j); -} - -int gpio_free(unsigned num) -{ - int i = 0, j = 0; - - if (notmine(num, &i, &j)) - return -1; - - clear_gpio(i, j); return 0; } -int gpio_direction_input(unsigned num) +static int ich6_gpio_direction_input(struct udevice *dev, unsigned offset) { + struct ich6_bank_priv *bank = dev_get_priv(dev); u32 tmplong; - int i = 0, j = 0; - - if (notmine(num, &i, &j)) - return -1; - tmplong = inl(gpiobase + gpio_bank[i].io_sel); - tmplong |= (1UL << j); - outl(gpiobase + gpio_bank[i].io_sel, tmplong); + tmplong = inl(bank->io_sel); + tmplong |= (1UL << offset); + outl(bank->io_sel, tmplong); return 0; } -int gpio_direction_output(unsigned num, int value) +static int ich6_gpio_direction_output(struct udevice *dev, unsigned offset, + int value) { + struct ich6_bank_priv *bank = dev_get_priv(dev); u32 tmplong; - int i = 0, j = 0; - if (notmine(num, &i, &j)) - return -1; + gpio_set_value(offset, value); - tmplong = inl(gpiobase + gpio_bank[i].io_sel); - tmplong &= ~(1UL << j); - outl(gpiobase + gpio_bank[i].io_sel, tmplong); + tmplong = inl(bank->io_sel); + tmplong &= ~(1UL << offset); + outl(bank->io_sel, tmplong); return 0; } -int gpio_get_value(unsigned num) +static int ich6_gpio_get_value(struct udevice *dev, unsigned offset) + { + struct ich6_bank_priv *bank = dev_get_priv(dev); u32 tmplong; - int i = 0, j = 0; int r; - if (notmine(num, &i, &j)) - return -1; - - tmplong = inl(gpiobase + gpio_bank[i].lvl); - r = (tmplong & (1UL << j)) ? 1 : 0; + tmplong = inl(bank->lvl); + r = (tmplong & (1UL << offset)) ? 1 : 0; return r; } -int gpio_set_value(unsigned num, int value) +static int ich6_gpio_set_value(struct udevice *dev, unsigned offset, + int value) { + struct ich6_bank_priv *bank = dev_get_priv(dev); u32 tmplong; - int i = 0, j = 0; - - if (notmine(num, &i, &j)) - return -1; - tmplong = inl(gpiobase + gpio_bank[i].lvl); + tmplong = inl(bank->lvl); if (value) - tmplong |= (1UL << j); + tmplong |= (1UL << offset); else - tmplong &= ~(1UL << j); - outl(gpiobase + gpio_bank[i].lvl, tmplong); + tmplong &= ~(1UL << offset); + outl(bank->lvl, tmplong); return 0; } + +static int ich6_gpio_get_function(struct udevice *dev, unsigned offset) +{ + struct ich6_bank_priv *bank = dev_get_priv(dev); + u32 mask = 1UL << offset; + + if (!(inl(bank->use_sel) & mask)) + return GPIOF_FUNC; + if (inl(bank->io_sel) & mask) + return GPIOF_INPUT; + else + return GPIOF_OUTPUT; +} + +static const struct dm_gpio_ops gpio_ich6_ops = { + .request = ich6_gpio_request, + .direction_input = ich6_gpio_direction_input, + .direction_output = ich6_gpio_direction_output, + .get_value = ich6_gpio_get_value, + .set_value = ich6_gpio_set_value, + .get_function = ich6_gpio_get_function, +}; + +static const struct udevice_id intel_ich6_gpio_ids[] = { + { .compatible = "intel,ich6-gpio" }, + { } +}; + +U_BOOT_DRIVER(gpio_ich6) = { + .name = "gpio_ich6", + .id = UCLASS_GPIO, + .of_match = intel_ich6_gpio_ids, + .ops = &gpio_ich6_ops, + .ofdata_to_platdata = gpio_ich6_ofdata_to_platdata, + .probe = ich6_gpio_probe, + .priv_auto_alloc_size = sizeof(struct ich6_bank_priv), + .platdata_auto_alloc_size = sizeof(struct ich6_bank_platdata), +};