diff options
Diffstat (limited to 'drivers/gpio/gpio-ich.c')
| -rw-r--r-- | drivers/gpio/gpio-ich.c | 102 | 
1 files changed, 87 insertions, 15 deletions
diff --git a/drivers/gpio/gpio-ich.c b/drivers/gpio/gpio-ich.c index 814addb62d2..70304220a47 100644 --- a/drivers/gpio/gpio-ich.c +++ b/drivers/gpio/gpio-ich.c @@ -1,5 +1,5 @@  /* - * Intel ICH6-10, Series 5 and 6 GPIO driver + * Intel ICH6-10, Series 5 and 6, Atom C2000 (Avoton/Rangeley) GPIO driver   *   * Copyright (C) 2010 Extreme Engineering Solutions.   * @@ -55,6 +55,16 @@ static const u8 ichx_reglen[3] = {  	0x30, 0x10, 0x10,  }; +static const u8 avoton_regs[4][3] = { +	{0x00, 0x80, 0x00}, +	{0x04, 0x84, 0x00}, +	{0x08, 0x88, 0x00}, +}; + +static const u8 avoton_reglen[3] = { +	0x10, 0x10, 0x00, +}; +  #define ICHX_WRITE(val, reg, base_res)	outl(val, (reg) + (base_res)->start)  #define ICHX_READ(reg, base_res)	inl((reg) + (base_res)->start) @@ -62,6 +72,13 @@ struct ichx_desc {  	/* Max GPIO pins the chipset can have */  	uint ngpio; +	/* chipset registers */ +	const u8 (*regs)[3]; +	const u8 *reglen; + +	/* GPO_BLINK is available on this chipset */ +	bool have_blink; +  	/* Whether the chipset has GPIO in GPE0_STS in the PM IO region */  	bool uses_gpe0; @@ -71,6 +88,12 @@ struct ichx_desc {  	/* Some chipsets have quirks, let these use their own request/get */  	int (*request)(struct gpio_chip *chip, unsigned offset);  	int (*get)(struct gpio_chip *chip, unsigned offset); + +	/* +	 * Some chipsets don't let reading output values on GPIO_LVL register +	 * this option allows driver caching written output values +	 */ +	bool use_outlvl_cache;  };  static struct { @@ -82,6 +105,7 @@ static struct {  	struct ichx_desc *desc;	/* Pointer to chipset-specific description */  	u32 orig_gpio_ctrl;	/* Orig CTRL value, used to restore on exit */  	u8 use_gpio;		/* Which GPIO groups are usable */ +	int outlvl_cache[3];	/* cached output values */  } ichx_priv;  static int modparam_gpiobase = -1;	/* dynamic */ @@ -99,13 +123,23 @@ static int ichx_write_bit(int reg, unsigned nr, int val, int verify)  	spin_lock_irqsave(&ichx_priv.lock, flags); -	data = ICHX_READ(ichx_regs[reg][reg_nr], ichx_priv.gpio_base); +	if (reg == GPIO_LVL && ichx_priv.desc->use_outlvl_cache) +		data = ichx_priv.outlvl_cache[reg_nr]; +	else +		data = ICHX_READ(ichx_priv.desc->regs[reg][reg_nr], +				 ichx_priv.gpio_base); +  	if (val)  		data |= 1 << bit;  	else  		data &= ~(1 << bit); -	ICHX_WRITE(data, ichx_regs[reg][reg_nr], ichx_priv.gpio_base); -	tmp = ICHX_READ(ichx_regs[reg][reg_nr], ichx_priv.gpio_base); +	ICHX_WRITE(data, ichx_priv.desc->regs[reg][reg_nr], +			 ichx_priv.gpio_base); +	if (reg == GPIO_LVL && ichx_priv.desc->use_outlvl_cache) +		ichx_priv.outlvl_cache[reg_nr] = data; + +	tmp = ICHX_READ(ichx_priv.desc->regs[reg][reg_nr], +			ichx_priv.gpio_base);  	if (verify && data != tmp)  		ret = -EPERM; @@ -123,7 +157,11 @@ static int ichx_read_bit(int reg, unsigned nr)  	spin_lock_irqsave(&ichx_priv.lock, flags); -	data = ICHX_READ(ichx_regs[reg][reg_nr], ichx_priv.gpio_base); +	data = ICHX_READ(ichx_priv.desc->regs[reg][reg_nr], +			 ichx_priv.gpio_base); + +	if (reg == GPIO_LVL && ichx_priv.desc->use_outlvl_cache) +		data = ichx_priv.outlvl_cache[reg_nr] | data;  	spin_unlock_irqrestore(&ichx_priv.lock, flags); @@ -151,7 +189,7 @@ static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,  					int val)  {  	/* Disable blink hardware which is available for GPIOs from 0 to 31. */ -	if (nr < 32) +	if (nr < 32 && ichx_priv.desc->have_blink)  		ichx_write_bit(GPO_BLINK, nr, 0, 0);  	/* Set GPIO output value. */ @@ -252,7 +290,7 @@ static void ichx_gpiolib_setup(struct gpio_chip *chip)  	chip->direction_output = ichx_gpio_direction_output;  	chip->base = modparam_gpiobase;  	chip->ngpio = ichx_priv.desc->ngpio; -	chip->can_sleep = 0; +	chip->can_sleep = false;  	chip->dbg_show = NULL;  } @@ -266,6 +304,9 @@ static struct ichx_desc ich6_desc = {  	.uses_gpe0 = true,  	.ngpio = 50, +	.have_blink = true, +	.regs = ichx_regs, +	.reglen = ichx_reglen,  };  /* Intel 3100 */ @@ -285,29 +326,56 @@ static struct ichx_desc i3100_desc = {  	.uses_gpe0 = true,  	.ngpio = 50, +	.regs = ichx_regs, +	.reglen = ichx_reglen,  };  /* ICH7 and ICH8-based */  static struct ichx_desc ich7_desc = {  	.ngpio = 50, +	.have_blink = true, +	.regs = ichx_regs, +	.reglen = ichx_reglen,  };  /* ICH9-based */  static struct ichx_desc ich9_desc = {  	.ngpio = 61, +	.have_blink = true, +	.regs = ichx_regs, +	.reglen = ichx_reglen,  };  /* ICH10-based - Consumer/corporate versions have different amount of GPIO */  static struct ichx_desc ich10_cons_desc = {  	.ngpio = 61, +	.have_blink = true, +	.regs = ichx_regs, +	.reglen = ichx_reglen,  };  static struct ichx_desc ich10_corp_desc = {  	.ngpio = 72, +	.have_blink = true, +	.regs = ichx_regs, +	.reglen = ichx_reglen,  };  /* Intel 5 series, 6 series, 3400 series, and C200 series */  static struct ichx_desc intel5_desc = {  	.ngpio = 76, +	.regs = ichx_regs, +	.reglen = ichx_reglen, +}; + +/* Avoton */ +static struct ichx_desc avoton_desc = { +	/* Avoton has only 59 GPIOs, but we assume the first set of register +	 * (Core) has 32 instead of 31 to keep gpio-ich compliance +	 */ +	.ngpio = 60, +	.regs = avoton_regs, +	.reglen = avoton_reglen, +	.use_outlvl_cache = true,  };  static int ichx_gpio_request_regions(struct resource *res_base, @@ -318,11 +386,12 @@ static int ichx_gpio_request_regions(struct resource *res_base,  	if (!res_base || !res_base->start || !res_base->end)  		return -ENODEV; -	for (i = 0; i < ARRAY_SIZE(ichx_regs[0]); i++) { +	for (i = 0; i < ARRAY_SIZE(ichx_priv.desc->regs[0]); i++) {  		if (!(use_gpio & (1 << i)))  			continue; -		if (!request_region(res_base->start + ichx_regs[0][i], -				    ichx_reglen[i], name)) +		if (!request_region( +				res_base->start + ichx_priv.desc->regs[0][i], +				ichx_priv.desc->reglen[i], name))  			goto request_err;  	}  	return 0; @@ -332,8 +401,8 @@ request_err:  	for (i--; i >= 0; i--) {  		if (!(use_gpio & (1 << i)))  			continue; -		release_region(res_base->start + ichx_regs[0][i], -			       ichx_reglen[i]); +		release_region(res_base->start + ichx_priv.desc->regs[0][i], +			       ichx_priv.desc->reglen[i]);  	}  	return -EBUSY;  } @@ -342,11 +411,11 @@ static void ichx_gpio_release_regions(struct resource *res_base, u8 use_gpio)  {  	int i; -	for (i = 0; i < ARRAY_SIZE(ichx_regs[0]); i++) { +	for (i = 0; i < ARRAY_SIZE(ichx_priv.desc->regs[0]); i++) {  		if (!(use_gpio & (1 << i)))  			continue; -		release_region(res_base->start + ichx_regs[0][i], -			       ichx_reglen[i]); +		release_region(res_base->start + ichx_priv.desc->regs[0][i], +			       ichx_priv.desc->reglen[i]);  	}  } @@ -383,6 +452,9 @@ static int ichx_gpio_probe(struct platform_device *pdev)  	case ICH_V10CONS_GPIO:  		ichx_priv.desc = &ich10_cons_desc;  		break; +	case AVOTON_GPIO: +		ichx_priv.desc = &avoton_desc; +		break;  	default:  		return -ENODEV;  	}  | 
