/*
* Copyright (C) 2002 MIZI Research, Inc.
*
* machine dependent irq handling routine
*
* Author: Nandy Lyu <nandy@mizi.com>
* Date : $Date: 2002/05/14 02:19:42 $
*
* $Revision: 1.1.2.4 $
Tue May 21 2002 Nandy Lyu <nandy@mizi.com>
- initial
- BUG:
1) set_EINT_IRQ_edge 손봐야 됨
2) INTSUBMSK는 각 device driver에서 제어하도록 해야 함.
Wed Aug 14 2002 Yong-iL Joh <tolkien@mizi.com>
- new irq scheme을 적용, 위에 언급된 bug 고침
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive
* for more details.
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <asm/hardware.h>
#include <asm/irq.h>
#include <asm/mach/irq.h>
#define ClearPending(x) { \
SRCPND = (1 << (x)); \
INTPND = INTPND;}// //(1 << (x));}
#define EINT_OFFSET(x) ((x) - NORMAL_IRQ_OFFSET + 4)
#define SUBIRQ_OFFSET(x) ((x) - EXT_IRQ_OFFSET)
#define EXTINT_MASK 0x7
#if 0
/*
* set_GPIO_IRQ_edge - set interrupt signal for External Interrupts
*
* parameters:
* irq number of external interrupt (IRQ_EINT0 ~ IRQ_EINT23)
* edge signal method
*/
#define EXTINT_OFFSET 0x4
#define EXTINT_MASK 0x7
int set_EXT_IRQ_mode(int irq, int edge) {
unsigned long flags;
int shift_value;
if (!(((IRQ_EINT4 <= irq) && (irq <= IRQ_EINT23)) ||
((IRQ_EINT0 <= irq) && (irq <= IRQ_EINT3))))
return -EINVAL;
local_irq_save(flags);
if (irq < IRQ_EINT4) { /* IRQ_EINT0 ~ IRQ_EINT3 */
shift_value = (irq % 8) * EXTINT_OFFSET;
EXTINT0 &= ~(EXTINT_MASK << shift_value);
EXTINT0 |= (edge << shift_value);
ClearPending(irq);
} else {
shift_value = ((irq + 4) % 8) * EXTINT_OFFSET;
if (irq < IRQ_EINT8) { /* IRQ_EINT4 ~ IRQ_EINT7 */
EXTINT0 &= ~(EXTINT_MASK << shift_value);
EXTINT0 |= (edge << shift_value);
EINTPEND = (1 << shift_value);
ClearPending(IRQ_EINT4_7);
} else if (irq < IRQ_EINT16) { /* IRQ_EINT8 ~ IRQ_EINT15 */
EXTINT1 &= ~(EXTINT_MASK << shift_value);
EXTINT1 |= (edge << shift_value);
EINTPEND = (1 << shift_value);
ClearPending(IRQ_EINT8_23);
} else { /* IRQ_EINT16 ~ IRQ_EINT23 */
EXTINT2 &= ~(EXTINT_MASK << shift_value);
EXTINT2 |= (edge << shift_value);
EINTPEND = (1 << shift_value);
ClearPending(IRQ_EINT8_23);
}
}
irq_desc[irq].valid = 1;
restore_flags(flags);
return 0;
}
EXPORT_SYMBOL(set_EXT_IRQ_mode);
#endif
/*
* External IRQ를 제외한 IRQ들을 request_irq()로 등록하기 전에
* 뭔가 해주어야 될 일들이 있을까요? 제 생각에는 없다고 봅니다.
* IRQ들 중에서 External IRQ(삼성에서 주장하는 용어)만이 GPIO를
* 미리 설정하고 들어가야 됩니다. 그래서 하나의 함수에서
* 모든 것을 해결해 버리는 것은 어떨까 싶군요.
*
* 2002.09.03 장훈.
*/
static int inline
fixup_irq_num(int irq)
{
if (irq < IRQ_EINT4) return irq;
else return ((irq + 4) - NORMAL_IRQ_OFFSET);
}
static void inline
set_gpios(int irq, int pullup)
{
int shift;
if (irq < 8) {
shift = 2*irq;
GPFCON &= ~(0x3 << shift);
GPFCON |= (0x2 << shift);
//GPFUP &= ~(GRAB_PULLUP(pullup) << irq); modify by threewater
GPFUP &= ~(1<< irq);
GPFUP |= (GRAB_PULLUP(pullup) << irq);
} else {
shift = 2*(irq - 8);
GPGCON &= ~(0x3 << shift);
GPGCON |= (0x2 << shift);
//GPGUP &= ~(GRAB_PULLUP(pullup) << (irq - 8));
GPGUP &= ~(1<< (irq - 8));
GPGUP |= (GRAB_PULLUP(pullup) << (irq - 8));
}
}
int
set_external_irq(int irq, int edge, int pullup)
{
unsigned long flags;
int real_irq, reg_ofs, shift;
volatile u32 *extint = (volatile u32 *)io_p2v(0x56000088);
//printk(__FUNCTION__" called\n");
if (((irq < IRQ_EINT0) && (irq > IRQ_EINT23)) ||
((irq > IRQ_EINT3) && (irq < IRQ_EINT4)))
return -EINVAL;
real_irq = fixup_irq_num(irq);
//printk(__FUNCTION__"(): real_irq = %d\n", real_irq);
set_gpios(real_irq, pullup);
local_irq_save(flags);
reg_ofs = (real_irq / 8);
//printk(__FUNCTION__"(): regs_ofs = %d\n", reg_ofs);
shift = 4 * (real_irq - 8 * reg_ofs);
extint += reg_ofs;
*extint &= ~(EXTINT_MASK << shift);
*extint |= (edge << shift);
if (irq < 4) {
SRCPND |= (1 << real_irq);
INTPND |= (1 << real_irq);
} else {
EINTPEND |= (1 << real_irq);
if(irq<8)
SRCPND |= (1 << IRQ_EINT4_7);
else
SRCPND |= (1 << IRQ_EINT8_23);
}
irq_desc[irq].valid = 1;
restore_flags(flags);
return 0;
}
EXPORT_SYMBOL(set_external_irq);
/*
* Defined irq handlers
*/
static void s3c2410_mask_ack_irq(unsigned int irq)
{
INTMSK |= (1 << irq);
SRCPND = (1 << irq);
INTPND = (1 << irq);
}
static void s3c2410_mask_irq(unsigned int irq)
{
INTMSK |= (1 << irq);
}
static void s3c2410_unmask_irq(unsigned int irq)
{
INTMSK &= ~(1 << irq);
}
/* for EINT? */
static void EINT4_23mask_ack_irq(unsigned int irq)
{
irq = EINT_OFFSET(irq);
EINTMASK |= (1 << irq);
EINTPEND = (1 << irq);
if (irq < EINT_OFFSET(IRQ_EINT8)) {
// INTMSK |= (1 << SHIFT_EINT4_7);
ClearPending(SHIFT_EINT4_7);
} else {
// INTMSK |= (1 << SHIFT_EINT8_23);
ClearPending(SHIFT_EINT8_23);
}
}
static void EINT4_23mask_irq(unsigned int irq)
{
#if 0
if (irq < IRQ_EINT8) {
INTMSK |= (1 << SHIFT_EINT4_7);
} else {
INTMSK |= (1 << SHIFT_EINT8_23);
}
#endif
irq = EINT_OFFSET(irq);
EINTMASK |= (1 << irq);
}
static void EINT4_23unmask_irq(unsigned int irq)
{
EINTMASK &= ~(1 << EINT_OFFSET(irq));
if (irq < IRQ_EINT8) {
INTMSK &= ~(1 << SHIFT_EINT4_7);
} else {
INTMSK &= ~(1 << SHIFT_EINT8_23);
}
}
/* for sub_IRQ */
static void SUB_mask_ack_irq(unsigned int irq)
{
INTSUBMSK |= (1 << SUBIRQ_OFFSET(irq));
SUBSRCPND = (1 << SUBIRQ_OFFSET(irq));
if (irq <= IRQ_ERR0) {
ClearPending(SHIFT_UART0);
} else if (irq <= IRQ_ERR1) {
ClearPending(SHIFT_UART1);
} else if (irq <= IRQ_ERR2){
ClearPending(SHIFT_UART2);
} else { /* if ( irq <= IRQ_ADC_DONE ) { */
ClearPending(SHIFT_ADCTC);
}
}
static void SUB_mask_irq(unsigned int irq)
{
INTSUBMSK |= (1 << SUBIRQ_OFFSET(irq));
}
static void SUB_unmask_irq(unsigned int irq)
{
INTSUBMSK &= ~(1 << SUBIRQ_OFFSET(irq));
if (irq <= IRQ_ERR0) {
INTMSK &= ~(1 << SHIFT_UART0);
} else if (irq <= IRQ_ERR1) {
INTMSK &= ~(1 << SHIFT_UART1);
} else if (irq <= IRQ_ERR2){
INTMSK &= ~(1 << SHIFT_UART2);
} else { /* if ( irq <= IRQ_ADC_DONE ) { */
INTMSK &= ~(1 << SHIFT_ADCTC);
}
}
/*
* fixup_irq() for do_IRQ() in kernel/irq.c
*/
inline unsigned int get_subIRQ(int irq, int begin, int end, int fail_irq) {
int i;
for(i=begin; i <= end; i++) {
if (irq & (1 << i))
return (EXT_IRQ_OFFSET + i);
}
return fail_irq;
}
inline unsigned int get_extIRQ(int irq, int begin, int end, int fail_irq) {
int i;
for(i=begin; i <= end; i++) {
if (irq & (1 << i))
return (NORMAL_IRQ_OFFSET - 4 + i);
}
return fail_irq;
}
unsigned int fixup_irq(int irq) {
unsigned int ret;
unsigned long sub_mask, ext_mask;
if (irq == OS_TIMER)
return irq;
switch (irq) {
case IRQ_UART0:
sub_mask = SUBSRCPND & ~INTSUBMSK;
ret = get_subIRQ(sub_mask, 0, 2, irq);
break;
case IRQ_UART1:
sub_mask = SUBSRCPND & ~INTSUBMSK;
ret = get_subIRQ(sub_mask, 3, 5, irq);
break;
case IRQ_UART2:
sub_mask = SUBSRCPND & ~INTSUBMSK;
ret = get_subIRQ(sub_mask, 6, 8, irq);
break;
case IRQ_ADCTC:
sub_mask = SUBSRCPND & ~INTSUBMSK;
ret = get_subIRQ(sub_mask, 9, 10, irq);
break;
case IRQ_EINT4_7:
ext_mask = EINTPEND & ~EINTMASK;
ret = get_extIRQ(ext_mask, 4, 7, irq);
break;
case IRQ_EINT8_23:
ext_mask = EINTPEND & ~EINTMASK;
ret = get_extIRQ(ext_mask, 8, 23, irq);
break;
default:
ret = irq;
}
return ret;
}
static struct resource irq_resource = {
name: "irqs",
start: 0x4a000000,
end: 0x4a00001f,
};
static struct resource eint_r
- 1
- 2
前往页