linux arm irq (3): gpio interrupt

linux arm irq (3)


3 gpio interrupt



Author: Yangkai Wang
wang_yangkai@163.com
Coding in 2021/05/16
转载请注明author,出处.



linux version 3.4.39
s5p6818 soc

Cortex-A53 Octa core CPU
Interrupt Controller,GIC400

GIC (Generic Interrupt Controllers), reference:Arm Generic Interrupt Controller Architecture version 2.0,Architecture Specification

GPIO controller,reference:S5P6818 Application Processor Datasheet

  • gpio interrupt init
asmlinkage void __init start_kernel(void)
    |
    init_IRQ();
        |
        machine_desc->init_irq(); /* call nxp_cpu_irq_init() */
/* arch/arm/mach-s5p6818/irq.c */

/*----------------------------------------------------------------------------
 *  cpu irq handler
 */
#define GIC_DIST_BASE		(void __iomem *)(INTC_BASE + 0x00001000)		// 0xC0009000
#define GIC_CPUI_BASE		(void __iomem *)(INTC_BASE + 0x00002000)		// 0xC000a000

#define GPIO_INT_BASE		(void __iomem *)IO_ADDRESS(PHY_BASEADDR_GPIOA)
#define GPIO_BASE_OFFSET	(0x1000)
#define GPIO_INT_MASK		(0xFFFFFFFF)

#define ALIVE_INT_BASE		(void __iomem *)IO_ADDRESS(PHY_BASEADDR_CLKPWR_MODULE + 0x800)
#define ALIVE_INT_MASK		(0x000000FF)


/*
 *  cpu irq handler
 */
void __init nxp_cpu_irq_init(void)
{
	pr_debug("%s:%d\n", __func__, __LINE__);
	printk("~~~ %s()\n", __func__);

	__gic_init(GIC_DIST_BASE, (void __iomem *)GIC_CPUI_BASE);
	gpio_init(GPIO_INT_BASE , IRQ_GPIO_START, GPIO_INT_MASK, 0);	/* 64 ~ 223 (A,B,C,D,E) */
	alive_init(ALIVE_INT_BASE, IRQ_ALIVE_START, ALIVE_INT_MASK, 0); /* 224 ~ 231 */

#ifdef CONFIG_FIQ
	init_FIQ();
#endif

	/* wake up source from idle */
	irq_set_irq_wake(IRQ_PHY_CLKPWR_ALIVEIRQ + GIC_PHY_OFFSET, 1);
#if PM_RTC_WAKE
	irq_set_irq_wake(IRQ_PHY_CLKPWR_RTCIRQ + GIC_PHY_OFFSET, 1);
#endif
}

nxp_cpu_irq_init(),中
gic 初始化:
__gic_init(GIC_DIST_BASE, (void __iomem *)GIC_CPUI_BASE);

gpio 中断相关初始化:
gpio_init(GPIO_INT_BASE , IRQ_GPIO_START, GPIO_INT_MASK, 0);C,D,E);
alive_init(ALIVE_INT_BASE, IRQ_ALIVE_START, ALIVE_INT_MASK, 0);

  • gpio_interrupt_init(void __iomem *base, unsigned int irq_start,
    u32 irq_sources, u32 resume_sources)
/* arch/arm/mach-s5p6818/irq.c */

static void __init gpio_init(void __iomem *base, unsigned int irq_start,
		     u32 irq_sources, u32 resume_sources)
{
	int irq_gpio = IRQ_PHY_GPIOA + GIC_PHY_OFFSET;
	int num = 5;  /* A,B,C,D,E */
	int ios = 32; /* GPIO 32 */
	int n = 0,i = 0;

	/* set gpio irq handler */
	for (n = 0; num > n; n++) {
		printk(KERN_INFO "GPIO  @%p: start %3d, mask 0x%08x (gpio %d)\n",
			base, irq_start, irq_sources, irq_gpio);

		for (i = 0; ios > i; i++) {
			if (irq_sources & (1 << i)) {
				int irq = irq_start + i; /* irq gpio ABCDE_i irq number */
				irq_set_chip_data(irq, base);  /* desc->irq_data.chip_data:gpioABCDE controller base address; */
				irq_set_chip_and_handler(irq, &gpio_chip, handle_level_irq); /* desc->irq_data.chip:gpio_chip; desc->handle_irq:handle_level_irq() */
				set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
			}
		}
		/* init gpio irq register  */
		writel(0xFFFFFFFF, base + GPIO_INT_STATUS);
		writel(0x0, base + GPIO_INT_ENB);
		writel(0x0, base + GPIO_INT_DET);

		printk("~~~ %s() set GPIO* handler_data chained_handler\n", __func__);

		/* register gpio irq handler data */
		irq_set_handler_data(irq_gpio, base); /* irq_gpio:gpio ABCDE controller irq number */

		/*
	 	 * call gpio_mask_irq
	 	 * chip and chip data is registerd at gic_init
	 	 */
		irq_set_chained_handler(irq_gpio, gpio_handler); /* set gpio ABCDE irq desc->handle_irq:gpio_handler() */

		struct irq_desc *desc = irq_to_desc(irq_gpio);
		struct irq_chip *chip = desc->irq_data.chip;
		printk("~~~ %s() GPIO parent irq:%u, chip name:%s\n",\
			__func__, irq_gpio, chip->name);

		/* next */
		irq_gpio++;
		irq_start += ios; /* irq_start += 32, group gpioABCDE 32 io */
		base += GPIO_BASE_OFFSET; /* add base address */
	}
}

s5p6818有A B C D E ALIVE 6个gpio controller;(A B C D E(controller)组GPIO),每组gpio 下有32个io;
代码很简单:

  1. set gpio ABCDE_i io 的struct irq_desc desc;(one of struct irq_desc irq_desc[NR_IRQS]);
    desc->irq_data.chip_data:gpioABCDE controller base address;
    desc->irq_data.chip:gpio_chip;
    desc->handle_irq:handle_level_irq();
  2. set gpio ABCDE controller 的struct irq_desc desc;(one of struct irq_desc irq_desc[NR_IRQS]);
    desc->irq_data.handler_data:gpioABCDE controller base address;
    desc->handle_irq:gpio_handler;
    and irq_startup(desc, true);
