| /* |
| * 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"); |
| } |
| |
| |