1/* 2 * sun4c irq support 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 12#include <linux/init.h> 13 14#include <asm/oplib.h> 15#include <asm/timer.h> 16#include <asm/irq.h> 17#include <asm/io.h> 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 */ 47 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 62/* 63 * Pointer to the interrupt enable byte 64 * Used by entry.S 65 */ 66unsigned char __iomem *interrupt_enable; 67 68static void sun4c_mask_irq(struct irq_data *data) 69{ 70 unsigned long mask = (unsigned long)data->chip_data; 71 72 if (mask) { 73 unsigned long flags; 74 75 local_irq_save(flags); 76 mask = sbus_readb(interrupt_enable) & ~mask; 77 sbus_writeb(mask, interrupt_enable); 78 local_irq_restore(flags); 79 } 80} 81 82static void sun4c_unmask_irq(struct irq_data *data) 83{ 84 unsigned long mask = (unsigned long)data->chip_data; 85 86 if (mask) { 87 unsigned long flags; 88 89 local_irq_save(flags); 90 mask = sbus_readb(interrupt_enable) | mask; 91 sbus_writeb(mask, interrupt_enable); 92 local_irq_restore(flags); 93 } 94} 95 96static unsigned int sun4c_startup_irq(struct irq_data *data) 97{ 98 irq_link(data->irq); 99 sun4c_unmask_irq(data); 100 101 return 0; 102} 103 104static void sun4c_shutdown_irq(struct irq_data *data) 105{ 106 sun4c_mask_irq(data); 107 irq_unlink(data->irq); 108} 109 110static struct irq_chip sun4c_irq = { 111 .name = "sun4c", 112 .irq_startup = sun4c_startup_irq, 113 .irq_shutdown = sun4c_shutdown_irq, 114 .irq_mask = sun4c_mask_irq, 115 .irq_unmask = sun4c_unmask_irq, 116}; 117 118static unsigned int sun4c_build_device_irq(struct platform_device *op, 119 unsigned int real_irq) 120{ 121 unsigned int irq; 122 123 if (real_irq >= 16) { 124 prom_printf("Bogus sun4c IRQ %u\n", real_irq); 125 prom_halt(); 126 } 127 128 irq = irq_alloc(real_irq, real_irq); 129 if (irq) { 130 unsigned long mask = 0UL; 131 132 switch (real_irq) { 133 case 1: 134 mask = SUN4C_INT_E1; 135 break; 136 case 8: 137 mask = SUN4C_INT_E8; 138 break; 139 case 10: 140 mask = SUN4C_INT_E10; 141 break; 142 case 14: 143 mask = SUN4C_INT_E14; 144 break; 145 default: 146 /* All the rest are either always enabled, 147 * or are for signalling software interrupts. 148 */ 149 break; 150 } 151 irq_set_chip_and_handler_name(irq, &sun4c_irq, 152 handle_level_irq, "level"); 153 irq_set_chip_data(irq, (void *)mask); 154 } 155 return irq; 156} 157 158struct sun4c_timer_info { 159 u32 l10_count; 160 u32 l10_limit; 161 u32 l14_count; 162 u32 l14_limit; 163}; 164 165static struct sun4c_timer_info __iomem *sun4c_timers; 166 167static void sun4c_clear_clock_irq(void) 168{ 169 sbus_readl(&sun4c_timers->l10_limit); 170} 171 172static void sun4c_load_profile_irq(int cpu, unsigned int limit) 173{ 174 /* Errm.. not sure how to do this.. */ 175} 176 177static void __init sun4c_init_timers(irq_handler_t counter_fn) 178{ 179 const struct linux_prom_irqs *prom_irqs; 180 struct device_node *dp; 181 unsigned int irq; 182 const u32 *addr; 183 int err; 184 185 dp = of_find_node_by_name(NULL, "counter-timer"); 186 if (!dp) { 187 prom_printf("sun4c_init_timers: Unable to find counter-timer\n"); 188 prom_halt(); 189 } 190 191 addr = of_get_property(dp, "address", NULL); 192 if (!addr) { 193 prom_printf("sun4c_init_timers: No address property\n"); 194 prom_halt(); 195 } 196 197 sun4c_timers = (void __iomem *) (unsigned long) addr[0]; 198 199 prom_irqs = of_get_property(dp, "intr", NULL); 200 of_node_put(dp); 201 if (!prom_irqs) { 202 prom_printf("sun4c_init_timers: No intr property\n"); 203 prom_halt(); 204 } 205 206 /* Have the level 10 timer tick at 100HZ. We don't touch the 207 * level 14 timer limit since we are letting the prom handle 208 * them until we have a real console driver so L1-A works. 209 */ 210 sbus_writel((((1000000/HZ) + 1) << 10), &sun4c_timers->l10_limit); 211 212 master_l10_counter = &sun4c_timers->l10_count; 213 214 irq = sun4c_build_device_irq(NULL, prom_irqs[0].pri); 215 err = request_irq(irq, counter_fn, IRQF_TIMER, "timer", NULL); 216 if (err) { 217 prom_printf("sun4c_init_timers: request_irq() fails with %d\n", err); 218 prom_halt(); 219 } 220 221 /* disable timer interrupt */ 222 sun4c_mask_irq(irq_get_irq_data(irq)); 223} 224 225#ifdef CONFIG_SMP 226static void sun4c_nop(void) 227{ 228} 229#endif 230 231void __init sun4c_init_IRQ(void) 232{ 233 struct device_node *dp; 234 const u32 *addr; 235 236 dp = of_find_node_by_name(NULL, "interrupt-enable"); 237 if (!dp) { 238 prom_printf("sun4c_init_IRQ: Unable to find interrupt-enable\n"); 239 prom_halt(); 240 } 241 242 addr = of_get_property(dp, "address", NULL); 243 of_node_put(dp); 244 if (!addr) { 245 prom_printf("sun4c_init_IRQ: No address property\n"); 246 prom_halt(); 247 } 248 249 interrupt_enable = (void __iomem *) (unsigned long) addr[0]; 250 251 BTFIXUPSET_CALL(clear_clock_irq, sun4c_clear_clock_irq, BTFIXUPCALL_NORM); 252 BTFIXUPSET_CALL(load_profile_irq, sun4c_load_profile_irq, BTFIXUPCALL_NOP); 253 254 sparc_irq_config.init_timers = sun4c_init_timers; 255 sparc_irq_config.build_device_irq = sun4c_build_device_irq; 256 257#ifdef CONFIG_SMP 258 BTFIXUPSET_CALL(set_cpu_int, sun4c_nop, BTFIXUPCALL_NOP); 259 BTFIXUPSET_CALL(clear_cpu_int, sun4c_nop, BTFIXUPCALL_NOP); 260 BTFIXUPSET_CALL(set_irq_udt, sun4c_nop, BTFIXUPCALL_NOP); 261#endif 262 sbus_writeb(SUN4C_INT_ENABLE, interrupt_enable); 263 /* Cannot enable interrupts until OBP ticker is disabled. */ 264} 265