| Konrad Eisele | 5213a78 | 2009-08-17 00:13:29 +0000 | [diff] [blame] | 1 | /* | 
 | 2 |  * Copyright (C) 2009 Daniel Hellstrom (daniel@gaisler.com) Aeroflex Gaisler AB | 
 | 3 |  * Copyright (C) 2009 Konrad Eisele (konrad@gaisler.com) Aeroflex Gaisler AB | 
 | 4 |  */ | 
 | 5 |  | 
 | 6 | #include <linux/kernel.h> | 
| Konrad Eisele | 5213a78 | 2009-08-17 00:13:29 +0000 | [diff] [blame] | 7 | #include <linux/errno.h> | 
 | 8 | #include <linux/mutex.h> | 
| Konrad Eisele | 5213a78 | 2009-08-17 00:13:29 +0000 | [diff] [blame] | 9 | #include <linux/of.h> | 
 | 10 | #include <linux/of_platform.h> | 
 | 11 | #include <linux/interrupt.h> | 
 | 12 | #include <linux/of_device.h> | 
| Konrad Eisele | 8401707 | 2009-08-31 22:08:13 +0000 | [diff] [blame] | 13 |  | 
| Konrad Eisele | 5213a78 | 2009-08-17 00:13:29 +0000 | [diff] [blame] | 14 | #include <asm/oplib.h> | 
 | 15 | #include <asm/timer.h> | 
 | 16 | #include <asm/prom.h> | 
 | 17 | #include <asm/leon.h> | 
 | 18 | #include <asm/leon_amba.h> | 
| Konrad Eisele | 8401707 | 2009-08-31 22:08:13 +0000 | [diff] [blame] | 19 | #include <asm/traps.h> | 
 | 20 | #include <asm/cacheflush.h> | 
| Daniel Hellstrom | 4c6773c | 2011-04-19 23:41:22 +0000 | [diff] [blame] | 21 | #include <asm/smp.h> | 
| Daniel Hellstrom | 01dae0f | 2011-04-21 04:20:24 +0000 | [diff] [blame] | 22 | #include <asm/setup.h> | 
| Konrad Eisele | 5213a78 | 2009-08-17 00:13:29 +0000 | [diff] [blame] | 23 |  | 
 | 24 | #include "prom.h" | 
 | 25 | #include "irq.h" | 
 | 26 |  | 
| Daniel Hellstrom | 53aea7c | 2011-01-04 01:41:29 +0000 | [diff] [blame] | 27 | struct leon3_irqctrl_regs_map *leon3_irqctrl_regs; /* interrupt controller base address */ | 
 | 28 | struct leon3_gptimer_regs_map *leon3_gptimer_regs; /* timer controller base address */ | 
| Konrad Eisele | 5213a78 | 2009-08-17 00:13:29 +0000 | [diff] [blame] | 29 |  | 
 | 30 | int leondebug_irq_disable; | 
 | 31 | int leon_debug_irqout; | 
 | 32 | static int dummy_master_l10_counter; | 
| Daniel Hellstrom | 7279b82 | 2011-01-27 01:26:58 +0000 | [diff] [blame] | 33 | unsigned long amba_system_id; | 
| Daniel Hellstrom | d61a38b | 2011-04-19 23:41:21 +0000 | [diff] [blame] | 34 | static DEFINE_SPINLOCK(leon_irq_lock); | 
| Konrad Eisele | 5213a78 | 2009-08-17 00:13:29 +0000 | [diff] [blame] | 35 |  | 
| Daniel Hellstrom | 53aea7c | 2011-01-04 01:41:29 +0000 | [diff] [blame] | 36 | unsigned long leon3_gptimer_irq; /* interrupt controller irq number */ | 
| Daniel Hellstrom | 2791c1a | 2011-01-04 01:41:32 +0000 | [diff] [blame] | 37 | unsigned long leon3_gptimer_idx; /* Timer Index (0..6) within Timer Core */ | 
| Daniel Hellstrom | 2cf9530 | 2011-04-19 23:41:23 +0000 | [diff] [blame] | 38 | int leon3_ticker_irq; /* Timer ticker IRQ */ | 
| Konrad Eisele | 5213a78 | 2009-08-17 00:13:29 +0000 | [diff] [blame] | 39 | unsigned int sparc_leon_eirq; | 
| Daniel Hellstrom | a481b5d | 2011-04-19 23:41:25 +0000 | [diff] [blame] | 40 | #define LEON_IMASK(cpu) (&leon3_irqctrl_regs->mask[cpu]) | 
| Daniel Hellstrom | 4c6773c | 2011-04-19 23:41:22 +0000 | [diff] [blame] | 41 | #define LEON_IACK (&leon3_irqctrl_regs->iclear) | 
 | 42 | #define LEON_DO_ACK_HW 1 | 
| Konrad Eisele | 5213a78 | 2009-08-17 00:13:29 +0000 | [diff] [blame] | 43 |  | 
| Daniel Hellstrom | 4c6773c | 2011-04-19 23:41:22 +0000 | [diff] [blame] | 44 | /* Return the last ACKed IRQ by the Extended IRQ controller. It has already | 
 | 45 |  * been (automatically) ACKed when the CPU takes the trap. | 
 | 46 |  */ | 
 | 47 | static inline unsigned int leon_eirq_get(int cpu) | 
| Konrad Eisele | 5213a78 | 2009-08-17 00:13:29 +0000 | [diff] [blame] | 48 | { | 
 | 49 | 	return LEON3_BYPASS_LOAD_PA(&leon3_irqctrl_regs->intid[cpu]) & 0x1f; | 
 | 50 | } | 
 | 51 |  | 
