diff options
author | Philippe Langlais <philippe.langlais@stericsson.com> | 2012-06-04 19:45:25 +0800 |
---|---|---|
committer | Philippe Langlais <philippe.langlais@stericsson.com> | 2012-06-04 19:45:25 +0800 |
commit | 137b939337b44c446e31414d5782e16006fb256c (patch) | |
tree | 017e337a7d4922628fedc683b050c5d3714a194f /drivers | |
parent | aede9c200012ac488f5973ff6edca1392a254914 (diff) | |
parent | 441386c9d8d142c903361360065cbbe2fbe57a6b (diff) |
Merge topic branch 'gpio' into integration-linux-ux500
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/gpio/Kconfig | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-ab8500.c | 287 | ||||
-rw-r--r-- | drivers/gpio/gpio-nomadik.c | 122 |
3 files changed, 332 insertions, 79 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index e03653d6935..6226d0cd30a 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -504,7 +504,7 @@ config GPIO_JANZ_TTL config GPIO_AB8500 bool "ST-Ericsson AB8500 Mixed Signal Circuit gpio functions" - depends on AB8500_CORE && BROKEN + depends on AB8500_CORE help Select this to enable the AB8500 IC GPIO driver diff --git a/drivers/gpio/gpio-ab8500.c b/drivers/gpio/gpio-ab8500.c index 050c05d9189..ef75984486c 100644 --- a/drivers/gpio/gpio-ab8500.c +++ b/drivers/gpio/gpio-ab8500.c @@ -18,9 +18,16 @@ #include <linux/gpio.h> #include <linux/irq.h> #include <linux/interrupt.h> -#include <linux/mfd/ab8500.h> #include <linux/mfd/abx500.h> -#include <linux/mfd/ab8500/gpio.h> +#include <linux/mfd/abx500/ab8500-gpio.h> + + +/* + * The AB9540 GPIO support is an extended version of the + * AB8500 GPIO support. The AB9540 supports an additional + * (7th) register so that more GPIO may be configured and + * used. + */ /* * GPIO registers offset @@ -32,6 +39,7 @@ #define AB8500_GPIO_SEL4_REG 0x03 #define AB8500_GPIO_SEL5_REG 0x04 #define AB8500_GPIO_SEL6_REG 0x05 +#define AB9540_GPIO_SEL7_REG 0x06 #define AB8500_GPIO_DIR1_REG 0x10 #define AB8500_GPIO_DIR2_REG 0x11 @@ -39,6 +47,7 @@ #define AB8500_GPIO_DIR4_REG 0x13 #define AB8500_GPIO_DIR5_REG 0x14 #define AB8500_GPIO_DIR6_REG 0x15 +#define AB9540_GPIO_DIR7_REG 0x16 #define AB8500_GPIO_OUT1_REG 0x20 #define AB8500_GPIO_OUT2_REG 0x21 @@ -46,6 +55,7 @@ #define AB8500_GPIO_OUT4_REG 0x23 #define AB8500_GPIO_OUT5_REG 0x24 #define AB8500_GPIO_OUT6_REG 0x25 +#define AB9540_GPIO_OUT7_REG 0x26 #define AB8500_GPIO_PUD1_REG 0x30 #define AB8500_GPIO_PUD2_REG 0x31 @@ -53,6 +63,7 @@ #define AB8500_GPIO_PUD4_REG 0x33 #define AB8500_GPIO_PUD5_REG 0x34 #define AB8500_GPIO_PUD6_REG 0x35 +#define AB9540_GPIO_PUD7_REG 0x36 #define AB8500_GPIO_IN1_REG 0x40 #define AB8500_GPIO_IN2_REG 0x41 @@ -60,9 +71,13 @@ #define AB8500_GPIO_IN4_REG 0x43 #define AB8500_GPIO_IN5_REG 0x44 #define AB8500_GPIO_IN6_REG 0x45 -#define AB8500_GPIO_ALTFUN_REG 0x45 -#define ALTFUN_REG_INDEX 6 +#define AB9540_GPIO_IN7_REG 0x46 +#define AB8500_GPIO_ALTFUN_REG 0x50 +#define AB8500_ALTFUN_REG_INDEX 6 +#define AB9540_ALTFUN_REG_INDEX 7 #define AB8500_NUM_GPIO 42 +#define AB9540_NUM_GPIO 54 +#define AB8505_NUM_GPIO 53 #define AB8500_NUM_VIR_GPIO_IRQ 16 enum ab8500_gpio_action { @@ -73,6 +88,11 @@ enum ab8500_gpio_action { UNMASK }; +struct ab8500_gpio_irq_cluster { + int start; + int end; +}; + struct ab8500_gpio { struct gpio_chip chip; struct ab8500 *parent; @@ -82,7 +102,50 @@ struct ab8500_gpio { enum ab8500_gpio_action irq_action; u16 rising; u16 falling; + struct ab8500_gpio_irq_cluster *irq_cluster; + int irq_cluster_size; +}; + +/* + * Only some GPIOs are interrupt capable, and they are + * organized in discontiguous clusters: + * + * GPIO6 to GPIO13 + * GPIO24 and GPIO25 + * GPIO36 to GPIO41 + * GPIO50 to GPIO54 (AB9540 only) + */ +static struct ab8500_gpio_irq_cluster ab8500_irq_clusters[] = { + {.start = 5, .end = 12}, /* GPIO numbers start from 1 */ + {.start = 23, .end = 24}, + {.start = 35, .end = 40}, +}; + +static struct ab8500_gpio_irq_cluster ab9540_irq_clusters[] = { + {.start = 5, .end = 12}, /* GPIO numbers start from 1 */ + {.start = 23, .end = 24}, + {.start = 35, .end = 40}, + {.start = 49, .end = 53}, +}; + +/* + * For AB8505 Only some GPIOs are interrupt capable, and they are + * organized in discontiguous clusters: + * + * GPIO10 to GPIO11 + * GPIO13 + * GPIO40 and GPIO41 + * GPIO50 + * GPIO52 to GPIO53 + */ +static struct ab8500_gpio_irq_cluster ab8505_irq_clusters[] = { + {.start = 9, .end = 10}, /* GPIO numbers start from 1 */ + {.start = 12, .end = 12}, + {.start = 39, .end = 40}, + {.start = 49, .end = 49}, + {.start = 51, .end = 52}, }; + /** * to_ab8500_gpio() - get the pointer to ab8500_gpio * @chip: Member of the structure ab8500_gpio @@ -115,7 +178,7 @@ static int ab8500_gpio_get(struct gpio_chip *chip, unsigned offset) { struct ab8500_gpio *ab8500_gpio = to_ab8500_gpio(chip); u8 mask = 1 << (offset % 8); - u8 reg = AB8500_GPIO_OUT1_REG + (offset / 8); + u8 reg = AB8500_GPIO_IN1_REG + (offset / 8); int ret; u8 data; ret = abx500_get_register_interruptible(ab8500_gpio->dev, AB8500_MISC, @@ -132,7 +195,7 @@ static void ab8500_gpio_set(struct gpio_chip *chip, unsigned offset, int val) struct ab8500_gpio *ab8500_gpio = to_ab8500_gpio(chip); int ret; /* Write the data */ - ret = ab8500_gpio_set_bits(chip, AB8500_GPIO_OUT1_REG, offset, 1); + ret = ab8500_gpio_set_bits(chip, AB8500_GPIO_OUT1_REG, offset, val); if (ret < 0) dev_err(ab8500_gpio->dev, "%s write failed\n", __func__); } @@ -162,28 +225,13 @@ static int ab8500_gpio_direction_input(struct gpio_chip *chip, unsigned offset) static int ab8500_gpio_to_irq(struct gpio_chip *chip, unsigned offset) { - /* - * Only some GPIOs are interrupt capable, and they are - * organized in discontiguous clusters: - * - * GPIO6 to GPIO13 - * GPIO24 and GPIO25 - * GPIO36 to GPIO41 - */ - static struct ab8500_gpio_irq_cluster { - int start; - int end; - } clusters[] = { - {.start = 6, .end = 13}, - {.start = 24, .end = 25}, - {.start = 36, .end = 41}, - }; struct ab8500_gpio *ab8500_gpio = to_ab8500_gpio(chip); int base = ab8500_gpio->irq_base; int i; - for (i = 0; i < ARRAY_SIZE(clusters); i++) { - struct ab8500_gpio_irq_cluster *cluster = &clusters[i]; + for (i = 0; i < ab8500_gpio->irq_cluster_size; i++) { + struct ab8500_gpio_irq_cluster *cluster = + &ab8500_gpio->irq_cluster[i]; if (offset >= cluster->start && offset <= cluster->end) return base + offset - cluster->start; @@ -207,7 +255,7 @@ static struct gpio_chip ab8500gpio_chip = { static unsigned int irq_to_rising(unsigned int irq) { - struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq); + struct ab8500_gpio *ab8500_gpio = irq_get_chip_data(irq); int offset = irq - ab8500_gpio->irq_base; int new_irq = offset + AB8500_INT_GPIO6R + ab8500_gpio->parent->irq_base; @@ -216,7 +264,7 @@ static unsigned int irq_to_rising(unsigned int irq) static unsigned int irq_to_falling(unsigned int irq) { - struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq); + struct ab8500_gpio *ab8500_gpio = irq_get_chip_data(irq); int offset = irq - ab8500_gpio->irq_base; int new_irq = offset + AB8500_INT_GPIO6F + ab8500_gpio->parent->irq_base; @@ -261,15 +309,16 @@ static irqreturn_t handle_falling(int irq, void *dev) return IRQ_HANDLED; } -static void ab8500_gpio_irq_lock(unsigned int irq) +static void ab8500_gpio_irq_lock(struct irq_data *data) { - struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq); + struct ab8500_gpio *ab8500_gpio = irq_data_get_irq_chip_data(data); mutex_lock(&ab8500_gpio->lock); } -static void ab8500_gpio_irq_sync_unlock(unsigned int irq) +static void ab8500_gpio_irq_sync_unlock(struct irq_data *data) { - struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq); + struct ab8500_gpio *ab8500_gpio = irq_data_get_irq_chip_data(data); + unsigned int irq = data->irq; int offset = irq - ab8500_gpio->irq_base; bool rising = ab8500_gpio->rising & BIT(offset); bool falling = ab8500_gpio->falling & BIT(offset); @@ -280,12 +329,12 @@ static void ab8500_gpio_irq_sync_unlock(unsigned int irq) if (rising) ret = request_threaded_irq(irq_to_rising(irq), NULL, handle_rising, - IRQF_TRIGGER_RISING, + IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND, "ab8500-gpio-r", ab8500_gpio); if (falling) ret = request_threaded_irq(irq_to_falling(irq), NULL, handle_falling, - IRQF_TRIGGER_FALLING, + IRQF_TRIGGER_FALLING | IRQF_NO_SUSPEND, "ab8500-gpio-f", ab8500_gpio); break; case SHUTDOWN: @@ -316,21 +365,22 @@ static void ab8500_gpio_irq_sync_unlock(unsigned int irq) } -static void ab8500_gpio_irq_mask(unsigned int irq) +static void ab8500_gpio_irq_mask(struct irq_data *data) { - struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq); + struct ab8500_gpio *ab8500_gpio = irq_data_get_irq_chip_data(data); ab8500_gpio->irq_action = MASK; } -static void ab8500_gpio_irq_unmask(unsigned int irq) +static void ab8500_gpio_irq_unmask(struct irq_data *data) { - struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq); + struct ab8500_gpio *ab8500_gpio = irq_data_get_irq_chip_data(data); ab8500_gpio->irq_action = UNMASK; } -static int ab8500_gpio_irq_set_type(unsigned int irq, unsigned int type) +static int ab8500_gpio_irq_set_type(struct irq_data *data, unsigned int type) { - struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq); + struct ab8500_gpio *ab8500_gpio = irq_data_get_irq_chip_data(data); + unsigned int irq = data->irq; int offset = irq - ab8500_gpio->irq_base; if (type == IRQ_TYPE_EDGE_BOTH) { @@ -344,28 +394,28 @@ static int ab8500_gpio_irq_set_type(unsigned int irq, unsigned int type) return 0; } -unsigned int ab8500_gpio_irq_startup(unsigned int irq) +unsigned int ab8500_gpio_irq_startup(struct irq_data *data) { - struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq); + struct ab8500_gpio *ab8500_gpio = irq_data_get_irq_chip_data(data); ab8500_gpio->irq_action = STARTUP; return 0; } -void ab8500_gpio_irq_shutdown(unsigned int irq) +void ab8500_gpio_irq_shutdown(struct irq_data *data) { - struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq); + struct ab8500_gpio *ab8500_gpio = irq_data_get_irq_chip_data(data); ab8500_gpio->irq_action = SHUTDOWN; } static struct irq_chip ab8500_gpio_irq_chip = { .name = "ab8500-gpio", - .startup = ab8500_gpio_irq_startup, - .shutdown = ab8500_gpio_irq_shutdown, - .bus_lock = ab8500_gpio_irq_lock, - .bus_sync_unlock = ab8500_gpio_irq_sync_unlock, - .mask = ab8500_gpio_irq_mask, - .unmask = ab8500_gpio_irq_unmask, - .set_type = ab8500_gpio_irq_set_type, + .irq_startup = ab8500_gpio_irq_startup, + .irq_shutdown = ab8500_gpio_irq_shutdown, + .irq_bus_lock = ab8500_gpio_irq_lock, + .irq_bus_sync_unlock = ab8500_gpio_irq_sync_unlock, + .irq_mask = ab8500_gpio_irq_mask, + .irq_unmask = ab8500_gpio_irq_unmask, + .irq_set_type = ab8500_gpio_irq_set_type, }; static int ab8500_gpio_irq_init(struct ab8500_gpio *ab8500_gpio) @@ -374,14 +424,14 @@ static int ab8500_gpio_irq_init(struct ab8500_gpio *ab8500_gpio) int irq; for (irq = base; irq < base + AB8500_NUM_VIR_GPIO_IRQ ; irq++) { - set_irq_chip_data(irq, ab8500_gpio); - set_irq_chip_and_handler(irq, &ab8500_gpio_irq_chip, + irq_set_chip_data(irq, ab8500_gpio); + irq_set_chip_and_handler(irq, &ab8500_gpio_irq_chip, handle_simple_irq); - set_irq_nested_thread(irq, 1); + irq_set_nested_thread(irq, 1); #ifdef CONFIG_ARM set_irq_flags(irq, IRQF_VALID); #else - set_irq_noprobe(irq); + irq_set_noprobe(irq); #endif } @@ -397,19 +447,22 @@ static void ab8500_gpio_irq_remove(struct ab8500_gpio *ab8500_gpio) #ifdef CONFIG_ARM set_irq_flags(irq, 0); #endif - set_irq_chip_and_handler(irq, NULL, NULL); - set_irq_chip_data(irq, NULL); + irq_set_chip_and_handler(irq, NULL, NULL); + irq_set_chip_data(irq, NULL); } } static int __devinit ab8500_gpio_probe(struct platform_device *pdev) { + struct ab8500 *parent = dev_get_drvdata(pdev->dev.parent); struct ab8500_platform_data *ab8500_pdata = dev_get_platdata(pdev->dev.parent); struct ab8500_gpio_platform_data *pdata; struct ab8500_gpio *ab8500_gpio; int ret; int i; + int last_gpio_sel_reg; + int altfun_reg_index; pdata = ab8500_pdata->gpio; if (!pdata) { @@ -425,10 +478,36 @@ static int __devinit ab8500_gpio_probe(struct platform_device *pdev) ab8500_gpio->dev = &pdev->dev; ab8500_gpio->parent = dev_get_drvdata(pdev->dev.parent); ab8500_gpio->chip = ab8500gpio_chip; - ab8500_gpio->chip.ngpio = AB8500_NUM_GPIO; ab8500_gpio->chip.dev = &pdev->dev; ab8500_gpio->chip.base = pdata->gpio_base; ab8500_gpio->irq_base = pdata->irq_base; + + /* Configure GPIO Settings for specific AB devices */ + if (cpu_is_u9540()) { + ab8500_gpio->chip.ngpio = AB9540_NUM_GPIO; + ab8500_gpio->irq_cluster = ab9540_irq_clusters; + ab8500_gpio->irq_cluster_size = + ARRAY_SIZE(ab9540_irq_clusters); + last_gpio_sel_reg = AB9540_GPIO_SEL7_REG; + altfun_reg_index = AB9540_ALTFUN_REG_INDEX; + } else { + if (is_ab8505(parent)) { + ab8500_gpio->chip.ngpio = AB8505_NUM_GPIO; + ab8500_gpio->irq_cluster = ab8505_irq_clusters; + ab8500_gpio->irq_cluster_size = + ARRAY_SIZE(ab8505_irq_clusters); + last_gpio_sel_reg = AB9540_GPIO_SEL7_REG; + altfun_reg_index = AB9540_ALTFUN_REG_INDEX; + } else { + ab8500_gpio->chip.ngpio = AB8500_NUM_GPIO; + ab8500_gpio->irq_cluster = ab8500_irq_clusters; + ab8500_gpio->irq_cluster_size = + ARRAY_SIZE(ab8500_irq_clusters); + last_gpio_sel_reg = AB8500_GPIO_SEL6_REG; + altfun_reg_index = AB8500_ALTFUN_REG_INDEX; + } + } + /* initialize the lock */ mutex_init(&ab8500_gpio->lock); /* @@ -437,16 +516,28 @@ static int __devinit ab8500_gpio_probe(struct platform_device *pdev) * These values are for selecting the PINs as * GPIO or alternate function */ - for (i = AB8500_GPIO_SEL1_REG; i <= AB8500_GPIO_SEL6_REG; i++) { + for (i = AB8500_GPIO_SEL1_REG; i <= last_gpio_sel_reg; i++) { ret = abx500_set_register_interruptible(ab8500_gpio->dev, AB8500_MISC, i, pdata->config_reg[i]); if (ret < 0) goto out_free; + + ret = abx500_set_register_interruptible(ab8500_gpio->dev, + AB8500_MISC, i + AB8500_GPIO_DIR1_REG, + pdata->config_direction[i]); + if (ret < 0) + goto out_free; + + ret = abx500_set_register_interruptible(ab8500_gpio->dev, + AB8500_MISC, i + AB8500_GPIO_PUD1_REG, + pdata->config_pullups[i]); + if (ret < 0) + goto out_free; } ret = abx500_set_register_interruptible(ab8500_gpio->dev, AB8500_MISC, AB8500_GPIO_ALTFUN_REG, - pdata->config_reg[ALTFUN_REG_INDEX]); + pdata->config_reg[altfun_reg_index]); if (ret < 0) goto out_free; @@ -493,6 +584,86 @@ static int __devexit ab8500_gpio_remove(struct platform_device *pdev) return 0; } +int ab8500_config_pulldown(struct device *dev, + enum ab8500_pin gpio, bool enable) +{ + u8 offset = gpio - AB8500_PIN_GPIO1; + u8 pos = offset % 8; + u8 val = enable ? 0 : 1; + u8 reg = AB8500_GPIO_PUD1_REG + (offset / 8); + int ret; + + ret = abx500_mask_and_set_register_interruptible(dev, + AB8500_MISC, reg, 1 << pos, val << pos); + if (ret < 0) + dev_err(dev, "%s write failed\n", __func__); + return ret; +} +EXPORT_SYMBOL(ab8500_config_pulldown); + +/* + * ab8500_gpio_config_select() + * + * Configure functionality of pin, either specific use or GPIO. + * @dev: device pointer + * @gpio: gpio number + * @gpio_select: true if the pin should be used as GPIO + */ +int ab8500_gpio_config_select(struct device *dev, + enum ab8500_pin gpio, bool gpio_select) +{ + u8 offset = gpio - AB8500_PIN_GPIO1; + u8 reg = AB8500_GPIO_SEL1_REG + (offset / 8); + u8 pos = offset % 8; + u8 val = gpio_select ? 1 : 0; + int ret; + + ret = abx500_mask_and_set_register_interruptible(dev, + AB8500_MISC, reg, 1 << pos, val << pos); + if (ret < 0) + dev_err(dev, "%s write failed\n", __func__); + + dev_vdbg(dev, "%s (bank, addr, mask, value): 0x%x, 0x%x, 0x%x, 0x%x\n", + __func__, AB8500_MISC, reg, 1 << pos, val << pos); + + return ret; +} + +/* + * ab8500_gpio_config_get_select() + * + * Read currently configured functionality, either specific use or GPIO. + * @dev: device pointer + * @gpio: gpio number + * @gpio_select: pointer to pin selection status + */ +int ab8500_gpio_config_get_select(struct device *dev, + enum ab8500_pin gpio, bool *gpio_select) +{ + u8 offset = gpio - AB8500_PIN_GPIO1; + u8 reg = AB8500_GPIO_SEL1_REG + (offset / 8); + u8 pos = offset % 8; + u8 val; + int ret; + + ret = abx500_get_register_interruptible(dev, + AB8500_MISC, reg, &val); + if (ret < 0) { + dev_err(dev, "%s read failed\n", __func__); + return ret; + } + + if (val & (1 << pos)) + *gpio_select = true; + else + *gpio_select = false; + + dev_vdbg(dev, "%s (bank, addr, mask, value): 0x%x, 0x%x, 0x%x, 0x%x\n", + __func__, AB8500_MISC, reg, 1 << pos, val); + + return 0; +} + static struct platform_driver ab8500_gpio_driver = { .driver = { .name = "ab8500-gpio", diff --git a/drivers/gpio/gpio-nomadik.c b/drivers/gpio/gpio-nomadik.c index d350c8d7d2d..05547ed48ff 100644 --- a/drivers/gpio/gpio-nomadik.c +++ b/drivers/gpio/gpio-nomadik.c @@ -23,12 +23,11 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/slab.h> +#include <linux/gpio/nomadik.h> #include <asm/mach/irq.h> #include <plat/pincfg.h> -#include <plat/gpio-nomadik.h> -#include <mach/hardware.h> #include <asm/gpio.h> /* @@ -58,8 +57,11 @@ struct nmk_gpio_chip { u32 real_wake; u32 rwimsc; u32 fwimsc; + u32 rimsc; + u32 fimsc; u32 slpm; u32 pull_up; + u32 lowemi; }; static struct nmk_gpio_chip * @@ -124,6 +126,24 @@ static void __nmk_gpio_set_pull(struct nmk_gpio_chip *nmk_chip, } } +static void __nmk_gpio_set_lowemi(struct nmk_gpio_chip *nmk_chip, + unsigned offset, bool lowemi) +{ + u32 bit = BIT(offset); + bool enabled = nmk_chip->lowemi & bit; + + if (lowemi == enabled) + return; + + if (lowemi) + nmk_chip->lowemi |= bit; + else + nmk_chip->lowemi &= ~bit; + + writel_relaxed(nmk_chip->lowemi, + nmk_chip->addr + NMK_GPIO_LOWEMI); +} + static void __nmk_gpio_make_input(struct nmk_gpio_chip *nmk_chip, unsigned offset) { @@ -150,8 +170,8 @@ static void __nmk_gpio_set_mode_safe(struct nmk_gpio_chip *nmk_chip, unsigned offset, int gpio_mode, bool glitch) { - u32 rwimsc = readl(nmk_chip->addr + NMK_GPIO_RWIMSC); - u32 fwimsc = readl(nmk_chip->addr + NMK_GPIO_FWIMSC); + u32 rwimsc = nmk_chip->rwimsc; + u32 fwimsc = nmk_chip->fwimsc; if (glitch && nmk_chip->set_ioforce) { u32 bit = BIT(offset); @@ -173,6 +193,36 @@ static void __nmk_gpio_set_mode_safe(struct nmk_gpio_chip *nmk_chip, } } +static void +nmk_gpio_disable_lazy_irq(struct nmk_gpio_chip *nmk_chip, unsigned offset) +{ + u32 falling = nmk_chip->fimsc & BIT(offset); + u32 rising = nmk_chip->rimsc & BIT(offset); + int gpio = nmk_chip->chip.base + offset; + int irq = NOMADIK_GPIO_TO_IRQ(gpio); + struct irq_data *d = irq_get_irq_data(irq); + + if (!rising && !falling) + return; + + if (!d || !irqd_irq_disabled(d)) + return; + + if (rising) { + nmk_chip->rimsc &= ~BIT(offset); + writel_relaxed(nmk_chip->rimsc, + nmk_chip->addr + NMK_GPIO_RIMSC); + } + + if (falling) { + nmk_chip->fimsc &= ~BIT(offset); + writel_relaxed(nmk_chip->fimsc, + nmk_chip->addr + NMK_GPIO_FIMSC); + } + + dev_dbg(nmk_chip->chip.dev, "%d: clearing interrupt mask\n", gpio); +} + static void __nmk_config_pin(struct nmk_gpio_chip *nmk_chip, unsigned offset, pin_cfg_t cfg, bool sleep, unsigned int *slpmregs) { @@ -238,6 +288,17 @@ static void __nmk_config_pin(struct nmk_gpio_chip *nmk_chip, unsigned offset, __nmk_gpio_set_pull(nmk_chip, offset, pull); } + __nmk_gpio_set_lowemi(nmk_chip, offset, PIN_LOWEMI(cfg)); + + /* + * If the pin is switching to altfunc, and there was an interrupt + * installed on it which has been lazy disabled, actually mask the + * interrupt to prevent spurious interrupts that would occur while the + * pin is under control of the peripheral. Only SKE does this. + */ + if (af != NMK_GPIO_ALT_GPIO) + nmk_gpio_disable_lazy_irq(nmk_chip, offset); + /* * If we've backed up the SLPM registers (glitch workaround), modify * the backups since they will be restored. @@ -556,27 +617,38 @@ static void __nmk_gpio_irq_modify(struct nmk_gpio_chip *nmk_chip, int gpio, enum nmk_gpio_irq_type which, bool enable) { - u32 rimsc = which == WAKE ? NMK_GPIO_RWIMSC : NMK_GPIO_RIMSC; - u32 fimsc = which == WAKE ? NMK_GPIO_FWIMSC : NMK_GPIO_FIMSC; u32 bitmask = nmk_gpio_get_bitmask(gpio); - u32 reg; + u32 *rimscval; + u32 *fimscval; + u32 rimscreg; + u32 fimscreg; + + if (which == NORMAL) { + rimscreg = NMK_GPIO_RIMSC; + fimscreg = NMK_GPIO_FIMSC; + rimscval = &nmk_chip->rimsc; + fimscval = &nmk_chip->fimsc; + } else { + rimscreg = NMK_GPIO_RWIMSC; + fimscreg = NMK_GPIO_FWIMSC; + rimscval = &nmk_chip->rwimsc; + fimscval = &nmk_chip->fwimsc; + } /* we must individually set/clear the two edges */ if (nmk_chip->edge_rising & bitmask) { - reg = readl(nmk_chip->addr + rimsc); if (enable) - reg |= bitmask; + *rimscval |= bitmask; else - reg &= ~bitmask; - writel(reg, nmk_chip->addr + rimsc); + *rimscval &= ~bitmask; + writel(*rimscval, nmk_chip->addr + rimscreg); } if (nmk_chip->edge_falling & bitmask) { - reg = readl(nmk_chip->addr + fimsc); if (enable) - reg |= bitmask; + *fimscval |= bitmask; else - reg &= ~bitmask; - writel(reg, nmk_chip->addr + fimsc); + *fimscval &= ~bitmask; + writel(*fimscval, nmk_chip->addr + fimscreg); } } @@ -1008,9 +1080,6 @@ void nmk_gpio_wakeups_suspend(void) clk_enable(chip->clk); - chip->rwimsc = readl(chip->addr + NMK_GPIO_RWIMSC); - chip->fwimsc = readl(chip->addr + NMK_GPIO_FWIMSC); - writel(chip->rwimsc & chip->real_wake, chip->addr + NMK_GPIO_RWIMSC); writel(chip->fwimsc & chip->real_wake, @@ -1076,6 +1145,7 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev) struct resource *res; struct clk *clk; int secondary_irq; + void __iomem *base; int irq; int ret; @@ -1106,10 +1176,16 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev) goto out; } + base = ioremap(res->start, resource_size(res)); + if (!base) { + ret = -ENOMEM; + goto out_release; + } + clk = clk_get(&dev->dev, NULL); if (IS_ERR(clk)) { ret = PTR_ERR(clk); - goto out_release; + goto out_unmap; } nmk_chip = kzalloc(sizeof(*nmk_chip), GFP_KERNEL); @@ -1123,7 +1199,7 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev) */ nmk_chip->bank = dev->id; nmk_chip->clk = clk; - nmk_chip->addr = io_p2v(res->start); + nmk_chip->addr = base; nmk_chip->chip = nmk_gpio_template; nmk_chip->parent_irq = irq; nmk_chip->secondary_parent_irq = secondary_irq; @@ -1139,6 +1215,10 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev) chip->dev = &dev->dev; chip->owner = THIS_MODULE; + clk_enable(nmk_chip->clk); + nmk_chip->lowemi = readl_relaxed(nmk_chip->addr + NMK_GPIO_LOWEMI); + clk_disable(nmk_chip->clk); + ret = gpiochip_add(&nmk_chip->chip); if (ret) goto out_free; @@ -1159,6 +1239,8 @@ out_free: out_clk: clk_disable(clk); clk_put(clk); +out_unmap: + iounmap(base); out_release: release_mem_region(res->start, resource_size(res)); out: |