commit: http://blackfin.uclinux.org/git/?p=linux-kernel;a=commitdiff;h=961b7b262b2541b54ee83fdbb6dc9916a7845274
branch: http://blackfin.uclinux.org/git/?p=linux-kernel;a=shortlog;h=refs/heads/trunk

This patch move the GPIO interrupt configuration into the gpio-adi2 driver
in drivers folder as well.

Signed-off-by: Sonic Zhang <[email protected]>
---
 drivers/gpio/gpio-adi2.c |  440 ++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 410 insertions(+), 30 deletions(-)

diff --git a/drivers/gpio/gpio-adi2.c b/drivers/gpio/gpio-adi2.c
index de5f1fb..20ac95a 100644
--- a/drivers/gpio/gpio-adi2.c
+++ b/drivers/gpio/gpio-adi2.c
@@ -81,59 +81,273 @@ DECLARE_RESERVED_MAP(gpio, GPIO_BANK_NUM);
 DECLARE_RESERVED_MAP(peri, DIV_ROUND_UP(MAX_RESOURCES, GPIO_BANKSIZE));
 DECLARE_RESERVED_MAP(gpio_irq, GPIO_BANK_NUM);
 
-static inline int check_gpio(unsigned gpio)
+
+#define NR_PINT_BITS		32
+#define IRQ_NOT_AVAIL		0xFF
+
+#define PINT_2_BANK(x)		((x) >> 5)
+#define PINT_2_BIT(x)		((x) & 0x1F)
+#define PINT_BIT(x)		(1 << (PINT_2_BIT(x)))
+
+static unsigned char irq2pint_lut[NR_PINTS];
+static unsigned char pint2irq_lut[NR_PINT_SYS_IRQS * NR_PINT_BITS];
+
+static struct gpio_pint_regs * const pint[NR_PINT_SYS_IRQS] = {
+	(struct gpio_pint_regs *)PINT0_MASK_SET,
+	(struct gpio_pint_regs *)PINT1_MASK_SET,
+	(struct gpio_pint_regs *)PINT2_MASK_SET,
+	(struct gpio_pint_regs *)PINT3_MASK_SET,
+#ifdef CONFIG_BF60x
+	(struct gpio_pint_regs *)PINT4_MASK_SET,
+	(struct gpio_pint_regs *)PINT5_MASK_SET,
+#endif
+};
+
+inline unsigned int get_irq_base(u32 bank, u8 bmap)
 {
-#if defined(CONFIG_BF54x)
-	if (gpio == GPIO_PB15 || gpio == GPIO_PC14 || gpio == GPIO_PC15
-	    || gpio == GPIO_PH14 || gpio == GPIO_PH15
-	    || gpio == GPIO_PJ14 || gpio == GPIO_PJ15)
-		return -EINVAL;
+	unsigned int irq_base;
+
+#ifdef CONFIG_BF54x
+	if (bank < 2) {		/*PA-PB */
+		irq_base = IRQ_PA0 + bmap * 16;
+	} else {		/*PC-PJ */
+		irq_base = IRQ_PC0 + bmap * 16;
+	}
+#else
+	irq_base = IRQ_PA0 + bank * 16 + bmap * 16;
 #endif
-	if (gpio >= MAX_BLACKFIN_GPIOS)
-		return -EINVAL;
-	return 0;
+	return irq_base;
 }
 