| Daniel Hellstrom | 4c6773c | 2011-04-19 23:41:22 +0000 | [diff] [blame] | 52 | /* Handle one or multiple IRQs from the extended interrupt controller */ | 
 | 53 | static void leon_handle_ext_irq(unsigned int irq, struct irq_desc *desc) | 
| Konrad Eisele | 5213a78 | 2009-08-17 00:13:29 +0000 | [diff] [blame] | 54 | { | 
| Daniel Hellstrom | 4c6773c | 2011-04-19 23:41:22 +0000 | [diff] [blame] | 55 | 	unsigned int eirq; | 
| Daniel Hellstrom | 01dae0f | 2011-04-21 04:20:24 +0000 | [diff] [blame] | 56 | 	int cpu = sparc_leon3_cpuid(); | 
| Daniel Hellstrom | 4c6773c | 2011-04-19 23:41:22 +0000 | [diff] [blame] | 57 |  | 
 | 58 | 	eirq = leon_eirq_get(cpu); | 
 | 59 | 	if ((eirq & 0x10) && irq_map[eirq]->irq) /* bit4 tells if IRQ happened */ | 
 | 60 | 		generic_handle_irq(irq_map[eirq]->irq); | 
| Konrad Eisele | 5213a78 | 2009-08-17 00:13:29 +0000 | [diff] [blame] | 61 | } | 
 | 62 |  | 
 | 63 | /* The extended IRQ controller has been found, this function registers it */ | 
| Daniel Hellstrom | 4c6773c | 2011-04-19 23:41:22 +0000 | [diff] [blame] | 64 | void leon_eirq_setup(unsigned int eirq) | 
| Konrad Eisele | 5213a78 | 2009-08-17 00:13:29 +0000 | [diff] [blame] | 65 | { | 
| Daniel Hellstrom | 4c6773c | 2011-04-19 23:41:22 +0000 | [diff] [blame] | 66 | 	unsigned long mask, oldmask; | 
 | 67 | 	unsigned int veirq; | 
| Konrad Eisele | 5213a78 | 2009-08-17 00:13:29 +0000 | [diff] [blame] | 68 |  | 
| Daniel Hellstrom | 4c6773c | 2011-04-19 23:41:22 +0000 | [diff] [blame] | 69 | 	if (eirq < 1 || eirq > 0xf) { | 
 | 70 | 		printk(KERN_ERR "LEON EXT IRQ NUMBER BAD: %d\n", eirq); | 
 | 71 | 		return; | 
| Konrad Eisele | 5213a78 | 2009-08-17 00:13:29 +0000 | [diff] [blame] | 72 | 	} | 
 | 73 |  | 
| Daniel Hellstrom | 4c6773c | 2011-04-19 23:41:22 +0000 | [diff] [blame] | 74 | 	veirq = leon_build_device_irq(eirq, leon_handle_ext_irq, "extirq", 0); | 
 | 75 |  | 
 | 76 | 	/* | 
 | 77 | 	 * Unmask the Extended IRQ, the IRQs routed through the Ext-IRQ | 
 | 78 | 	 * controller have a mask-bit of their own, so this is safe. | 
 | 79 | 	 */ | 
 | 80 | 	irq_link(veirq); | 
 | 81 | 	mask = 1 << eirq; | 
| Daniel Hellstrom | 01dae0f | 2011-04-21 04:20:24 +0000 | [diff] [blame] | 82 | 	oldmask = LEON3_BYPASS_LOAD_PA(LEON_IMASK(boot_cpu_id)); | 
 | 83 | 	LEON3_BYPASS_STORE_PA(LEON_IMASK(boot_cpu_id), (oldmask | mask)); | 
| Daniel Hellstrom | 4c6773c | 2011-04-19 23:41:22 +0000 | [diff] [blame] | 84 | 	sparc_leon_eirq = eirq; | 
| Konrad Eisele | 5213a78 | 2009-08-17 00:13:29 +0000 | [diff] [blame] | 85 | } | 
 | 86 |  | 
 | 87 | static inline unsigned long get_irqmask(unsigned int irq) | 
 | 88 | { | 
 | 89 | 	unsigned long mask; | 
 | 90 |  | 
 | 91 | 	if (!irq || ((irq > 0xf) && !sparc_leon_eirq) | 
 | 92 | 	    || ((irq > 0x1f) && sparc_leon_eirq)) { | 
 | 93 | 		printk(KERN_ERR | 
 | 94 | 		       "leon_get_irqmask: false irq number: %d\n", irq); | 
 | 95 | 		mask = 0; | 
 | 96 | 	} else { | 
 | 97 | 		mask = LEON_HARD_INT(irq); | 
 | 98 | 	} | 
 | 99 | 	return mask; | 
 | 100 | } | 
 | 101 |  | 
| Daniel Hellstrom | 5eb1f4fc | 2011-04-19 23:41:26 +0000 | [diff] [blame] | 102 | #ifdef CONFIG_SMP | 
 | 103 | static int irq_choose_cpu(const struct cpumask *affinity) | 
 | 104 | { | 
 | 105 | 	cpumask_t mask; | 
 | 106 |  | 
| Rusty Russell | 0b5f9c0 | 2012-03-29 15:38:30 +1030 | [diff] [blame] | 107 | 	cpumask_and(&mask, cpu_online_mask, affinity); | 
 | 108 | 	if (cpumask_equal(&mask, cpu_online_mask) || cpumask_empty(&mask)) | 
| Daniel Hellstrom | 01dae0f | 2011-04-21 04:20:24 +0000 | [diff] [blame] | 109 | 		return boot_cpu_id; | 
| Daniel Hellstrom | 5eb1f4fc | 2011-04-19 23:41:26 +0000 | [diff] [blame] | 110 | 	else | 
| Rusty Russell | 0b5f9c0 | 2012-03-29 15:38:30 +1030 | [diff] [blame] | 111 | 		return cpumask_first(&mask); | 
| Daniel Hellstrom | 5eb1f4fc | 2011-04-19 23:41:26 +0000 | [diff] [blame] | 112 | } | 
 | 113 | #else | 
