diff options
Diffstat (limited to 'drivers/gpio/gpio-ich.c')
| -rw-r--r-- | drivers/gpio/gpio-ich.c | 140 |
1 files changed, 105 insertions, 35 deletions
diff --git a/drivers/gpio/gpio-ich.c b/drivers/gpio/gpio-ich.c index d4d61796669..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. * @@ -41,18 +41,30 @@ enum GPIO_REG { GPIO_USE_SEL = 0, GPIO_IO_SEL, GPIO_LVL, + GPO_BLINK }; -static const u8 ichx_regs[3][3] = { +static const u8 ichx_regs[4][3] = { {0x00, 0x30, 0x40}, /* USE_SEL[1-3] offsets */ {0x04, 0x34, 0x44}, /* IO_SEL[1-3] offsets */ {0x0c, 0x38, 0x48}, /* LVL[1-3] offsets */ + {0x18, 0x18, 0x18}, /* BLINK offset */ }; 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) @@ -60,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; @@ -69,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 { @@ -80,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 */ @@ -97,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; @@ -121,23 +157,24 @@ 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); return data & (1 << bit) ? 1 : 0; } -static int ichx_gpio_check_available(struct gpio_chip *gpio, unsigned nr) +static bool ichx_gpio_check_available(struct gpio_chip *gpio, unsigned nr) { - return (ichx_priv.use_gpio & (1 << (nr / 32))) ? 0 : -ENXIO; + return !!(ichx_priv.use_gpio & (1 << (nr / 32))); } static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) { - if (!ichx_gpio_check_available(gpio, nr)) - return -ENXIO; - /* * Try setting pin as an input and verify it worked since many pins * are output-only. @@ -151,8 +188,9 @@ static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, int val) { - if (!ichx_gpio_check_available(gpio, nr)) - return -ENXIO; + /* Disable blink hardware which is available for GPIOs from 0 to 31. */ + if (nr < 32 && ichx_priv.desc->have_blink) + ichx_write_bit(GPO_BLINK, nr, 0, 0); /* Set GPIO output value. */ ichx_write_bit(GPIO_LVL, nr, val, 0); @@ -169,9 +207,6 @@ static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, static int ichx_gpio_get(struct gpio_chip *chip, unsigned nr) { - if (!ichx_gpio_check_available(chip, nr)) - return -ENXIO; - return ichx_read_bit(GPIO_LVL, nr); } @@ -180,9 +215,6 @@ static int ich6_gpio_get(struct gpio_chip *chip, unsigned nr) unsigned long flags; u32 data; - if (!ichx_gpio_check_available(chip, nr)) - return -ENXIO; - /* * GPI 0 - 15 need to be read from the power management registers on * a ICH6/3100 bridge. @@ -207,6 +239,9 @@ static int ich6_gpio_get(struct gpio_chip *chip, unsigned nr) static int ichx_gpio_request(struct gpio_chip *chip, unsigned nr) { + if (!ichx_gpio_check_available(chip, nr)) + return -ENXIO; + /* * Note we assume the BIOS properly set a bridge's USE value. Some * chips (eg Intel 3100) have bogus USE values though, so first see if @@ -214,7 +249,7 @@ static int ichx_gpio_request(struct gpio_chip *chip, unsigned nr) * If it can't be trusted, assume that the pin can be used as a GPIO. */ if (ichx_priv.desc->use_sel_ignore[nr / 32] & (1 << (nr & 0x1f))) - return 1; + return 0; return ichx_read_bit(GPIO_USE_SEL, nr) ? 0 : -ENODEV; } @@ -238,7 +273,7 @@ static void ichx_gpio_set(struct gpio_chip *chip, unsigned nr, int val) ichx_write_bit(GPIO_LVL, nr, val, 0); } -static void __devinit ichx_gpiolib_setup(struct gpio_chip *chip) +static void ichx_gpiolib_setup(struct gpio_chip *chip) { chip->owner = THIS_MODULE; chip->label = DRV_NAME; @@ -255,7 +290,7 @@ static void __devinit 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; } @@ -269,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 */ @@ -288,32 +326,59 @@ 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, }; -static int __devinit ichx_gpio_request_regions(struct resource *res_base, +/* 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, const char *name, u8 use_gpio) { int i; @@ -321,11 +386,12 @@ static int __devinit 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; @@ -335,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; } @@ -345,19 +411,19 @@ 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]); } } -static int __devinit ichx_gpio_probe(struct platform_device *pdev) +static int ichx_gpio_probe(struct platform_device *pdev) { struct resource *res_base, *res_pm; int err; - struct lpc_ich_info *ich_info = pdev->dev.platform_data; + struct lpc_ich_info *ich_info = dev_get_platdata(&pdev->dev); if (!ich_info) return -ENODEV; @@ -386,10 +452,14 @@ static int __devinit 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; } + spin_lock_init(&ichx_priv.lock); res_base = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_GPIO); ichx_priv.use_gpio = ich_info->use_gpio; err = ichx_gpio_request_regions(res_base, pdev->name, @@ -442,7 +512,7 @@ add_err: return err; } -static int __devexit ichx_gpio_remove(struct platform_device *pdev) +static int ichx_gpio_remove(struct platform_device *pdev) { int err; @@ -467,7 +537,7 @@ static struct platform_driver ichx_gpio_driver = { .name = DRV_NAME, }, .probe = ichx_gpio_probe, - .remove = __devexit_p(ichx_gpio_remove), + .remove = ichx_gpio_remove, }; module_platform_driver(ichx_gpio_driver); |
