| /* |
| * 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 <linux/interrupt.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 <mach/board.h> |
| |
| #if NEW_LINUX_FRAME |
| #include <linux/irqchip/arm-gic.h> |
| #else |
| #include <asm/hardware/gic.h> |
| #endif |
| #include <linux/irqdomain.h> |
| |
| #define A9_INT_MODE_BASE (A9_CRM_BASE + 0x50) |
| |
| |
| #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 int gic_set_type(struct irq_data *d, unsigned int type); |
| extern unsigned int gic_get_irq_nr(irq_hw_number_t hwirq); |
| |
| static int init_irq_flag = 0; |
| |
| |
| /* |
| * GIC only support IRQ_TYPE_LEVEL_HIGH and IRQ_TYPE_EDGE_RISING. In order to support |
| * full interrupt trigger mode(high,low,falling,rising), zx29 treats all level interrupt |
| * as high level interrupt and Edge interrupt is treated as rising interrupt . It is only |
| * for SPIs. External int need additional configuration in PCU |
| */ |
| |
| static int zx29_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; |
| } |
| |
| /* |
| * Enable/disable power management wakeup mode, which is |
| * disabled by default. Enables and disables must match, |
| * just as they match for non-wakeup mode support. |
| * |
| * Wakeup mode lets this IRQ wake the system from sleep |
| * states like "suspend to RAM". |
| */ |
| static int zx29_int_set_wake(struct irq_data *d, unsigned int on) |
| { |
| return pcu_set_irq_wake(d->hwirq, on); |
| } |
| |
| bool irq_type_valid(unsigned int int_type) |
| { |
| return ((int_type==IRQ_TYPE_LEVEL_HIGH) || (int_type==IRQ_TYPE_LEVEL_LOW) || \ |
| (int_type==IRQ_TYPE_EDGE_RISING) || (int_type==IRQ_TYPE_EDGE_FALLING)); |
| } |
| |
| |
| /* |
| * set default interrupt trigger type |
| */ |
| static void int_set_type_default(unsigned int line) |
| { |
| unsigned int int_type=0; |
| unsigned int irq=0; |
| struct irq_data *d; |
| |
| if(line<GIC_PPI_START) |
| return; |
| |
| irq = gic_get_irq_nr((unsigned long)line); |
| d = irq_get_irq_data(irq); |
| |
| /*set the interrupt mode --raise edge,fall edge, high level, low level*/ |
| switch ( line ) |
| { |
| case WDT_INT: |
| case AP_TIMER0_INT: |
| case GSM_SYS0_INT: |
| case GSM_SYS1_INT: |
| case AP_TIMER4_INT: |
| { |
| int_type = IRQ_TYPE_EDGE_RISING; |
| break; |
| } |
| default: |
| { |
| int_type = IRQ_TYPE_LEVEL_HIGH; |
| break; |
| } |
| } |
| gic_set_type(d,int_type); |
| } |
| |
| |
| /* |
| * Initialize the interrupt controller unit. |
| */ |
| void __init zx29_init_irq(void) |
| { |
| unsigned int i; |
| |
| gic_arch_extn.irq_set_type = zx29_int_set_type; |
| gic_arch_extn.irq_set_wake = zx29_int_set_wake; |
| |
| gic_init(0, GIC_PPI_START, GIC_DIST_BASE, GIC_CPU_BASE); |
| |
| for (i = GIC_PPI_START; i < NR_IRQS; i++) { |
| int_set_type_default(i); |
| } |
| |
| init_irq_flag = 1; |
| |
| pr_info("[GIC]ZTE-TSP zx297520v2 GIC initialized.\n"); |
| } |
| |
| |
| int get_init_irq_flag(void) |
| { |
| return init_irq_flag; |
| } |
| |
| |
| /* -------------------------------------------------------------------- |
| * extint_8in1 |
| * -------------------------------------------------------------------- */ |
| static void ext8in1_irq_lock(struct irq_data *data) |
| { |
| } |
| static void ext8in1_irq_sync_unlock(struct irq_data *data) |
| { |
| } |
| static void ext8in1_irq_mask(struct irq_data *data) |
| { |
| } |
| |
| static void ext8in1_irq_unmask(struct irq_data *data) |
| { |
| } |
| static struct irq_chip ext8in1_irq_chip = |
| { |
| .name = "ext8in1", |
| .irq_bus_lock = ext8in1_irq_lock, |
| .irq_bus_sync_unlock = ext8in1_irq_sync_unlock, |
| .irq_mask = ext8in1_irq_mask, |
| .irq_unmask = ext8in1_irq_unmask, |
| .irq_set_wake = zx29_int_set_wake, |
| |
| }; |
| static irqreturn_t ext8in1_irq_thread(int irq, void *irq_data) |
| { |
| u32 line; |
| u32 nextLine; |
| |
| line = pcu_get_8in1_int_source(); |
| |
| while (1) |
| { |
| pcu_clr_irq_pending(line + EX8IN1_INT_BASE); |
| handle_nested_irq(line + EX8IN1_INT_BASE); |
| |
| nextLine = pcu_get_8in1_int_source(); |
| |
| /* |
| * no new interrupt |
| * here is not absolutely right. the same interrupt maybe come again |
| * during ex8in1Isr() execution, or there is really no interrupt comes. |
| * in both situation, we can get same line number. we do nothing to the |
| * case. if there is really a new interrupt, it will trigger a new |
| * arm interrupt. |
| */ |
| if (line == nextLine) |
| break; |
| else |
| line = nextLine; |
| } |
| |
| // enable_irq(irq); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static irqreturn_t ext8in1_irq(int irq, void *dev_id) |
| { |
| // disable_irq_nosync(irq); |
| |
| return IRQ_WAKE_THREAD; |
| } |
| |
| static int __init zx29_ext_int_init(void) |
| { |
| int ret; |
| int cur_irq; |
| |
| for (cur_irq = EX8IN1_INT_BASE; cur_irq <= EX8IN1_INT_END; cur_irq++) |
| { |
| irq_set_chip_and_handler(cur_irq, &ext8in1_irq_chip, handle_simple_irq); |
| irq_set_nested_thread(cur_irq, 1); |
| set_irq_flags(cur_irq, IRQF_VALID); |
| } |
| |
| ret = request_threaded_irq(EX8IN1_INT, ext8in1_irq, ext8in1_irq_thread, IRQF_ONESHOT, |
| "ext8in1", NULL); |
| if(ret<0) |
| { |
| pr_info("[GIC]ZTE-TSP zx297520v2 EXTINT_8IN1 initialized failed.\n"); |
| } |
| |
| return ret; |
| } |
| subsys_initcall(zx29_ext_int_init); |
| |