| Daniel Hellstrom | 01dae0f | 2011-04-21 04:20:24 +0000 | [diff] [blame] | 114 | #define irq_choose_cpu(affinity) boot_cpu_id | 
| Daniel Hellstrom | 5eb1f4fc | 2011-04-19 23:41:26 +0000 | [diff] [blame] | 115 | #endif | 
 | 116 |  | 
 | 117 | static int leon_set_affinity(struct irq_data *data, const struct cpumask *dest, | 
 | 118 | 			     bool force) | 
 | 119 | { | 
 | 120 | 	unsigned long mask, oldmask, flags; | 
 | 121 | 	int oldcpu, newcpu; | 
 | 122 |  | 
 | 123 | 	mask = (unsigned long)data->chip_data; | 
 | 124 | 	oldcpu = irq_choose_cpu(data->affinity); | 
 | 125 | 	newcpu = irq_choose_cpu(dest); | 
 | 126 |  | 
 | 127 | 	if (oldcpu == newcpu) | 
 | 128 | 		goto out; | 
 | 129 |  | 
 | 130 | 	/* unmask on old CPU first before enabling on the selected CPU */ | 
 | 131 | 	spin_lock_irqsave(&leon_irq_lock, flags); | 
 | 132 | 	oldmask = LEON3_BYPASS_LOAD_PA(LEON_IMASK(oldcpu)); | 
 | 133 | 	LEON3_BYPASS_STORE_PA(LEON_IMASK(oldcpu), (oldmask & ~mask)); | 
 | 134 | 	oldmask = LEON3_BYPASS_LOAD_PA(LEON_IMASK(newcpu)); | 
 | 135 | 	LEON3_BYPASS_STORE_PA(LEON_IMASK(newcpu), (oldmask | mask)); | 
 | 136 | 	spin_unlock_irqrestore(&leon_irq_lock, flags); | 
 | 137 | out: | 
 | 138 | 	return IRQ_SET_MASK_OK; | 
 | 139 | } | 
 | 140 |  | 
| Sam Ravnborg | 6baa9b2 | 2011-04-18 11:25:44 +0000 | [diff] [blame] | 141 | static void leon_unmask_irq(struct irq_data *data) | 
| Konrad Eisele | 5213a78 | 2009-08-17 00:13:29 +0000 | [diff] [blame] | 142 | { | 
| Daniel Hellstrom | a481b5d | 2011-04-19 23:41:25 +0000 | [diff] [blame] | 143 | 	unsigned long mask, oldmask, flags; | 
| Daniel Hellstrom | 5eb1f4fc | 2011-04-19 23:41:26 +0000 | [diff] [blame] | 144 | 	int cpu; | 
| Sam Ravnborg | 6baa9b2 | 2011-04-18 11:25:44 +0000 | [diff] [blame] | 145 |  | 
 | 146 | 	mask = (unsigned long)data->chip_data; | 
| Daniel Hellstrom | 5eb1f4fc | 2011-04-19 23:41:26 +0000 | [diff] [blame] | 147 | 	cpu = irq_choose_cpu(data->affinity); | 
| Daniel Hellstrom | d61a38b | 2011-04-19 23:41:21 +0000 | [diff] [blame] | 148 | 	spin_lock_irqsave(&leon_irq_lock, flags); | 
| Daniel Hellstrom | 5eb1f4fc | 2011-04-19 23:41:26 +0000 | [diff] [blame] | 149 | 	oldmask = LEON3_BYPASS_LOAD_PA(LEON_IMASK(cpu)); | 
 | 150 | 	LEON3_BYPASS_STORE_PA(LEON_IMASK(cpu), (oldmask | mask)); | 
| Daniel Hellstrom | d61a38b | 2011-04-19 23:41:21 +0000 | [diff] [blame] | 151 | 	spin_unlock_irqrestore(&leon_irq_lock, flags); | 
| Konrad Eisele | 5213a78 | 2009-08-17 00:13:29 +0000 | [diff] [blame] | 152 | } | 
 | 153 |  | 
| Sam Ravnborg | 6baa9b2 | 2011-04-18 11:25:44 +0000 | [diff] [blame] | 154 | static void leon_mask_irq(struct irq_data *data) | 
| Konrad Eisele | 5213a78 | 2009-08-17 00:13:29 +0000 | [diff] [blame] | 155 | { | 
| Daniel Hellstrom | a481b5d | 2011-04-19 23:41:25 +0000 | [diff] [blame] | 156 | 	unsigned long mask, oldmask, flags; | 
| Daniel Hellstrom | 5eb1f4fc | 2011-04-19 23:41:26 +0000 | [diff] [blame] | 157 | 	int cpu; | 
| Sam Ravnborg | 6baa9b2 | 2011-04-18 11:25:44 +0000 | [diff] [blame] | 158 |  | 
 | 159 | 	mask = (unsigned long)data->chip_data; | 
| Daniel Hellstrom | 5eb1f4fc | 2011-04-19 23:41:26 +0000 | [diff] [blame] | 160 | 	cpu = irq_choose_cpu(data->affinity); | 
| Daniel Hellstrom | d61a38b | 2011-04-19 23:41:21 +0000 | [diff] [blame] | 161 | 	spin_lock_irqsave(&leon_irq_lock, flags); | 
| Daniel Hellstrom | 5eb1f4fc | 2011-04-19 23:41:26 +0000 | [diff] [blame] | 162 | 	oldmask = LEON3_BYPASS_LOAD_PA(LEON_IMASK(cpu)); | 
 | 163 | 	LEON3_BYPASS_STORE_PA(LEON_IMASK(cpu), (oldmask & ~mask)); | 
| Daniel Hellstrom | d61a38b | 2011-04-19 23:41:21 +0000 | [diff] [blame] | 164 | 	spin_unlock_irqrestore(&leon_irq_lock, flags); | 
| Konrad Eisele | 5213a78 | 2009-08-17 00:13:29 +0000 | [diff] [blame] | 165 | } | 
 | 166 |  | 
