diff options
Diffstat (limited to 'arch/sparc/kernel/leon_kernel.c')
| -rw-r--r-- | arch/sparc/kernel/leon_kernel.c | 114 |
1 files changed, 97 insertions, 17 deletions
diff --git a/arch/sparc/kernel/leon_kernel.c b/arch/sparc/kernel/leon_kernel.c index f01c42661ee5..fdab7f854f80 100644 --- a/arch/sparc/kernel/leon_kernel.c +++ b/arch/sparc/kernel/leon_kernel.c | |||
| @@ -23,15 +23,16 @@ | |||
| 23 | #include "prom.h" | 23 | #include "prom.h" |
| 24 | #include "irq.h" | 24 | #include "irq.h" |
| 25 | 25 | ||
| 26 | struct leon3_irqctrl_regs_map *leon3_irqctrl_regs; /* interrupt controller base address, initialized by amba_init() */ | 26 | struct leon3_irqctrl_regs_map *leon3_irqctrl_regs; /* interrupt controller base address */ |
| 27 | struct leon3_gptimer_regs_map *leon3_gptimer_regs; /* timer controller base address, initialized by amba_init() */ | 27 | struct leon3_gptimer_regs_map *leon3_gptimer_regs; /* timer controller base address */ |
| 28 | struct amba_apb_device leon_percpu_timer_dev[16]; | 28 | struct amba_apb_device leon_percpu_timer_dev[16]; |
| 29 | 29 | ||
| 30 | int leondebug_irq_disable; | 30 | int leondebug_irq_disable; |
| 31 | int leon_debug_irqout; | 31 | int leon_debug_irqout; |
| 32 | static int dummy_master_l10_counter; | 32 | static int dummy_master_l10_counter; |
| 33 | 33 | ||
| 34 | unsigned long leon3_gptimer_irq; /* interrupt controller irq number, initialized by amba_init() */ | 34 | unsigned long leon3_gptimer_irq; /* interrupt controller irq number */ |
| 35 | unsigned long leon3_gptimer_idx; /* Timer Index (0..6) within Timer Core */ | ||
| 35 | unsigned int sparc_leon_eirq; | 36 | unsigned int sparc_leon_eirq; |
| 36 | #define LEON_IMASK ((&leon3_irqctrl_regs->mask[0])) | 37 | #define LEON_IMASK ((&leon3_irqctrl_regs->mask[0])) |
| 37 | 38 | ||
| @@ -105,21 +106,79 @@ static void leon_disable_irq(unsigned int irq_nr) | |||
| 105 | void __init leon_init_timers(irq_handler_t counter_fn) | 106 | void __init leon_init_timers(irq_handler_t counter_fn) |
| 106 | { | 107 | { |
| 107 | int irq; | 108 | int irq; |
| 109 | struct device_node *rootnp, *np, *nnp; | ||
| 110 | struct property *pp; | ||
| 111 | int len; | ||
| 112 | int cpu, icsel; | ||
| 113 | int ampopts; | ||
| 108 | 114 | ||
| 109 | leondebug_irq_disable = 0; | 115 | leondebug_irq_disable = 0; |
| 110 | leon_debug_irqout = 0; | 116 | leon_debug_irqout = 0; |
| 111 | master_l10_counter = (unsigned int *)&dummy_master_l10_counter; | 117 | master_l10_counter = (unsigned int *)&dummy_master_l10_counter; |
| 112 | dummy_master_l10_counter = 0; | 118 | dummy_master_l10_counter = 0; |
| 113 | 119 | ||
| 114 | if (leon3_gptimer_regs && leon3_irqctrl_regs) { | 120 | /*Find IRQMP IRQ Controller Registers base address otherwise bail out.*/ |
| 115 | LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[0].val, 0); | 121 | rootnp = of_find_node_by_path("/ambapp0"); |
| 116 | LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[0].rld, | 122 | if (!rootnp) |
| 117 | (((1000000 / HZ) - 1))); | 123 | goto bad; |
| 118 | LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[0].ctrl, 0); | 124 | np = of_find_node_by_name(rootnp, "GAISLER_IRQMP"); |
| 125 | if (!np) { | ||
| 126 | np = of_find_node_by_name(rootnp, "01_00d"); | ||
| 127 | if (!np) | ||
| 128 | goto bad; | ||
| 129 | } | ||
| 130 | pp = of_find_property(np, "reg", &len); | ||
| 131 | if (!pp) | ||
| 132 | goto bad; | ||
| 133 | leon3_irqctrl_regs = *(struct leon3_irqctrl_regs_map **)pp->value; | ||
| 134 | |||
| 135 | /* Find GPTIMER Timer Registers base address otherwise bail out. */ | ||
| 136 | nnp = rootnp; | ||
| 137 | do { | ||
| 138 | np = of_find_node_by_name(nnp, "GAISLER_GPTIMER"); | ||
| 139 | if (!np) { | ||
| 140 | np = of_find_node_by_name(nnp, "01_011"); | ||
| 141 | if (!np) | ||
| 142 | goto bad; | ||
| 143 | } | ||
| 144 | |||
| 145 | ampopts = 0; | ||
| 146 | pp = of_find_property(np, "ampopts", &len); | ||
| 147 | if (pp) { | ||
| 148 | ampopts = *(int *)pp->value; | ||
| 149 | if (ampopts == 0) { | ||
| 150 | /* Skip this instance, resource already | ||
| 151 | * allocated by other OS */ | ||
| 152 | nnp = np; | ||
| 153 | continue; | ||
| 154 | } | ||
| 155 | } | ||
| 156 | |||
| 157 | /* Select Timer-Instance on Timer Core. Default is zero */ | ||
| 158 | leon3_gptimer_idx = ampopts & 0x7; | ||
| 159 | |||
| 160 | pp = of_find_property(np, "reg", &len); | ||
| 161 | if (pp) | ||
| 162 | leon3_gptimer_regs = *(struct leon3_gptimer_regs_map **) | ||
| 163 | pp->value; | ||
| 164 | pp = of_find_property(np, "interrupts", &len); | ||
| 165 | if (pp) | ||
| 166 | leon3_gptimer_irq = *(unsigned int *)pp->value; | ||
| 167 | } while (0); | ||
| 168 | |||
| 169 | if (leon3_gptimer_regs && leon3_irqctrl_regs && leon3_gptimer_irq) { | ||
| 170 | LEON3_BYPASS_STORE_PA( | ||
| 171 | &leon3_gptimer_regs->e[leon3_gptimer_idx].val, 0); | ||
| 172 | LEON3_BYPASS_STORE_PA( | ||
| 173 | &leon3_gptimer_regs->e[leon3_gptimer_idx].rld, | ||
| 174 | (((1000000 / HZ) - 1))); | ||
| 175 | LEON3_BYPASS_STORE_PA( | ||
| 176 | &leon3_gptimer_regs->e[leon3_gptimer_idx].ctrl, 0); | ||
| 119 | 177 | ||
| 120 | #ifdef CONFIG_SMP | 178 | #ifdef CONFIG_SMP |
| 121 | leon_percpu_timer_dev[0].start = (int)leon3_gptimer_regs; | 179 | leon_percpu_timer_dev[0].start = (int)leon3_gptimer_regs; |
| 122 | leon_percpu_timer_dev[0].irq = leon3_gptimer_irq+1; | 180 | leon_percpu_timer_dev[0].irq = leon3_gptimer_irq + 1 + |
| 181 | leon3_gptimer_idx; | ||
| 123 | 182 | ||
| 124 | if (!(LEON3_BYPASS_LOAD_PA(&leon3_gptimer_regs->config) & | 183 | if (!(LEON3_BYPASS_LOAD_PA(&leon3_gptimer_regs->config) & |
| 125 | (1<<LEON3_GPTIMER_SEPIRQ))) { | 184 | (1<<LEON3_GPTIMER_SEPIRQ))) { |
| @@ -127,17 +186,33 @@ void __init leon_init_timers(irq_handler_t counter_fn) | |||
| 127 | BUG(); | 186 | BUG(); |
| 128 | } | 187 | } |
| 129 | 188 | ||
| 130 | LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[1].val, 0); | 189 | LEON3_BYPASS_STORE_PA( |
| 131 | LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[1].rld, (((1000000/HZ) - 1))); | 190 | &leon3_gptimer_regs->e[leon3_gptimer_idx+1].val, 0); |
| 132 | LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[1].ctrl, 0); | 191 | LEON3_BYPASS_STORE_PA( |
| 192 | &leon3_gptimer_regs->e[leon3_gptimer_idx+1].rld, | ||
| 193 | (((1000000/HZ) - 1))); | ||
| 194 | LEON3_BYPASS_STORE_PA( | ||
| 195 | &leon3_gptimer_regs->e[leon3_gptimer_idx+1].ctrl, 0); | ||
| 133 | # endif | 196 | # endif |
| 134 | 197 | ||
| 198 | /* | ||
| 199 | * The IRQ controller may (if implemented) consist of multiple | ||
| 200 | * IRQ controllers, each mapped on a 4Kb boundary. | ||
| 201 | * Each CPU may be routed to different IRQCTRLs, however | ||
| 202 | * we assume that all CPUs (in SMP system) is routed to the | ||
| 203 | * same IRQ Controller, and for non-SMP only one IRQCTRL is | ||
| 204 | * accessed anyway. | ||
| 205 | * In AMP systems, Linux must run on CPU0 for the time being. | ||
| 206 | */ | ||
| 207 | cpu = sparc_leon3_cpuid(); | ||
| 208 | icsel = LEON3_BYPASS_LOAD_PA(&leon3_irqctrl_regs->icsel[cpu/8]); | ||
| 209 | icsel = (icsel >> ((7 - (cpu&0x7)) * 4)) & 0xf; | ||
| 210 | leon3_irqctrl_regs += icsel; | ||
| 135 | } else { | 211 | } else { |
| 136 | printk(KERN_ERR "No Timer/irqctrl found\n"); | 212 | goto bad; |
| 137 | BUG(); | ||
| 138 | } | 213 | } |
| 139 | 214 | ||
| 140 | irq = request_irq(leon3_gptimer_irq, | 215 | irq = request_irq(leon3_gptimer_irq+leon3_gptimer_idx, |
| 141 | counter_fn, | 216 | counter_fn, |
| 142 | (IRQF_DISABLED | SA_STATIC_ALLOC), "timer", NULL); | 217 | (IRQF_DISABLED | SA_STATIC_ALLOC), "timer", NULL); |
| 143 | 218 | ||
| @@ -169,13 +244,13 @@ void __init leon_init_timers(irq_handler_t counter_fn) | |||
| 169 | # endif | 244 | # endif |
| 170 | 245 | ||
| 171 | if (leon3_gptimer_regs) { | 246 | if (leon3_gptimer_regs) { |
| 172 | LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[0].ctrl, | 247 | LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].ctrl, |
| 173 | LEON3_GPTIMER_EN | | 248 | LEON3_GPTIMER_EN | |
| 174 | LEON3_GPTIMER_RL | | 249 | LEON3_GPTIMER_RL | |
| 175 | LEON3_GPTIMER_LD | LEON3_GPTIMER_IRQEN); | 250 | LEON3_GPTIMER_LD | LEON3_GPTIMER_IRQEN); |
| 176 | 251 | ||
| 177 | #ifdef CONFIG_SMP | 252 | #ifdef CONFIG_SMP |
| 178 | LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[1].ctrl, | 253 | LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx+1].ctrl, |
| 179 | LEON3_GPTIMER_EN | | 254 | LEON3_GPTIMER_EN | |
| 180 | LEON3_GPTIMER_RL | | 255 | LEON3_GPTIMER_RL | |
| 181 | LEON3_GPTIMER_LD | | 256 | LEON3_GPTIMER_LD | |
| @@ -183,6 +258,11 @@ void __init leon_init_timers(irq_handler_t counter_fn) | |||
| 183 | #endif | 258 | #endif |
| 184 | 259 | ||
| 185 | } | 260 | } |
| 261 | return; | ||
| 262 | bad: | ||
| 263 | printk(KERN_ERR "No Timer/irqctrl found\n"); | ||
| 264 | BUG(); | ||
| 265 | return; | ||
| 186 | } | 266 | } |
| 187 | 267 | ||
| 188 | void leon_clear_clock_irq(void) | 268 | void leon_clear_clock_irq(void) |