/*
 * Set a highlevel chained flow handler for a given IRQ.
 * (a chained handler is automatically enabled and set to
 *  IRQ_NOREQUEST, IRQ_NOPROBE, and IRQ_NOTHREAD)
 */
static inline void
irq_set_chained_handler(unsigned int irq, irq_flow_handler_t handle)
{
	printk("~~~ %s() irq:%u, is_chained:%d\n", \
		__func__, irq, 1);
	__irq_set_handler(irq, handle, 1(is_chained)), NULL);
    |
    {
        desc->handle_irq = handle;
        if (handle != handle_bad_irq && is_chained) {
		    irq_settings_set_noprobe(desc);
		    irq_settings_set_norequest(desc);
		    irq_settings_set_nothread(desc);
		    irq_startup(desc, true);
	    }
    }
}

如上,可知void __irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained, const char *name);
is_chained的用意;


当发生gpio中断,会先call gpio ABCDE controller desc->handle_irq();

  • gpio_handler();
/* arch/arm/mach-s5p6818/irq.c */

static void gpio_handler(unsigned int irq, struct irq_desc *desc)
{
	void __iomem *base = irq_desc_get_handler_data(desc);
	u32 stat, mask;
	int phy, bit;

	mask = readl(base + GPIO_INT_ENB);
	stat = readl(base + GPIO_INT_STATUS) & mask;
	bit  = ffs(stat) - 1;
	phy  = irq;

	pr_debug("%s: gpio irq=%d [%s.%d], stat=0x%08x, mask=0x%08x\n",
		__func__, phy, PIO_NAME(phy), bit, stat, mask);
	printk("~~~ %s: gpio irq:%d [%s.%d], stat=0x%08x, mask=0x%08x\n",
		__func__, phy, PIO_NAME(phy), bit, stat, mask);

	if (-1 == bit) {
		printk(KERN_ERR "Unknown gpio phy irq=%d, status=0x%08x, mask=0x%08x\r\n",
			phy, stat, mask);
		writel(-1, (base + GPIO_INT_STATUS));	/* clear gpio status all */
		writel_relaxed(phy, GIC_CPUI_BASE + GIC_CPU_EOI);
		return;
	}

	/* gpio descriptor */
	irq  = (VIO_IRQ_BASE + bit + (32 * (phy - PIO_IRQ_BASE)));	// virtual irq
	printk("~~~ %s() irq:%u, irq_desc->irq_data.irq:%u\n", \
		__func__, irq, irq_desc->irq_data.irq);
	/*desc = irq_desc + irq;*/ /*the global struct irq_desc irq_desc[NR_IRQS]*/
	desc = irq_to_desc(irq);

	if (desc && desc->action) {
		/* disable irq reentrant */
		desc->action->flags |= IRQF_DISABLED;
		printk("~~~ %s() call generic_handle_irq_desc(), irq_desc->irq_data.irq:%u, atcion name:%s\n", \
			__func__, desc->irq_data.irq, desc->action->name);
		generic_handle_irq_desc(irq, desc);
	} else {
		printk(KERN_ERR "Error, not registered gpio interrupt=%d (%s.%d), disable !!!\n",
			irq, PIO_NAME(phy), bit);
		writel(readl(base + GPIO_INT_ENB) & ~(1<<bit), base + GPIO_INT_ENB);		/* gpio mask : irq disable */
		writel(readl(base + GPIO_INT_STATUS) | (1<<bit), base + GPIO_INT_STATUS);	/* gpio ack  : irq pend clear */
		readl(base + GPIO_INT_STATUS);	/* Guarantee */
	}

	printk("~~~ %s() write CPUI end of INT reg, irq:%u\n", __func__, irq);
	writel_relaxed(phy, GIC_CPUI_BASE + GIC_CPU_EOI);

	return;
}

读寄存器,确认是具体哪个io,得到对应的struct irq_desc desc;
call generic_handle_irq_desc(irq, desc);
之后operate gic chip, end of gpio ABCDE controller irq:writel_relaxed(phy, GIC_CPUI_BASE + GIC_CPU_EOI);


  • inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
/* include/linux/irqdesc.h */

/*
 * Architectures call this to let the generic IRQ layer
 * handle an interrupt. If the descriptor is attached to an
 * irqchip-style controller then we call the ->handle_irq() handler,
 * and it calls __do_IRQ() if it's attached to an irqtype-style controller.
 */
static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
{
	if (irq >= IRQ_PHY_GPIOA)
		printk("~~~ %s() irq:%u, irq_desc->irq_data.irq:%u\n", \
			__func__, irq, irq_desc->irq_data.irq);

	desc->handle_irq(irq, desc);
}

call desc->handle_irq(irq, desc);
也就是:

  • void handle_level_irq(unsigned int irq, struct irq_desc *desc);
/* kernel/irq/chip.c */

/**
 *	handle_level_irq - Level type irq handler
 *	@irq:	the interrupt number
 *	@desc:	the interrupt description structure for this irq
 *
 *	Level type interrupts are active as long as the hardware line has
 *	the active level. This may require to mask the interrupt and unmask
 *	it after the associated handler has acknowledged the device, so the
 *	interrupt line is back to inactive.
 */
void
handle_level_irq(unsigned int irq, struct irq_desc *desc)
{
	printk("~~~ %s() irq:%d\n", __func__, irq);

	raw_spin_lock(&desc->lock);
	mask_ack_irq(desc);

	if (unlikely(irqd_irq_inprogress(&desc->irq_data)))
		if (!irq_check_poll(desc))
			goto out_unlock;

	desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
	kstat_incr_irqs_this_cpu(irq, desc);

	/*
	 * If its disabled or no action available
	 * keep it masked and get out of here
	 */
	if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data)))
		goto out_unlock;

	printk("~~~ %s() irq:%d, call handle_irq_event(), action name:%s\n", \
		__func__, irq, desc->action->name);
	handle_irq_event(desc);

	cond_unmask_irq(desc);

out_unlock:
	raw_spin_unlock(&desc->lock);
}
EXPORT_SYMBOL_GPL(handle_level_irq);

call:
...
mask_ack_irq(desc);
...
handle_irq_event(desc);
cond_unmask_irq(desc);
...