| Sam Ravnborg | 6baa9b2 | 2011-04-18 11:25:44 +0000 | [diff] [blame] | 167 | static unsigned int leon_startup_irq(struct irq_data *data) | 
 | 168 | { | 
 | 169 | 	irq_link(data->irq); | 
 | 170 | 	leon_unmask_irq(data); | 
 | 171 | 	return 0; | 
 | 172 | } | 
 | 173 |  | 
 | 174 | static void leon_shutdown_irq(struct irq_data *data) | 
 | 175 | { | 
 | 176 | 	leon_mask_irq(data); | 
 | 177 | 	irq_unlink(data->irq); | 
 | 178 | } | 
 | 179 |  | 
| Daniel Hellstrom | 4c6773c | 2011-04-19 23:41:22 +0000 | [diff] [blame] | 180 | /* Used by external level sensitive IRQ handlers on the LEON: ACK IRQ ctrl */ | 
 | 181 | static void leon_eoi_irq(struct irq_data *data) | 
 | 182 | { | 
 | 183 | 	unsigned long mask = (unsigned long)data->chip_data; | 
 | 184 |  | 
 | 185 | 	if (mask & LEON_DO_ACK_HW) | 
 | 186 | 		LEON3_BYPASS_STORE_PA(LEON_IACK, mask & ~LEON_DO_ACK_HW); | 
 | 187 | } | 
 | 188 |  | 
| Sam Ravnborg | 6baa9b2 | 2011-04-18 11:25:44 +0000 | [diff] [blame] | 189 | static struct irq_chip leon_irq = { | 
| Daniel Hellstrom | 5eb1f4fc | 2011-04-19 23:41:26 +0000 | [diff] [blame] | 190 | 	.name			= "leon", | 
 | 191 | 	.irq_startup		= leon_startup_irq, | 
 | 192 | 	.irq_shutdown		= leon_shutdown_irq, | 
 | 193 | 	.irq_mask		= leon_mask_irq, | 
 | 194 | 	.irq_unmask		= leon_unmask_irq, | 
 | 195 | 	.irq_eoi		= leon_eoi_irq, | 
 | 196 | 	.irq_set_affinity	= leon_set_affinity, | 
| Sam Ravnborg | 6baa9b2 | 2011-04-18 11:25:44 +0000 | [diff] [blame] | 197 | }; | 
 | 198 |  | 
| Daniel Hellstrom | 4c6773c | 2011-04-19 23:41:22 +0000 | [diff] [blame] | 199 | /* | 
 | 200 |  * Build a LEON IRQ for the edge triggered LEON IRQ controller: | 
 | 201 |  *  Edge (normal) IRQ           - handle_simple_irq, ack=DONT-CARE, never ack | 
 | 202 |  *  Level IRQ (PCI|Level-GPIO)  - handle_fasteoi_irq, ack=1, ack after ISR | 
 | 203 |  *  Per-CPU Edge                - handle_percpu_irq, ack=0 | 
 | 204 |  */ | 
 | 205 | unsigned int leon_build_device_irq(unsigned int real_irq, | 
 | 206 | 				    irq_flow_handler_t flow_handler, | 
 | 207 | 				    const char *name, int do_ack) | 
| Sam Ravnborg | 6baa9b2 | 2011-04-18 11:25:44 +0000 | [diff] [blame] | 208 | { | 
 | 209 | 	unsigned int irq; | 
 | 210 | 	unsigned long mask; | 
 | 211 |  | 
 | 212 | 	irq = 0; | 
 | 213 | 	mask = get_irqmask(real_irq); | 
 | 214 | 	if (mask == 0) | 
 | 215 | 		goto out; | 
 | 216 |  | 
 | 217 | 	irq = irq_alloc(real_irq, real_irq); | 
 | 218 | 	if (irq == 0) | 
 | 219 | 		goto out; | 
 | 220 |  | 
| Daniel Hellstrom | 4c6773c | 2011-04-19 23:41:22 +0000 | [diff] [blame] | 221 | 	if (do_ack) | 
 | 222 | 		mask |= LEON_DO_ACK_HW; | 
 | 223 |  | 
| Sam Ravnborg | 6baa9b2 | 2011-04-18 11:25:44 +0000 | [diff] [blame] | 224 | 	irq_set_chip_and_handler_name(irq, &leon_irq, | 
| Daniel Hellstrom | 4c6773c | 2011-04-19 23:41:22 +0000 | [diff] [blame] | 225 | 				      flow_handler, name); | 
| Sam Ravnborg | 6baa9b2 | 2011-04-18 11:25:44 +0000 | [diff] [blame] | 226 | 	irq_set_chip_data(irq, (void *)mask); | 
 | 227 |  | 
 | 228 | out: | 
 | 229 | 	return irq; | 
 | 230 | } | 
 | 231 |  | 
