linux arm irq : gpio interrupt
Posted WangYangkai
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux arm irq : 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;
代码很简单:
- 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(); - 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;
}
}
以上是关于linux arm irq : gpio interrupt的主要内容,如果未能解决你的问题,请参考以下文章
linux arm irq : interrupt handling
linux arm irq : interrupt driver interface