mask_ack_irq(desc)
    |
    desc->irq_data.chip->irq_mask(&desc->irq_data);  / * gpio mask : irq disable  */
    desc->irq_data.chip->irq_ack(&desc->irq_data);  / * gpio ack : irq pend clear */

/**/

cond_unmask_irq(desc)
    |
    desc->irq_data.chip->irq_unmask(&desc->irq_data); /* gpio unmask : irq enable */

  • irqreturn_t handle_irq_event(struct irq_desc *desc)
/* kernel/irq/handle.c */

irqreturn_t handle_irq_event(struct irq_desc *desc)
{
	struct irqaction *action = desc->action;
	irqreturn_t ret;

	if (desc->irq_data.irq >= IRQ_PHY_GPIOA)
		printk("~~~ %s() irq:%d, name:%s, irq_chip:%s\n", \
			__func__, desc->irq_data.irq, desc->name, \
			(desc->irq_data.chip)->name);

	desc->istate &= ~IRQS_PENDING;
	irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);
	raw_spin_unlock(&desc->lock);

	ret = handle_irq_event_percpu(desc, action);

	raw_spin_lock(&desc->lock);
	irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);
	return ret;
}


/* kernel/irq/handle.c */

irqreturn_t
handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action)
{
	irqreturn_t retval = IRQ_NONE;
	unsigned int flags = 0, irq = desc->irq_data.irq;

	if (irq >= IRQ_PHY_GPIOA)
		printk("~~~ %s() irq:%d, name:%s\n", __func__, irq, desc->name);

	do {
		irqreturn_t res;

		trace_irq_handler_entry(irq, action);
		res = action->handler(irq, action->dev_id);
		if (irq >= IRQ_PHY_GPIOA)
			printk("~~~ %s() after call action->handler(), name:%s, res:%d\n", \
				__func__, action->name, res);
		trace_irq_handler_exit(irq, action, res);

		if (WARN_ONCE(!irqs_disabled(),"irq %u handler %pF enabled interrupts\n",
			      irq, action->handler))
			local_irq_disable();

		switch (res) {
		case IRQ_WAKE_THREAD:
			/*
			 * Catch drivers which return WAKE_THREAD but
			 * did not set up a thread function
			 */
			if (unlikely(!action->thread_fn)) {
				warn_no_thread(irq, action);
				break;
			}

			if (irq >= IRQ_PHY_GPIOA)
				printk("~~~ %s() irq_wake_thread\n", \
					__func__);
			irq_wake_thread(desc, action);

			/* Fall through to add to randomness */
		case IRQ_HANDLED:
			flags |= action->flags;
			break;

		default:
			break;
		}

		retval |= res;
		action = action->next;
	} while (action);

	add_interrupt_randomness(irq, flags);

	if (!noirqdebug)
		note_interrupt(irq, desc, retval);
	return retval;
}

终于到达:
res = action->handler(irq, action->dev_id);



  • gpio_interrupt_init/ alive_gpio_interrupt initialize, log
[    0.000000] ~~~ gpio_init() set GPIO* handler_data chained_handler
[    0.000000] ~~~ irq_set_chained_handler() irq:85, is_chained:1
[    0.000000] ~~~ irq_startup() irq:85, call irq_enable()
[    0.000000] ~~~ gpio_init() GPIO parent irq:85, chip name:GIC
[    0.000000] GPIO  @f001b000: start 138, mask 0xffffffff (gpio 86)
[    0.000000] ~~~ gpio_init() set GPIO* handler_data chained_handler
[    0.000000] ~~~ irq_set_chained_handler() irq:86, is_chained:1
[    0.000000] ~~~ irq_startup() irq:86, call irq_enable()
[    0.000000] ~~~ gpio_init() GPIO parent irq:86, chip name:GIC
[    0.000000] GPIO  @f001c000: start 170, mask 0xffffffff (gpio 87)
[    0.000000] ~~~ gpio_init() set GPIO* handler_data chained_handler
[    0.000000] ~~~ irq_set_chained_handler() irq:87, is_chained:1
[    0.000000] ~~~ irq_startup() irq:87, call irq_enable()
[    0.000000] ~~~ gpio_init() GPIO parent irq:87, chip name:GIC
[    0.000000] GPIO  @f001d000: start 202, mask 0xffffffff (gpio 88)
[    0.000000] ~~~ gpio_init() set GPIO* handler_data chained_handler
[    0.000000] ~~~ irq_set_chained_handler() irq:88, is_chained:1
[    0.000000] ~~~ irq_startup() irq:88, call irq_enable()
[    0.000000] ~~~ gpio_init() GPIO parent irq:88, chip name:GIC
[    0.000000] GPIO  @f001e000: start 234, mask 0xffffffff (gpio 89)
[    0.000000] ~~~ gpio_init() set GPIO* handler_data chained_handler
[    0.000000] ~~~ irq_set_chained_handler() irq:89, is_chained:1
[    0.000000] ~~~ irq_startup() irq:89, call irq_enable()
[    0.000000] ~~~ gpio_init() GPIO parent irq:89, chip name:GIC
[    0.000000] ALIVE @f0010800: start 266, mask 0x000000ff (alive 36, num 6)
[    0.000000] ~~~ irq_set_chained_handler() irq:36, is_chained:1
[    0.000000] ~~~ irq_startup() irq:36, call irq_enable()
[    0.000000] ~~~ alive_init() ALIVE GPIO parent irq:36, chip name:GIC

  • press the button and release, trigger gpio interrupt handling, key driver code and log
#define CFG_KEYPAD_KEY_OK				{ PAD_GPIO_B + 31 }
#define CFG_KEYPAD_KEY_OK_CODE						{ KEY_OK } /* 352 */

