blob: e9c210419cac0c4b2566c89df0a3b436c7472462 [file] [log] [blame]
/*
* linux/arch/arm/mach-zx297510/irq.c
*
* Copyright (C) 2013 ZTE-TSP
*
* 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.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/types.h>
#include <linux/irq.h>
#include <mach/iomap.h>
#include <mach/irqs.h>
#include <mach/debug.h>
#include <mach/pcu.h>
#include <asm/irq.h>
#include <asm/setup.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/cp15.h>
#include <asm/mach/arch.h>
#include <asm/mach/irq.h>
#include <asm/mach/map.h>
#include <asm/hardware/gic.h>
//#pragma GCC optimize("O0")
#define GIC_DIST_BASE (A9_PERIPHERAL_VA+0x1000)
#define GIC_CPU_BASE (A9_PERIPHERAL_VA+0x100)
#define A9_INT_MODE_BASE (A9_PERIPHERAL_VA+0x3050)
#define INT_HIGHLEVEL (0x0) /* 00: high level */
#define INT_LOWLEVEL (0x1) /* 01: low level */
#define INT_POSEDGE (0x2) /* 10: raise edge */
#define INT_NEGEDGE (0x3) /* 11: fall edge */
extern struct gic_chip_data gic_data[MAX_GIC_NR];
extern int gic_set_type(struct irq_data *d, unsigned int type);
/*
* copy from tegra, get pending intterrupt number
*/
unsigned int zx29_gic_pending_interrupt(void)
{
unsigned int irq;
irq = ioread32(GIC_CPU_BASE + GIC_CPU_HIGHPRI);
irq &= 0x3ff;
return irq;
}
/*
*GIC only support IRQ_TYPE_LEVEL_HIGH and IRQ_TYPE_EDGE_RISING. In order to support
*full interrupt trigger mode(high,low,falling,rising), zx297510 add a little thing to do that.It
*is only for SPIs. The little thing treats all level interrupt as high level interrupt, and then
*sends them to GIC. Edge interrupt is treated as rising interrupt as well.
*external int need additional configuration in PCU
*/
static int zx297510_int_set_type(struct irq_data *d, unsigned int type)
{
unsigned int data_tmp=0;
unsigned int srctype=0;
unsigned int reg_index=0,pos_index=0;
unsigned int hwirq = d->hwirq;
switch (type) {
case IRQ_TYPE_LEVEL_HIGH:
srctype = INT_HIGHLEVEL;
break;
case IRQ_TYPE_EDGE_RISING:
srctype = INT_POSEDGE;
break;
case IRQ_TYPE_LEVEL_LOW:
srctype = INT_LOWLEVEL;
break;
case IRQ_TYPE_EDGE_FALLING:
srctype = INT_NEGEDGE;
break;
default:
return -EINVAL;
}
reg_index=(hwirq-32)/16;
pos_index=((hwirq-32)%16)*2;
data_tmp=ioread32(A9_INT_MODE_BASE+reg_index*4);
data_tmp &= ~(3<<pos_index);
data_tmp |= srctype<<pos_index;
iowrite32(data_tmp,A9_INT_MODE_BASE+reg_index*4);
return 0;
}
/*
* set default interrupt trigger type
*/
static void int_set_type_default(unsigned int line)
{
unsigned int int_type=0;
unsigned int irq=0;
struct gic_chip_data *gic = &gic_data[0];
struct irq_data *d;
if(line<GIC_PPI_START)
return;
irq = irq_find_mapping(gic->domain, (unsigned long)line); //get linux irq number
d = irq_get_irq_data(irq); //get irq_data
/*set the interrupt mode --raise edge,fall edge, high level, low level*/
switch ( line )
{
case TIMER0_INT:
case TIMER1_INT:
case GSM_SYS0_INT:
case GSM_SYS1_INT:
case SSC0_CONFLICT_INT:
case SSC1_CONFLICT_INT:
case TD_FRM_INT: // it is edge interrupt, maybe rising or falling???
case TD_LPM4_INT: // it is edge interrupt, maybe rising or falling???
{
int_type = IRQ_TYPE_EDGE_RISING;
break;
}
case I2C0_INT:
case I2C1_INT:
{
int_type = IRQ_TYPE_LEVEL_LOW;
break;
}
default:
{
int_type = IRQ_TYPE_LEVEL_HIGH;
break;
}
}
gic_set_type(d,int_type);
}
/*
* Initialize the interrupt controller unit.
*/
void __init zx297510_init_irq(void)
{
unsigned int i;
/*
* in order to avoid config repeatedly, let bits needed by a9 in these registers default
* maybe some devices used by a9 should invoke functions in PCU.h to set interrupt type
* and clear interrupt
*/
#if 0
/*
* config int type and polarity in PCU
* timer2 and timer3 int is not taken care, its be config in timer driver
* some times external int may need different type, so zx297510_int_set_type()
* will config it automatically
*/
iowrite32(0xfffff3f9, PCU_BASE_VA+0x60); /*int type*/
iowrite32(0xffffffe7,PCU_BASE_VA+0x70); /*int pol*/
iowrite32(0x04c3, PCU_BASE_VA+0xe4); /*int type*/
iowrite32(0x1ff8, PCU_BASE_VA+0xe8); /*int pol*/
/*clear fake int*/
iowrite32(0xffffffff, PCU_BASE_VA+0x80);
iowrite32(0x1fff, PCU_BASE_VA+0xec);
#endif
gic_arch_extn.irq_set_type = zx297510_int_set_type;
gic_init(0, GIC_PPI_START, GIC_DIST_BASE, GIC_CPU_BASE);
/*config int type and polarity int INT_MODE_REG and GIC*/
for (i = GIC_PPI_START; i < NR_IRQS; i++) {
int_set_type_default(i);
}
printk(KERN_INFO "ZTE-TSP zx297510 GIC initialized\n");
}