Marek Szyprowski wrote: > > This patch adds common code to enable support of gpio interrupts on s5p > series. > > The total number of gpio pins is quite large on s5p series. Registering > irq support for all of them would be a resource waste. Because of that > the interrupt support for standard gpio pins is registered dynamically > by the s5p_register_gpio_interrupt() function. > > Signed-off-by: Marek Szyprowski <m.szyprowski@xxxxxxxxxxx> > Signed-off-by: Joonyoung Shim <jy0922.shim@xxxxxxxxxxx> > Signed-off-by: Kyungmin Park <kyungmin.park@xxxxxxxxxxx> > > --- > > Changes since v3: > - merged default and IRQ_TYPE_NONE cases in s5p_gpioint_set_type function > - fixed mask in GPIOINT_CON_OFFSET register from 0xf to 0x7 > - renamed the chip to s5p_gpioint to match the s5p_extint style > - extended comments in plat/irqs.h > > --- > arch/arm/plat-s5p/Kconfig | 5 + > arch/arm/plat-s5p/Makefile | 1 + > arch/arm/plat-s5p/include/plat/irqs.h | 9 + > arch/arm/plat-s5p/irq-gpioint.c | 242 > ++++++++++++++++++++++++ > arch/arm/plat-samsung/include/plat/gpio-cfg.h | 18 ++ > arch/arm/plat-samsung/include/plat/gpio-core.h | 4 + > 6 files changed, 279 insertions(+), 0 deletions(-) > create mode 100644 arch/arm/plat-s5p/irq-gpioint.c > > diff --git a/arch/arm/plat-s5p/Kconfig b/arch/arm/plat-s5p/Kconfig > index 2596096..65dbfa8 100644 > --- a/arch/arm/plat-s5p/Kconfig > +++ b/arch/arm/plat-s5p/Kconfig > @@ -32,6 +32,11 @@ config S5P_EXT_INT > Use the external interrupts (other than GPIO interrupts.) > Note: Do not choose this for S5P6440 and S5P6450. > > +config S5P_GPIO_INT > + bool > + help > + Common code for the GPIO interrupts (other than external interrupts.) > + > config S5P_DEV_FIMC0 > bool > help > diff --git a/arch/arm/plat-s5p/Makefile b/arch/arm/plat-s5p/Makefile > index f3e917e..e0823be 100644 > --- a/arch/arm/plat-s5p/Makefile > +++ b/arch/arm/plat-s5p/Makefile > @@ -18,6 +18,7 @@ obj-y += cpu.o > obj-y += clock.o > obj-y += irq.o > obj-$(CONFIG_S5P_EXT_INT) += irq-eint.o > +obj-$(CONFIG_S5P_GPIO_INT) += irq-gpioint.o > > # devices > > diff --git a/arch/arm/plat-s5p/include/plat/irqs.h b/arch/arm/plat- > s5p/include/plat/irqs.h > index 3fb3a3a..5a7bf96 100644 > --- a/arch/arm/plat-s5p/include/plat/irqs.h > +++ b/arch/arm/plat-s5p/include/plat/irqs.h > @@ -94,4 +94,13 @@ > ((irq) - S5P_EINT_BASE1) : \ > ((irq) + 16 - > S5P_EINT_BASE2)) > > +/* Typically only a few gpio chips require gpio interrupt support. > + To avoid memory waste irq descriptors are allocated only for > + S5P_GPIOINT_GROUP_COUNT chips, each with total number of > + S5P_GPIOINT_GROUP_SIZE pins/irqs. Each GPIOINT group can be assiged > + to any gpio chip with the s5p_register_gpio_interrupt() function */ > +#define S5P_GPIOINT_GROUP_COUNT 4 > +#define S5P_GPIOINT_GROUP_SIZE 8 > +#define S5P_GPIOINT_COUNT (S5P_GPIOINT_GROUP_COUNT * > S5P_GPIOINT_GROUP_SIZE) > + > #endif /* __ASM_PLAT_S5P_IRQS_H */ > diff --git a/arch/arm/plat-s5p/irq-gpioint.c b/arch/arm/plat-s5p/irq-gpioint.c > new file mode 100644 > index 0000000..32263a3 > --- /dev/null > +++ b/arch/arm/plat-s5p/irq-gpioint.c > @@ -0,0 +1,242 @@ > +/* linux/arch/arm/plat-s5p/irq-gpioint.c > + * > + * Copyright (c) 2010 Samsung Electronics Co., Ltd. > + * Author: Kyungmin Park <kyungmin.park@xxxxxxxxxxx> > + * Author: Joonyoung Shim <jy0922.shim@xxxxxxxxxxx> > + * Author: Marek Szyprowski <m.szyprowski@xxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License as published by the > + * Free Software Foundation; either version 2 of the License, or (at your > + * option) any later version. > + * > + */ > + > +#include <linux/kernel.h> > +#include <linux/interrupt.h> > +#include <linux/irq.h> > +#include <linux/io.h> > +#include <linux/gpio.h> > + > +#include <mach/map.h> > +#include <plat/gpio-core.h> > +#include <plat/gpio-cfg.h> > + > +#define S5P_GPIOREG(x) (S5P_VA_GPIO + (x)) > + > +#define GPIOINT_CON_OFFSET 0x700 > +#define GPIOINT_MASK_OFFSET 0x900 > +#define GPIOINT_PEND_OFFSET 0xA00 > + > +#define GPIOINT_LEVEL_LOW 0x0 > +#define GPIOINT_LEVEL_HIGH 0x1 > +#define GPIOINT_EDGE_FALLING 0x2 > +#define GPIOINT_EDGE_RISING 0x3 > +#define GPIOINT_EDGE_BOTH 0x4 > + > +static struct s3c_gpio_chip *irq_chips[S5P_GPIOINT_GROUP_MAXNR]; > + > +static int s5p_gpioint_get_group(unsigned int irq) > +{ > + struct gpio_chip *chip = get_irq_data(irq); > + struct s3c_gpio_chip *s3c_chip = container_of(chip, > + struct s3c_gpio_chip, chip); > + int group; > + > + for (group = 0; group < S5P_GPIOINT_GROUP_MAXNR; group++) > + if (s3c_chip == irq_chips[group]) > + break; > + > + return group; > +} > + > +static int s5p_gpioint_get_offset(unsigned int irq) > +{ > + struct gpio_chip *chip = get_irq_data(irq); > + struct s3c_gpio_chip *s3c_chip = container_of(chip, > + struct s3c_gpio_chip, chip); > + > + return irq - s3c_chip->irq_base; > +} > + > +static void s5p_gpioint_ack(unsigned int irq) > +{ > + int group, offset, pend_offset; > + unsigned int value; > + > + group = s5p_gpioint_get_group(irq); > + offset = s5p_gpioint_get_offset(irq); > + pend_offset = group << 2; > + > + value = __raw_readl(S5P_GPIOREG(GPIOINT_PEND_OFFSET) + > pend_offset); > + value |= 1 << offset; > + __raw_writel(value, S5P_GPIOREG(GPIOINT_PEND_OFFSET) + > pend_offset); > +} > + > +static void s5p_gpioint_mask(unsigned int irq) > +{ > + int group, offset, mask_offset; > + unsigned int value; > + > + group = s5p_gpioint_get_group(irq); > + offset = s5p_gpioint_get_offset(irq); > + mask_offset = group << 2; > + > + value = __raw_readl(S5P_GPIOREG(GPIOINT_MASK_OFFSET) + > mask_offset); > + value |= 1 << offset; > + __raw_writel(value, S5P_GPIOREG(GPIOINT_MASK_OFFSET) + > mask_offset); > +} > + > +static void s5p_gpioint_unmask(unsigned int irq) > +{ > + int group, offset, mask_offset; > + unsigned int value; > + > + group = s5p_gpioint_get_group(irq); > + offset = s5p_gpioint_get_offset(irq); > + mask_offset = group << 2; > + > + value = __raw_readl(S5P_GPIOREG(GPIOINT_MASK_OFFSET) + > mask_offset); > + value &= ~(1 << offset); > + __raw_writel(value, S5P_GPIOREG(GPIOINT_MASK_OFFSET) + > mask_offset); > +} > + > +static void s5p_gpioint_mask_ack(unsigned int irq) > +{ > + s5p_gpioint_mask(irq); > + s5p_gpioint_ack(irq); > +} > + > +static int s5p_gpioint_set_type(unsigned int irq, unsigned int type) > +{ > + int group, offset, con_offset; > + unsigned int value; > + > + group = s5p_gpioint_get_group(irq); > + offset = s5p_gpioint_get_offset(irq); > + con_offset = group << 2; > + > + switch (type) { > + case IRQ_TYPE_EDGE_RISING: > + type = GPIOINT_EDGE_RISING; > + break; > + case IRQ_TYPE_EDGE_FALLING: > + type = GPIOINT_EDGE_FALLING; > + break; > + case IRQ_TYPE_EDGE_BOTH: > + type = GPIOINT_EDGE_BOTH; > + break; > + case IRQ_TYPE_LEVEL_HIGH: > + type = GPIOINT_LEVEL_HIGH; > + break; > + case IRQ_TYPE_LEVEL_LOW: > + type = GPIOINT_LEVEL_LOW; > + break; > + case IRQ_TYPE_NONE: > + default: > + printk(KERN_WARNING "No irq type\n"); > + return -EINVAL; > + } > + > + value = __raw_readl(S5P_GPIOREG(GPIOINT_CON_OFFSET) + > con_offset); > + value &= ~(0x7 << (offset * 0x4)); > + value |= (type << (offset * 0x4)); > + __raw_writel(value, S5P_GPIOREG(GPIOINT_CON_OFFSET) + > con_offset); > + > + return 0; > +} > + > +struct irq_chip s5p_gpioint = { > + .name = "s5p_gpioint", > + .ack = s5p_gpioint_ack, > + .mask = s5p_gpioint_mask, > + .mask_ack = s5p_gpioint_mask_ack, > + .unmask = s5p_gpioint_unmask, > + .set_type = s5p_gpioint_set_type, > +}; > + > +static void s5p_gpioint_handler(unsigned int irq, struct irq_desc *desc) > +{ > + int group, offset, pend_offset, mask_offset; > + int real_irq; > + unsigned int pend, mask; > + > + for (group = 0; group < S5P_GPIOINT_GROUP_MAXNR; group++) { > + pend_offset = group << 2; > + pend = __raw_readl(S5P_GPIOREG(GPIOINT_PEND_OFFSET) + > + pend_offset); > + if (!pend) > + continue; > + > + mask_offset = group << 2; > + mask = __raw_readl(S5P_GPIOREG(GPIOINT_MASK_OFFSET) + > + mask_offset); > + pend &= ~mask; > + > + for (offset = 0; offset < 8; offset++) { > + if (pend & (1 << offset)) { > + struct s3c_gpio_chip *chip = irq_chips[group]; > + if (chip) { > + real_irq = chip->irq_base + offset; > + generic_handle_irq(real_irq); > + } > + } > + } > + } > +} > + > +static __init int s5p_gpioint_add(struct s3c_gpio_chip *chip) > +{ > + static int used_gpioint_groups = 0; > + static bool handler_registered = 0; > + int irq, group = chip->group; > + int i; > + > + if (used_gpioint_groups >= S5P_GPIOINT_GROUP_COUNT) > + return -ENOMEM; > + > + chip->irq_base = S5P_GPIOINT_BASE + > + used_gpioint_groups * S5P_GPIOINT_GROUP_SIZE; > + used_gpioint_groups++; > + > + if (!handler_registered) { > + set_irq_chained_handler(IRQ_GPIOINT, s5p_gpioint_handler); > + handler_registered = 1; > + } > + > + irq_chips[group] = chip; > + for (i = 0; i < chip->chip.ngpio; i++) { > + irq = chip->irq_base + i; > + set_irq_chip(irq, &s5p_gpioint); > + set_irq_data(irq, &chip->chip); > + set_irq_handler(irq, handle_level_irq); > + set_irq_flags(irq, IRQF_VALID); > + } > + return 0; > +} > + > +int __init s5p_register_gpio_interrupt(int pin) > +{ > + struct s3c_gpio_chip *my_chip = s3c_gpiolib_getchip(pin); > + int offset, group; > + int ret; > + > + if (!my_chip) > + return -EINVAL; > + > + offset = pin - my_chip->chip.base; > + group = my_chip->group; > + > + /* check if the group has been already registered */ > + if (my_chip->irq_base) > + return my_chip->irq_base + offset; > + > + /* register gpio group */ > + ret = s5p_gpioint_add(my_chip); > + if (ret == 0) { > + printk(KERN_INFO "Registered interrupt support for gpio > group %d.\n", > + group); > + return my_chip->irq_base + offset; > + } > + return ret; > +} > diff --git a/arch/arm/plat-samsung/include/plat/gpio-cfg.h b/arch/arm/plat- > samsung/include/plat/gpio-cfg.h > index 1c6b929..5b43b95 100644 > --- a/arch/arm/plat-samsung/include/plat/gpio-cfg.h > +++ b/arch/arm/plat-samsung/include/plat/gpio-cfg.h > @@ -169,4 +169,22 @@ extern s5p_gpio_drvstr_t s5p_gpio_get_drvstr(unsigned > int pin); > */ > extern int s5p_gpio_set_drvstr(unsigned int pin, s5p_gpio_drvstr_t drvstr); > > +/** > + * s5p_register_gpio_interrupt() - register interrupt support for a gpio group > + * @pin: The pin number from the group to be registered > + * > + * This function registers gpio interrupt support for the group that the > + * specified pin belongs to. > + * > + * The total number of gpio pins is quite large ob s5p series. Registering > + * irq support for all of them would be a resource waste. Because of that the > + * interrupt support for standard gpio pins is registered dynamically. > + * > + * It will return the irq number of the interrupt that has been registered > + * or -ENOMEM if no more gpio interrupts can be registered. It is allowed > + * to call this function more than once for the same gpio group (the group > + * will be registered only once). > + */ > +extern int s5p_register_gpio_interrupt(int pin); > + > #endif /* __PLAT_GPIO_CFG_H */ > diff --git a/arch/arm/plat-samsung/include/plat/gpio-core.h b/arch/arm/plat- > samsung/include/plat/gpio-core.h > index e358c7d..c22c27c 100644 > --- a/arch/arm/plat-samsung/include/plat/gpio-core.h > +++ b/arch/arm/plat-samsung/include/plat/gpio-core.h > @@ -43,6 +43,8 @@ struct s3c_gpio_cfg; > * struct s3c_gpio_chip - wrapper for specific implementation of gpio > * @chip: The chip structure to be exported via gpiolib. > * @base: The base pointer to the gpio configuration registers. > + * @group: The group register number for gpio interrupt support. > + * @irq_base: The base irq number. > * @config: special function and pull-resistor control information. > * @lock: Lock for exclusive access to this gpio bank. > * @pm_save: Save information for suspend/resume support. > @@ -63,6 +65,8 @@ struct s3c_gpio_chip { > struct s3c_gpio_cfg *config; > struct s3c_gpio_pm *pm; > void __iomem *base; > + int irq_base; > + int group; > spinlock_t lock; > #ifdef CONFIG_PM > u32 pm_save[4]; > -- Looks ok to me...will apply. Thanks. Best regards, Kgene. -- Kukjin Kim <kgene.kim@xxxxxxxxxxx>, Senior Engineer, SW Solution Development Team, Samsung Electronics Co., Ltd. -- To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html