X-Git-Url: http://git.cascardo.eti.br/?a=blobdiff_plain;f=arch%2Farm%2Fplat-nomadik%2Fgpio.c;h=971e5d3798e47838ba9a928f3fb2babb9de0e5d2;hb=33b744b35189baaa260f526cf117392095331843;hp=eda4e3a11a3d1129acbd8885ec67414db2b7638f;hpb=509e4aef44eb10e4aef1f81c3c3ff1214671503b;p=cascardo%2Flinux.git diff --git a/arch/arm/plat-nomadik/gpio.c b/arch/arm/plat-nomadik/gpio.c index eda4e3a11a3d..971e5d3798e4 100644 --- a/arch/arm/plat-nomadik/gpio.c +++ b/arch/arm/plat-nomadik/gpio.c @@ -35,16 +35,19 @@ * Symbols in this file are called "nmk_gpio" for "nomadik gpio" */ -#define NMK_GPIO_PER_CHIP 32 struct nmk_gpio_chip { struct gpio_chip chip; void __iomem *addr; struct clk *clk; + unsigned int bank; unsigned int parent_irq; + unsigned int secondary_parent_irq; + u32 (*get_secondary_status)(unsigned int bank); spinlock_t lock; /* Keep track of configured edges */ u32 edge_rising; u32 edge_falling; + u32 backup[10]; }; static void __nmk_gpio_set_mode(struct nmk_gpio_chip *nmk_chip, @@ -356,13 +359,13 @@ static inline int nmk_gpio_get_bitmask(int gpio) return 1 << (gpio % 32); } -static void nmk_gpio_irq_ack(unsigned int irq) +static void nmk_gpio_irq_ack(struct irq_data *d) { int gpio; struct nmk_gpio_chip *nmk_chip; - gpio = NOMADIK_IRQ_TO_GPIO(irq); - nmk_chip = get_irq_chip_data(irq); + gpio = NOMADIK_IRQ_TO_GPIO(d->irq); + nmk_chip = irq_data_get_irq_chip_data(d); if (!nmk_chip) return; writel(nmk_gpio_get_bitmask(gpio), nmk_chip->addr + NMK_GPIO_IC); @@ -401,7 +404,7 @@ static void __nmk_gpio_irq_modify(struct nmk_gpio_chip *nmk_chip, } } -static int nmk_gpio_irq_modify(unsigned int irq, enum nmk_gpio_irq_type which, +static int nmk_gpio_irq_modify(struct irq_data *d, enum nmk_gpio_irq_type which, bool enable) { int gpio; @@ -409,8 +412,8 @@ static int nmk_gpio_irq_modify(unsigned int irq, enum nmk_gpio_irq_type which, unsigned long flags; u32 bitmask; - gpio = NOMADIK_IRQ_TO_GPIO(irq); - nmk_chip = get_irq_chip_data(irq); + gpio = NOMADIK_IRQ_TO_GPIO(d->irq); + nmk_chip = irq_data_get_irq_chip_data(d); bitmask = nmk_gpio_get_bitmask(gpio); if (!nmk_chip) return -EINVAL; @@ -422,24 +425,24 @@ static int nmk_gpio_irq_modify(unsigned int irq, enum nmk_gpio_irq_type which, return 0; } -static void nmk_gpio_irq_mask(unsigned int irq) +static void nmk_gpio_irq_mask(struct irq_data *d) { - nmk_gpio_irq_modify(irq, NORMAL, false); + nmk_gpio_irq_modify(d, NORMAL, false); } -static void nmk_gpio_irq_unmask(unsigned int irq) +static void nmk_gpio_irq_unmask(struct irq_data *d) { - nmk_gpio_irq_modify(irq, NORMAL, true); + nmk_gpio_irq_modify(d, NORMAL, true); } -static int nmk_gpio_irq_set_wake(unsigned int irq, unsigned int on) +static int nmk_gpio_irq_set_wake(struct irq_data *d, unsigned int on) { struct nmk_gpio_chip *nmk_chip; unsigned long flags; int gpio; - gpio = NOMADIK_IRQ_TO_GPIO(irq); - nmk_chip = get_irq_chip_data(irq); + gpio = NOMADIK_IRQ_TO_GPIO(d->irq); + nmk_chip = irq_data_get_irq_chip_data(d); if (!nmk_chip) return -EINVAL; @@ -457,9 +460,9 @@ static int nmk_gpio_irq_set_wake(unsigned int irq, unsigned int on) return 0; } -static int nmk_gpio_irq_set_type(unsigned int irq, unsigned int type) +static int nmk_gpio_irq_set_type(struct irq_data *d, unsigned int type) { - struct irq_desc *desc = irq_to_desc(irq); + struct irq_desc *desc = irq_to_desc(d->irq); bool enabled = !(desc->status & IRQ_DISABLED); bool wake = desc->wake_depth; int gpio; @@ -467,8 +470,8 @@ static int nmk_gpio_irq_set_type(unsigned int irq, unsigned int type) unsigned long flags; u32 bitmask; - gpio = NOMADIK_IRQ_TO_GPIO(irq); - nmk_chip = get_irq_chip_data(irq); + gpio = NOMADIK_IRQ_TO_GPIO(d->irq); + nmk_chip = irq_data_get_irq_chip_data(d); bitmask = nmk_gpio_get_bitmask(gpio); if (!nmk_chip) return -EINVAL; @@ -507,37 +510,55 @@ static int nmk_gpio_irq_set_type(unsigned int irq, unsigned int type) static struct irq_chip nmk_gpio_irq_chip = { .name = "Nomadik-GPIO", - .ack = nmk_gpio_irq_ack, - .mask = nmk_gpio_irq_mask, - .unmask = nmk_gpio_irq_unmask, - .set_type = nmk_gpio_irq_set_type, - .set_wake = nmk_gpio_irq_set_wake, + .irq_ack = nmk_gpio_irq_ack, + .irq_mask = nmk_gpio_irq_mask, + .irq_unmask = nmk_gpio_irq_unmask, + .irq_set_type = nmk_gpio_irq_set_type, + .irq_set_wake = nmk_gpio_irq_set_wake, }; -static void nmk_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) +static void __nmk_gpio_irq_handler(unsigned int irq, struct irq_desc *desc, + u32 status) { struct nmk_gpio_chip *nmk_chip; struct irq_chip *host_chip = get_irq_chip(irq); - unsigned int gpio_irq; - u32 pending; unsigned int first_irq; - if (host_chip->mask_ack) - host_chip->mask_ack(irq); + if (host_chip->irq_mask_ack) + host_chip->irq_mask_ack(&desc->irq_data); else { - host_chip->mask(irq); - if (host_chip->ack) - host_chip->ack(irq); + host_chip->irq_mask(&desc->irq_data); + if (host_chip->irq_ack) + host_chip->irq_ack(&desc->irq_data); } nmk_chip = get_irq_data(irq); first_irq = NOMADIK_GPIO_TO_IRQ(nmk_chip->chip.base); - while ( (pending = readl(nmk_chip->addr + NMK_GPIO_IS)) ) { - gpio_irq = first_irq + __ffs(pending); - generic_handle_irq(gpio_irq); + while (status) { + int bit = __ffs(status); + + generic_handle_irq(first_irq + bit); + status &= ~BIT(bit); } - host_chip->unmask(irq); + host_chip->irq_unmask(&desc->irq_data); +} + +static void nmk_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + struct nmk_gpio_chip *nmk_chip = get_irq_data(irq); + u32 status = readl(nmk_chip->addr + NMK_GPIO_IS); + + __nmk_gpio_irq_handler(irq, desc, status); +} + +static void nmk_gpio_secondary_irq_handler(unsigned int irq, + struct irq_desc *desc) +{ + struct nmk_gpio_chip *nmk_chip = get_irq_data(irq); + u32 status = nmk_chip->get_secondary_status(nmk_chip->bank); + + __nmk_gpio_irq_handler(irq, desc, status); } static int nmk_gpio_init_irq(struct nmk_gpio_chip *nmk_chip) @@ -546,15 +567,23 @@ static int nmk_gpio_init_irq(struct nmk_gpio_chip *nmk_chip) int i; first_irq = NOMADIK_GPIO_TO_IRQ(nmk_chip->chip.base); - for (i = first_irq; i < first_irq + NMK_GPIO_PER_CHIP; i++) { + for (i = first_irq; i < first_irq + nmk_chip->chip.ngpio; i++) { set_irq_chip(i, &nmk_gpio_irq_chip); set_irq_handler(i, handle_edge_irq); set_irq_flags(i, IRQF_VALID); set_irq_chip_data(i, nmk_chip); set_irq_type(i, IRQ_TYPE_EDGE_FALLING); } + set_irq_chained_handler(nmk_chip->parent_irq, nmk_gpio_irq_handler); set_irq_data(nmk_chip->parent_irq, nmk_chip); + + if (nmk_chip->secondary_parent_irq >= 0) { + set_irq_chained_handler(nmk_chip->secondary_parent_irq, + nmk_gpio_secondary_irq_handler); + set_irq_data(nmk_chip->secondary_parent_irq, nmk_chip); + } + return 0; } @@ -605,6 +634,97 @@ static int nmk_gpio_to_irq(struct gpio_chip *chip, unsigned offset) return NOMADIK_GPIO_TO_IRQ(nmk_chip->chip.base) + offset; } +#ifdef CONFIG_DEBUG_FS + +#include + +static void nmk_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) +{ + int mode; + unsigned i; + unsigned gpio = chip->base; + int is_out; + struct nmk_gpio_chip *nmk_chip = + container_of(chip, struct nmk_gpio_chip, chip); + const char *modes[] = { + [NMK_GPIO_ALT_GPIO] = "gpio", + [NMK_GPIO_ALT_A] = "altA", + [NMK_GPIO_ALT_B] = "altB", + [NMK_GPIO_ALT_C] = "altC", + }; + + for (i = 0; i < chip->ngpio; i++, gpio++) { + const char *label = gpiochip_is_requested(chip, i); + bool pull; + u32 bit = 1 << i; + + if (!label) + continue; + + is_out = readl(nmk_chip->addr + NMK_GPIO_DIR) & bit; + pull = !(readl(nmk_chip->addr + NMK_GPIO_PDIS) & bit); + mode = nmk_gpio_get_mode(gpio); + seq_printf(s, " gpio-%-3d (%-20.20s) %s %s %s %s", + gpio, label, + is_out ? "out" : "in ", + chip->get + ? (chip->get(chip, i) ? "hi" : "lo") + : "? ", + (mode < 0) ? "unknown" : modes[mode], + pull ? "pull" : "none"); + + if (!is_out) { + int irq = gpio_to_irq(gpio); + struct irq_desc *desc = irq_to_desc(irq); + + /* This races with request_irq(), set_irq_type(), + * and set_irq_wake() ... but those are "rare". + * + * More significantly, trigger type flags aren't + * currently maintained by genirq. + */ + if (irq >= 0 && desc->action) { + char *trigger; + + switch (desc->status & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_NONE: + trigger = "(default)"; + break; + case IRQ_TYPE_EDGE_FALLING: + trigger = "edge-falling"; + break; + case IRQ_TYPE_EDGE_RISING: + trigger = "edge-rising"; + break; + case IRQ_TYPE_EDGE_BOTH: + trigger = "edge-both"; + break; + case IRQ_TYPE_LEVEL_HIGH: + trigger = "level-high"; + break; + case IRQ_TYPE_LEVEL_LOW: + trigger = "level-low"; + break; + default: + trigger = "?trigger?"; + break; + } + + seq_printf(s, " irq-%d %s%s", + irq, trigger, + (desc->status & IRQ_WAKEUP) + ? " wakeup" : ""); + } + } + + seq_printf(s, "\n"); + } +} + +#else +#define nmk_gpio_dbg_show NULL +#endif + /* This structure is replicated for each GPIO block allocated at probe time */ static struct gpio_chip nmk_gpio_template = { .direction_input = nmk_gpio_make_input, @@ -612,7 +732,7 @@ static struct gpio_chip nmk_gpio_template = { .direction_output = nmk_gpio_make_output, .set = nmk_gpio_set_output, .to_irq = nmk_gpio_to_irq, - .ngpio = NMK_GPIO_PER_CHIP, + .dbg_show = nmk_gpio_dbg_show, .can_sleep = 0, }; @@ -623,6 +743,7 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev) struct gpio_chip *chip; struct resource *res; struct clk *clk; + int secondary_irq; int irq; int ret; @@ -641,6 +762,12 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev) goto out; } + secondary_irq = platform_get_irq(dev, 1); + if (secondary_irq >= 0 && !pdata->get_secondary_status) { + ret = -EINVAL; + goto out; + } + if (request_mem_region(res->start, resource_size(res), dev_name(&dev->dev)) == NULL) { ret = -EBUSY; @@ -664,14 +791,18 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev) * The virt address in nmk_chip->addr is in the nomadik register space, * so we can simply convert the resource address, without remapping */ + nmk_chip->bank = dev->id; nmk_chip->clk = clk; nmk_chip->addr = io_p2v(res->start); nmk_chip->chip = nmk_gpio_template; nmk_chip->parent_irq = irq; + nmk_chip->secondary_parent_irq = secondary_irq; + nmk_chip->get_secondary_status = pdata->get_secondary_status; spin_lock_init(&nmk_chip->lock); chip = &nmk_chip->chip; chip->base = pdata->first_gpio; + chip->ngpio = pdata->num_gpio; chip->label = pdata->name ?: dev_name(&dev->dev); chip->dev = &dev->dev; chip->owner = THIS_MODULE; @@ -701,14 +832,60 @@ out: return ret; } +#ifdef CONFIG_PM +static int nmk_gpio_pm(struct platform_device *dev, bool suspend) +{ + struct nmk_gpio_chip *nmk_chip = platform_get_drvdata(dev); + int i; + static const unsigned int regs[] = { + NMK_GPIO_DAT, + NMK_GPIO_PDIS, + NMK_GPIO_DIR, + NMK_GPIO_AFSLA, + NMK_GPIO_AFSLB, + NMK_GPIO_SLPC, + NMK_GPIO_RIMSC, + NMK_GPIO_FIMSC, + NMK_GPIO_RWIMSC, + NMK_GPIO_FWIMSC, + }; + + BUILD_BUG_ON(ARRAY_SIZE(nmk_chip->backup) != ARRAY_SIZE(regs)); + + /* XXX: is this sufficient? what about pull-up/down configuration? */ + + for (i = 0; i < ARRAY_SIZE(regs); i++) { + if (suspend) + nmk_chip->backup[i] = readl(nmk_chip->addr + regs[i]); + else + writel(nmk_chip->backup[i], nmk_chip->addr + regs[i]); + } + + return 0; +} + +static int nmk_gpio_suspend(struct platform_device *dev, pm_message_t state) +{ + return nmk_gpio_pm(dev, true); +} + +static int nmk_gpio_resume(struct platform_device *dev) +{ + return nmk_gpio_pm(dev, false); +} +#else +#define nmk_gpio_suspend NULL +#define nmk_gpio_resume NULL +#endif + static struct platform_driver nmk_gpio_driver = { .driver = { .owner = THIS_MODULE, .name = "gpio", }, .probe = nmk_gpio_probe, - .suspend = NULL, /* to be done */ - .resume = NULL, + .suspend = nmk_gpio_suspend, + .resume = nmk_gpio_resume, }; static int __init nmk_gpio_init(void)