Commit | Line | Data |
---|---|---|
e787098c SR |
1 | /* |
2 | * sun4c irq support | |
1da177e4 LT |
3 | * |
4 | * djhr: Hacked out of irq.c into a CPU dependent version. | |
5 | * | |
6 | * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) | |
7 | * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx) | |
8 | * Copyright (C) 1995 Pete A. Zaitcev (zaitcev@yahoo.com) | |
9 | * Copyright (C) 1996 Dave Redman (djhr@tadpole.co.uk) | |
10 | */ | |
11 | ||
1da177e4 LT |
12 | #include <linux/init.h> |
13 | ||
1da177e4 | 14 | #include <asm/oplib.h> |
e787098c | 15 | #include <asm/timer.h> |
1da177e4 LT |
16 | #include <asm/irq.h> |
17 | #include <asm/io.h> | |
e787098c SR |
18 | |
19 | #include "irq.h" | |
20 | ||
21 | /* Sun4c interrupts are typically laid out as follows: | |
22 | * | |
23 | * 1 - Software interrupt, SBUS level 1 | |
24 | * 2 - SBUS level 2 | |
25 | * 3 - ESP SCSI, SBUS level 3 | |
26 | * 4 - Software interrupt | |
27 | * 5 - Lance ethernet, SBUS level 4 | |
28 | * 6 - Software interrupt | |
29 | * 7 - Graphics card, SBUS level 5 | |
30 | * 8 - SBUS level 6 | |
31 | * 9 - SBUS level 7 | |
32 | * 10 - Counter timer | |
33 | * 11 - Floppy | |
34 | * 12 - Zilog uart | |
35 | * 13 - CS4231 audio | |
36 | * 14 - Profiling timer | |
37 | * 15 - NMI | |
38 | * | |
39 | * The interrupt enable bits in the interrupt mask register are | |
40 | * really only used to enable/disable the timer interrupts, and | |
41 | * for signalling software interrupts. There is also a master | |
42 | * interrupt enable bit in this register. | |
43 | * | |
44 | * Interrupts are enabled by setting the SUN4C_INT_* bits, they | |
45 | * are disabled by clearing those bits. | |
46 | */ | |
1da177e4 | 47 | |
32231a66 AV |
48 | /* |
49 | * Bit field defines for the interrupt registers on various | |
50 | * Sparc machines. | |
51 | */ | |
52 | ||
53 | /* The sun4c interrupt register. */ | |
54 | #define SUN4C_INT_ENABLE 0x01 /* Allow interrupts. */ | |
55 | #define SUN4C_INT_E14 0x80 /* Enable level 14 IRQ. */ | |
56 | #define SUN4C_INT_E10 0x20 /* Enable level 10 IRQ. */ | |
57 | #define SUN4C_INT_E8 0x10 /* Enable level 8 IRQ. */ | |
58 | #define SUN4C_INT_E6 0x08 /* Enable level 6 IRQ. */ | |
59 | #define SUN4C_INT_E4 0x04 /* Enable level 4 IRQ. */ | |
60 | #define SUN4C_INT_E1 0x02 /* Enable level 1 IRQ. */ | |
61 | ||
e787098c SR |
62 | /* |
63 | * Pointer to the interrupt enable byte | |
64 | * Used by entry.S | |
1da177e4 | 65 | */ |
e787098c | 66 | unsigned char __iomem *interrupt_enable; |
1da177e4 | 67 | |
1da177e4 LT |
68 | static void sun4c_disable_irq(unsigned int irq_nr) |
69 | { | |
70 | unsigned long flags; | |
71 | unsigned char current_mask, new_mask; | |
e787098c | 72 | |
1da177e4 LT |
73 | local_irq_save(flags); |
74 | irq_nr &= (NR_IRQS - 1); | |
45bb5a7c | 75 | current_mask = sbus_readb(interrupt_enable); |
e787098c | 76 | switch (irq_nr) { |
1da177e4 LT |
77 | case 1: |
78 | new_mask = ((current_mask) & (~(SUN4C_INT_E1))); | |
79 | break; | |
80 | case 8: | |
81 | new_mask = ((current_mask) & (~(SUN4C_INT_E8))); | |
82 | break; | |
83 | case 10: | |
84 | new_mask = ((current_mask) & (~(SUN4C_INT_E10))); | |
85 | break; | |
86 | case 14: | |
87 | new_mask = ((current_mask) & (~(SUN4C_INT_E14))); | |
88 | break; | |
89 | default: | |
90 | local_irq_restore(flags); | |
91 | return; | |
92 | } | |
45bb5a7c | 93 | sbus_writeb(new_mask, interrupt_enable); |
1da177e4 LT |
94 | local_irq_restore(flags); |
95 | } | |
96 | ||
97 | static void sun4c_enable_irq(unsigned int irq_nr) | |
98 | { | |
99 | unsigned long flags; | |
100 | unsigned char current_mask, new_mask; | |
e787098c | 101 | |
1da177e4 LT |
102 | local_irq_save(flags); |
103 | irq_nr &= (NR_IRQS - 1); | |
45bb5a7c | 104 | current_mask = sbus_readb(interrupt_enable); |
e787098c | 105 | switch (irq_nr) { |
1da177e4 LT |
106 | case 1: |
107 | new_mask = ((current_mask) | SUN4C_INT_E1); | |
108 | break; | |
109 | case 8: | |
110 | new_mask = ((current_mask) | SUN4C_INT_E8); | |
111 | break; | |
112 | case 10: | |
113 | new_mask = ((current_mask) | SUN4C_INT_E10); | |
114 | break; | |
115 | case 14: | |
116 | new_mask = ((current_mask) | SUN4C_INT_E14); | |
117 | break; | |
118 | default: | |
119 | local_irq_restore(flags); | |
120 | return; | |
121 | } | |
45bb5a7c | 122 | sbus_writeb(new_mask, interrupt_enable); |
1da177e4 LT |
123 | local_irq_restore(flags); |
124 | } | |
125 | ||
8bd8deea DM |
126 | struct sun4c_timer_info { |
127 | u32 l10_count; | |
128 | u32 l10_limit; | |
129 | u32 l14_count; | |
130 | u32 l14_limit; | |
131 | }; | |
1da177e4 | 132 | |
8bd8deea | 133 | static struct sun4c_timer_info __iomem *sun4c_timers; |
1da177e4 | 134 | |
1da177e4 LT |
135 | static void sun4c_clear_clock_irq(void) |
136 | { | |
8bd8deea | 137 | sbus_readl(&sun4c_timers->l10_limit); |
1da177e4 LT |
138 | } |
139 | ||
1da177e4 LT |
140 | static void sun4c_load_profile_irq(int cpu, unsigned int limit) |
141 | { | |
142 | /* Errm.. not sure how to do this.. */ | |
143 | } | |
144 | ||
40220c1a | 145 | static void __init sun4c_init_timers(irq_handler_t counter_fn) |
1da177e4 | 146 | { |
8bd8deea DM |
147 | const struct linux_prom_irqs *irq; |
148 | struct device_node *dp; | |
149 | const u32 *addr; | |
150 | int err; | |
1da177e4 | 151 | |
8bd8deea DM |
152 | dp = of_find_node_by_name(NULL, "counter-timer"); |
153 | if (!dp) { | |
154 | prom_printf("sun4c_init_timers: Unable to find counter-timer\n"); | |
155 | prom_halt(); | |
156 | } | |
157 | ||
158 | addr = of_get_property(dp, "address", NULL); | |
159 | if (!addr) { | |
160 | prom_printf("sun4c_init_timers: No address property\n"); | |
161 | prom_halt(); | |
162 | } | |
163 | ||
164 | sun4c_timers = (void __iomem *) (unsigned long) addr[0]; | |
165 | ||
166 | irq = of_get_property(dp, "intr", NULL); | |
c2e27c35 | 167 | of_node_put(dp); |
8bd8deea DM |
168 | if (!irq) { |
169 | prom_printf("sun4c_init_timers: No intr property\n"); | |
170 | prom_halt(); | |
171 | } | |
1da177e4 LT |
172 | |
173 | /* Have the level 10 timer tick at 100HZ. We don't touch the | |
174 | * level 14 timer limit since we are letting the prom handle | |
175 | * them until we have a real console driver so L1-A works. | |
176 | */ | |
8bd8deea DM |
177 | sbus_writel((((1000000/HZ) + 1) << 10), &sun4c_timers->l10_limit); |
178 | ||
179 | master_l10_counter = &sun4c_timers->l10_count; | |
1da177e4 | 180 | |
8bd8deea | 181 | err = request_irq(irq[0].pri, counter_fn, |
67413202 | 182 | (IRQF_DISABLED | SA_STATIC_ALLOC), |
1da177e4 | 183 | "timer", NULL); |
8bd8deea DM |
184 | if (err) { |
185 | prom_printf("sun4c_init_timers: request_irq() fails with %d\n", err); | |
1da177e4 LT |
186 | prom_halt(); |
187 | } | |
e787098c | 188 | |
8bd8deea | 189 | sun4c_disable_irq(irq[1].pri); |
1da177e4 LT |
190 | } |
191 | ||
192 | #ifdef CONFIG_SMP | |
e787098c SR |
193 | static void sun4c_nop(void) |
194 | { | |
195 | } | |
1da177e4 LT |
196 | #endif |
197 | ||
1da177e4 LT |
198 | void __init sun4c_init_IRQ(void) |
199 | { | |
45bb5a7c DM |
200 | struct device_node *dp; |
201 | const u32 *addr; | |
202 | ||
203 | dp = of_find_node_by_name(NULL, "interrupt-enable"); | |
204 | if (!dp) { | |
205 | prom_printf("sun4c_init_IRQ: Unable to find interrupt-enable\n"); | |
206 | prom_halt(); | |
207 | } | |
208 | ||
209 | addr = of_get_property(dp, "address", NULL); | |
c2e27c35 | 210 | of_node_put(dp); |
45bb5a7c DM |
211 | if (!addr) { |
212 | prom_printf("sun4c_init_IRQ: No address property\n"); | |
213 | prom_halt(); | |
1da177e4 | 214 | } |
45bb5a7c DM |
215 | |
216 | interrupt_enable = (void __iomem *) (unsigned long) addr[0]; | |
1da177e4 | 217 | |
1da177e4 LT |
218 | BTFIXUPSET_CALL(enable_irq, sun4c_enable_irq, BTFIXUPCALL_NORM); |
219 | BTFIXUPSET_CALL(disable_irq, sun4c_disable_irq, BTFIXUPCALL_NORM); | |
220 | BTFIXUPSET_CALL(enable_pil_irq, sun4c_enable_irq, BTFIXUPCALL_NORM); | |
221 | BTFIXUPSET_CALL(disable_pil_irq, sun4c_disable_irq, BTFIXUPCALL_NORM); | |
222 | BTFIXUPSET_CALL(clear_clock_irq, sun4c_clear_clock_irq, BTFIXUPCALL_NORM); | |
1da177e4 | 223 | BTFIXUPSET_CALL(load_profile_irq, sun4c_load_profile_irq, BTFIXUPCALL_NOP); |
bbdc2661 SR |
224 | |
225 | sparc_irq_config.init_timers = sun4c_init_timers; | |
226 | ||
1da177e4 LT |
227 | #ifdef CONFIG_SMP |
228 | BTFIXUPSET_CALL(set_cpu_int, sun4c_nop, BTFIXUPCALL_NOP); | |
229 | BTFIXUPSET_CALL(clear_cpu_int, sun4c_nop, BTFIXUPCALL_NOP); | |
230 | BTFIXUPSET_CALL(set_irq_udt, sun4c_nop, BTFIXUPCALL_NOP); | |
231 | #endif | |
45bb5a7c | 232 | sbus_writeb(SUN4C_INT_ENABLE, interrupt_enable); |
1da177e4 LT |
233 | /* Cannot enable interrupts until OBP ticker is disabled. */ |
234 | } |