blob: 1349168c69d68c517d441a90980b1a29c27934b2 [file] [log] [blame]
/*
* 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/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/sysdev.h>
#include <mach/hardware.h>
#include <asm/io.h>
#include <asm/setup.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include "ctrlEnv/mvCtrlEnvLib.h"
#include "boardEnv/mvBoardEnvLib.h"
#include "gpp/mvGpp.h"
#include "mvOs.h"
/*
* Note: FIQ mask registers are IRQ mask registers + 4 bytes.
*/
unsigned int fiq_int_type[NR_IRQS];
void mv_mask_fiq(unsigned int irq)
{
if (irq < 32) {
MV_REG_BIT_RESET(MV_FIQ_MASK_LOW_REG, (1 << irq) );
} else if(irq < 64) { /* (irq > 32 && irq < 64) */
MV_REG_BIT_RESET(MV_FIQ_MASK_HIGH_REG, (1 << (irq - 32)) );
} else if(irq < 96) { /* (irq > 64 && irq < 96) */
MV_REG_BIT_RESET(MV_FIQ_MASK_ERROR_REG, (1 << (irq - 64)) );
} else if(irq < NR_IRQS) { /* (irq > 96 && irq < NR_IRQS) */
MV_U32 bitmask = 1 << (irq & 0x1F);
MV_U32 reg = (irq - IRQ_GPP_START) >> 5;
MV_REG_BIT_RESET(MV_GPP_IRQ_MASK_REG(reg), bitmask);
} else
printk("mv_mask_fiq: ERR, irq-%u out of scope\n",irq);
}
void mv_unmask_fiq(unsigned int irq)
{
if (irq < 32) {
MV_REG_BIT_SET(MV_FIQ_MASK_LOW_REG, (1 << irq) );
} else if(irq < 64) { /* (irq > 32 && irq < 64) */
MV_REG_BIT_SET(MV_FIQ_MASK_HIGH_REG, (1 << (irq - 32)) );
} else if(irq < 96) { /* (irq > 64 && irq < 96) */
MV_REG_BIT_SET(MV_FIQ_MASK_ERROR_REG, (1 << (irq - 64)) );
} else if(irq < NR_IRQS) {
MV_U32 bitmask = 1 << (irq & 0x1F);
MV_U32 reg = (irq - IRQ_GPP_START) >> 5;
MV_REG_BIT_SET(MV_GPP_IRQ_MASK_REG(reg), bitmask);
if (fiq_int_type[irq] == GPP_IRQ_TYPE_CHANGE_LEVEL) {
(MV_REG_READ(MV_GPP_IRQ_POLARITY(reg)) & bitmask)?
MV_REG_BIT_RESET(MV_GPP_IRQ_POLARITY(reg), bitmask):
MV_REG_BIT_SET(MV_GPP_IRQ_POLARITY(reg), bitmask);
}
} else
printk("mv_unmask_fiq: ERR, irq-%u out of scope\n",irq);
}
irq_handler_t fiq_handlers[NR_IRQS] = {
[0 ... NR_IRQS-1] = NULL,
};
static inline int find_fiq_bit(void)
{
int bit, high_cause_sum_bit;
MV_U32 mask;
unsigned long cause;
/*
* Handle Low Cause interrupts
*/
cause = MV_REG_READ(MV_IRQ_CAUSE_LOW_REG);
/* Save High Cause Summary bit to save access to Cause register. */
high_cause_sum_bit = cause & 0x1;
cause &= MV_REG_READ(MV_FIQ_MASK_LOW_REG);
bit = find_first_bit(&cause, 32);
if (bit < 32)
goto out;
/*
* Handle High Cause interrupts
*/
if (!high_cause_sum_bit)
goto check_cause_error;
cause = MV_REG_READ(MV_IRQ_CAUSE_HIGH_REG);
cause &= MV_REG_READ(MV_FIQ_MASK_HIGH_REG);
bit = find_first_bit(&cause, 32);
if (bit >= 32)
goto check_cause_error;
if (bit >= 4 && bit <= 15) {
/* Handle GPIO case */
bit = (bit - 4) * 8 + IRQ_GPP_START;
if (bit > IRQ_GPP_START + 88) {
/* Print some info in the future.
* Currently, this case may be traced
* only through FIQ exception counter
* (cat /proc/fiq_dump).
*/
goto err_out;
}
} else
bit += 32;
goto out;
/*
* Handle Error Cause interrupts
*/
check_cause_error:
mask = MV_REG_READ(MV_FIQ_MASK_ERROR_REG);
cause = mask & MV_REG_READ(MV_IRQ_CAUSE_ERROR_REG);
bit = find_first_bit(&cause, 32);
if (bit < 32)
goto out;
err_out:
/* No interrupt found. */
return -1;
out:
return bit;
}
typedef void (*fiq_handler_t)(void);
extern fiq_handler_t fiq_userptr;
void handle_all_fiqs(void)
{
irq_handler_t handler;
int fiq = find_fiq_bit();
handler = fiq_handlers[fiq];
if (handler)
handler(fiq, NULL);
}
int __must_check
request_fiq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev)
{
if ((irq < 0) | (irq >= NR_IRQS))
return -EINVAL;
/*
* if (flags & IRQF_DISABLED)
* return -EINVAL;
*/
mv_mask_fiq(irq);
if (!fiq_userptr)
fiq_userptr = handle_all_fiqs;
if (fiq_handlers[irq])
return -EINVAL;
fiq_handlers[irq] = handler;
mv_unmask_fiq(irq);
return 0;
}
void free_fiq(unsigned int irq, void *dev_id)
{
mv_mask_fiq(irq);
fiq_handlers[irq] = NULL;
}