| Daniel Hellstrom | 4c6773c | 2011-04-19 23:41:22 +0000 | [diff] [blame] | 232 | static unsigned int _leon_build_device_irq(struct platform_device *op, | 
 | 233 | 					   unsigned int real_irq) | 
 | 234 | { | 
 | 235 | 	return leon_build_device_irq(real_irq, handle_simple_irq, "edge", 0); | 
 | 236 | } | 
 | 237 |  | 
| Daniel Hellstrom | 5d07b78 | 2011-05-23 21:04:48 +0000 | [diff] [blame] | 238 | void leon_update_virq_handling(unsigned int virq, | 
 | 239 | 			      irq_flow_handler_t flow_handler, | 
 | 240 | 			      const char *name, int do_ack) | 
 | 241 | { | 
 | 242 | 	unsigned long mask = (unsigned long)irq_get_chip_data(virq); | 
 | 243 |  | 
 | 244 | 	mask &= ~LEON_DO_ACK_HW; | 
 | 245 | 	if (do_ack) | 
 | 246 | 		mask |= LEON_DO_ACK_HW; | 
 | 247 |  | 
 | 248 | 	irq_set_chip_and_handler_name(virq, &leon_irq, | 
 | 249 | 				      flow_handler, name); | 
 | 250 | 	irq_set_chip_data(virq, (void *)mask); | 
 | 251 | } | 
 | 252 |  | 
| Konrad Eisele | 5213a78 | 2009-08-17 00:13:29 +0000 | [diff] [blame] | 253 | void __init leon_init_timers(irq_handler_t counter_fn) | 
 | 254 | { | 
| Daniel Hellstrom | 4c6773c | 2011-04-19 23:41:22 +0000 | [diff] [blame] | 255 | 	int irq, eirq; | 
| Daniel Hellstrom | 2791c1a | 2011-01-04 01:41:32 +0000 | [diff] [blame] | 256 | 	struct device_node *rootnp, *np, *nnp; | 
| Daniel Hellstrom | 53aea7c | 2011-01-04 01:41:29 +0000 | [diff] [blame] | 257 | 	struct property *pp; | 
 | 258 | 	int len; | 
| Daniel Hellstrom | 01dae0f | 2011-04-21 04:20:24 +0000 | [diff] [blame] | 259 | 	int icsel; | 
| Daniel Hellstrom | 2791c1a | 2011-01-04 01:41:32 +0000 | [diff] [blame] | 260 | 	int ampopts; | 
| Sam Ravnborg | 6baa9b2 | 2011-04-18 11:25:44 +0000 | [diff] [blame] | 261 | 	int err; | 
| Konrad Eisele | 5213a78 | 2009-08-17 00:13:29 +0000 | [diff] [blame] | 262 |  | 
 | 263 | 	leondebug_irq_disable = 0; | 
 | 264 | 	leon_debug_irqout = 0; | 
 | 265 | 	master_l10_counter = (unsigned int *)&dummy_master_l10_counter; | 
 | 266 | 	dummy_master_l10_counter = 0; | 
 | 267 |  | 
| Daniel Hellstrom | 53aea7c | 2011-01-04 01:41:29 +0000 | [diff] [blame] | 268 | 	rootnp = of_find_node_by_path("/ambapp0"); | 
 | 269 | 	if (!rootnp) | 
 | 270 | 		goto bad; | 
| Daniel Hellstrom | 7279b82 | 2011-01-27 01:26:58 +0000 | [diff] [blame] | 271 |  | 
 | 272 | 	/* Find System ID: GRLIB build ID and optional CHIP ID */ | 
 | 273 | 	pp = of_find_property(rootnp, "systemid", &len); | 
 | 274 | 	if (pp) | 
 | 275 | 		amba_system_id = *(unsigned long *)pp->value; | 
 | 276 |  | 
 | 277 | 	/* Find IRQMP IRQ Controller Registers base adr otherwise bail out */ | 
| Daniel Hellstrom | 53aea7c | 2011-01-04 01:41:29 +0000 | [diff] [blame] | 278 | 	np = of_find_node_by_name(rootnp, "GAISLER_IRQMP"); | 
| Daniel Hellstrom | 9742e72 | 2011-01-04 01:41:31 +0000 | [diff] [blame] | 279 | 	if (!np) { | 
 | 280 | 		np = of_find_node_by_name(rootnp, "01_00d"); | 
 | 281 | 		if (!np) | 
 | 282 | 			goto bad; | 
 | 283 | 	} | 
| Daniel Hellstrom | 53aea7c | 2011-01-04 01:41:29 +0000 | [diff] [blame] | 284 | 	pp = of_find_property(np, "reg", &len); | 
 | 285 | 	if (!pp) | 
 | 286 | 		goto bad; | 
 | 287 | 	leon3_irqctrl_regs = *(struct leon3_irqctrl_regs_map **)pp->value; | 
 | 288 |  | 
 | 289 | 	/* Find GPTIMER Timer Registers base address otherwise bail out. */ | 
| Daniel Hellstrom | 2791c1a | 2011-01-04 01:41:32 +0000 | [diff] [blame] | 290 | 	nnp = rootnp; | 
 | 291 | 	do { | 
 | 292 | 		np = of_find_node_by_name(nnp, "GAISLER_GPTIMER"); | 
 | 293 | 		if (!np) { | 
 | 294 | 			np = of_find_node_by_name(nnp, "01_011"); | 
 | 295 | 			if (!np) | 
 | 296 | 				goto bad; | 
 | 297 | 		} | 
 | 298 |  | 
 | 299 | 		ampopts = 0; | 
 | 300 | 		pp = of_find_property(np, "ampopts", &len); | 
 | 301 | 		if (pp) { | 
 | 302 | 			ampopts = *(int *)pp->value; | 
 | 303 | 			if (ampopts == 0) { | 
 | 304 | 				/* Skip this instance, resource already | 
 | 305 | 				 * allocated by other OS */ | 
 | 306 | 				nnp = np; | 
 | 307 | 				continue; | 
 | 308 | 			} | 
 | 309 | 		} | 
 | 310 |  | 
 | 311 | 		/* Select Timer-Instance on Timer Core. Default is zero */ | 
 | 312 | 		leon3_gptimer_idx = ampopts & 0x7; | 
 | 313 |  | 
 | 314 | 		pp = of_find_property(np, "reg", &len); | 
 | 315 | 		if (pp) | 
 | 316 | 			leon3_gptimer_regs = *(struct leon3_gptimer_regs_map **) | 
 | 317 | 						pp->value; | 
 | 318 | 		pp = of_find_property(np, "interrupts", &len); | 
 | 319 | 		if (pp) | 
 | 320 | 			leon3_gptimer_irq = *(unsigned int *)pp->value; | 
 | 321 | 	} while (0); | 
| Daniel Hellstrom | 53aea7c | 2011-01-04 01:41:29 +0000 | [diff] [blame] | 322 |  | 
| Daniel Hellstrom | a481b5d | 2011-04-19 23:41:25 +0000 | [diff] [blame] | 323 | 	if (!(leon3_gptimer_regs && leon3_irqctrl_regs && leon3_gptimer_irq)) | 
 | 324 | 		goto bad; | 
 | 325 |  | 
 | 326 | 	LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].val, 0); | 
 | 327 | 	LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].rld, | 
 | 328 | 				(((1000000 / HZ) - 1))); | 
 | 329 | 	LEON3_BYPASS_STORE_PA( | 
| Daniel Hellstrom | 2791c1a | 2011-01-04 01:41:32 +0000 | [diff] [blame] | 330 | 			&leon3_gptimer_regs->e[leon3_gptimer_idx].ctrl, 0); | 
| Konrad Eisele | 5213a78 | 2009-08-17 00:13:29 +0000 | [diff] [blame] | 331 |  | 
| Konrad Eisele | 8401707 | 2009-08-31 22:08:13 +0000 | [diff] [blame] | 332 | #ifdef CONFIG_SMP | 
| Daniel Hellstrom | a481b5d | 2011-04-19 23:41:25 +0000 | [diff] [blame] | 333 | 	leon3_ticker_irq = leon3_gptimer_irq + 1 + leon3_gptimer_idx; | 
| Konrad Eisele | 8401707 | 2009-08-31 22:08:13 +0000 | [diff] [blame] | 334 |  | 
| Daniel Hellstrom | a481b5d | 2011-04-19 23:41:25 +0000 | [diff] [blame] | 335 | 	if (!(LEON3_BYPASS_LOAD_PA(&leon3_gptimer_regs->config) & | 
 | 336 | 	      (1<<LEON3_GPTIMER_SEPIRQ))) { | 
 | 337 | 		printk(KERN_ERR "timer not configured with separate irqs\n"); | 
 | 338 | 		BUG(); | 
| Konrad Eisele | 5213a78 | 2009-08-17 00:13:29 +0000 | [diff] [blame] | 339 | 	} | 
 | 340 |  | 