/* drivers/input/keyboard/nxp_io_key.c */
...
static int nxp_key_probe(struct platform_device *pdev)
{
...
	ret = request_irq(gpio_to_irq(code->io), nxp_key_irqhnd,
					(IRQF_SHARED | IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING), pdev->name, code);
...
}
...
static irqreturn_t nxp_key_irqhnd(int irqno, void *dev_id)
{
	struct key_code *code = dev_id;

	printk("~~~ %s() irqno:%d\n", __func__, irqno);

	queue_delayed_work(code->kcode_wq,
				&code->kcode_work, DELAY_WORK_JIFFIES);

	return IRQ_HANDLED;
}
...
static void nxp_key_event_wq(struct work_struct *work)
{
	struct key_code *code = (struct key_code *)work;
	struct key_info *key = code->info;
	unsigned int keycode = code->keycode;
	int press = 0;
	u_long flags;

	local_irq_save(flags);

	press = gpio_get_value_cansleep(code->io);
	if (code->detect_high)
		press = !press;

	local_irq_restore(flags);

	if(press != code->keystat) {
		code->keystat = press;
		if (KEY_STAT_PRESS == press) {
			input_report_key(key->input, keycode, 1);
			input_sync(key->input);
		} else {
			input_report_key(key->input, keycode, 0);
			input_sync(key->input);
		}
		pr_debug("key io:%d, code:%4d %s\n", code->io, keycode,
			(KEY_STAT_PRESS==press?"DN":"UP"));
	}
}
...
[root@machine /]# 
[root@machine /]# 
[   41.944000] ~~~ gic_handle_irq() hwirq:86
[   41.944000] ~~~ irq_domain_legacy_revmap() hwirq:86
[   41.948000] ~~~ irq_find_mapping() hwirq:86, irq:86
[   41.952000] ~~~ gic_handle_irq() irqnr:86
[   41.956000] ~~~ generic_handle_irq() irq:86, irq_desc->irq_data.irq:0
[   41.964000] ~~~ generic_handle_irq_desc() irq:86, irq_desc->irq_data.irq:0
[   41.972000] gpio_handler: gpio irq=86 [GPIOB.31], stat=0x80000000, mask=0x88000000
[   41.976000] ~~~ gpio_handler: gpio irq:86 [GPIOB.31], stat=0x80000000, mask=0x88000000
[   41.984000] ~~~ gpio_handler() irq:169, irq_desc->irq_data.irq:0
[   41.992000] ~~~ gpio_handler() call generic_handle_irq_desc(), irq_desc->irq_data.irq:169, atcion name:nxp-keypad
[   42.004000] ~~~ generic_handle_irq_desc() irq:169, irq_desc->irq_data.irq:0
[   42.008000] ~~~ handle_level_irq() irq:169
[   42.012000] gpio_mask_irq: gpio irq = 169, GPIOB.31
[   42.016000] gpio_ack_irq: gpio irq = 169, GPIOB.31
[   42.024000] ~~~ handle_level_irq() irq:169, call handle_irq_event(), action name:nxp-keypad
[   42.032000] ~~~ handle_irq_event() irq:169, name:(null), irq_chip:GPIO
[   42.036000] ~~~ handle_irq_event_percpu() irq:169, name:(null)
[   42.044000] ~~~ nxp_key_irqhnd() irqno:169
[   42.048000] ~~~ handle_irq_event_percpu() after call action->handler(), name:nxp-keypad, res:1
[   42.056000] gpio_unmask_irq: gpio irq = 169, GPIOB.31
[   42.060000] ~~~ gpio_handler() write CPUI end of INT reg, irq:169
[   42.068000] key io:63, code: 352 DN

[root@machine /]# 
[root@machine /]# 
[root@machine /]# 
[root@machine /]# 
[root@machine /]# 
[root@machine /]# 
[root@machine /]# 
[   46.620000] ~~~ gic_handle_irq() hwirq:86
[   46.620000] ~~~ irq_domain_legacy_revmap() hwirq:86
[   46.624000] ~~~ irq_find_mapping() hwirq:86, irq:86
[   46.628000] ~~~ gic_handle_irq() irqnr:86
[   46.632000] ~~~ generic_handle_irq() irq:86, irq_desc->irq_data.irq:0
[   46.640000] ~~~ generic_handle_irq_desc() irq:86, irq_desc->irq_data.irq:0
[   46.644000] gpio_handler: gpio irq=86 [GPIOB.31], stat=0x80000000, mask=0x88000000
[   46.652000] ~~~ gpio_handler: gpio irq:86 [GPIOB.31], stat=0x80000000, mask=0x88000000
[   46.660000] ~~~ gpio_handler() irq:169, irq_desc->irq_data.irq:0
[   46.668000] ~~~ gpio_handler() call generic_handle_irq_desc(), irq_desc->irq_data.irq:169, atcion name:nxp-keypad
[   46.676000] ~~~ generic_handle_irq_desc() irq:169, irq_desc->irq_data.irq:0
[   46.684000] ~~~ handle_level_irq() irq:169
[   46.688000] gpio_mask_irq: gpio irq = 169, GPIOB.31
[   46.692000] gpio_ack_irq: gpio irq = 169, GPIOB.31
[   46.696000] ~~~ handle_level_irq() irq:169, call handle_irq_event(), action name:nxp-keypad
[   46.704000] ~~~ handle_irq_event() irq:169, name:(null), irq_chip:GPIO
[   46.712000] ~~~ handle_irq_event_percpu() irq:169, name:(null)
[   46.716000] ~~~ nxp_key_irqhnd() irqno:169
[   46.724000] ~~~ handle_irq_event_percpu() after call action->handler(), name:nxp-keypad, res:1
[   46.732000] gpio_unmask_irq: gpio irq = 169, GPIOB.31
[   46.736000] ~~~ gpio_handler() write CPUI end of INT reg, irq:169
[   46.740000] key io:63, code: 352 UP

[root@machine /]# 


  • s5p6818 gpio irq code:
/* arch/arm/mach-s5p6818/irq.c */

/*
 * (C) Copyright 2009
 * jung hyun kim, Nexell Co, <jhkim@nexell.co.kr>
 *
 * 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.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/export.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/hardware/gic.h>
#include <asm/mach/irq.h>
#include <mach/platform.h>
#include <mach/gpio.h>

/*
#define pr_debug	printk
*/

#define	INTC_BASE		(void __iomem *)IO_ADDRESS(PHY_BASEADDR_INTC)
#define	GIC_PHY_OFFSET	(0)

//----------------------------------------------------------------------------
static void __init __gic_init(void __iomem *dist_base, void __iomem *cpu_base);
static void __init gpio_init(void __iomem *base, unsigned int irq_start,
							u32 irq_sources, u32 resume_sources);