-static void port_setup(unsigned gpio, unsigned short usage)
+	/* Whenever PINTx_ASSIGN is altered init_pint_lut() must be executed! */
+static void init_pint_lut(void)
 {
-	if (check_gpio(gpio))
-		return;
+	u16 bank, bit, irq_base, bit_pos;
+	u32 pint_assign;
+	u8 bmap;
 
-	if (usage == GPIO_USAGE)
-		gpio_array[gpio_bank(gpio)]->port_fer &= ~gpio_bit(gpio);
-	else
-		gpio_array[gpio_bank(gpio)]->port_fer |= gpio_bit(gpio);
-	SSYNC();
+	memset(irq2pint_lut, IRQ_NOT_AVAIL, sizeof(irq2pint_lut));
+
+	for (bank = 0; bank < NR_PINT_SYS_IRQS; bank++) {
+
+		pint_assign = pint[bank]->assign;
+
+		for (bit = 0; bit < NR_PINT_BITS; bit++) {
+
+			bmap = (pint_assign >> ((bit / 8) * 8)) & 0xFF;
+
+			irq_base = get_irq_base(bank, bmap);
+
+			irq_base += (bit % 8) + ((bit / 8) & 1 ? 8 : 0);
+			bit_pos = bit + bank * NR_PINT_BITS;
+
+			pint2irq_lut[bit_pos] = irq_base - SYS_IRQS;
+			irq2pint_lut[irq_base - SYS_IRQS] = bit_pos;
+		}
+	}
 }
 
-static inline void portmux_setup(unsigned short per)
+static DECLARE_BITMAP(gpio_enabled, MAX_BLACKFIN_GPIOS);
+
+void adi_gpio_irq_prepare(unsigned gpio);
+int adi_gpio_irq_request(unsigned gpio, const char *label);
+void adi_gpio_irq_free(unsigned gpio);
+
+static void adi_gpio_ack_irq(struct irq_data *d)
 {
-	u16 ident = P_IDENT(per);
-	u16 function = P_FUNCT2MUX(per);
-	u32 pmux;
+	u32 pint_val = irq2pint_lut[d->irq - SYS_IRQS];
+	u32 pintbit = PINT_BIT(pint_val);
+	u32 bank = PINT_2_BANK(pint_val);
 
-	pmux = gpio_array[gpio_bank(ident)]->port_mux;
+	if (irqd_get_trigger_type(d) == IRQ_TYPE_EDGE_BOTH) {
+		if (pint[bank]->invert_set & pintbit)
+			pint[bank]->invert_clear = pintbit;
+		else
+			pint[bank]->invert_set = pintbit;
+	}
+	pint[bank]->request = pintbit;
 
-	pmux &= ~(0x3 << (2 * gpio_sub_n(ident)));
-	pmux |= (function & 0x3) << (2 * gpio_sub_n(ident));
+}
 
-	gpio_array[gpio_bank(ident)]->port_mux = pmux;
+static void adi_gpio_mask_ack_irq(struct irq_data *d)
+{
+	u32 pint_val = irq2pint_lut[d->irq - SYS_IRQS];
+	u32 pintbit = PINT_BIT(pint_val);
+	u32 bank = PINT_2_BANK(pint_val);
+
+	if (irqd_get_trigger_type(d) == IRQ_TYPE_EDGE_BOTH) {
+		if (pint[bank]->invert_set & pintbit)
+			pint[bank]->invert_clear = pintbit;
+		else
+			pint[bank]->invert_set = pintbit;
+	}
+
+	pint[bank]->request = pintbit;
+	pint[bank]->mask_clear = pintbit;
 }
 