| Daniel Hellstrom | a481b5d | 2011-04-19 23:41:25 +0000 | [diff] [blame] | 341 | 	LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx+1].val, | 
 | 342 | 				0); | 
 | 343 | 	LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx+1].rld, | 
 | 344 | 				(((1000000/HZ) - 1))); | 
 | 345 | 	LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx+1].ctrl, | 
 | 346 | 				0); | 
 | 347 | #endif | 
 | 348 |  | 
 | 349 | 	/* | 
 | 350 | 	 * The IRQ controller may (if implemented) consist of multiple | 
 | 351 | 	 * IRQ controllers, each mapped on a 4Kb boundary. | 
 | 352 | 	 * Each CPU may be routed to different IRQCTRLs, however | 
 | 353 | 	 * we assume that all CPUs (in SMP system) is routed to the | 
 | 354 | 	 * same IRQ Controller, and for non-SMP only one IRQCTRL is | 
 | 355 | 	 * accessed anyway. | 
 | 356 | 	 * In AMP systems, Linux must run on CPU0 for the time being. | 
 | 357 | 	 */ | 
| Daniel Hellstrom | 01dae0f | 2011-04-21 04:20:24 +0000 | [diff] [blame] | 358 | 	icsel = LEON3_BYPASS_LOAD_PA(&leon3_irqctrl_regs->icsel[boot_cpu_id/8]); | 
 | 359 | 	icsel = (icsel >> ((7 - (boot_cpu_id&0x7)) * 4)) & 0xf; | 
| Daniel Hellstrom | a481b5d | 2011-04-19 23:41:25 +0000 | [diff] [blame] | 360 | 	leon3_irqctrl_regs += icsel; | 
 | 361 |  | 
| Daniel Hellstrom | 970def6 | 2011-04-21 04:20:25 +0000 | [diff] [blame] | 362 | 	/* Mask all IRQs on boot-cpu IRQ controller */ | 
 | 363 | 	LEON3_BYPASS_STORE_PA(&leon3_irqctrl_regs->mask[boot_cpu_id], 0); | 
 | 364 |  | 