static void __init alive_init(void __iomem *base, unsigned int irq_start,
							u32 irq_sources, u32 resume_sources);

/*----------------------------------------------------------------------------
 *  cpu irq handler
 */
#define GIC_DIST_BASE		(void __iomem *)(INTC_BASE + 0x00001000)		// 0xC0009000
#define GIC_CPUI_BASE		(void __iomem *)(INTC_BASE + 0x00002000)		// 0xC000a000

#define GPIO_INT_BASE		(void __iomem *)IO_ADDRESS(PHY_BASEADDR_GPIOA)
#define GPIO_BASE_OFFSET	(0x1000)
#define GPIO_INT_MASK		(0xFFFFFFFF)

#define ALIVE_INT_BASE		(void __iomem *)IO_ADDRESS(PHY_BASEADDR_CLKPWR_MODULE + 0x800)
#define ALIVE_INT_MASK		(0x000000FF)

/*
 *  cpu irq handler
 */
void __init nxp_cpu_irq_init(void)
{
	pr_debug("%s:%d\n", __func__, __LINE__);
	printk("~~~ %s()\n", __func__);

	__gic_init(GIC_DIST_BASE, (void __iomem *)GIC_CPUI_BASE);
	gpio_init(GPIO_INT_BASE , IRQ_GPIO_START, GPIO_INT_MASK, 0);	/* 64 ~ 223 (A,B,C,D,E) */
	alive_init(ALIVE_INT_BASE, IRQ_ALIVE_START, ALIVE_INT_MASK, 0); /* 224 ~ 231 */

#ifdef CONFIG_FIQ
	init_FIQ();
#endif

	/* wake up source from idle */
	irq_set_irq_wake(IRQ_PHY_CLKPWR_ALIVEIRQ + GIC_PHY_OFFSET, 1);
#if PM_RTC_WAKE
	irq_set_irq_wake(IRQ_PHY_CLKPWR_RTCIRQ + GIC_PHY_OFFSET, 1);
#endif
}

static void __init __gic_init(void __iomem *dist_base, void __iomem *cpu_base)
{
	int irq = IRQ_GIC_PPI_VIC;

	printk(KERN_INFO "GIC   @%p: start %3d (gic %d)\n",
		dist_base, IRQ_GIC_START, (irq-IRQ_GIC_START));

	printk("~~~ %s() call gic_init()\n", __func__);
	gic_init(0, IRQ_GIC_PPI_START, dist_base, cpu_base);
}

/*----------------------------------------------------------------------------
 *  ALIVE irq chain handler
 *  start  -> request_irq -> alive irq_unmask
 *  do IRQ -> alive handler -> alive irq_mask -> alive irq_ack -> driver handler -> alive irq_unmask ->
 *  end    -> disable
 ----------------------------------------------------------------------------*/
#define	ALIVE_MOD_REST		(0x04)	// detect mode reset
#define	ALIVE_MOD_SET		(0x08)	// detect mode
#define	ALIVE_MOD_READ		(0x0C)	// detect mode read

#define	ALIVE_DET_RESET		(0x4C)
#define	ALIVE_DET_SET		(0x50)
#define	ALIVE_DET_READ		(0x54)

#define	ALIVE_INT_RESET		(0x58)	// interrupt reset 	: disable
#define	ALIVE_INT_SET		(0x5C)	// interrupt set	: enable
#define	ALIVE_INT_SET_READ	(0x60)	// interrupt set read
#define	ALIVE_INT_STATUS	(0x64)	// interrupt detect pending and clear

#define	ALIVE_OUT_RESET		(0x74)
#define	ALIVE_OUT_SET		(0x78)
#define	ALIVE_OUT_READ		(0x7C)

static void alive_ack_irq(struct irq_data *d)
{
	void __iomem *base = irq_data_get_irq_chip_data(d);
	int bit = (d->irq - IRQ_ALIVE_START) & 0x1F;
	pr_debug("%s: alive irq = %d, io = %d\n", __func__, d->irq, bit);

	/* alive ack : irq pend clear */
	writel((1<<bit), base + ALIVE_INT_STATUS);
	readl(base + ALIVE_INT_STATUS);
}

static void alive_mask_irq(struct irq_data *d)
{
	void __iomem *base = irq_data_get_irq_chip_data(d);
	int bit = (d->irq - IRQ_ALIVE_START) & 0x1F;
	pr_debug("%s: alive irq = %d, io = %d\n", __func__, d->irq, bit);

	/* alive mask : irq reset (disable) */
	writel((1<<bit), base + ALIVE_INT_RESET);
}

static void alive_unmask_irq(struct irq_data *d)
{
	void __iomem *base = irq_data_get_irq_chip_data(d);
	int bit = (d->irq - IRQ_ALIVE_START) & 0x1F;
	pr_debug("%s: alive irq = %d, io = %d\n", __func__, d->irq, bit);

	/* alive unmask : irq set (enable) */
	writel((1<<bit), base + ALIVE_INT_SET);
	readl(base + ALIVE_INT_SET_READ);
}

static int alive_set_type_irq(struct irq_data *d, unsigned int type)
{
	void __iomem *base = irq_data_get_irq_chip_data(d);
	u32 reg = 0;
	int bit = (d->irq - IRQ_ALIVE_START) & 0x1F;
	int offs = 0, i = 0;
	NX_ALIVE_DETECTMODE mode = 0;

	pr_debug("%s: alive irq = %d, io = %d, type=0x%x\n",
		__func__, d->irq, bit, type);

	switch (type) {
	case IRQ_TYPE_NONE:	printk(KERN_WARNING "%s: No edge setting!\n", __func__);
		break;
	case IRQ_TYPE_EDGE_FALLING:	mode = NX_ALIVE_DETECTMODE_SYNC_FALLINGEDGE; break;
	case IRQ_TYPE_EDGE_RISING:	mode = NX_ALIVE_DETECTMODE_SYNC_RISINGEDGE;	break;
	case IRQ_TYPE_EDGE_BOTH:	mode = NX_ALIVE_DETECTMODE_SYNC_FALLINGEDGE; break;	/* and Rising Edge */
	case IRQ_TYPE_LEVEL_LOW:	mode = NX_ALIVE_DETECTMODE_ASYNC_LOWLEVEL; break;
	case IRQ_TYPE_LEVEL_HIGH:	mode = NX_ALIVE_DETECTMODE_ASYNC_HIGHLEVEL; break;
	default:
		printk(KERN_ERR "%s: No such irq type %d", __func__, type);
		return -1;
	}

	for ( ; 6 > i; i++, offs += 0x0C) {
		reg = (i == mode ? ALIVE_MOD_SET : ALIVE_MOD_REST);
		writel(1<<bit, (base + reg  + offs));	/* set o reset mode */
	}

	/*
	 * set risingedge mode for both edge
	 * 0x2C : Risingedge
	 */
	if (IRQ_TYPE_EDGE_BOTH == type)
		writel(1<<bit, (base + 0x2C));

	writel(1<<bit, base + ALIVE_DET_SET);
	writel(1<<bit, base + ALIVE_INT_SET);
	writel(1<<bit, base + ALIVE_OUT_RESET);

	return 0;
}