-static inline u16 get_portmux(unsigned short per)
+static void adi_gpio_mask_irq(struct irq_data *d)
 {
-	u16 ident = P_IDENT(per);
-	u32 pmux = gpio_array[gpio_bank(ident)]->port_mux;
-	return (pmux >> (2 * gpio_sub_n(ident)) & 0x3);
+	u32 pint_val = irq2pint_lut[d->irq - SYS_IRQS];
+
+	pint[PINT_2_BANK(pint_val)]->mask_clear = PINT_BIT(pint_val);
+}
+
+static void adi_gpio_unmask_irq(struct irq_data *d)
+{
+	u32 pint_val = irq2pint_lut[d->irq - SYS_IRQS];
+	u32 pintbit = PINT_BIT(pint_val);
+	u32 bank = PINT_2_BANK(pint_val);
+
+	pint[bank]->mask_set = pintbit;
+}
+
+static unsigned int adi_gpio_irq_startup(struct irq_data *d)
+{
+	unsigned int irq = d->irq;
+	u32 gpionr = irq_to_gpio(irq);
+	u32 pint_val = irq2pint_lut[irq - SYS_IRQS];
+
+	if (pint_val == IRQ_NOT_AVAIL) {
+		printk(KERN_ERR
+		"GPIO IRQ %d :Not in PINT Assign table "
+		"Reconfigure Interrupt to Port Assignemt\n", irq);
+		return -ENODEV;
+	}
+
+	if (__test_and_set_bit(gpionr, gpio_enabled))
+		adi_gpio_irq_prepare(gpionr);
+
+	adi_gpio_unmask_irq(d);
+
+	return 0;
+}
+
+static void adi_gpio_irq_shutdown(struct irq_data *d)
+{
+	u32 gpionr = irq_to_gpio(d->irq);
+
+	adi_gpio_mask_irq(d);
+	__clear_bit(gpionr, gpio_enabled);
+	adi_gpio_irq_free(gpionr);
+}
+
+static int adi_gpio_irq_type(struct irq_data *d, unsigned int type)
+{
+	unsigned int irq = d->irq;
+	int ret;
+	char buf[16];
+	u32 gpionr = irq_to_gpio(irq);
+	u32 pint_val = irq2pint_lut[irq - SYS_IRQS];
+	u32 pintbit = PINT_BIT(pint_val);
+	u32 bank = PINT_2_BANK(pint_val);
+
+	if (pint_val == IRQ_NOT_AVAIL)
+		return -ENODEV;
+
+	if (type == IRQ_TYPE_PROBE) {
+		/* only probe unenabled GPIO interrupt lines */
+		if (test_bit(gpionr, gpio_enabled))
+			return 0;
+		type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
+	}
+
+	if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING |
+		    IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) {
+
+		snprintf(buf, 16, "gpio-irq%d", irq);
+		ret = adi_gpio_irq_request(gpionr, buf);
+		if (ret)
+			return ret;
+
+		if (__test_and_set_bit(gpionr, gpio_enabled))
+			adi_gpio_irq_prepare(gpionr);
+	} else {
+		__clear_bit(gpionr, gpio_enabled);
+		return 0;
+	}
+
+	if ((type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_LEVEL_LOW)))
+		pint[bank]->invert_set = pintbit;	/* low or falling edge denoted by one */
+	else
+		pint[bank]->invert_clear = pintbit;	/* high or rising edge denoted by zero */
+
+	if ((type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING))
+	    == (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) {
+		if (gpio_get_value(gpionr))
+			pint[bank]->invert_set = pintbit;
+		else
+			pint[bank]->invert_clear = pintbit;
+	}
+
+	if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) {
+		pint[bank]->edge_set = pintbit;
+		__irq_set_handler_locked(irq, handle_edge_irq);
+	} else {
+		pint[bank]->edge_clear = pintbit;
+		__irq_set_handler_locked(irq, handle_level_irq);
+	}
+
+	return 0;
 }
 
 #ifdef CONFIG_PM
+static struct adi_pm_pint_save save_pint_reg[NR_PINT_SYS_IRQS];
 