| Daniel Hellstrom | a481b5d | 2011-04-19 23:41:25 +0000 | [diff] [blame] | 365 | 	/* Probe extended IRQ controller */ | 
 | 366 | 	eirq = (LEON3_BYPASS_LOAD_PA(&leon3_irqctrl_regs->mpstatus) | 
 | 367 | 		>> 16) & 0xf; | 
 | 368 | 	if (eirq != 0) | 
 | 369 | 		leon_eirq_setup(eirq); | 
 | 370 |  | 
| Daniel Hellstrom | 4c6773c | 2011-04-19 23:41:22 +0000 | [diff] [blame] | 371 | 	irq = _leon_build_device_irq(NULL, leon3_gptimer_irq+leon3_gptimer_idx); | 
| Sam Ravnborg | 6baa9b2 | 2011-04-18 11:25:44 +0000 | [diff] [blame] | 372 | 	err = request_irq(irq, counter_fn, IRQF_TIMER, "timer", NULL); | 
| Sam Ravnborg | 6baa9b2 | 2011-04-18 11:25:44 +0000 | [diff] [blame] | 373 | 	if (err) { | 
| Daniel Hellstrom | a481b5d | 2011-04-19 23:41:25 +0000 | [diff] [blame] | 374 | 		printk(KERN_ERR "unable to attach timer IRQ%d\n", irq); | 
| Konrad Eisele | 5213a78 | 2009-08-17 00:13:29 +0000 | [diff] [blame] | 375 | 		prom_halt(); | 
 | 376 | 	} | 
 | 377 |  | 
| Daniel Hellstrom | 10f0d07 | 2011-06-09 02:54:09 +0000 | [diff] [blame] | 378 | #ifdef CONFIG_SMP | 
 | 379 | 	{ | 
 | 380 | 		unsigned long flags; | 
 | 381 |  | 
 | 382 | 		/* | 
 | 383 | 		 * In SMP, sun4m adds a IPI handler to IRQ trap handler that | 
 | 384 | 		 * LEON never must take, sun4d and LEON overwrites the branch | 
 | 385 | 		 * with a NOP. | 
 | 386 | 		 */ | 
 | 387 | 		local_irq_save(flags); | 
 | 388 | 		patchme_maybe_smp_msg[0] = 0x01000000; /* NOP out the branch */ | 
 | 389 | 		local_flush_cache_all(); | 
 | 390 | 		local_irq_restore(flags); | 
 | 391 | 	} | 
 | 392 | #endif | 
 | 393 |  | 
| Daniel Hellstrom | a481b5d | 2011-04-19 23:41:25 +0000 | [diff] [blame] | 394 | 	LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].ctrl, | 
 | 395 | 			      LEON3_GPTIMER_EN | | 
 | 396 | 			      LEON3_GPTIMER_RL | | 
 | 397 | 			      LEON3_GPTIMER_LD | | 
 | 398 | 			      LEON3_GPTIMER_IRQEN); | 
| Konrad Eisele | 8401707 | 2009-08-31 22:08:13 +0000 | [diff] [blame] | 399 |  | 
 | 400 | #ifdef CONFIG_SMP | 
| Daniel Hellstrom | a481b5d | 2011-04-19 23:41:25 +0000 | [diff] [blame] | 401 | 	/* Install per-cpu IRQ handler for broadcasted ticker */ | 
 | 402 | 	irq = leon_build_device_irq(leon3_ticker_irq, handle_percpu_irq, | 
 | 403 | 				    "per-cpu", 0); | 
 | 404 | 	err = request_irq(irq, leon_percpu_timer_interrupt, | 
 | 405 | 			  IRQF_PERCPU | IRQF_TIMER, "ticker", | 
 | 406 | 			  NULL); | 
 | 407 | 	if (err) { | 
 | 408 | 		printk(KERN_ERR "unable to attach ticker IRQ%d\n", irq); | 
 | 409 | 		prom_halt(); | 
| Konrad Eisele | 5213a78 | 2009-08-17 00:13:29 +0000 | [diff] [blame] | 410 | 	} | 
| Daniel Hellstrom | a481b5d | 2011-04-19 23:41:25 +0000 | [diff] [blame] | 411 |  | 
 | 412 | 	LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx+1].ctrl, | 
 | 413 | 			      LEON3_GPTIMER_EN | | 
 | 414 | 			      LEON3_GPTIMER_RL | | 
 | 415 | 			      LEON3_GPTIMER_LD | | 
 | 416 | 			      LEON3_GPTIMER_IRQEN); | 
 | 417 | #endif | 
| Daniel Hellstrom | 53aea7c | 2011-01-04 01:41:29 +0000 | [diff] [blame] | 418 | 	return; | 
 | 419 | bad: | 
 | 420 | 	printk(KERN_ERR "No Timer/irqctrl found\n"); | 
 | 421 | 	BUG(); | 
 | 422 | 	return; | 
| Konrad Eisele | 5213a78 | 2009-08-17 00:13:29 +0000 | [diff] [blame] | 423 | } | 
 | 424 |  | 
 | 425 | void leon_clear_clock_irq(void) | 
 | 426 | { | 
 | 427 | } | 
 | 428 |  | 
 | 429 | void leon_load_profile_irq(int cpu, unsigned int limit) | 
 | 430 | { | 
 | 431 | 	BUG(); | 
 | 432 | } | 
 | 433 |  | 
