diff options
Diffstat (limited to 'drivers/gpio')
| -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 1ccb64175..6fed01f71 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;  } |