-int adi_gpio_pm_standby_ctrl(unsigned ctrl)
+static int adi_gpio_set_wake(struct irq_data *d, unsigned int state)
 {
+#ifndef SEC_GCTL
+	u32 pint_irq;
+	u32 pint_val = irq2pint_lut[d->irq - SYS_IRQS];
+	u32 bank = PINT_2_BANK(pint_val);
+
+	switch (bank) {
+	case 0:
+		pint_irq = IRQ_PINT0;
+		break;
+	case 2:
+		pint_irq = IRQ_PINT2;
+		break;
+	case 3:
+		pint_irq = IRQ_PINT3;
+		break;
+	case 1:
+		pint_irq = IRQ_PINT1;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	adi_internal_set_wake(pint_irq, state);
+#endif
+
 	return 0;
 }
 
+void adi_pint_suspend(void)
+{
+	u32 bank;
+
+	for (bank = 0; bank < NR_PINT_SYS_IRQS; bank++) {
+		save_pint_reg[bank].mask_set = pint[bank]->mask_set;
+		save_pint_reg[bank].assign = pint[bank]->assign;
+		save_pint_reg[bank].edge_set = pint[bank]->edge_set;
+		save_pint_reg[bank].invert_set = pint[bank]->invert_set;
+	}
+}
+
+void adi_pint_resume(void)
+{
+	u32 bank;
+
+	for (bank = 0; bank < NR_PINT_SYS_IRQS; bank++) {
+		pint[bank]->mask_set = save_pint_reg[bank].mask_set;
+		pint[bank]->assign = save_pint_reg[bank].assign;
+		pint[bank]->edge_set = save_pint_reg[bank].edge_set;
+		pint[bank]->invert_set = save_pint_reg[bank].invert_set;
+	}
+}
+
 void adi_gpio_pm_hibernate_suspend(void)
 {
 	int i, bank;
@@ -164,7 +378,168 @@ void adi_gpio_pm_hibernate_restore(void)
 		gpio_array[bank]->dir_set = gpio_bank_saved[bank].dir;
 	}
 }