static int alive_set_wake(struct irq_data *d, unsigned int on)
{
#if (0)
	void __iomem *base = irq_data_get_irq_chip_data(d);
	int bit = (d->irq - IRQ_ALIVE_START) & 0x1F;

	pr_info("%s: alive irq = %d, io = %d wake %s\n",
		__func__, d->irq, bit, on?"on":"off");
#endif
	return 0;
}

static void alive_irq_enable(struct irq_data *d)
{
	void __iomem *base = irq_data_get_irq_chip_data(d);
	int bit = (d->irq - IRQ_ALIVE_START) & 0x1F;
	pr_debug("%s: alive irq = %d, io = %d\n", __func__, d->irq, bit);
	printk("~~~ %s() alive irq:%d, io:%d\n", __func__, d->irq, bit);

	/* alive unmask : irq set (enable) */
	writel((1<<bit), base + ALIVE_INT_SET);
	readl(base + ALIVE_INT_SET_READ);
}

static void alive_irq_disable(struct irq_data *d)
{
	void __iomem *base = irq_data_get_irq_chip_data(d);
	int bit = (d->irq - IRQ_ALIVE_START) & 0x1F;
	pr_debug("%s: alive irq = %d, io = %d\n", __func__, d->irq, bit);
	printk("~~~ %s() alive irq:%d, io:%d\n", __func__, d->irq, bit);

	/* alive mask : irq reset (disable) */
	writel((1<<bit), base + ALIVE_INT_RESET);
}

static struct irq_chip alive_chip = {
	.name			= "ALIVE",
	.irq_ack		= alive_ack_irq,
	.irq_mask		= alive_mask_irq,
	.irq_unmask		= alive_unmask_irq,
	.irq_set_type	= alive_set_type_irq,
	.irq_set_wake	= alive_set_wake,
	.irq_enable		= alive_irq_enable,
	.irq_disable	= alive_irq_disable,
};

static void alive_handler(unsigned int irq, struct irq_desc *desc)
{
	void __iomem *base = irq_desc_get_handler_data(desc);
	u32 stat, mask;
	int phy, bit;

	mask = readl(base + ALIVE_INT_SET_READ);
	stat = readl(base + ALIVE_INT_STATUS) & mask;
	bit  = ffs(stat) - 1;
	phy  = irq;

	pr_debug("%s: alive irq=%d [io=%d], stat=0x%02x, mask=0x%02x\n",
		__func__, phy, bit, stat, mask);

	if (-1 == bit) {
		printk(KERN_ERR "Unknown alive irq=%d, stat=0x%08x, mask=0x%02x\r\n",
			phy, stat, mask);
		writel(-1, (base + ALIVE_INT_STATUS));	/* clear alive status all */
		writel_relaxed(phy, GIC_CPUI_BASE + GIC_CPU_EOI);
		return;
	}

	/* alive descriptor */
	irq  = IRQ_ALIVE_START + bit;
	desc = irq_desc + irq;

	if (desc && desc->action) {
		desc->action->flags |= IRQF_DISABLED;	/* disable irq reentrant */
		generic_handle_irq_desc(irq, desc);
	} else {
		printk(KERN_ERR "Error, not registered alive interrupt=%d (%d.%d), disable !!!\n",
			irq, phy, bit);
		writel(readl(base + ALIVE_INT_SET) & ~(1<<bit), base + ALIVE_INT_SET);		/* alive mask : irq disable */
		writel(readl(base + ALIVE_INT_STATUS) | (1<<bit), base + ALIVE_INT_STATUS);	/* alive ack  : irq pend clear */
		readl(base + ALIVE_INT_STATUS);	/* Guarantee */
	}

	writel_relaxed(phy, GIC_CPUI_BASE + GIC_CPU_EOI);
	return;
}

static void __init alive_init(void __iomem *base, unsigned int irq_start,
		     u32 irq_sources, u32 resume_sources)
{
	int irq_alive = IRQ_PHY_CLKPWR_ALIVEIRQ + GIC_PHY_OFFSET;
	int num = IRQ_ALIVE_END - IRQ_ALIVE_START;
	int i = 0;

	printk(KERN_INFO "ALIVE @%p: start %3d, mask 0x%08x (alive %d, num %d)\n",
		base, irq_start, irq_sources, irq_alive, num);

	/* set alive irq handler */
	for (i = 0; num > i; i++) {
		if (irq_sources & (1 << i)) {
			int irq = irq_start + i;
			irq_set_chip_data(irq, base);
			irq_set_chip_and_handler(irq, &alive_chip, handle_level_irq);
			set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
		}
	}

	/* register alive irq handler data */
	irq_set_handler_data(irq_alive, base);

	/*
	 * call alive_mask_irq
	 * chip and chip data is registerd at gic_init
	 */
	irq_set_chained_handler(irq_alive, alive_handler);

	struct irq_desc *desc = irq_to_desc(irq_alive);
	struct irq_chip *chip = desc->irq_data.chip;
	printk("~~~ %s() ALIVE GPIO parent irq:%u, chip name:%s\n",\
		__func__, irq_alive, chip->name);
}

/*----------------------------------------------------------------------------
 *  GPIO irq chain handler
 *  start  -> request_irq -> gpio irq_unmask
 *  do IRQ -> gpio handler -> gpio irq_mask -> gpio irq_ack -> driver handler -> gpio irq_unmask ->
 *  end    -> disable
 ----------------------------------------------------------------------------*/
