summaryrefslogtreecommitdiff
path: root/drivers/platform/sprd/irq.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform/sprd/irq.c')
-rw-r--r--drivers/platform/sprd/irq.c319
1 files changed, 319 insertions, 0 deletions
diff --git a/drivers/platform/sprd/irq.c b/drivers/platform/sprd/irq.c
new file mode 100644
index 00000000..4a3db24c
--- /dev/null
+++ b/drivers/platform/sprd/irq.c
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ *
+ * Contact: steve.zhan <steve.zhan@spreadtrum.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+
+#include <linux/irqchip/arm-gic.h>
+
+#include <soc/sprd/hardware.h>
+//#include <mach/irqs.h>
+#include <soc/sprd/irqs.h>
+#include <soc/sprd/sci_glb_regs.h>
+#include <soc/sprd/sci.h>
+#include <soc/sprd/arch_misc.h>
+//#include <asm/fiq.h>
+#include <soc/sprd/adi.h>
+#include <linux/of.h>
+#include <linux/irqchip.h>
+
+#include <soc/sprd/sci_glb_regs.h>
+
+/* general interrupt registers */
+#define INTC_IRQ_MSKSTS (0x0000)
+#define INTC_IRQ_RAW (0x0004)
+#define INTC_IRQ_EN (0x0008)
+#define INTC_IRQ_DIS (0x000C)
+#define INTC_IRQ_SOFT (0x0010)
+#define INTC_FIQ_STS (0x0020)
+#define INTC_FIQ_EN (0x0028)
+#define INTC_FIQ_DIS (0x002C)
+
+struct intc {
+ u32 intc_reg_base;
+ u32 min_int_num;/*global logical number max value in intc*/
+ u32 max_min_num;/*global logical number min value in intc */
+ u32 min_bit_offset;/*It is equal to "min_int_num - actual bit offset"*/
+};
+
+struct intc_mux_irq {/*multi irqs mapping to one bit*/
+ u32 irq_number;
+ u32 intc_base;
+ u32 bits;
+ u32 is_unmask;
+};
+
+#if defined(CONFIG_ARCH_SCX35)
+/*_intc and mux_irq is separately, we just simply enable/disable it at the same time
+ When system deepsleep, only mux_irq(AON) can wakeup system(intc0,1,2,3,gic,armcore)
+ intc0/1/2/3 will lose if system in deepsleep, _mux can wakeup. intc0/1/2/3 need SW retention.
+ gic clock will stop if all of core has been wfi.intc0/1/2/3 can wakeup.
+*/
+#define UNINITIALIZED 0xdeadbeaf
+static struct intc _intc[] = {
+ {UNINITIALIZED, 2, 31, 0},
+ {UNINITIALIZED, 34, 63, 32},
+ {UNINITIALIZED, 66, 95, 64},
+ {UNINITIALIZED, 98, 124, 96},
+};
+#if defined(CONFIG_ARCH_SCX35L)
+static struct intc_mux_irq _mux[] = {
+ {30,UNINITIALIZED,2,0},{31,UNINITIALIZED,2,0},{28,UNINITIALIZED,2,0},
+ {121,UNINITIALIZED,2,0},{120,UNINITIALIZED,2,0},{119,UNINITIALIZED,2,0},
+ {118,UNINITIALIZED,2,0},{29,UNINITIALIZED,2,0},
+ {21,UNINITIALIZED,3,0},{22,UNINITIALIZED,3,0},{23,UNINITIALIZED,3,0},{24,UNINITIALIZED,3,0},
+ {35,UNINITIALIZED,4,0},{36,UNINITIALIZED,4,0},{38,UNINITIALIZED,4,0},{25,UNINITIALIZED,4,0},
+ {20,UNINITIALIZED,4,0},{34,UNINITIALIZED,4,0},/* missing lvds_trx, mdar */
+ {41,UNINITIALIZED,5,0},{40,UNINITIALIZED,5,0},{42,UNINITIALIZED,5,0},{44,UNINITIALIZED,5,0},
+ {45,UNINITIALIZED,5,0},{43,UNINITIALIZED,5,0},
+ {39,UNINITIALIZED,6,0},
+ {84,UNINITIALIZED,7,0},{83,UNINITIALIZED,7,0},
+ {123,UNINITIALIZED,8,0},{124,UNINITIALIZED,8,0},
+ {122,UNINITIALIZED,9,0},{26,UNINITIALIZED,9,0},
+ {86,UNINITIALIZED,10,0},
+ /* bit11: missing mbox_tar_arm7 */
+ {69,UNINITIALIZED,12,0},
+ {37,UNINITIALIZED,31,0},
+};
+#else
+static struct intc_mux_irq _mux[] = {
+ {30, UNINITIALIZED, 2, 0},
+ {31, UNINITIALIZED, 2, 0},
+ {28, UNINITIALIZED, 2, 0},
+ {121,UNINITIALIZED, 2, 0},
+ {120,UNINITIALIZED, 2, 0},
+ {119,UNINITIALIZED, 2, 0},
+ {118,UNINITIALIZED, 2, 0},
+ {29, UNINITIALIZED, 2, 0},
+ {21, UNINITIALIZED, 3, 0},
+ {22, UNINITIALIZED, 3, 0},
+ {23, UNINITIALIZED, 3, 0},
+ {24, UNINITIALIZED, 3, 0},
+ {35, UNINITIALIZED, 4, 0},
+ {36, UNINITIALIZED, 4, 0},
+ {38, UNINITIALIZED, 4, 0},
+ {25, UNINITIALIZED, 4, 0},
+ {27, UNINITIALIZED, 4, 0},
+ {20, UNINITIALIZED, 4, 0},
+ {34, UNINITIALIZED, 4, 0},
+ {41, UNINITIALIZED, 5, 0},
+ {40, UNINITIALIZED, 5, 0},
+ {42, UNINITIALIZED, 5, 0},
+ {44, UNINITIALIZED, 5, 0},
+ {45, UNINITIALIZED, 5, 0},
+ {43, UNINITIALIZED, 5, 0},
+ {39, UNINITIALIZED, 6, 0},
+ {85, UNINITIALIZED, 7, 0},
+ {84, UNINITIALIZED, 7, 0},
+ {83, UNINITIALIZED, 7, 0},
+ {67, UNINITIALIZED, 8, 0},
+ {68, UNINITIALIZED, 8, 0},
+ {69, UNINITIALIZED, 8, 0},
+ {70, UNINITIALIZED, 9, 0},
+ {71, UNINITIALIZED, 9, 0},
+ {72, UNINITIALIZED, 9, 0},
+ {73, UNINITIALIZED,10, 0},
+ {74, UNINITIALIZED,10, 0},
+ {123,UNINITIALIZED,11, 0},
+ {124,UNINITIALIZED,11, 0},
+ {26, UNINITIALIZED,12, 0},
+ {122,UNINITIALIZED,12, 0},
+ {86, UNINITIALIZED,13, 0},
+ {37, UNINITIALIZED,14, 0},
+};
+#endif
+
+#define LEGACY_FIQ_BIT (32)
+#define LEGACY_IRQ_BIT (29)
+
+static __init void __irq_init(void)
+{
+ if (soc_is_scx35_v0())
+ sci_glb_clr(REG_AP_AHB_AP_SYS_AUTO_SLEEP_CFG,BIT_CA7_CORE_AUTO_GATE_EN);
+
+ /*enable all intc*/
+ sci_glb_set(REG_AP_APB_APB_EB, BIT_INTC0_EB | BIT_INTC1_EB |
+ BIT_INTC2_EB | BIT_INTC3_EB);
+ sci_glb_set(REG_AON_APB_APB_EB0, BIT_INTC_EB);
+
+ /*disable default for startup*/
+ __raw_writel(~0, (void *)(SPRD_INTC0_BASE + INTC_IRQ_DIS));
+ __raw_writel(~0, (void *)(SPRD_INTC0_BASE + INTC_FIQ_DIS));
+ __raw_writel(~0, (void *)(SPRD_INTC1_BASE + INTC_IRQ_DIS));
+ __raw_writel(~0, (void *)(SPRD_INTC1_BASE + INTC_FIQ_DIS));
+ __raw_writel(~0, (void *)(SPRD_INTC2_BASE + INTC_IRQ_DIS));
+ __raw_writel(~0, (void *)(SPRD_INTC2_BASE + INTC_FIQ_DIS));
+ __raw_writel(~0, (void *)(SPRD_INTC3_BASE + INTC_IRQ_DIS));
+ __raw_writel(~0, (void *)(SPRD_INTC3_BASE + INTC_FIQ_DIS));
+ __raw_writel(~0, (void *)(SPRD_INT_BASE + INTC_IRQ_DIS));
+ __raw_writel(~0, (void *)(SPRD_INT_BASE + INTC_FIQ_DIS));
+}
+
+#else
+
+static const struct intc _intc[] = {
+ {SPRD_INTC0_BASE, 0, 31, 0},
+ {SPRD_INTC1_BASE, 32, 61, 32},
+};
+
+static struct intc_mux_irq _mux[] = {};
+
+#define LEGACY_FIQ_BIT (31)
+#define LEGACY_IRQ_BIT (28)
+
+static __init void __irq_init(void)
+{
+ /*
+ * gic clock will be stopped after 2 cores enter standby in the same time,
+ * dsp assert if IRQ_DSP0_INT and IRQ_DSP1_INT are disabled. so enable IRQ_DSP0_INT
+ * and IRQ_DSP1_INT in INTC0 here.
+ */
+ u32 val = __raw_readl(SPRD_INTC0_BASE + INTC_IRQ_EN);
+ val |= (SCI_INTC_IRQ_BIT(IRQ_DSP0_INT) | SCI_INTC_IRQ_BIT(IRQ_DSP1_INT) | SCI_INTC_IRQ_BIT(IRQ_EPT_INT));
+ val |= (SCI_INTC_IRQ_BIT(IRQ_SIM0_INT) | SCI_INTC_IRQ_BIT(IRQ_SIM1_INT));
+ val |= (SCI_INTC_IRQ_BIT(IRQ_TIMER0_INT));
+ __raw_writel(val, SPRD_INTC0_BASE + INTC_IRQ_EN);
+}
+#endif
+
+static inline int __irq_mux_irq_find(u32 irq, u32 *base, u32 *bit, int *p_index)
+{
+ int s = ARRAY_SIZE(_mux);
+ while (s--)
+ if (_mux[s].irq_number == irq) {
+ *base = _mux[s].intc_base;
+ *bit = _mux[s].bits;
+ *p_index = s;
+ return 0;
+ }
+ return -ENXIO;
+}
+
+static inline int __irq_find_base(u32 irq, u32 *base, u32 *bit)
+{
+ int s = ARRAY_SIZE(_intc);
+ while (s--)
+ if (irq >= _intc[s].min_int_num && irq <= _intc[s].max_min_num) {
+ *base = _intc[s].intc_reg_base;
+ *bit = irq - _intc[s].min_bit_offset;
+ return 0;
+ }
+ return -ENXIO;
+}
+
+static inline void __mux_irq(u32 irq, u32 offset, u32 is_unmask)
+{
+ u32 base, bit, s;
+ int index = 0;
+ int dont_mask = 0;
+ if (!__irq_mux_irq_find(irq, &base, &bit, &index)) {
+ _mux[index].is_unmask = !!is_unmask;
+
+ if (is_unmask) {/*if any one need unmask(wakeup source), let the irq mux bit enable*/
+ __raw_writel(1 << bit, (void *)(base + offset));
+ } else {/*maybe mask*/
+ s = ARRAY_SIZE(_mux);
+ while (s--) {
+ if (_mux[s].is_unmask) {
+ dont_mask = 1;
+ break;
+ }
+ }
+ if (!dont_mask)
+ __raw_writel(1 << bit, (void *)(base + offset));
+ }
+ }
+}
+
+void sci_intc_mask(u32 __irq)
+{
+ unsigned int irq = SCI_GET_INTC_IRQ(__irq);
+ u32 base;
+ u32 bit;
+ u32 offset = INTC_IRQ_DIS;
+#ifdef CONFIG_SPRD_WATCHDOG_SYS_FIQ
+ if (unlikely(irq == (IRQ_CA7WDG_INT - IRQ_GIC_START)))
+ offset = INTC_FIQ_DIS;
+#endif
+ __mux_irq(irq, offset, 0);
+ if (!__irq_find_base(irq, &base, &bit))
+ __raw_writel(1 << bit, (void *)(base + offset));
+}
+EXPORT_SYMBOL(sci_intc_mask);
+
+void sci_intc_unmask(u32 __irq)
+{
+ unsigned int irq = SCI_GET_INTC_IRQ(__irq);
+ u32 base;
+ u32 bit;
+ u32 offset = INTC_IRQ_EN;
+#ifdef CONFIG_SPRD_WATCHDOG_SYS_FIQ
+ if (unlikely(irq == (IRQ_CA7WDG_INT - IRQ_GIC_START)))
+ offset = INTC_FIQ_EN;
+#endif
+ __mux_irq(irq, offset, 1);
+ if (!__irq_find_base(irq, &base, &bit))
+ __raw_writel(1 << bit, (void *)(base + offset));
+}
+
+EXPORT_SYMBOL(sci_intc_unmask);
+
+static void __irq_mask(struct irq_data *data)
+{
+ sci_intc_mask(data->irq);
+}
+
+static void __irq_unmask(struct irq_data *data)
+{
+ sci_intc_unmask(data->irq);
+}
+
+static int __set_wake(struct irq_data *d, unsigned int on)
+{
+ /*we don't add mask/unmask in this now, this is not mean mask interrupt*/
+ return 0;
+}
+
+extern int sprd_init_before_irq(void);
+void __init sci_init_irq(void)
+{
+ int i;
+ sprd_init_before_irq();
+ irqchip_init();
+
+ _intc[0].intc_reg_base = SPRD_INTC0_BASE;
+ _intc[1].intc_reg_base = SPRD_INTC1_BASE;
+ _intc[2].intc_reg_base = SPRD_INTC2_BASE;
+ _intc[3].intc_reg_base = SPRD_INTC3_BASE;
+
+ for (i = 0; i < ARRAY_SIZE(_mux); i++)
+ _mux[i].intc_base = SPRD_INT_BASE;
+
+ gic_arch_extn.irq_mask = __irq_mask;
+ gic_arch_extn.irq_unmask = __irq_unmask;
+ gic_arch_extn.irq_set_wake = __set_wake;
+
+
+ /*disable legacy interrupt*/
+#if (LEGACY_FIQ_BIT < 32)
+ __raw_writel(1<<LEGACY_FIQ_BIT, (void *)(CORE_GIC_DIS_VA + GIC_DIST_ENABLE_CLEAR));
+#endif
+ __raw_writel(1<<LEGACY_IRQ_BIT, (void *)(CORE_GIC_DIS_VA + GIC_DIST_ENABLE_CLEAR));
+
+ __irq_init();
+}