-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ARM: 6414/1: ux500: DB5500 generic modem IRQ handler
This is a (threaded) IRQ handler for the modems that appear from the modem part of the DB5500 ASIC. Signed-off-by: Stefan Nilsson XK <stefan.xk.nilsson@stericsson.com> Signed-off-by: Linus Walleij <linus.walleij@stericsson.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
- Loading branch information
Linus Walleij
authored and
Russell King
committed
Oct 4, 2010
1 parent
4d4a4b0
commit b84ccd7
Showing
1 changed file
with
139 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
/* | ||
* Copyright (C) ST-Ericsson SA 2010 | ||
* Author: Stefan Nilsson <stefan.xk.nilsson@stericsson.com> for ST-Ericsson. | ||
* Author: Martin Persson <martin.persson@stericsson.com> for ST-Ericsson. | ||
* License terms: GNU General Public License (GPL), version 2. | ||
*/ | ||
|
||
#include <linux/module.h> | ||
#include <linux/kernel.h> | ||
#include <linux/irq.h> | ||
#include <linux/interrupt.h> | ||
#include <linux/io.h> | ||
#include <linux/slab.h> | ||
|
||
#define MODEM_INTCON_BASE_ADDR 0xBFFD3000 | ||
#define MODEM_INTCON_SIZE 0xFFF | ||
|
||
#define DEST_IRQ41_OFFSET 0x2A4 | ||
#define DEST_IRQ43_OFFSET 0x2AC | ||
#define DEST_IRQ45_OFFSET 0x2B4 | ||
|
||
#define PRIO_IRQ41_OFFSET 0x6A4 | ||
#define PRIO_IRQ43_OFFSET 0x6AC | ||
#define PRIO_IRQ45_OFFSET 0x6B4 | ||
|
||
#define ALLOW_IRQ_OFFSET 0x104 | ||
|
||
#define MODEM_INTCON_CPU_NBR 0x1 | ||
#define MODEM_INTCON_PRIO_HIGH 0x0 | ||
|
||
#define MODEM_INTCON_ALLOW_IRQ41 0x0200 | ||
#define MODEM_INTCON_ALLOW_IRQ43 0x0800 | ||
#define MODEM_INTCON_ALLOW_IRQ45 0x2000 | ||
|
||
#define MODEM_IRQ_REG_OFFSET 0x4 | ||
|
||
struct modem_irq { | ||
void __iomem *modem_intcon_base; | ||
}; | ||
|
||
|
||
static void setup_modem_intcon(void __iomem *modem_intcon_base) | ||
{ | ||
/* IC_DESTINATION_BASE_ARRAY - Which CPU to receive the IRQ */ | ||
writel(MODEM_INTCON_CPU_NBR, modem_intcon_base + DEST_IRQ41_OFFSET); | ||
writel(MODEM_INTCON_CPU_NBR, modem_intcon_base + DEST_IRQ43_OFFSET); | ||
writel(MODEM_INTCON_CPU_NBR, modem_intcon_base + DEST_IRQ45_OFFSET); | ||
|
||
/* IC_PRIORITY_BASE_ARRAY - IRQ priority in modem IRQ controller */ | ||
writel(MODEM_INTCON_PRIO_HIGH, modem_intcon_base + PRIO_IRQ41_OFFSET); | ||
writel(MODEM_INTCON_PRIO_HIGH, modem_intcon_base + PRIO_IRQ43_OFFSET); | ||
writel(MODEM_INTCON_PRIO_HIGH, modem_intcon_base + PRIO_IRQ45_OFFSET); | ||
|
||
/* IC_ALLOW_ARRAY - IRQ enable */ | ||
writel(MODEM_INTCON_ALLOW_IRQ41 | | ||
MODEM_INTCON_ALLOW_IRQ43 | | ||
MODEM_INTCON_ALLOW_IRQ45, | ||
modem_intcon_base + ALLOW_IRQ_OFFSET); | ||
} | ||
|
||
static irqreturn_t modem_cpu_irq_handler(int irq, void *data) | ||
{ | ||
int real_irq; | ||
int virt_irq; | ||
struct modem_irq *mi = (struct modem_irq *)data; | ||
|
||
/* Read modem side IRQ number from modem IRQ controller */ | ||
real_irq = readl(mi->modem_intcon_base + MODEM_IRQ_REG_OFFSET) & 0xFF; | ||
virt_irq = IRQ_MODEM_EVENTS_BASE + real_irq; | ||
|
||
pr_debug("modem_irq: Worker read addr 0x%X and got value 0x%X " | ||
"which will be 0x%X (%d) which translates to " | ||
"virtual IRQ 0x%X (%d)!\n", | ||
(u32)mi->modem_intcon_base + MODEM_IRQ_REG_OFFSET, | ||
real_irq, | ||
real_irq & 0xFF, | ||
real_irq & 0xFF, | ||
virt_irq, | ||
virt_irq); | ||
|
||
if (virt_irq != 0) | ||
generic_handle_irq(virt_irq); | ||
|
||
pr_debug("modem_irq: Done handling virtual IRQ %d!\n", virt_irq); | ||
|
||
return IRQ_HANDLED; | ||
} | ||
|
||
static void create_virtual_irq(int irq, struct irq_chip *modem_irq_chip) | ||
{ | ||
set_irq_chip(irq, modem_irq_chip); | ||
set_irq_handler(irq, handle_simple_irq); | ||
set_irq_flags(irq, IRQF_VALID); | ||
|
||
pr_debug("modem_irq: Created virtual IRQ %d\n", irq); | ||
} | ||
|
||
static int modem_irq_init(void) | ||
{ | ||
int err; | ||
static struct irq_chip modem_irq_chip; | ||
struct modem_irq *mi; | ||
|
||
pr_info("modem_irq: Set up IRQ handler for incoming modem IRQ %d\n", | ||
IRQ_DB5500_MODEM); | ||
|
||
mi = kmalloc(sizeof(struct modem_irq), GFP_KERNEL); | ||
if (!mi) { | ||
pr_err("modem_irq: Could not allocate device\n"); | ||
return -ENOMEM; | ||
} | ||
|
||
mi->modem_intcon_base = | ||
ioremap(MODEM_INTCON_BASE_ADDR, MODEM_INTCON_SIZE); | ||
pr_debug("modem_irq: ioremapped modem_intcon_base from " | ||
"phy 0x%x to virt 0x%x\n", MODEM_INTCON_BASE_ADDR, | ||
(u32)mi->modem_intcon_base); | ||
|
||
setup_modem_intcon(mi->modem_intcon_base); | ||
|
||
modem_irq_chip = dummy_irq_chip; | ||
modem_irq_chip.name = "modem_irq"; | ||
|
||
/* Create the virtual IRQ:s needed */ | ||
create_virtual_irq(MBOX_PAIR0_VIRT_IRQ, &modem_irq_chip); | ||
create_virtual_irq(MBOX_PAIR1_VIRT_IRQ, &modem_irq_chip); | ||
create_virtual_irq(MBOX_PAIR2_VIRT_IRQ, &modem_irq_chip); | ||
|
||
err = request_threaded_irq(IRQ_DB5500_MODEM, NULL, | ||
modem_cpu_irq_handler, IRQF_ONESHOT, | ||
"modem_irq", mi); | ||
if (err) | ||
pr_err("modem_irq: Could not register IRQ %d\n", | ||
IRQ_DB5500_MODEM); | ||
|
||
return 0; | ||
} | ||
|
||
arch_initcall(modem_irq_init); |