static const char *io_name[] = { "GPIOA", "GPIOB", "GPIOC", "GPIOD", "GPIOE", };

#define	PIO_IRQ_BASE	IRQ_PHY_GPIOA
#define	VIO_IRQ_BASE	IRQ_GPIO_START
#define	VIO_NAME(i)		(io_name[(i-VIO_IRQ_BASE)/32])
#define	PIO_NAME(i)		(io_name[(i-PIO_IRQ_BASE)])

#define	GPIO_OUT_ENB		0x04
#define	GPIO_INT_MODE0		0x08	// 0x08,0x0C
#define	GPIO_INT_MODE1		0x28
#define	GPIO_INT_ENB		0x10
#define	GPIO_INT_STATUS		0x14
#define	GPIO_ALT_MODE		0x20	// 0x20,0x24
#define	GPIO_INT_DET		0x3C

static void gpio_ack_irq(struct irq_data *d)
{
	void __iomem *base = irq_data_get_irq_chip_data(d);
	int bit = (d->irq - IRQ_GPIO_START) & 0x1F;
	pr_debug("%s: gpio irq = %d, %s.%d\n", __func__, d->irq, VIO_NAME(d->irq), bit);

	/* gpio ack : irq pend clear */
	writel((1<<bit), base + GPIO_INT_STATUS);
	readl(base + GPIO_INT_STATUS);
}

static void gpio_mask_irq(struct irq_data *d)
{
	void __iomem *base = irq_data_get_irq_chip_data(d);
	int bit = (d->irq - IRQ_GPIO_START) & 0x1F;
	pr_debug("%s: gpio irq = %d, %s.%d\n", __func__, d->irq, VIO_NAME(d->irq), bit);

	/* gpio mask : irq disable */
	writel(readl(base + GPIO_INT_ENB) & ~(1<<bit), base + GPIO_INT_ENB);
	writel(readl(base + GPIO_INT_DET) & ~(1<<bit), base + GPIO_INT_DET);
}

static void gpio_unmask_irq(struct irq_data *d)
{
	void __iomem *base = irq_data_get_irq_chip_data(d);
	int bit = (d->irq - IRQ_GPIO_START) & 0x1F;
	pr_debug("%s: gpio irq = %d, %s.%d\n", __func__, d->irq, VIO_NAME(d->irq), bit);

	/* gpio unmask : irq enable */
	writel(readl(base + GPIO_INT_ENB) | (1<<bit), base + GPIO_INT_ENB);
	writel(readl(base + GPIO_INT_DET) | (1<<bit), base + GPIO_INT_DET);
	readl(base + GPIO_INT_ENB);
}

static int gpio_set_type_irq(struct irq_data *d, unsigned int type)
{
	void __iomem *base = irq_data_get_irq_chip_data(d);
	int bit = (d->irq - IRQ_GPIO_START) & 0x1F;
	unsigned int reg, val, alt;
	NX_GPIO_INTMODE mode = 0;
	pr_debug("%s: gpio irq = %d, %s.%d, type=0x%x\n",
		__func__, d->irq, VIO_NAME(d->irq), bit, type);

	switch (type) {
	case IRQ_TYPE_NONE:	printk(KERN_WARNING "%s: No edge setting!\n", __func__);
		break;
	case IRQ_TYPE_EDGE_RISING:	mode = NX_GPIO_INTMODE_RISINGEDGE;	break;
	case IRQ_TYPE_EDGE_FALLING:	mode = NX_GPIO_INTMODE_FALLINGEDGE;	break;
	case IRQ_TYPE_EDGE_BOTH:	mode = NX_GPIO_INTMODE_BOTHEDGE;	break;
	case IRQ_TYPE_LEVEL_LOW:	mode = NX_GPIO_INTMODE_LOWLEVEL;	break;
	case IRQ_TYPE_LEVEL_HIGH:	mode = NX_GPIO_INTMODE_HIGHLEVEL;	break;
	default:
		printk(KERN_ERR "%s: No such irq type %d", __func__, type);
		return -1;
	}

	/* gpio out : output disable */
	writel(readl(base + GPIO_OUT_ENB) & ~(1<<bit), base + GPIO_OUT_ENB);

	/* gpio mode : interrupt mode */
	reg  = (unsigned int)(base + GPIO_INT_MODE0 + (bit/16) * 4);
	val  = readl(reg) & ~(3<<((bit&0xf) * 2));
	val |= (mode&0x3) << ((bit&0xf) * 2);
	pr_debug("reg=0x%08x, val=0x%08x\n", reg, val);

	writel(val, reg);

	reg  = (unsigned int)(base + GPIO_INT_MODE1);
	val  = readl(reg) & ~(1<<bit);
	val |= ((mode>>2) & 0x1) << bit;
	pr_debug("reg=0x%08x, val=0x%08x\n", reg, val);

	writel(val, reg);

	/* gpio alt : gpio mode for irq */
	reg  = (unsigned int)(base + GPIO_ALT_MODE + (bit/16) * 4);
	val  = readl(reg) & ~(3<<((bit&0xf) * 2));
	alt  = gpio_alt_no[(d->irq-VIO_IRQ_BASE)/32][bit];
	val |= alt << ((bit&0xf) * 2);
	pr_debug("reg=0x%08x, val=0x%08x\n", reg, val);
	writel(val, reg);

	return 0;
}

static int gpio_set_wake(struct irq_data *d, unsigned int on)
{
#if (0)
	void __iomem *base = irq_data_get_irq_chip_data(d);
	int bit = (d->irq - IRQ_GPIO_START) & 0x1F;
	pr_debug("%s: gpio irq = %d, %s.%d wake %s\n",
		__func__, d->irq, VIO_NAME(d->irq), bit, on?"on":"off");
#endif
	return 0;
}

