android_kernel_samsung_msm8976/arch/sh/boards/bigsur/irq.c

335 lines
11 KiB
C
Raw Normal View History

/*
*
* By Dustin McIntire (dustin@sensoria.com) (c)2001
*
* Setup and IRQ handling code for the HD64465 companion chip.
* by Greg Banks <gbanks@pocketpenguins.com>
* Copyright (c) 2000 PocketPenguins Inc
*
* Derived from setup_hd64465.c which bore the message:
* Greg Banks <gbanks@pocketpenguins.com>
* Copyright (c) 2000 PocketPenguins Inc and
* Copyright (C) 2000 YAEGASHI Takeshi
* and setup_cqreek.c which bore message:
* Copyright (C) 2000 Niibe Yutaka
*
* May be copied or modified under the terms of the GNU General Public
* License. See linux/COPYING for more information.
*
* IRQ functions for a Hitachi Big Sur Evaluation Board.
*
*/
#undef DEBUG
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/param.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/bitops.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/bigsur/io.h>
#include <asm/hd64465/hd64465.h>
#include <asm/bigsur/bigsur.h>
//#define BIGSUR_DEBUG 3
#undef BIGSUR_DEBUG
#ifdef BIGSUR_DEBUG
#define DIPRINTK(n, args...) if (BIGSUR_DEBUG>(n)) printk(args)
#else
#define DIPRINTK(n, args...)
#endif /* BIGSUR_DEBUG */
#ifdef CONFIG_HD64465
extern int hd64465_irq_demux(int irq);
#endif /* CONFIG_HD64465 */
/*===========================================================*/
// Big Sur CPLD IRQ Routines
/*===========================================================*/
/* Level 1 IRQ routines */
static void disable_bigsur_l1irq(unsigned int irq)
{
unsigned char mask;
unsigned int mask_port = ((irq - BIGSUR_IRQ_LOW)/8) ? BIGSUR_IRLMR1 : BIGSUR_IRLMR0;
unsigned char bit = (1 << ((irq - MGATE_IRQ_LOW)%8) );
if(irq >= BIGSUR_IRQ_LOW && irq < BIGSUR_IRQ_HIGH) {
pr_debug("Disable L1 IRQ %d\n", irq);
DIPRINTK(2,"disable_bigsur_l1irq: IMR=0x%08x mask=0x%x\n",
mask_port, bit);
/* Disable IRQ - set mask bit */
mask = inb(mask_port) | bit;
outb(mask, mask_port);
return;
}
pr_debug("disable_bigsur_l1irq: Invalid IRQ %d\n", irq);
}
static void enable_bigsur_l1irq(unsigned int irq)
{
unsigned char mask;
unsigned int mask_port = ((irq - BIGSUR_IRQ_LOW)/8) ? BIGSUR_IRLMR1 : BIGSUR_IRLMR0;
unsigned char bit = (1 << ((irq - MGATE_IRQ_LOW)%8) );
if(irq >= BIGSUR_IRQ_LOW && irq < BIGSUR_IRQ_HIGH) {
pr_debug("Enable L1 IRQ %d\n", irq);
DIPRINTK(2,"enable_bigsur_l1irq: IMR=0x%08x mask=0x%x\n",
mask_port, bit);
/* Enable L1 IRQ - clear mask bit */
mask = inb(mask_port) & ~bit;
outb(mask, mask_port);
return;
}
pr_debug("enable_bigsur_l1irq: Invalid IRQ %d\n", irq);
}
/* Level 2 irq masks and registers for L2 decoding */
/* Level2 bitmasks for each level 1 IRQ */
const u32 bigsur_l2irq_mask[] =
{0x40,0x80,0x08,0x01,0x01,0x3C,0x3E,0xFF,0x40,0x80,0x06,0x03};
/* Level2 to ISR[n] map for each level 1 IRQ */
const u32 bigsur_l2irq_reg[] =
{ 2, 2, 3, 3, 1, 2, 1, 0, 1, 1, 3, 2};
/* Level2 to Level 1 IRQ map */
const u32 bigsur_l2_l1_map[] =
{7,7,7,7,7,7,7,7, 4,6,6,6,6,6,8,9, 11,11,5,5,5,5,0,1, 3,10,10,2,-1,-1,-1,-1};
/* IRQ inactive level (high or low) */
const u32 bigsur_l2_inactv_state[] = {0x00, 0xBE, 0xFC, 0xF7};
/* CPLD external status and mask registers base and offsets */
static const u32 isr_base = BIGSUR_IRQ0;
static const u32 isr_offset = BIGSUR_IRQ0 - BIGSUR_IRQ1;
static const u32 imr_base = BIGSUR_IMR0;
static const u32 imr_offset = BIGSUR_IMR0 - BIGSUR_IMR1;
#define REG_NUM(irq) ((irq-BIGSUR_2NDLVL_IRQ_LOW)/8 )
/* Level 2 IRQ routines */
static void disable_bigsur_l2irq(unsigned int irq)
{
unsigned char mask;
unsigned char bit = 1 << ((irq-BIGSUR_2NDLVL_IRQ_LOW)%8);
unsigned int mask_port = imr_base - REG_NUM(irq)*imr_offset;
if(irq >= BIGSUR_2NDLVL_IRQ_LOW && irq < BIGSUR_2NDLVL_IRQ_HIGH) {
pr_debug("Disable L2 IRQ %d\n", irq);
DIPRINTK(2,"disable_bigsur_l2irq: IMR=0x%08x mask=0x%x\n",
mask_port, bit);
/* Disable L2 IRQ - set mask bit */
mask = inb(mask_port) | bit;
outb(mask, mask_port);
return;
}
pr_debug("disable_bigsur_l2irq: Invalid IRQ %d\n", irq);
}
static void enable_bigsur_l2irq(unsigned int irq)
{
unsigned char mask;
unsigned char bit = 1 << ((irq-BIGSUR_2NDLVL_IRQ_LOW)%8);
unsigned int mask_port = imr_base - REG_NUM(irq)*imr_offset;
if(irq >= BIGSUR_2NDLVL_IRQ_LOW && irq < BIGSUR_2NDLVL_IRQ_HIGH) {
pr_debug("Enable L2 IRQ %d\n", irq);
DIPRINTK(2,"enable_bigsur_l2irq: IMR=0x%08x mask=0x%x\n",
mask_port, bit);
/* Enable L2 IRQ - clear mask bit */
mask = inb(mask_port) & ~bit;
outb(mask, mask_port);
return;
}
pr_debug("enable_bigsur_l2irq: Invalid IRQ %d\n", irq);
}
static void mask_and_ack_bigsur(unsigned int irq)
{
pr_debug("mask_and_ack_bigsur IRQ %d\n", irq);
if(irq >= BIGSUR_IRQ_LOW && irq < BIGSUR_IRQ_HIGH)
disable_bigsur_l1irq(irq);
else
disable_bigsur_l2irq(irq);
}
static void end_bigsur_irq(unsigned int irq)
{
pr_debug("end_bigsur_irq IRQ %d\n", irq);
if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) {
if(irq >= BIGSUR_IRQ_LOW && irq < BIGSUR_IRQ_HIGH)
enable_bigsur_l1irq(irq);
else
enable_bigsur_l2irq(irq);
}
}
static unsigned int startup_bigsur_irq(unsigned int irq)
{
u8 mask;
u32 reg;
pr_debug("startup_bigsur_irq IRQ %d\n", irq);
if(irq >= BIGSUR_IRQ_LOW && irq < BIGSUR_IRQ_HIGH) {
/* Enable the L1 IRQ */
enable_bigsur_l1irq(irq);
/* Enable all L2 IRQs in this L1 IRQ */
mask = ~(bigsur_l2irq_mask[irq-BIGSUR_IRQ_LOW]);
reg = imr_base - bigsur_l2irq_reg[irq-BIGSUR_IRQ_LOW] * imr_offset;
mask &= inb(reg);
outb(mask,reg);
DIPRINTK(2,"startup_bigsur_irq: IMR=0x%08x mask=0x%x\n",reg,inb(reg));
}
else {
/* Enable the L2 IRQ - clear mask bit */
enable_bigsur_l2irq(irq);
/* Enable the L1 bit masking this L2 IRQ */
enable_bigsur_l1irq(bigsur_l2_l1_map[irq-BIGSUR_2NDLVL_IRQ_LOW]);
DIPRINTK(2,"startup_bigsur_irq: L1=%d L2=%d\n",
bigsur_l2_l1_map[irq-BIGSUR_2NDLVL_IRQ_LOW],irq);
}
return 0;
}
static void shutdown_bigsur_irq(unsigned int irq)
{
pr_debug("shutdown_bigsur_irq IRQ %d\n", irq);
if(irq >= BIGSUR_IRQ_LOW && irq < BIGSUR_IRQ_HIGH)
disable_bigsur_l1irq(irq);
else
disable_bigsur_l2irq(irq);
}
/* Define the IRQ structures for the L1 and L2 IRQ types */
static struct hw_interrupt_type bigsur_l1irq_type = {
.typename = "BigSur-CPLD-Level1-IRQ",
.startup = startup_bigsur_irq,
.shutdown = shutdown_bigsur_irq,
.enable = enable_bigsur_l1irq,
.disable = disable_bigsur_l1irq,
.ack = mask_and_ack_bigsur,
.end = end_bigsur_irq
};
static struct hw_interrupt_type bigsur_l2irq_type = {
.typename = "BigSur-CPLD-Level2-IRQ",
.startup = startup_bigsur_irq,
.shutdown =shutdown_bigsur_irq,
.enable = enable_bigsur_l2irq,
.disable = disable_bigsur_l2irq,
.ack = mask_and_ack_bigsur,
.end = end_bigsur_irq
};
static void make_bigsur_l1isr(unsigned int irq) {
/* sanity check first */
if(irq >= BIGSUR_IRQ_LOW && irq < BIGSUR_IRQ_HIGH) {
/* save the handler in the main description table */
[PATCH] genirq: rename desc->handler to desc->chip This patch-queue improves the generic IRQ layer to be truly generic, by adding various abstractions and features to it, without impacting existing functionality. While the queue can be best described as "fix and improve everything in the generic IRQ layer that we could think of", and thus it consists of many smaller features and lots of cleanups, the one feature that stands out most is the new 'irq chip' abstraction. The irq-chip abstraction is about describing and coding and IRQ controller driver by mapping its raw hardware capabilities [and quirks, if needed] in a straightforward way, without having to think about "IRQ flow" (level/edge/etc.) type of details. This stands in contrast with the current 'irq-type' model of genirq architectures, which 'mixes' raw hardware capabilities with 'flow' details. The patchset supports both types of irq controller designs at once, and converts i386 and x86_64 to the new irq-chip design. As a bonus side-effect of the irq-chip approach, chained interrupt controllers (master/slave PIC constructs, etc.) are now supported by design as well. The end result of this patchset intends to be simpler architecture-level code and more consolidation between architectures. We reused many bits of code and many concepts from Russell King's ARM IRQ layer, the merging of which was one of the motivations for this patchset. This patch: rename desc->handler to desc->chip. Originally i did not want to do this, because it's a big patch. But having both "desc->handler", "desc->handle_irq" and "action->handler" caused a large degree of confusion and made the code appear alot less clean than it truly is. I have also attempted a dual approach as well by introducing a desc->chip alias - but that just wasnt robust enough and broke frequently. So lets get over with this quickly. The conversion was done automatically via scripts and converts all the code in the kernel. This renaming patch is the first one amongst the patches, so that the remaining patches can stay flexible and can be merged and split up without having some big monolithic patch act as a merge barrier. [akpm@osdl.org: build fix] [akpm@osdl.org: another build fix] Signed-off-by: Ingo Molnar <mingo@elte.hu> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-29 09:24:36 +00:00
irq_desc[irq].chip = &bigsur_l1irq_type;
irq_desc[irq].status = IRQ_DISABLED;
irq_desc[irq].action = 0;
irq_desc[irq].depth = 1;
disable_bigsur_l1irq(irq);
return;
}
pr_debug("make_bigsur_l1isr: bad irq, %d\n", irq);
return;
}
static void make_bigsur_l2isr(unsigned int irq) {
/* sanity check first */
if(irq >= BIGSUR_2NDLVL_IRQ_LOW && irq < BIGSUR_2NDLVL_IRQ_HIGH) {
/* save the handler in the main description table */
[PATCH] genirq: rename desc->handler to desc->chip This patch-queue improves the generic IRQ layer to be truly generic, by adding various abstractions and features to it, without impacting existing functionality. While the queue can be best described as "fix and improve everything in the generic IRQ layer that we could think of", and thus it consists of many smaller features and lots of cleanups, the one feature that stands out most is the new 'irq chip' abstraction. The irq-chip abstraction is about describing and coding and IRQ controller driver by mapping its raw hardware capabilities [and quirks, if needed] in a straightforward way, without having to think about "IRQ flow" (level/edge/etc.) type of details. This stands in contrast with the current 'irq-type' model of genirq architectures, which 'mixes' raw hardware capabilities with 'flow' details. The patchset supports both types of irq controller designs at once, and converts i386 and x86_64 to the new irq-chip design. As a bonus side-effect of the irq-chip approach, chained interrupt controllers (master/slave PIC constructs, etc.) are now supported by design as well. The end result of this patchset intends to be simpler architecture-level code and more consolidation between architectures. We reused many bits of code and many concepts from Russell King's ARM IRQ layer, the merging of which was one of the motivations for this patchset. This patch: rename desc->handler to desc->chip. Originally i did not want to do this, because it's a big patch. But having both "desc->handler", "desc->handle_irq" and "action->handler" caused a large degree of confusion and made the code appear alot less clean than it truly is. I have also attempted a dual approach as well by introducing a desc->chip alias - but that just wasnt robust enough and broke frequently. So lets get over with this quickly. The conversion was done automatically via scripts and converts all the code in the kernel. This renaming patch is the first one amongst the patches, so that the remaining patches can stay flexible and can be merged and split up without having some big monolithic patch act as a merge barrier. [akpm@osdl.org: build fix] [akpm@osdl.org: another build fix] Signed-off-by: Ingo Molnar <mingo@elte.hu> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-29 09:24:36 +00:00
irq_desc[irq].chip = &bigsur_l2irq_type;
irq_desc[irq].status = IRQ_DISABLED;
irq_desc[irq].action = 0;
irq_desc[irq].depth = 1;
disable_bigsur_l2irq(irq);
return;
}
pr_debug("make_bigsur_l2isr: bad irq, %d\n", irq);
return;
}
/* The IRQ's will be decoded as follows:
* If a level 2 handler exists and there is an unmasked active
* IRQ, the 2nd level handler will be called.
* If a level 2 handler does not exist for the active IRQ
* the 1st level handler will be called.
*/
int bigsur_irq_demux(int irq)
{
int dmux_irq = irq;
u8 mask, actv_irqs;
u32 reg_num;
DIPRINTK(3,"bigsur_irq_demux, irq=%d\n", irq);
/* decode the 1st level IRQ */
if(irq >= BIGSUR_IRQ_LOW && irq < BIGSUR_IRQ_HIGH) {
/* Get corresponding L2 ISR bitmask and ISR number */
mask = bigsur_l2irq_mask[irq-BIGSUR_IRQ_LOW];
reg_num = bigsur_l2irq_reg[irq-BIGSUR_IRQ_LOW];
/* find the active IRQ's (XOR with inactive level)*/
actv_irqs = inb(isr_base-reg_num*isr_offset) ^
bigsur_l2_inactv_state[reg_num];
/* decode active IRQ's */
actv_irqs = actv_irqs & mask & ~(inb(imr_base-reg_num*imr_offset));
/* if NEZ then we have an active L2 IRQ */
if(actv_irqs) dmux_irq = ffz(~actv_irqs) + reg_num*8+BIGSUR_2NDLVL_IRQ_LOW;
/* if no 2nd level IRQ action, but has 1st level, use 1st level handler */
if(!irq_desc[dmux_irq].action && irq_desc[irq].action)
dmux_irq = irq;
DIPRINTK(1,"bigsur_irq_demux: irq=%d dmux_irq=%d mask=0x%04x reg=%d\n",
irq, dmux_irq, mask, reg_num);
}
#ifdef CONFIG_HD64465
dmux_irq = hd64465_irq_demux(dmux_irq);
#endif /* CONFIG_HD64465 */
DIPRINTK(3,"bigsur_irq_demux, demux_irq=%d\n", dmux_irq);
return dmux_irq;
}
/*===========================================================*/
// Big Sur Init Routines
/*===========================================================*/
void __init init_bigsur_IRQ(void)
{
int i;
if (!MACH_BIGSUR) return;
/* Create ISR's for Big Sur CPLD IRQ's */
/*==============================================================*/
for(i=BIGSUR_IRQ_LOW;i<BIGSUR_IRQ_HIGH;i++)
make_bigsur_l1isr(i);
printk(KERN_INFO "Big Sur CPLD L1 interrupts %d to %d.\n",
BIGSUR_IRQ_LOW,BIGSUR_IRQ_HIGH);
for(i=BIGSUR_2NDLVL_IRQ_LOW;i<BIGSUR_2NDLVL_IRQ_HIGH;i++)
make_bigsur_l2isr(i);
printk(KERN_INFO "Big Sur CPLD L2 interrupts %d to %d.\n",
BIGSUR_2NDLVL_IRQ_LOW,BIGSUR_2NDLVL_IRQ_HIGH);
}