diff options
author | Vineet Gupta <vgupta@synopsys.com> | 2015-03-06 03:38:20 -0500 |
---|---|---|
committer | Vineet Gupta <vgupta@synopsys.com> | 2015-06-22 04:36:55 -0400 |
commit | 820970a5aa3c98be26e1df64da4b93294d20d4e7 (patch) | |
tree | a9b134845a29a0b789226a7ff1202eef7f99a4ac | |
parent | 10d11e580c50f8a6718a58f92198dbc031e63b0a (diff) |
ARCv2: [intc] HS38 core interrupt controller
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Vineet Gupta <vgupta@synopsys.com>
-rw-r--r-- | Documentation/devicetree/bindings/arc/archs-intc.txt | 22 | ||||
-rw-r--r-- | arch/arc/include/asm/arcregs.h | 1 | ||||
-rw-r--r-- | arch/arc/include/asm/irqflags-arcv2.h | 116 | ||||
-rw-r--r-- | arch/arc/kernel/intc-arcv2.c | 143 |
4 files changed, 282 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/arc/archs-intc.txt b/Documentation/devicetree/bindings/arc/archs-intc.txt new file mode 100644 index 000000000000..69f326d6a5ad --- /dev/null +++ b/Documentation/devicetree/bindings/arc/archs-intc.txt | |||
@@ -0,0 +1,22 @@ | |||
1 | * ARC-HS incore Interrupt Controller (Provided by cores implementing ARCv2 ISA) | ||
2 | |||
3 | Properties: | ||
4 | |||
5 | - compatible: "snps,archs-intc" | ||
6 | - interrupt-controller: This is an interrupt controller. | ||
7 | - #interrupt-cells: Must be <1>. | ||
8 | |||
9 | Single Cell "interrupts" property of a device specifies the IRQ number | ||
10 | between 16 to 256 | ||
11 | |||
12 | intc accessed via the special ARC AUX register interface, hence "reg" property | ||
13 | is not specified. | ||
14 | |||
15 | Example: | ||
16 | |||
17 | intc: interrupt-controller { | ||
18 | compatible = "snps,archs-intc"; | ||
19 | interrupt-controller; | ||
20 | #interrupt-cells = <1>; | ||
21 | interrupts = <16 17 18 19 20 21 22 23 24 25>; | ||
22 | }; | ||
diff --git a/arch/arc/include/asm/arcregs.h b/arch/arc/include/asm/arcregs.h index 336a9f694c2e..649646579986 100644 --- a/arch/arc/include/asm/arcregs.h +++ b/arch/arc/include/asm/arcregs.h | |||
@@ -31,6 +31,7 @@ | |||
31 | #define ARC_REG_BPU_BCR 0xc0 | 31 | #define ARC_REG_BPU_BCR 0xc0 |
32 | #define ARC_REG_ISA_CFG_BCR 0xc1 | 32 | #define ARC_REG_ISA_CFG_BCR 0xc1 |
33 | #define ARC_REG_RTT_BCR 0xF2 | 33 | #define ARC_REG_RTT_BCR 0xF2 |
34 | #define ARC_REG_IRQ_BCR 0xF3 | ||
34 | #define ARC_REG_SMART_BCR 0xFF | 35 | #define ARC_REG_SMART_BCR 0xFF |
35 | 36 | ||
36 | /* status32 Bits Positions */ | 37 | /* status32 Bits Positions */ |
diff --git a/arch/arc/include/asm/irqflags-arcv2.h b/arch/arc/include/asm/irqflags-arcv2.h new file mode 100644 index 000000000000..c946c56f141c --- /dev/null +++ b/arch/arc/include/asm/irqflags-arcv2.h | |||
@@ -0,0 +1,116 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-15 Synopsys, Inc. (www.synopsys.com) | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #ifndef __ASM_IRQFLAGS_ARCV2_H | ||
10 | #define __ASM_IRQFLAGS_ARCV2_H | ||
11 | |||
12 | #include <asm/arcregs.h> | ||
13 | |||
14 | /* status32 Bits */ | ||
15 | #define STATUS_AD_BIT 19 /* Disable Align chk: core supports non-aligned */ | ||
16 | #define STATUS_IE_BIT 31 | ||
17 | |||
18 | #define STATUS_AD_MASK (1<<STATUS_AD_BIT) | ||
19 | #define STATUS_IE_MASK (1<<STATUS_IE_BIT) | ||
20 | |||
21 | #define AUX_USER_SP 0x00D | ||
22 | #define AUX_IRQ_CTRL 0x00E | ||
23 | #define AUX_IRQ_ACT 0x043 /* Active Intr across all levels */ | ||
24 | #define AUX_IRQ_LVL_PEND 0x200 /* Pending Intr across all levels */ | ||
25 | #define AUX_IRQ_PRIORITY 0x206 | ||
26 | #define ICAUSE 0x40a | ||
27 | #define AUX_IRQ_SELECT 0x40b | ||
28 | #define AUX_IRQ_ENABLE 0x40c | ||
29 | |||
30 | /* 0 is highest level, but taken by FIRQs, if present in design */ | ||
31 | #define ARCV2_IRQ_DEF_PRIO 0 | ||
32 | |||
33 | /* seed value for status register */ | ||
34 | #define ISA_INIT_STATUS_BITS (STATUS_IE_MASK | STATUS_AD_MASK | \ | ||
35 | (ARCV2_IRQ_DEF_PRIO << 1)) | ||
36 | |||
37 | #ifndef __ASSEMBLY__ | ||
38 | |||
39 | /* | ||
40 | * Save IRQ state and disable IRQs | ||
41 | */ | ||
42 | static inline long arch_local_irq_save(void) | ||
43 | { | ||
44 | unsigned long flags; | ||
45 | |||
46 | __asm__ __volatile__(" clri %0 \n" : "=r" (flags) : : "memory"); | ||
47 | |||
48 | return flags; | ||
49 | } | ||
50 | |||
51 | /* | ||
52 | * restore saved IRQ state | ||
53 | */ | ||
54 | static inline void arch_local_irq_restore(unsigned long flags) | ||
55 | { | ||
56 | __asm__ __volatile__(" seti %0 \n" : : "r" (flags) : "memory"); | ||
57 | } | ||
58 | |||
59 | /* | ||
60 | * Unconditionally Enable IRQs | ||
61 | */ | ||
62 | static inline void arch_local_irq_enable(void) | ||
63 | { | ||
64 | __asm__ __volatile__(" seti \n" : : : "memory"); | ||
65 | } | ||
66 | |||
67 | /* | ||
68 | * Unconditionally Disable IRQs | ||
69 | */ | ||
70 | static inline void arch_local_irq_disable(void) | ||
71 | { | ||
72 | __asm__ __volatile__(" clri \n" : : : "memory"); | ||
73 | } | ||
74 | |||
75 | /* | ||
76 | * save IRQ state | ||
77 | */ | ||
78 | static inline long arch_local_save_flags(void) | ||
79 | { | ||
80 | unsigned long temp; | ||
81 | |||
82 | __asm__ __volatile__( | ||
83 | " lr %0, [status32] \n" | ||
84 | : "=&r"(temp) | ||
85 | : | ||
86 | : "memory"); | ||
87 | |||
88 | return temp; | ||
89 | } | ||
90 | |||
91 | /* | ||
92 | * Query IRQ state | ||
93 | */ | ||
94 | static inline int arch_irqs_disabled_flags(unsigned long flags) | ||
95 | { | ||
96 | return !(flags & (STATUS_IE_MASK)); | ||
97 | } | ||
98 | |||
99 | static inline int arch_irqs_disabled(void) | ||
100 | { | ||
101 | return arch_irqs_disabled_flags(arch_local_save_flags()); | ||
102 | } | ||
103 | |||
104 | #else | ||
105 | |||
106 | .macro IRQ_DISABLE scratch | ||
107 | clri | ||
108 | .endm | ||
109 | |||
110 | .macro IRQ_ENABLE scratch | ||
111 | seti | ||
112 | .endm | ||
113 | |||
114 | #endif /* __ASSEMBLY__ */ | ||
115 | |||
116 | #endif | ||
diff --git a/arch/arc/kernel/intc-arcv2.c b/arch/arc/kernel/intc-arcv2.c new file mode 100644 index 000000000000..c5c64dc47655 --- /dev/null +++ b/arch/arc/kernel/intc-arcv2.c | |||
@@ -0,0 +1,143 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014 Synopsys, Inc. (www.synopsys.com) | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | */ | ||
9 | |||
10 | #include <linux/interrupt.h> | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/of.h> | ||
13 | #include <linux/irqdomain.h> | ||
14 | #include <linux/irqchip.h> | ||
15 | #include "../../drivers/irqchip/irqchip.h" | ||
16 | #include <asm/irq.h> | ||
17 | |||
18 | /* | ||
19 | * Early Hardware specific Interrupt setup | ||
20 | * -Called very early (start_kernel -> setup_arch -> setup_processor) | ||
21 | * -Platform Independent (must for any ARC Core) | ||
22 | * -Needed for each CPU (hence not foldable into init_IRQ) | ||
23 | */ | ||
24 | void arc_init_IRQ(void) | ||
25 | { | ||
26 | unsigned int tmp; | ||
27 | |||
28 | struct aux_irq_ctrl { | ||
29 | #ifdef CONFIG_CPU_BIG_ENDIAN | ||
30 | unsigned int res3:18, save_idx_regs:1, res2:1, | ||
31 | save_u_to_u:1, save_lp_regs:1, save_blink:1, | ||
32 | res:4, save_nr_gpr_pairs:5; | ||
33 | #else | ||
34 | unsigned int save_nr_gpr_pairs:5, res:4, | ||
35 | save_blink:1, save_lp_regs:1, save_u_to_u:1, | ||
36 | res2:1, save_idx_regs:1, res3:18; | ||
37 | #endif | ||
38 | } ictrl; | ||
39 | |||
40 | *(unsigned int *)&ictrl = 0; | ||
41 | |||
42 | ictrl.save_nr_gpr_pairs = 6; /* r0 to r11 (r12 saved manually) */ | ||
43 | ictrl.save_blink = 1; | ||
44 | ictrl.save_lp_regs = 1; /* LP_COUNT, LP_START, LP_END */ | ||
45 | ictrl.save_u_to_u = 0; /* user ctxt saved on kernel stack */ | ||
46 | ictrl.save_idx_regs = 1; /* JLI, LDI, EI */ | ||
47 | |||
48 | WRITE_AUX(AUX_IRQ_CTRL, ictrl); | ||
49 | |||
50 | /* setup status32, don't enable intr yet as kernel doesn't want */ | ||
51 | tmp = read_aux_reg(0xa); | ||
52 | tmp |= ISA_INIT_STATUS_BITS; | ||
53 | tmp &= ~STATUS_IE_MASK; | ||
54 | asm volatile("flag %0 \n"::"r"(tmp)); | ||
55 | |||
56 | /* | ||
57 | * ARCv2 core intc provides multiple interrupt priorities (upto 16). | ||
58 | * Typical builds though have only two levels (0-high, 1-low) | ||
59 | * Linux by default uses lower prio 1 for most irqs, reserving 0 for | ||
60 | * NMI style interrupts in future (say perf) | ||
61 | * | ||
62 | * Read the intc BCR to confirm that Linux default priority is avail | ||
63 | * in h/w | ||
64 | * | ||
65 | * Note: | ||
66 | * IRQ_BCR[27..24] contains N-1 (for N priority levels) and prio level | ||
67 | * is 0 based. | ||
68 | */ | ||
69 | tmp = (read_aux_reg(ARC_REG_IRQ_BCR) >> 24 ) & 0xF; | ||
70 | if (ARCV2_IRQ_DEF_PRIO > tmp) | ||
71 | panic("Linux default irq prio incorrect\n"); | ||
72 | } | ||
73 | |||
74 | static void arcv2_irq_mask(struct irq_data *data) | ||
75 | { | ||
76 | write_aux_reg(AUX_IRQ_SELECT, data->irq); | ||
77 | write_aux_reg(AUX_IRQ_ENABLE, 0); | ||
78 | } | ||
79 | |||
80 | static void arcv2_irq_unmask(struct irq_data *data) | ||
81 | { | ||
82 | write_aux_reg(AUX_IRQ_SELECT, data->irq); | ||
83 | write_aux_reg(AUX_IRQ_ENABLE, 1); | ||
84 | } | ||
85 | |||
86 | void arcv2_irq_enable(struct irq_data *data) | ||
87 | { | ||
88 | /* set default priority */ | ||
89 | write_aux_reg(AUX_IRQ_SELECT, data->irq); | ||
90 | write_aux_reg(AUX_IRQ_PRIORITY, ARCV2_IRQ_DEF_PRIO); | ||
91 | |||
92 | /* | ||
93 | * hw auto enables (linux unmask) all by default | ||
94 | * So no need to do IRQ_ENABLE here | ||
95 | * XXX: However OSCI LAN need it | ||
96 | */ | ||
97 | write_aux_reg(AUX_IRQ_ENABLE, 1); | ||
98 | } | ||
99 | |||
100 | static struct irq_chip arcv2_irq_chip = { | ||
101 | .name = "ARCv2 core Intc", | ||
102 | .irq_mask = arcv2_irq_mask, | ||
103 | .irq_unmask = arcv2_irq_unmask, | ||
104 | .irq_enable = arcv2_irq_enable | ||
105 | }; | ||
106 | |||
107 | static int arcv2_irq_map(struct irq_domain *d, unsigned int irq, | ||
108 | irq_hw_number_t hw) | ||
109 | { | ||
110 | if (irq == TIMER0_IRQ) | ||
111 | irq_set_chip_and_handler(irq, &arcv2_irq_chip, handle_percpu_irq); | ||
112 | else | ||
113 | irq_set_chip_and_handler(irq, &arcv2_irq_chip, handle_level_irq); | ||
114 | |||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | static const struct irq_domain_ops arcv2_irq_ops = { | ||
119 | .xlate = irq_domain_xlate_onecell, | ||
120 | .map = arcv2_irq_map, | ||
121 | }; | ||
122 | |||
123 | static struct irq_domain *root_domain; | ||
124 | |||
125 | static int __init | ||
126 | init_onchip_IRQ(struct device_node *intc, struct device_node *parent) | ||
127 | { | ||
128 | if (parent) | ||
129 | panic("DeviceTree incore intc not a root irq controller\n"); | ||
130 | |||
131 | root_domain = irq_domain_add_legacy(intc, NR_CPU_IRQS, 0, 0, | ||
132 | &arcv2_irq_ops, NULL); | ||
133 | |||
134 | if (!root_domain) | ||
135 | panic("root irq domain not avail\n"); | ||
136 | |||
137 | /* with this we don't need to export root_domain */ | ||
138 | irq_set_default_host(root_domain); | ||
139 | |||
140 | return 0; | ||
141 | } | ||
142 | |||
143 | IRQCHIP_DECLARE(arc_intc, "snps,archs-intc", init_onchip_IRQ); | ||