summaryrefslogtreecommitdiff
path: root/drivers/gpio/gpio-ich.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio/gpio-ich.c')
-rw-r--r--drivers/gpio/gpio-ich.c70
1 files changed, 57 insertions, 13 deletions
diff --git a/drivers/gpio/gpio-ich.c b/drivers/gpio/gpio-ich.c
index f5bf3c38bca6..bfef20f8ab48 100644
--- a/drivers/gpio/gpio-ich.c
+++ b/drivers/gpio/gpio-ich.c
@@ -62,6 +62,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 +78,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 +95,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 +113,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 +147,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 +179,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. */
@@ -266,6 +294,7 @@ static struct ichx_desc ich6_desc = {
.uses_gpe0 = true,
.ngpio = 50,
+ .have_blink = true,
};
/* Intel 3100 */
@@ -290,24 +319,38 @@ static struct ichx_desc i3100_desc = {
/* 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,
};
static int ichx_gpio_request_regions(struct resource *res_base,
@@ -318,11 +361,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 +376,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 +386,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]);
}
}