static void gpio_irq_enable(struct irq_data *d)
{
	void __iomem *base = irq_data_get_irq_chip_data(d);
	int bit = (d->irq - IRQ_GPIO_START) & 0x1F;
	pr_debug("%s: gpio irq = %d, %s.%d\n", __func__, d->irq, VIO_NAME(d->irq), bit);
	printk("~~~ %s() gpio irq:%d, %s.%d\n", __func__, d->irq, VIO_NAME(d->irq), bit);

	/* gpio unmask : irq enable */
	writel(readl(base + GPIO_INT_ENB) | (1<<bit), base + GPIO_INT_ENB);
	writel(readl(base + GPIO_INT_DET) | (1<<bit), base + GPIO_INT_DET);
}

static void gpio_irq_disable(struct irq_data *d)
{
	void __iomem *base = irq_data_get_irq_chip_data(d);
	int bit = (d->irq - IRQ_GPIO_START) & 0x1F;
	pr_debug("%s: gpio irq = %d, %s.%d\n", __func__, d->irq, VIO_NAME(d->irq), bit);
	printk("~~~ %s() gpio irq:%d, %s.%d\n", __func__, d->irq, VIO_NAME(d->irq), bit);

	/* gpio mask : irq disable */
	writel(readl(base + GPIO_INT_ENB) & ~(1<<bit), base + GPIO_INT_ENB);
	writel(readl(base + GPIO_INT_DET) & ~(1<<bit), base + GPIO_INT_DET);
}

static struct irq_chip gpio_chip = {
	.name			= "GPIO",
	.irq_ack		= gpio_ack_irq,
	.irq_mask		= gpio_mask_irq,
	.irq_unmask		= gpio_unmask_irq,
	.irq_set_type	= gpio_set_type_irq,
	.irq_set_wake	= gpio_set_wake,
	.irq_enable		= gpio_irq_enable,
	.irq_disable	= gpio_irq_disable,
};

static void gpio_handler(unsigned int irq, struct irq_desc *desc)
{
	void __iomem *base = irq_desc_get_handler_data(desc);
	u32 stat, mask;
	int phy, bit;

	mask = readl(base + GPIO_INT_ENB);
	stat = readl(base + GPIO_INT_STATUS) & mask;
	bit  = ffs(stat) - 1;
	phy  = irq;

	pr_debug("%s: gpio irq=%d [%s.%d], stat=0x%08x, mask=0x%08x\n",
		__func__, phy, PIO_NAME(phy), bit, stat, mask);
	printk("~~~ %s: gpio irq:%d [%s.%d], stat=0x%08x, mask=0x%08x\n",
		__func__, phy, PIO_NAME(phy), bit, stat, mask);

	if (-1 == bit) {
		printk(KERN_ERR "Unknown gpio phy irq=%d, status=0x%08x, mask=0x%08x\r\n",
			phy, stat, mask);
		writel(-1, (base + GPIO_INT_STATUS));	/* clear gpio status all */
		writel_relaxed(phy, GIC_CPUI_BASE + GIC_CPU_EOI);
		return;
	}

	/* gpio descriptor */
	irq  = (VIO_IRQ_BASE + bit + (32 * (phy - PIO_IRQ_BASE)));	// virtual irq
	printk("~~~ %s() irq:%u, irq_desc->irq_data.irq:%u\n", \
		__func__, irq, irq_desc->irq_data.irq);
	/*desc = irq_desc + irq;*/ /*the global struct irq_desc irq_desc[NR_IRQS]*/
	desc = irq_to_desc(irq);

	if (desc && desc->action) {
		/* disable irq reentrant */
		desc->action->flags |= IRQF_DISABLED;
		printk("~~~ %s() call generic_handle_irq_desc(), irq_desc->irq_data.irq:%u, atcion name:%s\n", \
			__func__, desc->irq_data.irq, desc->action->name);
		generic_handle_irq_desc(irq, desc);
	} else {
		printk(KERN_ERR "Error, not registered gpio interrupt=%d (%s.%d), disable !!!\n",
			irq, PIO_NAME(phy), bit);
		writel(readl(base + GPIO_INT_ENB) & ~(1<<bit), base + GPIO_INT_ENB);		/* gpio mask : irq disable */
		writel(readl(base + GPIO_INT_STATUS) | (1<<bit), base + GPIO_INT_STATUS);	/* gpio ack  : irq pend clear */
		readl(base + GPIO_INT_STATUS);	/* Guarantee */
	}

	printk("~~~ %s() write CPUI end of INT reg, irq:%u\n", __func__, irq);
	writel_relaxed(phy, GIC_CPUI_BASE + GIC_CPU_EOI);

	return;
}

static void __init gpio_init(void __iomem *base, unsigned int irq_start,
		     u32 irq_sources, u32 resume_sources)
{
	int irq_gpio = IRQ_PHY_GPIOA + GIC_PHY_OFFSET;
	int num = 5;  /* A,B,C,D,E */
	int ios = 32; /* GPIO 32 */
	int n = 0,i = 0;

	/* set gpio irq handler */
	for (n = 0; num > n; n++) {
		printk(KERN_INFO "GPIO  @%p: start %3d, mask 0x%08x (gpio %d)\n",
			base, irq_start, irq_sources, irq_gpio);

		for (i = 0; ios > i; i++) {
			if (irq_sources & (1 << i)) {
				int irq = irq_start + i;
				irq_set_chip_data(irq, base);
				irq_set_chip_and_handler(irq, &gpio_chip, handle_level_irq);
				set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
			}
		}
		/* init gpio irq register  */
		writel(0xFFFFFFFF, base + GPIO_INT_STATUS);
		writel(0x0, base + GPIO_INT_ENB);
		writel(0x0, base + GPIO_INT_DET);

		printk("~~~ %s() set GPIO* handler_data chained_handler\n", __func__);

		/* register gpio irq handler data */
		irq_set_handler_data(irq_gpio, base);

		/*
	 	 * call gpio_mask_irq
	 	 * chip and chip data is registerd at gic_init
	 	 */
		irq_set_chained_handler(irq_gpio, gpio_handler);

		struct irq_desc *desc = irq_to_desc(irq_gpio);
		struct irq_chip *chip = desc->irq_data.chip;
		printk("~~~ %s() GPIO parent irq:%u, chip name:%s\n",\
			__func__, irq_gpio, chip->name);

		/* next */
		irq_gpio++;
		irq_start += ios;
		base += GPIO_BASE_OFFSET;
	}
}

上一篇:常用DBhelper封装方法


下一篇:(转+整理)C#中动态执行代码