+#else
+#define adi_gpio_set_wake NULL
+#endif
+
+static void adi_demux_gpio_irq(unsigned int inta_irq,
+			struct irq_desc *desc)
+{
+	u32 bank, pint_val;
+	u32 request, irq;
+	u32 level_mask;
+	int umask = 0;
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+
+	if (chip->irq_mask_ack) {
+		chip->irq_mask_ack(&desc->irq_data);
+	} else {
+		chip->irq_mask(&desc->irq_data);
+		if (chip->irq_ack)
+			chip->irq_ack(&desc->irq_data);
+	}
+
+	switch (inta_irq) {
+	case IRQ_PINT0:
+		bank = 0;
+		break;
+	case IRQ_PINT2:
+		bank = 2;
+		break;
+	case IRQ_PINT3:
+		bank = 3;
+		break;
+	case IRQ_PINT1:
+		bank = 1;
+		break;
+#ifdef CONFIG_BF60x
+	case IRQ_PINT4:
+		bank = 4;
+		break;
+	case IRQ_PINT5:
+		bank = 5;
+		break;
+#endif
+	default:
+		return;
+	}
+
+	pint_val = bank * NR_PINT_BITS;
+
+	request = pint[bank]->request;
+
+	level_mask = pint[bank]->edge_set & request;
+
+	while (request) {
+		if (request & 1) {
+			irq = pint2irq_lut[pint_val] + SYS_IRQS;
+			if (level_mask & PINT_BIT(pint_val)) {
+				umask = 1;
+				chip->irq_unmask(&desc->irq_data);
+			}
+			generic_handle_irq(irq);
+		}
+		pint_val++;
+		request >>= 1;
+	}
+
+	if (!umask)
+		chip->irq_unmask(&desc->irq_data);
+}
+
+static struct irq_chip adi_gpio_irqchip = {
+	.name = "GPIO",
+	.irq_ack = adi_gpio_ack_irq,
+	.irq_mask = adi_gpio_mask_irq,
+	.irq_mask_ack = adi_gpio_mask_ack_irq,
+	.irq_unmask = adi_gpio_unmask_irq,
+	.irq_disable = adi_gpio_mask_irq,
+	.irq_enable = adi_gpio_unmask_irq,
+	.irq_set_type = adi_gpio_irq_type,
+	.irq_startup = adi_gpio_irq_startup,
+	.irq_shutdown = adi_gpio_irq_shutdown,
+	.irq_set_wake = adi_gpio_set_wake,
+};
+
+static int __init gpiolib_pint_setup(void)
+{
+	int pint_list[] = {IRQ_PINT0, IRQ_PINT1, IRQ_PINT2, IRQ_PINT3,
+#ifdef COFNIG_BF60x
+		IRQ_PINT4, IRQ_PINT5,
+#endif
+		-1
+	};
+	int i, irq;
+
+# ifdef CONFIG_PINTx_REASSIGN
+	pint[0]->assign = CONFIG_PINT0_ASSIGN;
+	pint[1]->assign = CONFIG_PINT1_ASSIGN;
+	pint[2]->assign = CONFIG_PINT2_ASSIGN;
+	pint[3]->assign = CONFIG_PINT3_ASSIGN;
+# ifdef COFNIG_BF60x
+	pint[4]->assign = CONFIG_PINT4_ASSIGN;
+	pint[5]->assign = CONFIG_PINT5_ASSIGN;
+# endif
+#endif
+	/* Whenever PINTx_ASSIGN is altered init_pint_lut() must be executed! */
+	init_pint_lut();
+
+	for (i=0; pint_list[i]>=0; i++)
+		irq_set_chained_handler(pint_list[i], adi_demux_gpio_irq);
+
+	for (irq = GPIO_IRQ_BASE;
+		irq < (GPIO_IRQ_BASE + MAX_GPIOS); irq++)
+		irq_set_chip_and_handler(irq, &adi_gpio_irqchip,
+					handle_level_irq);
+
+	return 0;
+}
+
+static inline int check_gpio(unsigned gpio)
+{
+#ifdef CONFIG_BF54x
+	if (gpio == GPIO_PB15 || gpio == GPIO_PC14 || gpio == GPIO_PC15
+	    || gpio == GPIO_PH14 || gpio == GPIO_PH15
+	    || gpio == GPIO_PJ14 || gpio == GPIO_PJ15)
+		return -EINVAL;
 #endif
+	if (gpio >= MAX_BLACKFIN_GPIOS)
+		return -EINVAL;
+	return 0;
+}
+
+static void port_setup(unsigned gpio, unsigned short usage)
+{
+	if (check_gpio(gpio))
+		return;
+
+	if (usage == GPIO_USAGE)
+		gpio_array[gpio_bank(gpio)]->port_fer &= ~gpio_bit(gpio);
+	else
+		gpio_array[gpio_bank(gpio)]->port_fer |= gpio_bit(gpio);
+	SSYNC();
+}
+
+static inline void portmux_setup(unsigned short per)
+{
+	u16 ident = P_IDENT(per);
+	u16 function = P_FUNCT2MUX(per);
+	u32 pmux;
+
+	pmux = gpio_array[gpio_bank(ident)]->port_mux;
+
+	pmux &= ~(0x3 << (2 * gpio_sub_n(ident)));
+	pmux |= (function & 0x3) << (2 * gpio_sub_n(ident));
+
+	gpio_array[gpio_bank(ident)]->port_mux = pmux;
+}
+
+static inline u16 get_portmux(unsigned short per)
+{
+	u16 ident = P_IDENT(per);
+	u32 pmux = gpio_array[gpio_bank(ident)]->port_mux;
+	return (pmux >> (2 * gpio_sub_n(ident)) & 0x3);
+}
 
 static unsigned short get_gpio_dir(unsigned gpio)
 {
@@ -588,6 +963,11 @@ static struct gpio_chip gpio_adi2_chip = {
 
 static int __init gpiolib_setup(void)
 {
+	int ret;
+
+	if ((ret=gpiolib_pint_setup()) != 0)
+		return ret;
+
 	return gpiochip_add(&gpio_adi2_chip);
 }
 arch_initcall(gpiolib_setup);
_______________________________________________
Linux-kernel-commits mailing list
[email protected]
https://blackfin.uclinux.org/mailman/listinfo/linux-kernel-commits

Reply via email to