From 3c0227d262a5835849c68eb8328db016caad6085 Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Tue, 20 Sep 2011 10:50:03 +0200 Subject: gpio/nomadik: disable clocks when unused The GPIO clock is required for register access and interrupt detection. When interrupt detection is not required on any of the pin in a block, the block's clock can be disabled when the registers are not being accessed. Signed-off-by: Rabin Vincent Reviewed-by: Rickard Andersson Reviewed-by: Jonas Aberg [Adjust for new IRQ chip core code, use only local functions] Signed-off-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/gpio/gpio-nomadik.c | 120 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 113 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-nomadik.c b/drivers/gpio/gpio-nomadik.c index 2c212c732d7..97e9be94141 100644 --- a/drivers/gpio/gpio-nomadik.c +++ b/drivers/gpio/gpio-nomadik.c @@ -276,6 +276,8 @@ static void nmk_gpio_glitch_slpm_init(unsigned int *slpm) if (!chip) break; + clk_enable(chip->clk); + slpm[i] = readl(chip->addr + NMK_GPIO_SLPC); writel(temp, chip->addr + NMK_GPIO_SLPC); } @@ -292,6 +294,8 @@ static void nmk_gpio_glitch_slpm_restore(unsigned int *slpm) break; writel(slpm[i], chip->addr + NMK_GPIO_SLPC); + + clk_disable(chip->clk); } } @@ -336,10 +340,12 @@ static int __nmk_config_pins(pin_cfg_t *cfgs, int num, bool sleep) break; } + clk_enable(nmk_chip->clk); spin_lock(&nmk_chip->lock); __nmk_config_pin(nmk_chip, pin - nmk_chip->chip.base, cfgs[i], sleep, glitch ? slpm : NULL); spin_unlock(&nmk_chip->lock); + clk_disable(nmk_chip->clk); } if (glitch) @@ -424,6 +430,7 @@ int nmk_gpio_set_slpm(int gpio, enum nmk_gpio_slpm mode) if (!nmk_chip) return -EINVAL; + clk_enable(nmk_chip->clk); spin_lock_irqsave(&nmk_gpio_slpm_lock, flags); spin_lock(&nmk_chip->lock); @@ -431,6 +438,7 @@ int nmk_gpio_set_slpm(int gpio, enum nmk_gpio_slpm mode) spin_unlock(&nmk_chip->lock); spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags); + clk_disable(nmk_chip->clk); return 0; } @@ -457,9 +465,11 @@ int nmk_gpio_set_pull(int gpio, enum nmk_gpio_pull pull) if (!nmk_chip) return -EINVAL; + clk_enable(nmk_chip->clk); spin_lock_irqsave(&nmk_chip->lock, flags); __nmk_gpio_set_pull(nmk_chip, gpio - nmk_chip->chip.base, pull); spin_unlock_irqrestore(&nmk_chip->lock, flags); + clk_disable(nmk_chip->clk); return 0; } @@ -483,9 +493,11 @@ int nmk_gpio_set_mode(int gpio, int gpio_mode) if (!nmk_chip) return -EINVAL; + clk_enable(nmk_chip->clk); spin_lock_irqsave(&nmk_chip->lock, flags); __nmk_gpio_set_mode(nmk_chip, gpio - nmk_chip->chip.base, gpio_mode); spin_unlock_irqrestore(&nmk_chip->lock, flags); + clk_disable(nmk_chip->clk); return 0; } @@ -502,9 +514,13 @@ int nmk_gpio_get_mode(int gpio) bit = 1 << (gpio - nmk_chip->chip.base); + clk_enable(nmk_chip->clk); + afunc = readl(nmk_chip->addr + NMK_GPIO_AFSLA) & bit; bfunc = readl(nmk_chip->addr + NMK_GPIO_AFSLB) & bit; + clk_disable(nmk_chip->clk); + return (afunc ? NMK_GPIO_ALT_A : 0) | (bfunc ? NMK_GPIO_ALT_B : 0); } EXPORT_SYMBOL(nmk_gpio_get_mode); @@ -525,7 +541,10 @@ static void nmk_gpio_irq_ack(struct irq_data *d) nmk_chip = irq_data_get_irq_chip_data(d); if (!nmk_chip) return; + + clk_enable(nmk_chip->clk); writel(nmk_gpio_get_bitmask(gpio), nmk_chip->addr + NMK_GPIO_IC); + clk_disable(nmk_chip->clk); } enum nmk_gpio_irq_type { @@ -591,6 +610,7 @@ static int nmk_gpio_irq_maskunmask(struct irq_data *d, bool enable) else nmk_chip->enabled &= ~bitmask; + clk_enable(nmk_chip->clk); spin_lock_irqsave(&nmk_gpio_slpm_lock, flags); spin_lock(&nmk_chip->lock); @@ -601,6 +621,7 @@ static int nmk_gpio_irq_maskunmask(struct irq_data *d, bool enable) spin_unlock(&nmk_chip->lock); spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags); + clk_disable(nmk_chip->clk); return 0; } @@ -628,6 +649,7 @@ static int nmk_gpio_irq_set_wake(struct irq_data *d, unsigned int on) return -EINVAL; bitmask = nmk_gpio_get_bitmask(gpio); + clk_enable(nmk_chip->clk); spin_lock_irqsave(&nmk_gpio_slpm_lock, flags); spin_lock(&nmk_chip->lock); @@ -641,13 +663,15 @@ static int nmk_gpio_irq_set_wake(struct irq_data *d, unsigned int on) spin_unlock(&nmk_chip->lock); spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags); + clk_disable(nmk_chip->clk); return 0; } static int nmk_gpio_irq_set_type(struct irq_data *d, unsigned int type) { - bool enabled, wake = irqd_is_wakeup_set(d); + bool enabled; + bool wake = irqd_is_wakeup_set(d); int gpio; struct nmk_gpio_chip *nmk_chip; unsigned long flags; @@ -664,10 +688,10 @@ static int nmk_gpio_irq_set_type(struct irq_data *d, unsigned int type) if (type & IRQ_TYPE_LEVEL_LOW) return -EINVAL; - enabled = nmk_chip->enabled & bitmask; - + clk_enable(nmk_chip->clk); spin_lock_irqsave(&nmk_chip->lock, flags); + enabled = !!(nmk_chip->enabled & bitmask); if (enabled) __nmk_gpio_irq_modify(nmk_chip, gpio, NORMAL, false); @@ -689,10 +713,28 @@ static int nmk_gpio_irq_set_type(struct irq_data *d, unsigned int type) __nmk_gpio_irq_modify(nmk_chip, gpio, WAKE, true); spin_unlock_irqrestore(&nmk_chip->lock, flags); + clk_disable(nmk_chip->clk); + + return 0; +} + +static unsigned int nmk_gpio_irq_startup(struct irq_data *d) +{ + struct nmk_gpio_chip *nmk_chip = irq_data_get_irq_chip_data(d); + clk_enable(nmk_chip->clk); + nmk_gpio_irq_unmask(d); return 0; } +static void nmk_gpio_irq_shutdown(struct irq_data *d) +{ + struct nmk_gpio_chip *nmk_chip = irq_data_get_irq_chip_data(d); + + nmk_gpio_irq_mask(d); + clk_disable(nmk_chip->clk); +} + static struct irq_chip nmk_gpio_irq_chip = { .name = "Nomadik-GPIO", .irq_ack = nmk_gpio_irq_ack, @@ -700,6 +742,8 @@ static struct irq_chip nmk_gpio_irq_chip = { .irq_unmask = nmk_gpio_irq_unmask, .irq_set_type = nmk_gpio_irq_set_type, .irq_set_wake = nmk_gpio_irq_set_wake, + .irq_startup = nmk_gpio_irq_startup, + .irq_shutdown = nmk_gpio_irq_shutdown, }; static void __nmk_gpio_irq_handler(unsigned int irq, struct irq_desc *desc, @@ -726,7 +770,11 @@ 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) { struct nmk_gpio_chip *nmk_chip = irq_get_handler_data(irq); - u32 status = readl(nmk_chip->addr + NMK_GPIO_IS); + u32 status; + + clk_enable(nmk_chip->clk); + status = readl(nmk_chip->addr + NMK_GPIO_IS); + clk_disable(nmk_chip->clk); __nmk_gpio_irq_handler(irq, desc, status); } @@ -772,7 +820,12 @@ static int nmk_gpio_make_input(struct gpio_chip *chip, unsigned offset) struct nmk_gpio_chip *nmk_chip = container_of(chip, struct nmk_gpio_chip, chip); + clk_enable(nmk_chip->clk); + writel(1 << offset, nmk_chip->addr + NMK_GPIO_DIRC); + + clk_disable(nmk_chip->clk); + return 0; } @@ -781,8 +834,15 @@ static int nmk_gpio_get_input(struct gpio_chip *chip, unsigned offset) struct nmk_gpio_chip *nmk_chip = container_of(chip, struct nmk_gpio_chip, chip); u32 bit = 1 << offset; + int value; + + clk_enable(nmk_chip->clk); + + value = (readl(nmk_chip->addr + NMK_GPIO_DAT) & bit) != 0; - return (readl(nmk_chip->addr + NMK_GPIO_DAT) & bit) != 0; + clk_disable(nmk_chip->clk); + + return value; } static void nmk_gpio_set_output(struct gpio_chip *chip, unsigned offset, @@ -791,7 +851,11 @@ static void nmk_gpio_set_output(struct gpio_chip *chip, unsigned offset, struct nmk_gpio_chip *nmk_chip = container_of(chip, struct nmk_gpio_chip, chip); + clk_enable(nmk_chip->clk); + __nmk_gpio_set_output(nmk_chip, offset, val); + + clk_disable(nmk_chip->clk); } static int nmk_gpio_make_output(struct gpio_chip *chip, unsigned offset, @@ -800,8 +864,12 @@ static int nmk_gpio_make_output(struct gpio_chip *chip, unsigned offset, struct nmk_gpio_chip *nmk_chip = container_of(chip, struct nmk_gpio_chip, chip); + clk_enable(nmk_chip->clk); + __nmk_gpio_make_output(nmk_chip, offset, val); + clk_disable(nmk_chip->clk); + return 0; } @@ -832,6 +900,8 @@ static void nmk_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) [NMK_GPIO_ALT_C] = "altC", }; + clk_enable(nmk_chip->clk); + for (i = 0; i < chip->ngpio; i++, gpio++) { const char *label = gpiochip_is_requested(chip, i); bool pull; @@ -876,6 +946,8 @@ static void nmk_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) seq_printf(s, "\n"); } + + clk_disable(nmk_chip->clk); } #else @@ -893,6 +965,34 @@ static struct gpio_chip nmk_gpio_template = { .can_sleep = 0, }; +void nmk_gpio_clocks_enable(void) +{ + int i; + + for (i = 0; i < NUM_BANKS; i++) { + struct nmk_gpio_chip *chip = nmk_gpio_chips[i]; + + if (!chip) + continue; + + clk_enable(chip->clk); + } +} + +void nmk_gpio_clocks_disable(void) +{ + int i; + + for (i = 0; i < NUM_BANKS; i++) { + struct nmk_gpio_chip *chip = nmk_gpio_chips[i]; + + if (!chip) + continue; + + clk_disable(chip->clk); + } +} + /* * Called from the suspend/resume path to only keep the real wakeup interrupts * (those that have had set_irq_wake() called on them) as wakeup interrupts, @@ -912,6 +1012,8 @@ void nmk_gpio_wakeups_suspend(void) if (!chip) break; + clk_enable(chip->clk); + chip->rwimsc = readl(chip->addr + NMK_GPIO_RWIMSC); chip->fwimsc = readl(chip->addr + NMK_GPIO_FWIMSC); @@ -926,6 +1028,8 @@ void nmk_gpio_wakeups_suspend(void) /* 0 -> wakeup enable */ writel(~chip->real_wake, chip->addr + NMK_GPIO_SLPC); } + + clk_disable(chip->clk); } } @@ -939,11 +1043,15 @@ void nmk_gpio_wakeups_resume(void) if (!chip) break; + clk_enable(chip->clk); + writel(chip->rwimsc, chip->addr + NMK_GPIO_RWIMSC); writel(chip->fwimsc, chip->addr + NMK_GPIO_FWIMSC); if (chip->sleepmode) writel(chip->slpm, chip->addr + NMK_GPIO_SLPC); + + clk_disable(chip->clk); } } @@ -1010,8 +1118,6 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev) goto out_release; } - clk_enable(clk); - nmk_chip = kzalloc(sizeof(*nmk_chip), GFP_KERNEL); if (!nmk_chip) { ret = -ENOMEM; -- cgit v1.2.3-18-g5258 From 479a0c7eee68243ad9915a9ccc217a35c4e5e0e2 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 20 Sep 2011 10:50:15 +0200 Subject: gpio/nomadik: use genirq core to track enablement Currently the Nomadik GPIO driver tracks enabled/disabled interrupt status with a local variable, switch to using the interrupt core. Signed-off-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/gpio/gpio-nomadik.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-nomadik.c b/drivers/gpio/gpio-nomadik.c index 97e9be94141..0b7ec1258c8 100644 --- a/drivers/gpio/gpio-nomadik.c +++ b/drivers/gpio/gpio-nomadik.c @@ -58,7 +58,6 @@ struct nmk_gpio_chip { u32 rwimsc; u32 fwimsc; u32 slpm; - u32 enabled; u32 pull_up; }; @@ -605,11 +604,6 @@ static int nmk_gpio_irq_maskunmask(struct irq_data *d, bool enable) if (!nmk_chip) return -EINVAL; - if (enable) - nmk_chip->enabled |= bitmask; - else - nmk_chip->enabled &= ~bitmask; - clk_enable(nmk_chip->clk); spin_lock_irqsave(&nmk_gpio_slpm_lock, flags); spin_lock(&nmk_chip->lock); @@ -653,7 +647,7 @@ static int nmk_gpio_irq_set_wake(struct irq_data *d, unsigned int on) spin_lock_irqsave(&nmk_gpio_slpm_lock, flags); spin_lock(&nmk_chip->lock); - if (!(nmk_chip->enabled & bitmask)) + if (irqd_irq_disabled(d)) __nmk_gpio_set_wake(nmk_chip, gpio, on); if (on) @@ -670,7 +664,7 @@ static int nmk_gpio_irq_set_wake(struct irq_data *d, unsigned int on) static int nmk_gpio_irq_set_type(struct irq_data *d, unsigned int type) { - bool enabled; + bool enabled = !irqd_irq_disabled(d); bool wake = irqd_is_wakeup_set(d); int gpio; struct nmk_gpio_chip *nmk_chip; @@ -691,7 +685,6 @@ static int nmk_gpio_irq_set_type(struct irq_data *d, unsigned int type) clk_enable(nmk_chip->clk); spin_lock_irqsave(&nmk_chip->lock, flags); - enabled = !!(nmk_chip->enabled & bitmask); if (enabled) __nmk_gpio_irq_modify(nmk_chip, gpio, NORMAL, false); -- cgit v1.2.3-18-g5258 From 0e44b6eccfcb0b2da65b0e9eddd5d8b4eac5d8df Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Wed, 21 Sep 2011 21:24:04 +0800 Subject: gpio/mxc: add chained_irq_enter/exit() to mx3_gpio_irq_handler() The mx3_gpio_irq_handler() is also called on imx6q which has GIC as the primary interrupt controller. As GIC implements the fasteoi flow control, we need to add chained_irq_enter/exit() to mx3_gpio_irq_handler() for signaling EOI, otherwise system will hang whenever there is a gpio irq triggered. v2: use chained_irq_{enter,exit}() Signed-off-by: Shawn Guo Signed-off-by: Grant Likely --- drivers/gpio/gpio-mxc.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 4340acae3bd..82f7b65baf7 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -30,6 +30,7 @@ #include #include #include +#include enum mxc_gpio_hwtype { IMX1_GPIO, /* runs on i.mx1 */ @@ -232,10 +233,15 @@ static void mx3_gpio_irq_handler(u32 irq, struct irq_desc *desc) { u32 irq_stat; struct mxc_gpio_port *port = irq_get_handler_data(irq); + struct irq_chip *chip = irq_get_chip(irq); + + chained_irq_enter(chip, desc); irq_stat = readl(port->base + GPIO_ISR) & readl(port->base + GPIO_IMR); mxc_gpio_irq_handler(port, irq_stat); + + chained_irq_exit(chip, desc); } /* MX2 has one interrupt *for all* gpio ports */ -- cgit v1.2.3-18-g5258 From 45d198c4cf3a9a751b734eb32426b6de4631ef2e Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Fri, 5 Aug 2011 13:04:20 +0900 Subject: gpio-ml-ioh: Delete unnecessary code This register restore processing is unnecessary in suspend processing. (The restore processing is already in resume processing) Signed-off-by: Tomoya MORINAGA Signed-off-by: Grant Likely --- drivers/gpio/gpio-ml-ioh.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index a9016f56ed7..655e55c55e0 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -284,7 +284,6 @@ static int ioh_gpio_suspend(struct pci_dev *pdev, pm_message_t state) struct ioh_gpio *chip = pci_get_drvdata(pdev); ioh_gpio_save_reg_conf(chip); - ioh_gpio_restore_reg_conf(chip); ret = pci_save_state(pdev); if (ret) { -- cgit v1.2.3-18-g5258 From 54be566317b6aece2389a95bb19ea209af9359be Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Fri, 5 Aug 2011 13:04:21 +0900 Subject: gpio-ml-ioh: Support interrupt function Signed-off-by: Tomoya MORINAGA Signed-off-by: Grant Likely --- drivers/gpio/Kconfig | 1 + drivers/gpio/gpio-ml-ioh.c | 209 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 207 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index d539efd96d4..04499c19e98 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -402,6 +402,7 @@ config GPIO_PCH config GPIO_ML_IOH tristate "OKI SEMICONDUCTOR ML7213 IOH GPIO support" depends on PCI + select GENERIC_IRQ_CHIP help ML7213 is companion chip for Intel Atom E6xx series. This driver can be used for OKI SEMICONDUCTOR ML7213 IOH(Input/Output diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index 655e55c55e0..4fab37bf564 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -18,6 +18,17 @@ #include #include #include +#include +#include + +#define IOH_EDGE_FALLING 0 +#define IOH_EDGE_RISING BIT(0) +#define IOH_LEVEL_L BIT(1) +#define IOH_LEVEL_H (BIT(0) | BIT(1)) +#define IOH_EDGE_BOTH BIT(2) +#define IOH_IM_MASK (BIT(0) | BIT(1) | BIT(2)) + +#define IOH_IRQ_BASE 0 #define PCI_VENDOR_ID_ROHM 0x10DB @@ -46,12 +57,20 @@ struct ioh_regs { /** * struct ioh_gpio_reg_data - The register store data. + * @ien_reg To store contents of interrupt enable register. + * @imask_reg: To store contents of interrupt mask regist * @po_reg: To store contents of PO register. * @pm_reg: To store contents of PM register. + * @im0_reg: To store contents of interrupt mode regist0 + * @im1_reg: To store contents of interrupt mode regist1 */ struct ioh_gpio_reg_data { + u32 ien_reg; + u32 imask_reg; u32 po_reg; u32 pm_reg; + u32 im0_reg; + u32 im1_reg; }; /** @@ -63,6 +82,9 @@ struct ioh_gpio_reg_data { * @ioh_gpio_reg: Memory mapped Register data is saved here * when suspend. * @ch: Indicate GPIO channel + * @irq_base: Save base of IRQ number for interrupt + * @spinlock: Used for register access protection in + * interrupt context ioh_irq_type and PM; */ struct ioh_gpio { void __iomem *base; @@ -72,6 +94,8 @@ struct ioh_gpio { struct ioh_gpio_reg_data ioh_gpio_reg; struct mutex lock; int ch; + int irq_base; + spinlock_t spinlock; }; static const int num_ports[] = {6, 12, 16, 16, 15, 16, 16, 12}; @@ -147,6 +171,10 @@ static void ioh_gpio_save_reg_conf(struct ioh_gpio *chip) { chip->ioh_gpio_reg.po_reg = ioread32(&chip->reg->regs[chip->ch].po); chip->ioh_gpio_reg.pm_reg = ioread32(&chip->reg->regs[chip->ch].pm); + chip->ioh_gpio_reg.ien_reg = ioread32(&chip->reg->regs[chip->ch].ien); + chip->ioh_gpio_reg.imask_reg = ioread32(&chip->reg->regs[chip->ch].imask); + chip->ioh_gpio_reg.im0_reg = ioread32(&chip->reg->regs[chip->ch].im_0); + chip->ioh_gpio_reg.im1_reg = ioread32(&chip->reg->regs[chip->ch].im_1); } /* @@ -154,13 +182,21 @@ static void ioh_gpio_save_reg_conf(struct ioh_gpio *chip) */ static void ioh_gpio_restore_reg_conf(struct ioh_gpio *chip) { - /* to store contents of PO register */ iowrite32(chip->ioh_gpio_reg.po_reg, &chip->reg->regs[chip->ch].po); - /* to store contents of PM register */ iowrite32(chip->ioh_gpio_reg.pm_reg, &chip->reg->regs[chip->ch].pm); + iowrite32(chip->ioh_gpio_reg.ien_reg, &chip->reg->regs[chip->ch].ien); + iowrite32(chip->ioh_gpio_reg.imask_reg, &chip->reg->regs[chip->ch].imask); + iowrite32(chip->ioh_gpio_reg.im0_reg, &chip->reg->regs[chip->ch].im_0); + iowrite32(chip->ioh_gpio_reg.im1_reg, &chip->reg->regs[chip->ch].im_1); } #endif +static int ioh_gpio_to_irq(struct gpio_chip *gpio, unsigned offset) +{ + struct ioh_gpio *chip = container_of(gpio, struct ioh_gpio, gpio); + return chip->irq_base + offset; +} + static void ioh_gpio_setup(struct ioh_gpio *chip, int num_port) { struct gpio_chip *gpio = &chip->gpio; @@ -175,16 +211,148 @@ static void ioh_gpio_setup(struct ioh_gpio *chip, int num_port) gpio->base = -1; gpio->ngpio = num_port; gpio->can_sleep = 0; + gpio->to_irq = ioh_gpio_to_irq; +} + +static int ioh_irq_type(struct irq_data *d, unsigned int type) +{ + u32 im; + u32 *im_reg; + u32 ien; + u32 im_pos; + int ch; + unsigned long flags; + u32 val; + int irq = d->irq; + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct ioh_gpio *chip = gc->private; + + ch = irq - chip->irq_base; + if (irq <= chip->irq_base + 7) { + im_reg = &chip->reg->regs[chip->ch].im_0; + im_pos = ch; + } else { + im_reg = &chip->reg->regs[chip->ch].im_1; + im_pos = ch - 8; + } + dev_dbg(chip->dev, "%s:irq=%d type=%d ch=%d pos=%d type=%d\n", + __func__, irq, type, ch, im_pos, type); + + spin_lock_irqsave(&chip->spinlock, flags); + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + val = IOH_EDGE_RISING; + break; + case IRQ_TYPE_EDGE_FALLING: + val = IOH_EDGE_FALLING; + break; + case IRQ_TYPE_EDGE_BOTH: + val = IOH_EDGE_BOTH; + break; + case IRQ_TYPE_LEVEL_HIGH: + val = IOH_LEVEL_H; + break; + case IRQ_TYPE_LEVEL_LOW: + val = IOH_LEVEL_L; + break; + case IRQ_TYPE_PROBE: + goto end; + default: + dev_warn(chip->dev, "%s: unknown type(%dd)", + __func__, type); + goto end; + } + + /* Set interrupt mode */ + im = ioread32(im_reg) & ~(IOH_IM_MASK << (im_pos * 4)); + iowrite32(im | (val << (im_pos * 4)), im_reg); + + /* iclr */ + iowrite32(BIT(ch), &chip->reg->regs[chip->ch].iclr); + + /* IMASKCLR */ + iowrite32(BIT(ch), &chip->reg->regs[chip->ch].imaskclr); + + /* Enable interrupt */ + ien = ioread32(&chip->reg->regs[chip->ch].ien); + iowrite32(ien | BIT(ch), &chip->reg->regs[chip->ch].ien); +end: + spin_unlock_irqrestore(&chip->spinlock, flags); + + return 0; +} + +static void ioh_irq_unmask(struct irq_data *d) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct ioh_gpio *chip = gc->private; + + iowrite32(1 << (d->irq - chip->irq_base), + &chip->reg->regs[chip->ch].imaskclr); +} + +static void ioh_irq_mask(struct irq_data *d) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct ioh_gpio *chip = gc->private; + + iowrite32(1 << (d->irq - chip->irq_base), + &chip->reg->regs[chip->ch].imask); +} + +static irqreturn_t ioh_gpio_handler(int irq, void *dev_id) +{ + struct ioh_gpio *chip = dev_id; + u32 reg_val; + int i, j; + int ret = IRQ_NONE; + + for (i = 0; i < 8; i++) { + reg_val = ioread32(&chip->reg->regs[i].istatus); + for (j = 0; j < num_ports[i]; j++) { + if (reg_val & BIT(j)) { + dev_dbg(chip->dev, + "%s:[%d]:irq=%d status=0x%x\n", + __func__, j, irq, reg_val); + iowrite32(BIT(j), + &chip->reg->regs[chip->ch].iclr); + generic_handle_irq(chip->irq_base + j); + ret = IRQ_HANDLED; + } + } + } + return ret; +} + +static __devinit void ioh_gpio_alloc_generic_chip(struct ioh_gpio *chip, + unsigned int irq_start, unsigned int num) +{ + struct irq_chip_generic *gc; + struct irq_chip_type *ct; + + gc = irq_alloc_generic_chip("ioh_gpio", 1, irq_start, chip->base, + handle_simple_irq); + gc->private = chip; + ct = gc->chip_types; + + ct->chip.irq_mask = ioh_irq_mask; + ct->chip.irq_unmask = ioh_irq_unmask; + ct->chip.irq_set_type = ioh_irq_type; + + irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE, + IRQ_NOREQUEST | IRQ_NOPROBE, 0); } static int __devinit ioh_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id) { int ret; - int i; + int i, j; struct ioh_gpio *chip; void __iomem *base; void __iomem *chip_save; + int irq_base; ret = pci_enable_device(pdev); if (ret) { @@ -228,10 +396,41 @@ static int __devinit ioh_gpio_probe(struct pci_dev *pdev, } chip = chip_save; + for (j = 0; j < 8; j++, chip++) { + irq_base = irq_alloc_descs(-1, IOH_IRQ_BASE, num_ports[j], + GFP_KERNEL); + if (irq_base < 0) { + dev_warn(&pdev->dev, + "ml_ioh_gpio: Failed to get IRQ base num\n"); + chip->irq_base = -1; + goto err_irq_alloc_descs; + } + chip->irq_base = irq_base; + ioh_gpio_alloc_generic_chip(chip, irq_base, num_ports[j]); + } + + chip = chip_save; + ret = request_irq(pdev->irq, ioh_gpio_handler, + IRQF_SHARED, KBUILD_MODNAME, chip); + if (ret != 0) { + dev_err(&pdev->dev, + "%s request_irq failed\n", __func__); + goto err_request_irq; + } + pci_set_drvdata(pdev, chip); return 0; +err_request_irq: + chip = chip_save; +err_irq_alloc_descs: + while (--j >= 0) { + chip--; + irq_free_descs(chip->irq_base, num_ports[j]); + } + + chip = chip_save; err_gpiochip_add: while (--i >= 0) { chip--; @@ -264,7 +463,11 @@ static void __devexit ioh_gpio_remove(struct pci_dev *pdev) void __iomem *chip_save; chip_save = chip; + + free_irq(pdev->irq, chip); + for (i = 0; i < 8; i++, chip++) { + irq_free_descs(chip->irq_base, num_ports[i]); err = gpiochip_remove(&chip->gpio); if (err) dev_err(&pdev->dev, "Failed gpiochip_remove\n"); -- cgit v1.2.3-18-g5258 From b490fa0bf86edbc06562024cbace5e84f0e2cf0e Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Fri, 5 Aug 2011 13:04:22 +0900 Subject: gpio-ml-ioh: Fix suspend/resume issue Currently, some registers are not saved in case changing to suspend state. This patch fixes the issue. Signed-off-by: Tomoya MORINAGA Signed-off-by: Grant Likely --- drivers/gpio/gpio-ml-ioh.c | 60 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index 4fab37bf564..274fd4d0792 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -63,6 +63,7 @@ struct ioh_regs { * @pm_reg: To store contents of PM register. * @im0_reg: To store contents of interrupt mode regist0 * @im1_reg: To store contents of interrupt mode regist1 + * @use_sel_reg: To store contents of GPIO_USE_SEL0~3 */ struct ioh_gpio_reg_data { u32 ien_reg; @@ -71,6 +72,7 @@ struct ioh_gpio_reg_data { u32 pm_reg; u32 im0_reg; u32 im1_reg; + u32 use_sel_reg; }; /** @@ -81,6 +83,7 @@ struct ioh_gpio_reg_data { * @gpio: Data for GPIO infrastructure. * @ioh_gpio_reg: Memory mapped Register data is saved here * when suspend. + * @gpio_use_sel: Save GPIO_USE_SEL1~4 register for PM * @ch: Indicate GPIO channel * @irq_base: Save base of IRQ number for interrupt * @spinlock: Used for register access protection in @@ -92,6 +95,7 @@ struct ioh_gpio { struct device *dev; struct gpio_chip gpio; struct ioh_gpio_reg_data ioh_gpio_reg; + u32 gpio_use_sel; struct mutex lock; int ch; int irq_base; @@ -169,12 +173,25 @@ static int ioh_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) */ static void ioh_gpio_save_reg_conf(struct ioh_gpio *chip) { - chip->ioh_gpio_reg.po_reg = ioread32(&chip->reg->regs[chip->ch].po); - chip->ioh_gpio_reg.pm_reg = ioread32(&chip->reg->regs[chip->ch].pm); - chip->ioh_gpio_reg.ien_reg = ioread32(&chip->reg->regs[chip->ch].ien); - chip->ioh_gpio_reg.imask_reg = ioread32(&chip->reg->regs[chip->ch].imask); - chip->ioh_gpio_reg.im0_reg = ioread32(&chip->reg->regs[chip->ch].im_0); - chip->ioh_gpio_reg.im1_reg = ioread32(&chip->reg->regs[chip->ch].im_1); + int i; + + for (i = 0; i < 8; i ++, chip++) { + chip->ioh_gpio_reg.po_reg = + ioread32(&chip->reg->regs[chip->ch].po); + chip->ioh_gpio_reg.pm_reg = + ioread32(&chip->reg->regs[chip->ch].pm); + chip->ioh_gpio_reg.ien_reg = + ioread32(&chip->reg->regs[chip->ch].ien); + chip->ioh_gpio_reg.imask_reg = + ioread32(&chip->reg->regs[chip->ch].imask); + chip->ioh_gpio_reg.im0_reg = + ioread32(&chip->reg->regs[chip->ch].im_0); + chip->ioh_gpio_reg.im1_reg = + ioread32(&chip->reg->regs[chip->ch].im_1); + if (i < 4) + chip->ioh_gpio_reg.use_sel_reg = + ioread32(&chip->reg->ioh_sel_reg[i]); + } } /* @@ -182,12 +199,25 @@ static void ioh_gpio_save_reg_conf(struct ioh_gpio *chip) */ static void ioh_gpio_restore_reg_conf(struct ioh_gpio *chip) { - iowrite32(chip->ioh_gpio_reg.po_reg, &chip->reg->regs[chip->ch].po); - iowrite32(chip->ioh_gpio_reg.pm_reg, &chip->reg->regs[chip->ch].pm); - iowrite32(chip->ioh_gpio_reg.ien_reg, &chip->reg->regs[chip->ch].ien); - iowrite32(chip->ioh_gpio_reg.imask_reg, &chip->reg->regs[chip->ch].imask); - iowrite32(chip->ioh_gpio_reg.im0_reg, &chip->reg->regs[chip->ch].im_0); - iowrite32(chip->ioh_gpio_reg.im1_reg, &chip->reg->regs[chip->ch].im_1); + int i; + + for (i = 0; i < 8; i ++, chip++) { + iowrite32(chip->ioh_gpio_reg.po_reg, + &chip->reg->regs[chip->ch].po); + iowrite32(chip->ioh_gpio_reg.pm_reg, + &chip->reg->regs[chip->ch].pm); + iowrite32(chip->ioh_gpio_reg.ien_reg, + &chip->reg->regs[chip->ch].ien); + iowrite32(chip->ioh_gpio_reg.imask_reg, + &chip->reg->regs[chip->ch].imask); + iowrite32(chip->ioh_gpio_reg.im0_reg, + &chip->reg->regs[chip->ch].im_0); + iowrite32(chip->ioh_gpio_reg.im1_reg, + &chip->reg->regs[chip->ch].im_1); + if (i < 4) + iowrite32(chip->ioh_gpio_reg.use_sel_reg, + &chip->reg->ioh_sel_reg[i]); + } } #endif @@ -485,8 +515,11 @@ static int ioh_gpio_suspend(struct pci_dev *pdev, pm_message_t state) { s32 ret; struct ioh_gpio *chip = pci_get_drvdata(pdev); + unsigned long flags; + spin_lock_irqsave(&chip->spinlock, flags); ioh_gpio_save_reg_conf(chip); + spin_unlock_irqrestore(&chip->spinlock, flags); ret = pci_save_state(pdev); if (ret) { @@ -506,6 +539,7 @@ static int ioh_gpio_resume(struct pci_dev *pdev) { s32 ret; struct ioh_gpio *chip = pci_get_drvdata(pdev); + unsigned long flags; ret = pci_enable_wake(pdev, PCI_D0, 0); @@ -517,9 +551,11 @@ static int ioh_gpio_resume(struct pci_dev *pdev) } pci_restore_state(pdev); + spin_lock_irqsave(&chip->spinlock, flags); iowrite32(0x01, &chip->reg->srst); iowrite32(0x00, &chip->reg->srst); ioh_gpio_restore_reg_conf(chip); + spin_unlock_irqrestore(&chip->spinlock, flags); return 0; } -- cgit v1.2.3-18-g5258 From 829e8256f139a9665f861d7ba880ed90abd75b65 Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Thu, 21 Jul 2011 09:19:54 +0900 Subject: gpio-pch: Delete invalid "restore" code in suspend() Signed-off-by: Tomoya MORINAGA Signed-off-by: Grant Likely --- drivers/gpio/gpio-pch.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index 36919e77c49..ca9c7b051e0 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -241,7 +241,6 @@ static int pch_gpio_suspend(struct pci_dev *pdev, pm_message_t state) struct pch_gpio *chip = pci_get_drvdata(pdev); pch_gpio_save_reg_conf(chip); - pch_gpio_restore_reg_conf(chip); ret = pci_save_state(pdev); if (ret) { -- cgit v1.2.3-18-g5258 From d568a6814fde60f5ab6b0c29b6261ff1899da443 Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Thu, 21 Jul 2011 09:19:55 +0900 Subject: gpio-pch: add spinlock in suspend/resume processing Signed-off-by: Tomoya MORINAGA Signed-off-by: Grant Likely --- drivers/gpio/gpio-pch.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index ca9c7b051e0..252bddbd3f4 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -55,6 +55,9 @@ struct pch_gpio_reg_data { * @gpio: Data for GPIO infrastructure. * @pch_gpio_reg: Memory mapped Register data is saved here * when suspend. + * @spinlock: Used for register access protection in + * interrupt context pch_irq_mask, + * pch_irq_unmask and pch_irq_type; */ struct pch_gpio { void __iomem *base; @@ -63,6 +66,7 @@ struct pch_gpio { struct gpio_chip gpio; struct pch_gpio_reg_data pch_gpio_reg; struct mutex lock; + spinlock_t spinlock; }; static void pch_gpio_set(struct gpio_chip *gpio, unsigned nr, int val) @@ -239,8 +243,11 @@ static int pch_gpio_suspend(struct pci_dev *pdev, pm_message_t state) { s32 ret; struct pch_gpio *chip = pci_get_drvdata(pdev); + unsigned long flags; + spin_lock_irqsave(&chip->spinlock, flags); pch_gpio_save_reg_conf(chip); + spin_unlock_irqrestore(&chip->spinlock, flags); ret = pci_save_state(pdev); if (ret) { @@ -260,6 +267,7 @@ static int pch_gpio_resume(struct pci_dev *pdev) { s32 ret; struct pch_gpio *chip = pci_get_drvdata(pdev); + unsigned long flags; ret = pci_enable_wake(pdev, PCI_D0, 0); @@ -271,9 +279,11 @@ static int pch_gpio_resume(struct pci_dev *pdev) } pci_restore_state(pdev); + spin_lock_irqsave(&chip->spinlock, flags); iowrite32(0x01, &chip->reg->reset); iowrite32(0x00, &chip->reg->reset); pch_gpio_restore_reg_conf(chip); + spin_unlock_irqrestore(&chip->spinlock, flags); return 0; } -- cgit v1.2.3-18-g5258 From c3520a1a84f13becf7489ddee4571eaccf108934 Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Thu, 21 Jul 2011 09:19:56 +0900 Subject: gpio-pch: support ML7223 IOH n-Bus Signed-off-by: Tomoya MORINAGA Signed-off-by: Grant Likely --- drivers/gpio/gpio-pch.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index 252bddbd3f4..d548069d391 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -296,6 +296,7 @@ static int pch_gpio_resume(struct pci_dev *pdev) static DEFINE_PCI_DEVICE_TABLE(pch_gpio_pcidev_id) = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8803) }, { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8014) }, + { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8043) }, { 0, } }; MODULE_DEVICE_TABLE(pci, pch_gpio_pcidev_id); -- cgit v1.2.3-18-g5258 From d4260e6dddfe642ab50ec6398aeac794a6aff151 Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Thu, 21 Jul 2011 09:19:57 +0900 Subject: gpio-pch: modify gpio_nums and mask Currently, the number of GPIO pins is set fixed value(=12). Also PIN MASK is set as '0xfff'. However the pins differs by IOH. This patch sets the value correctly. Signed-off-by: Tomoya MORINAGA Signed-off-by: Grant Likely --- drivers/gpio/gpio-pch.c | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index d548069d391..4ac69bd7ad4 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -18,9 +18,6 @@ #include #include -#define PCH_GPIO_ALL_PINS 0xfff /* Mask for GPIO pins 0 to 11 */ -#define GPIO_NUM_PINS 12 /* Specifies number of GPIO PINS GPIO0-GPIO11 */ - struct pch_regs { u32 ien; u32 istatus; @@ -37,6 +34,19 @@ struct pch_regs { u32 reset; }; +enum pch_type_t { + INTEL_EG20T_PCH, + OKISEMI_ML7223m_IOH, /* OKISEMI ML7223 IOH PCIe Bus-m */ + OKISEMI_ML7223n_IOH /* OKISEMI ML7223 IOH PCIe Bus-n */ +}; + +/* Specifies number of GPIO PINS */ +static int gpio_pins[] = { + [INTEL_EG20T_PCH] = 12, + [OKISEMI_ML7223m_IOH] = 8, + [OKISEMI_ML7223n_IOH] = 8, +}; + /** * struct pch_gpio_reg_data - The register store data. * @po_reg: To store contents of PO register. @@ -55,6 +65,7 @@ struct pch_gpio_reg_data { * @gpio: Data for GPIO infrastructure. * @pch_gpio_reg: Memory mapped Register data is saved here * when suspend. + * @ioh: IOH ID * @spinlock: Used for register access protection in * interrupt context pch_irq_mask, * pch_irq_unmask and pch_irq_type; @@ -66,6 +77,7 @@ struct pch_gpio { struct gpio_chip gpio; struct pch_gpio_reg_data pch_gpio_reg; struct mutex lock; + enum pch_type_t ioh; spinlock_t spinlock; }; @@ -100,7 +112,7 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, u32 reg_val; mutex_lock(&chip->lock); - pm = ioread32(&chip->reg->pm) & PCH_GPIO_ALL_PINS; + pm = ioread32(&chip->reg->pm) & ((1 << gpio_pins[chip->ioh]) - 1); pm |= (1 << nr); iowrite32(pm, &chip->reg->pm); @@ -122,7 +134,7 @@ static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) u32 pm; mutex_lock(&chip->lock); - pm = ioread32(&chip->reg->pm) & PCH_GPIO_ALL_PINS; /*bits 0-11*/ + pm = ioread32(&chip->reg->pm) & ((1 << gpio_pins[chip->ioh]) - 1); pm &= ~(1 << nr); iowrite32(pm, &chip->reg->pm); mutex_unlock(&chip->lock); @@ -162,7 +174,7 @@ static void pch_gpio_setup(struct pch_gpio *chip) gpio->set = pch_gpio_set; gpio->dbg_show = NULL; gpio->base = -1; - gpio->ngpio = GPIO_NUM_PINS; + gpio->ngpio = gpio_pins[chip->ioh]; gpio->can_sleep = 0; } @@ -196,6 +208,13 @@ static int __devinit pch_gpio_probe(struct pci_dev *pdev, goto err_iomap; } + if (pdev->device == 0x8803) + chip->ioh = INTEL_EG20T_PCH; + else if (pdev->device == 0x8014) + chip->ioh = OKISEMI_ML7223m_IOH; + else if (pdev->device == 0x8043) + chip->ioh = OKISEMI_ML7223n_IOH; + chip->reg = chip->base; pci_set_drvdata(pdev, chip); mutex_init(&chip->lock); -- cgit v1.2.3-18-g5258 From e98bed7f0fa847492db8316db4605f2681f39868 Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Thu, 21 Jul 2011 09:19:58 +0900 Subject: gpio-pch: Save register value in suspend() Currently, when suspend is occurred, register im0/1 and gpio_use_sel are not saved. This patch modifies so that register im0/1 and gpio_use_sel are saved. Signed-off-by: Tomoya MORINAGA Signed-off-by: Grant Likely --- drivers/gpio/gpio-pch.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index 4ac69bd7ad4..7f773afeb34 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -30,7 +30,8 @@ struct pch_regs { u32 pm; u32 im0; u32 im1; - u32 reserved[4]; + u32 reserved[3]; + u32 gpio_use_sel; u32 reset; }; @@ -51,10 +52,17 @@ static int gpio_pins[] = { * struct pch_gpio_reg_data - The register store data. * @po_reg: To store contents of PO register. * @pm_reg: To store contents of PM register. + * @im0_reg: To store contents of IM0 register. + * @im1_reg: To store contents of IM1 register. + * @gpio_use_sel_reg : To store contents of GPIO_USE_SEL register. + * (Only ML7223 Bus-n) */ struct pch_gpio_reg_data { u32 po_reg; u32 pm_reg; + u32 im0_reg; + u32 im1_reg; + u32 gpio_use_sel_reg; }; /** @@ -149,6 +157,12 @@ static void pch_gpio_save_reg_conf(struct pch_gpio *chip) { chip->pch_gpio_reg.po_reg = ioread32(&chip->reg->po); chip->pch_gpio_reg.pm_reg = ioread32(&chip->reg->pm); + chip->pch_gpio_reg.im0_reg = ioread32(&chip->reg->im0); + if (chip->ioh == INTEL_EG20T_PCH) + chip->pch_gpio_reg.im1_reg = ioread32(&chip->reg->im1); + if (chip->ioh == OKISEMI_ML7223n_IOH) + chip->pch_gpio_reg.gpio_use_sel_reg =\ + ioread32(&chip->reg->gpio_use_sel); } /* @@ -160,6 +174,12 @@ static void pch_gpio_restore_reg_conf(struct pch_gpio *chip) iowrite32(chip->pch_gpio_reg.po_reg, &chip->reg->po); /* to store contents of PM register */ iowrite32(chip->pch_gpio_reg.pm_reg, &chip->reg->pm); + iowrite32(chip->pch_gpio_reg.im0_reg, &chip->reg->im0); + if (chip->ioh == INTEL_EG20T_PCH) + iowrite32(chip->pch_gpio_reg.im1_reg, &chip->reg->im1); + if (chip->ioh == OKISEMI_ML7223n_IOH) + iowrite32(chip->pch_gpio_reg.gpio_use_sel_reg, + &chip->reg->gpio_use_sel); } static void pch_gpio_setup(struct pch_gpio *chip) -- cgit v1.2.3-18-g5258 From 38eb18a6f92da886fc1af509d25e8f7a49e23d9a Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Thu, 21 Jul 2011 09:19:59 +0900 Subject: gpio-pch: Support interrupt function Signed-off-by: Tomoya MORINAGA Signed-off-by: Grant Likely --- drivers/gpio/Kconfig | 1 + drivers/gpio/gpio-pch.c | 187 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 188 insertions(+) (limited to 'drivers') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 04499c19e98..de5fea60b38 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -388,6 +388,7 @@ config GPIO_LANGWELL config GPIO_PCH tristate "Intel EG20T PCH / OKI SEMICONDUCTOR ML7223 IOH GPIO" depends on PCI && X86 + select GENERIC_IRQ_CHIP help This driver is for PCH(Platform controller Hub) GPIO of Intel Topcliff which is an IOH(Input/Output Hub) for x86 embedded processor. diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index 7f773afeb34..46b5209878f 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -17,6 +17,17 @@ #include #include #include +#include +#include + +#define PCH_EDGE_FALLING 0 +#define PCH_EDGE_RISING BIT(0) +#define PCH_LEVEL_L BIT(1) +#define PCH_LEVEL_H (BIT(0) | BIT(1)) +#define PCH_EDGE_BOTH BIT(2) +#define PCH_IM_MASK (BIT(0) | BIT(1) | BIT(2)) + +#define PCH_IRQ_BASE 24 struct pch_regs { u32 ien; @@ -50,6 +61,8 @@ static int gpio_pins[] = { /** * struct pch_gpio_reg_data - The register store data. + * @ien_reg: To store contents of IEN register. + * @imask_reg: To store contents of IMASK register. * @po_reg: To store contents of PO register. * @pm_reg: To store contents of PM register. * @im0_reg: To store contents of IM0 register. @@ -58,6 +71,8 @@ static int gpio_pins[] = { * (Only ML7223 Bus-n) */ struct pch_gpio_reg_data { + u32 ien_reg; + u32 imask_reg; u32 po_reg; u32 pm_reg; u32 im0_reg; @@ -73,6 +88,8 @@ struct pch_gpio_reg_data { * @gpio: Data for GPIO infrastructure. * @pch_gpio_reg: Memory mapped Register data is saved here * when suspend. + * @lock: Used for register access protection + * @irq_base: Save base of IRQ number for interrupt * @ioh: IOH ID * @spinlock: Used for register access protection in * interrupt context pch_irq_mask, @@ -85,6 +102,7 @@ struct pch_gpio { struct gpio_chip gpio; struct pch_gpio_reg_data pch_gpio_reg; struct mutex lock; + int irq_base; enum pch_type_t ioh; spinlock_t spinlock; }; @@ -155,6 +173,8 @@ static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) */ static void pch_gpio_save_reg_conf(struct pch_gpio *chip) { + chip->pch_gpio_reg.ien_reg = ioread32(&chip->reg->ien); + chip->pch_gpio_reg.imask_reg = ioread32(&chip->reg->imask); chip->pch_gpio_reg.po_reg = ioread32(&chip->reg->po); chip->pch_gpio_reg.pm_reg = ioread32(&chip->reg->pm); chip->pch_gpio_reg.im0_reg = ioread32(&chip->reg->im0); @@ -170,6 +190,8 @@ static void pch_gpio_save_reg_conf(struct pch_gpio *chip) */ static void pch_gpio_restore_reg_conf(struct pch_gpio *chip) { + iowrite32(chip->pch_gpio_reg.ien_reg, &chip->reg->ien); + iowrite32(chip->pch_gpio_reg.imask_reg, &chip->reg->imask); /* to store contents of PO register */ iowrite32(chip->pch_gpio_reg.po_reg, &chip->reg->po); /* to store contents of PM register */ @@ -182,6 +204,12 @@ static void pch_gpio_restore_reg_conf(struct pch_gpio *chip) &chip->reg->gpio_use_sel); } +static int pch_gpio_to_irq(struct gpio_chip *gpio, unsigned offset) +{ + struct pch_gpio *chip = container_of(gpio, struct pch_gpio, gpio); + return chip->irq_base + offset; +} + static void pch_gpio_setup(struct pch_gpio *chip) { struct gpio_chip *gpio = &chip->gpio; @@ -196,6 +224,130 @@ static void pch_gpio_setup(struct pch_gpio *chip) gpio->base = -1; gpio->ngpio = gpio_pins[chip->ioh]; gpio->can_sleep = 0; + gpio->to_irq = pch_gpio_to_irq; +} + +static int pch_irq_type(struct irq_data *d, unsigned int type) +{ + u32 im; + u32 *im_reg; + u32 ien; + u32 im_pos; + int ch; + unsigned long flags; + u32 val; + int irq = d->irq; + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct pch_gpio *chip = gc->private; + + ch = irq - chip->irq_base; + if (irq <= chip->irq_base + 7) { + im_reg = &chip->reg->im0; + im_pos = ch; + } else { + im_reg = &chip->reg->im1; + im_pos = ch - 8; + } + dev_dbg(chip->dev, "%s:irq=%d type=%d ch=%d pos=%d\n", + __func__, irq, type, ch, im_pos); + + spin_lock_irqsave(&chip->spinlock, flags); + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + val = PCH_EDGE_RISING; + break; + case IRQ_TYPE_EDGE_FALLING: + val = PCH_EDGE_FALLING; + break; + case IRQ_TYPE_EDGE_BOTH: + val = PCH_EDGE_BOTH; + break; + case IRQ_TYPE_LEVEL_HIGH: + val = PCH_LEVEL_H; + break; + case IRQ_TYPE_LEVEL_LOW: + val = PCH_LEVEL_L; + break; + case IRQ_TYPE_PROBE: + goto end; + default: + dev_warn(chip->dev, "%s: unknown type(%dd)", + __func__, type); + goto end; + } + + /* Set interrupt mode */ + im = ioread32(im_reg) & ~(PCH_IM_MASK << (im_pos * 4)); + iowrite32(im | (val << (im_pos * 4)), im_reg); + + /* iclr */ + iowrite32(BIT(ch), &chip->reg->iclr); + + /* IMASKCLR */ + iowrite32(BIT(ch), &chip->reg->imaskclr); + + /* Enable interrupt */ + ien = ioread32(&chip->reg->ien); + iowrite32(ien | BIT(ch), &chip->reg->ien); +end: + spin_unlock_irqrestore(&chip->spinlock, flags); + + return 0; +} + +static void pch_irq_unmask(struct irq_data *d) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct pch_gpio *chip = gc->private; + + iowrite32(1 << (d->irq - chip->irq_base), &chip->reg->imaskclr); +} + +static void pch_irq_mask(struct irq_data *d) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct pch_gpio *chip = gc->private; + + iowrite32(1 << (d->irq - chip->irq_base), &chip->reg->imask); +} + +static irqreturn_t pch_gpio_handler(int irq, void *dev_id) +{ + struct pch_gpio *chip = dev_id; + u32 reg_val = ioread32(&chip->reg->istatus); + int i; + int ret = IRQ_NONE; + + for (i = 0; i < gpio_pins[chip->ioh]; i++) { + if (reg_val & BIT(i)) { + dev_dbg(chip->dev, "%s:[%d]:irq=%d status=0x%x\n", + __func__, i, irq, reg_val); + iowrite32(BIT(i), &chip->reg->iclr); + generic_handle_irq(chip->irq_base + i); + ret = IRQ_HANDLED; + } + } + return ret; +} + +static __devinit void pch_gpio_alloc_generic_chip(struct pch_gpio *chip, + unsigned int irq_start, unsigned int num) +{ + struct irq_chip_generic *gc; + struct irq_chip_type *ct; + + gc = irq_alloc_generic_chip("pch_gpio", 1, irq_start, chip->base, + handle_simple_irq); + gc->private = chip; + ct = gc->chip_types; + + ct->chip.irq_mask = pch_irq_mask; + ct->chip.irq_unmask = pch_irq_unmask; + ct->chip.irq_set_type = pch_irq_type; + + irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE, + IRQ_NOREQUEST | IRQ_NOPROBE, 0); } static int __devinit pch_gpio_probe(struct pci_dev *pdev, @@ -203,6 +355,7 @@ static int __devinit pch_gpio_probe(struct pci_dev *pdev, { s32 ret; struct pch_gpio *chip; + int irq_base; chip = kzalloc(sizeof(*chip), GFP_KERNEL); if (chip == NULL) @@ -245,8 +398,36 @@ static int __devinit pch_gpio_probe(struct pci_dev *pdev, goto err_gpiochip_add; } + irq_base = irq_alloc_descs(-1, 0, gpio_pins[chip->ioh], GFP_KERNEL); + if (irq_base < 0) { + dev_warn(&pdev->dev, "PCH gpio: Failed to get IRQ base num\n"); + chip->irq_base = -1; + goto end; + } + chip->irq_base = irq_base; + + ret = request_irq(pdev->irq, pch_gpio_handler, + IRQF_SHARED, KBUILD_MODNAME, chip); + if (ret != 0) { + dev_err(&pdev->dev, + "%s request_irq failed\n", __func__); + goto err_request_irq; + } + + pch_gpio_alloc_generic_chip(chip, irq_base, gpio_pins[chip->ioh]); + + /* Initialize interrupt ien register */ + iowrite32(0, &chip->reg->ien); +end: return 0; +err_request_irq: + irq_free_descs(irq_base, gpio_pins[chip->ioh]); + + ret = gpiochip_remove(&chip->gpio); + if (ret) + dev_err(&pdev->dev, "%s gpiochip_remove failed\n", __func__); + err_gpiochip_add: pci_iounmap(pdev, chip->base); @@ -267,6 +448,12 @@ static void __devexit pch_gpio_remove(struct pci_dev *pdev) int err; struct pch_gpio *chip = pci_get_drvdata(pdev); + if (chip->irq_base != -1) { + free_irq(pdev->irq, chip); + + irq_free_descs(chip->irq_base, gpio_pins[chip->ioh]); + } + err = gpiochip_remove(&chip->gpio); if (err) dev_err(&pdev->dev, "Failed gpiochip_remove\n"); -- cgit v1.2.3-18-g5258 From 8c0f7b10f1028d0bc78486affe2ccf39cdf45282 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 3 Oct 2011 14:36:07 +0300 Subject: gpio: langwell: ensure alternate function is cleared Alternate function must be zero for the pin to act as a GPIO. Signed-off-by: Adrian Hunter Signed-off-by: Alan Cox Signed-off-by: Grant Likely --- drivers/gpio/gpio-langwell.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'drivers') diff --git a/drivers/gpio/gpio-langwell.c b/drivers/gpio/gpio-langwell.c index d2eb57c60e0..00692e89ef8 100644 --- a/drivers/gpio/gpio-langwell.c +++ b/drivers/gpio/gpio-langwell.c @@ -59,6 +59,7 @@ enum GPIO_REG { GRER, /* rising edge detect */ GFER, /* falling edge detect */ GEDR, /* edge detect result */ + GAFR, /* alt function */ }; struct lnw_gpio { @@ -81,6 +82,31 @@ static void __iomem *gpio_reg(struct gpio_chip *chip, unsigned offset, return ptr; } +static void __iomem *gpio_reg_2bit(struct gpio_chip *chip, unsigned offset, + enum GPIO_REG reg_type) +{ + struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip); + unsigned nreg = chip->ngpio / 32; + u8 reg = offset / 16; + void __iomem *ptr; + + ptr = (void __iomem *)(lnw->reg_base + reg_type * nreg * 4 + reg * 4); + return ptr; +} + +static int lnw_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + void __iomem *gafr = gpio_reg_2bit(chip, offset, GAFR); + u32 value = readl(gafr); + int shift = (offset % 16) << 1, af = (value >> shift) & 3; + + if (af) { + value &= ~(3 << shift); + writel(value, gafr); + } + return 0; +} + static int lnw_gpio_get(struct gpio_chip *chip, unsigned offset) { void __iomem *gplr = gpio_reg(chip, offset, GPLR); @@ -321,6 +347,7 @@ static int __devinit lnw_gpio_probe(struct pci_dev *pdev, lnw->reg_base = base; lnw->irq_base = irq_base; lnw->chip.label = dev_name(&pdev->dev); + lnw->chip.request = lnw_gpio_request; lnw->chip.direction_input = lnw_gpio_direction_input; lnw->chip.direction_output = lnw_gpio_direction_output; lnw->chip.get = lnw_gpio_get; -- cgit v1.2.3-18-g5258 From 2824bc9c38dcb9a3e8f2d72a6ede8563c222959f Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Wed, 19 Oct 2011 10:37:39 +0900 Subject: gpio-pch: Use NUMA_NO_NODE not GFP_KERNEL Currently, GFP_KERNEL is used as parameter of irq_alloc_descs like below. irq_base = irq_alloc_descs(-1, IOH_IRQ_BASE, num_ports[j], GFP_KERNEL); This is not true. So, this patch uses NUMA_NO_NODE not GFP_KERNEL. Reported-by: Dan Carpenter Reported-by: David Rientjes Signed-off-by: Tomoya MORINAGA Acked-by: David Rientjes Signed-off-by: Grant Likely --- drivers/gpio/gpio-pch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index 46b5209878f..1e8a4a53881 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -398,7 +398,7 @@ static int __devinit pch_gpio_probe(struct pci_dev *pdev, goto err_gpiochip_add; } - irq_base = irq_alloc_descs(-1, 0, gpio_pins[chip->ioh], GFP_KERNEL); + irq_base = irq_alloc_descs(-1, 0, gpio_pins[chip->ioh], NUMA_NO_NODE); if (irq_base < 0) { dev_warn(&pdev->dev, "PCH gpio: Failed to get IRQ base num\n"); chip->irq_base = -1; -- cgit v1.2.3-18-g5258 From a7aaa4f888968b1261c2701cc66f18a3d4b9777b Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Wed, 19 Oct 2011 10:37:40 +0900 Subject: gpio-ml-ioh: Use NUMA_NO_NODE not GFP_KERNEL Currently, GFP_KERNEL is used as parameter of irq_alloc_descs like below. irq_base = irq_alloc_descs(-1, IOH_IRQ_BASE, num_ports[j], GFP_KERNEL); This is not true. So, this patch uses NUMA_NO_NODE not GFP_KERNEL. Reported-by: Dan Carpenter Reported-by: David Rientjes Signed-off-by: Tomoya MORINAGA Acked-by: David Rientjes Signed-off-by: Grant Likely --- drivers/gpio/gpio-ml-ioh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index 274fd4d0792..3aa6beec8c1 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -428,7 +428,7 @@ static int __devinit ioh_gpio_probe(struct pci_dev *pdev, chip = chip_save; for (j = 0; j < 8; j++, chip++) { irq_base = irq_alloc_descs(-1, IOH_IRQ_BASE, num_ports[j], - GFP_KERNEL); + NUMA_NO_NODE); if (irq_base < 0) { dev_warn(&pdev->dev, "ml_ioh_gpio: Failed to get IRQ base num\n"); -- cgit v1.2.3-18-g5258 From 76c05c8a0d56faf210cb9681786bb3e17cd59793 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 10 Aug 2011 16:31:46 -0500 Subject: gpio: pl061: add DT binding support This adds devicetree binding support to the ARM pl061 driver removing the platform_data dependency. When DT binding is used, the gpio numbering is assigned dynamically. For now, interrupts are not supported with DT until irqdomains learn dynamic irq assignment. Rather than add another case of -1, updating the driver to use NO_IRQ. Signed-off-by: Rob Herring Acked-by: Baruch Siach Signed-off-by: Grant Likely --- drivers/gpio/gpio-pl061.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c index 2c5a18f32bf..093c90bd3c1 100644 --- a/drivers/gpio/gpio-pl061.c +++ b/drivers/gpio/gpio-pl061.c @@ -118,7 +118,7 @@ static int pl061_to_irq(struct gpio_chip *gc, unsigned offset) { struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); - if (chip->irq_base == (unsigned) -1) + if (chip->irq_base == NO_IRQ) return -EINVAL; return chip->irq_base + offset; @@ -246,6 +246,18 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id) if (chip == NULL) return -ENOMEM; + pdata = dev->dev.platform_data; + if (pdata) { + chip->gc.base = pdata->gpio_base; + chip->irq_base = pdata->irq_base; + } else if (dev->dev.of_node) { + chip->gc.base = -1; + chip->irq_base = NO_IRQ; + } else { + ret = -ENODEV; + goto free_mem; + } + if (!request_mem_region(dev->res.start, resource_size(&dev->res), "pl061")) { ret = -EBUSY; @@ -267,14 +279,11 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id) chip->gc.get = pl061_get_value; chip->gc.set = pl061_set_value; chip->gc.to_irq = pl061_to_irq; - chip->gc.base = pdata->gpio_base; chip->gc.ngpio = PL061_GPIO_NR; chip->gc.label = dev_name(&dev->dev); chip->gc.dev = &dev->dev; chip->gc.owner = THIS_MODULE; - chip->irq_base = pdata->irq_base; - ret = gpiochip_add(&chip->gc); if (ret) goto iounmap; @@ -283,7 +292,7 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id) * irq_chip support */ - if (chip->irq_base == (unsigned) -1) + if (chip->irq_base == NO_IRQ) return 0; writeb(0, chip->base + GPIOIE); /* disable irqs */ @@ -307,11 +316,13 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id) list_add(&chip->list, chip_list); for (i = 0; i < PL061_GPIO_NR; i++) { - if (pdata->directions & (1 << i)) - pl061_direction_output(&chip->gc, i, - pdata->values & (1 << i)); - else - pl061_direction_input(&chip->gc, i); + if (pdata) { + if (pdata->directions & (1 << i)) + pl061_direction_output(&chip->gc, i, + pdata->values & (1 << i)); + else + pl061_direction_input(&chip->gc, i); + } irq_set_chip_and_handler(i + chip->irq_base, &pl061_irqchip, handle_simple_irq); -- cgit v1.2.3-18-g5258