diff options
-rw-r--r-- | drivers/gpio/intel_ich6_gpio.c | 172 |
1 files changed, 110 insertions, 62 deletions
diff --git a/drivers/gpio/intel_ich6_gpio.c b/drivers/gpio/intel_ich6_gpio.c index 1ccb641..6fed01f 100644 --- a/drivers/gpio/intel_ich6_gpio.c +++ b/drivers/gpio/intel_ich6_gpio.c @@ -32,6 +32,14 @@ * PCI config register "GPIOBASE" * PCI I/O space + [GPIOBASE] => start of GPIO registers * GPIO registers => gpio pin function, direction, value + * + * + * Danger Will Robinson! Bank 0 (GPIOs 0-31) seems to be fairly stable. Most + * ICH versions have more, but the decoding the matrix that describes them is + * absurdly complex and constantly changing. We'll provide Bank 1 and Bank 2, + * but they will ONLY work for certain unspecified chipsets because the offset + * from GPIOBASE changes randomly. Even then, many GPIOs are unimplemented or + * reserved or subject to arcane restrictions. */ #include <common.h> @@ -42,21 +50,59 @@ /* Where in config space is the register that points to the GPIO registers? */ #define PCI_CFG_GPIOBASE 0x48 -/* - * There are often more than 32 GPIOs, depending on the ICH version. - * For now, we just support bank 0 because it's the same for all. - */ -#define GPIO_MAX 31 +#define NUM_BANKS 3 /* Within the I/O space, where are the registers to control the GPIOs? */ -#define OFS_GPIO_USE_SEL 0x00 -#define OFS_GPIO_IO_SEL 0x04 -#define OFS_GP_LVL 0x0C +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 */ +}; + +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 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 int in_use[GPIO_MAX]; /* "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) +{ + if (lock[bank] & (1UL << bitnum)) { + debug("%s: %d.%d already marked\n", __func__, bank, bitnum); + return -1; + } + lock[bank] |= (1 << bitnum); + return 0; +} + +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) { @@ -77,11 +123,14 @@ static int gpio_init(void) debug("%s: wrong VendorID\n", __func__); return -1; } + + pci_read_config_word(dev, PCI_DEVICE_ID, &tmpword); + debug("Found %04x:%04x\n", PCI_VENDOR_ID_INTEL, tmpword); /* - * We'd like to check the Device ID too, but pretty much any + * 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 other things - * instead... + * 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). */ @@ -143,100 +192,99 @@ static int gpio_init(void) return 0; } -int gpio_request(unsigned gpio, const char *label /* UNUSED */) +int gpio_request(unsigned num, const char *label /* UNUSED */) { u32 tmplong; + int i = 0, j = 0; - /* Are we doing it wrong? */ - if (gpio > GPIO_MAX || in_use[gpio]) { - debug("%s: gpio unavailable\n", __func__); + /* Is the hardware ready? */ + if (gpio_init()) return -1; - } - /* Is the hardware ready? */ - if (gpio_init()) { - debug("%s: gpio_init failed\n", __func__); + if (bad_arg(num, &i, &j)) return -1; - } /* * 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 + OFS_GPIO_USE_SEL); - if (!(tmplong & (1UL << gpio))) { - debug("%s: reserved for internal use\n", __func__); + tmplong = inl(gpiobase + gpio_bank[i].use_sel); + if (!(tmplong & (1UL << j))) { + debug("%s: gpio %d is reserved for internal use\n", __func__, + num); return -1; } - in_use[gpio] = 1; - return 0; + return mark_gpio(i, j); } -int gpio_free(unsigned gpio) +int gpio_free(unsigned num) { - if (gpio > GPIO_MAX || !in_use[gpio]) { - debug("%s: gpio unavailable\n", __func__); + int i = 0, j = 0; + + if (notmine(num, &i, &j)) return -1; - } - in_use[gpio] = 0; + + clear_gpio(i, j); return 0; } -int gpio_direction_input(unsigned gpio) +int gpio_direction_input(unsigned num) { u32 tmplong; + int i = 0, j = 0; - if (gpio > GPIO_MAX || !in_use[gpio]) { - debug("%s: gpio unavailable\n", __func__); + if (notmine(num, &i, &j)) return -1; - } - tmplong = inl(gpiobase + OFS_GPIO_IO_SEL); - tmplong |= (1UL << gpio); - outl(gpiobase + OFS_GPIO_IO_SEL, tmplong); + + tmplong = inl(gpiobase + gpio_bank[i].io_sel); + tmplong |= (1UL << j); + outl(gpiobase + gpio_bank[i].io_sel, tmplong); return 0; } -int gpio_direction_output(unsigned gpio, int value) +int gpio_direction_output(unsigned num, int value) { u32 tmplong; + int i = 0, j = 0; - if (gpio > GPIO_MAX || !in_use[gpio]) { - debug("%s: gpio unavailable\n", __func__); + if (notmine(num, &i, &j)) return -1; - } - tmplong = inl(gpiobase + OFS_GPIO_IO_SEL); - tmplong &= ~(1UL << gpio); - outl(gpiobase + OFS_GPIO_IO_SEL, tmplong); + + tmplong = inl(gpiobase + gpio_bank[i].io_sel); + tmplong &= ~(1UL << j); + outl(gpiobase + gpio_bank[i].io_sel, tmplong); return 0; } -int gpio_get_value(unsigned gpio) +int gpio_get_value(unsigned num) { u32 tmplong; + int i = 0, j = 0; + int r; - if (gpio > GPIO_MAX || !in_use[gpio]) { - debug("%s: gpio unavailable\n", __func__); + if (notmine(num, &i, &j)) return -1; - } - tmplong = inl(gpiobase + OFS_GP_LVL); - return (tmplong & (1UL << gpio)) ? 1 : 0; + + tmplong = inl(gpiobase + gpio_bank[i].lvl); + r = (tmplong & (1UL << j)) ? 1 : 0; + return r; } -int gpio_set_value(unsigned gpio, int value) +int gpio_set_value(unsigned num, int value) { u32 tmplong; + int i = 0, j = 0; - if (gpio > GPIO_MAX || !in_use[gpio]) { - debug("%s: gpio unavailable\n", __func__); + if (notmine(num, &i, &j)) return -1; - } - tmplong = inl(gpiobase + OFS_GP_LVL); + + tmplong = inl(gpiobase + gpio_bank[i].lvl); if (value) - tmplong |= (1UL << gpio); + tmplong |= (1UL << j); else - tmplong &= ~(1UL << gpio); - outl(gpiobase + OFS_GP_LVL, tmplong); + tmplong &= ~(1UL << j); + outl(gpiobase + gpio_bank[i].lvl, tmplong); return 0; } |