| Konrad Eisele | 5213a78 | 2009-08-17 00:13:29 +0000 | [diff] [blame] | 434 | void __init leon_trans_init(struct device_node *dp) | 
 | 435 | { | 
 | 436 | 	if (strcmp(dp->type, "cpu") == 0 && strcmp(dp->name, "<NULL>") == 0) { | 
 | 437 | 		struct property *p; | 
 | 438 | 		p = of_find_property(dp, "mid", (void *)0); | 
 | 439 | 		if (p) { | 
 | 440 | 			int mid; | 
 | 441 | 			dp->name = prom_early_alloc(5 + 1); | 
 | 442 | 			memcpy(&mid, p->value, p->length); | 
 | 443 | 			sprintf((char *)dp->name, "cpu%.2d", mid); | 
 | 444 | 		} | 
 | 445 | 	} | 
 | 446 | } | 
 | 447 |  | 
 | 448 | void __initdata (*prom_amba_init)(struct device_node *dp, struct device_node ***nextp) = 0; | 
 | 449 |  | 
 | 450 | void __init leon_node_init(struct device_node *dp, struct device_node ***nextp) | 
 | 451 | { | 
 | 452 | 	if (prom_amba_init && | 
 | 453 | 	    strcmp(dp->type, "ambapp") == 0 && | 
 | 454 | 	    strcmp(dp->name, "ambapp0") == 0) { | 
 | 455 | 		prom_amba_init(dp, nextp); | 
 | 456 | 	} | 
 | 457 | } | 
 | 458 |  | 
| Konrad Eisele | 8401707 | 2009-08-31 22:08:13 +0000 | [diff] [blame] | 459 | #ifdef CONFIG_SMP | 
 | 460 |  | 
 | 461 | void leon_set_cpu_int(int cpu, int level) | 
 | 462 | { | 
 | 463 | 	unsigned long mask; | 
 | 464 | 	mask = get_irqmask(level); | 
 | 465 | 	LEON3_BYPASS_STORE_PA(&leon3_irqctrl_regs->force[cpu], mask); | 
 | 466 | } | 
 | 467 |  | 
 | 468 | static void leon_clear_ipi(int cpu, int level) | 
 | 469 | { | 
 | 470 | 	unsigned long mask; | 
 | 471 | 	mask = get_irqmask(level); | 
 | 472 | 	LEON3_BYPASS_STORE_PA(&leon3_irqctrl_regs->force[cpu], mask<<16); | 
 | 473 | } | 
 | 474 |  | 
 | 475 | static void leon_set_udt(int cpu) | 
 | 476 | { | 
 | 477 | } | 
 | 478 |  | 
 | 479 | void leon_clear_profile_irq(int cpu) | 
 | 480 | { | 
 | 481 | } | 
 | 482 |  | 
 | 483 | void leon_enable_irq_cpu(unsigned int irq_nr, unsigned int cpu) | 
 | 484 | { | 
 | 485 | 	unsigned long mask, flags, *addr; | 
 | 486 | 	mask = get_irqmask(irq_nr); | 
| Daniel Hellstrom | d61a38b | 2011-04-19 23:41:21 +0000 | [diff] [blame] | 487 | 	spin_lock_irqsave(&leon_irq_lock, flags); | 
| Daniel Hellstrom | a481b5d | 2011-04-19 23:41:25 +0000 | [diff] [blame] | 488 | 	addr = (unsigned long *)LEON_IMASK(cpu); | 
 | 489 | 	LEON3_BYPASS_STORE_PA(addr, (LEON3_BYPASS_LOAD_PA(addr) | mask)); | 
| Daniel Hellstrom | d61a38b | 2011-04-19 23:41:21 +0000 | [diff] [blame] | 490 | 	spin_unlock_irqrestore(&leon_irq_lock, flags); | 
| Konrad Eisele | 8401707 | 2009-08-31 22:08:13 +0000 | [diff] [blame] | 491 | } | 
 | 492 |  | 
 | 493 | #endif | 
 | 494 |  | 
| Konrad Eisele | 5213a78 | 2009-08-17 00:13:29 +0000 | [diff] [blame] | 495 | void __init leon_init_IRQ(void) | 
 | 496 | { | 
| Sam Ravnborg | 6baa9b2 | 2011-04-18 11:25:44 +0000 | [diff] [blame] | 497 | 	sparc_irq_config.init_timers      = leon_init_timers; | 
| Daniel Hellstrom | 4c6773c | 2011-04-19 23:41:22 +0000 | [diff] [blame] | 498 | 	sparc_irq_config.build_device_irq = _leon_build_device_irq; | 
| Konrad Eisele | 5213a78 | 2009-08-17 00:13:29 +0000 | [diff] [blame] | 499 |  | 
 | 500 | 	BTFIXUPSET_CALL(clear_clock_irq, leon_clear_clock_irq, | 
 | 501 | 			BTFIXUPCALL_NORM); | 
 | 502 | 	BTFIXUPSET_CALL(load_profile_irq, leon_load_profile_irq, | 
 | 503 | 			BTFIXUPCALL_NOP); | 
 | 504 |  | 
 | 505 | #ifdef CONFIG_SMP | 
 | 506 | 	BTFIXUPSET_CALL(set_cpu_int, leon_set_cpu_int, BTFIXUPCALL_NORM); | 
 | 507 | 	BTFIXUPSET_CALL(clear_cpu_int, leon_clear_ipi, BTFIXUPCALL_NORM); | 
 | 508 | 	BTFIXUPSET_CALL(set_irq_udt, leon_set_udt, BTFIXUPCALL_NORM); | 
 | 509 | #endif | 
 | 510 |  | 
 | 511 | } | 
 | 512 |  | 
 | 513 | void __init leon_init(void) | 
 | 514 | { | 
| Andres Salomon | ed41850 | 2010-10-10 21:51:25 -0600 | [diff] [blame] | 515 | 	of_pdt_build_more = &leon_node_init; | 
| Konrad Eisele | 5213a78 | 2009-08-17 00:13:29 +0000 | [diff] [blame] | 516 | } |