1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
25213a780SKonrad Eisele /*
35213a780SKonrad Eisele * Copyright (C) 2009 Daniel Hellstrom ([email protected]) Aeroflex Gaisler AB
45213a780SKonrad Eisele * Copyright (C) 2009 Konrad Eisele ([email protected]) Aeroflex Gaisler AB
55213a780SKonrad Eisele */
65213a780SKonrad Eisele
75213a780SKonrad Eisele #include <linux/kernel.h>
85213a780SKonrad Eisele #include <linux/errno.h>
95213a780SKonrad Eisele #include <linux/mutex.h>
105213a780SKonrad Eisele #include <linux/of.h>
115213a780SKonrad Eisele #include <linux/interrupt.h>
1262f08283STkhai Kirill #include <linux/clocksource.h>
1362f08283STkhai Kirill #include <linux/clockchips.h>
148401707fSKonrad Eisele
155213a780SKonrad Eisele #include <asm/oplib.h>
165213a780SKonrad Eisele #include <asm/timer.h>
175213a780SKonrad Eisele #include <asm/prom.h>
185213a780SKonrad Eisele #include <asm/leon.h>
195213a780SKonrad Eisele #include <asm/leon_amba.h>
208401707fSKonrad Eisele #include <asm/traps.h>
218401707fSKonrad Eisele #include <asm/cacheflush.h>
224c6773c3SDaniel Hellstrom #include <asm/smp.h>
2301dae0f0SDaniel Hellstrom #include <asm/setup.h>
245213a780SKonrad Eisele
2593bb32f6SSam Ravnborg #include "kernel.h"
265213a780SKonrad Eisele #include "prom.h"
275213a780SKonrad Eisele #include "irq.h"
285213a780SKonrad Eisele
2953aea7caSDaniel Hellstrom struct leon3_irqctrl_regs_map *leon3_irqctrl_regs; /* interrupt controller base address */
3053aea7caSDaniel Hellstrom struct leon3_gptimer_regs_map *leon3_gptimer_regs; /* timer controller base address */
315213a780SKonrad Eisele
325213a780SKonrad Eisele int leondebug_irq_disable;
335213a780SKonrad Eisele int leon_debug_irqout;
34fcea8b27SSam Ravnborg static volatile u32 dummy_master_l10_counter;
357279b82cSDaniel Hellstrom unsigned long amba_system_id;
36d61a38b2SDaniel Hellstrom static DEFINE_SPINLOCK(leon_irq_lock);
375213a780SKonrad Eisele
384007b65aSSam Ravnborg static unsigned long leon3_gptimer_idx; /* Timer Index (0..6) within Timer Core */
39384859d2SAndreas Larsson static unsigned long leon3_gptimer_ackmask; /* For clearing pending bit */
4053aea7caSDaniel Hellstrom unsigned long leon3_gptimer_irq; /* interrupt controller irq number */
415213a780SKonrad Eisele unsigned int sparc_leon_eirq;
42a481b5d0SDaniel Hellstrom #define LEON_IMASK(cpu) (&leon3_irqctrl_regs->mask[cpu])
434c6773c3SDaniel Hellstrom #define LEON_IACK (&leon3_irqctrl_regs->iclear)
444c6773c3SDaniel Hellstrom #define LEON_DO_ACK_HW 1
455213a780SKonrad Eisele
464c6773c3SDaniel Hellstrom /* Return the last ACKed IRQ by the Extended IRQ controller. It has already
474c6773c3SDaniel Hellstrom * been (automatically) ACKed when the CPU takes the trap.
484c6773c3SDaniel Hellstrom */
leon_eirq_get(int cpu)494c6773c3SDaniel Hellstrom static inline unsigned int leon_eirq_get(int cpu)
505213a780SKonrad Eisele {
515213a780SKonrad Eisele return LEON3_BYPASS_LOAD_PA(&leon3_irqctrl_regs->intid[cpu]) & 0x1f;
525213a780SKonrad Eisele }
535213a780SKonrad Eisele
544c6773c3SDaniel Hellstrom /* Handle one or multiple IRQs from the extended interrupt controller */
leon_handle_ext_irq(struct irq_desc * desc)55bd0b9ac4SThomas Gleixner static void leon_handle_ext_irq(struct irq_desc *desc)
565213a780SKonrad Eisele {
574c6773c3SDaniel Hellstrom unsigned int eirq;
5820424d85SAndreas Larsson struct irq_bucket *p;
5901dae0f0SDaniel Hellstrom int cpu = sparc_leon3_cpuid();
604c6773c3SDaniel Hellstrom
614c6773c3SDaniel Hellstrom eirq = leon_eirq_get(cpu);
6220424d85SAndreas Larsson p = irq_map[eirq];
6320424d85SAndreas Larsson if ((eirq & 0x10) && p && p->irq) /* bit4 tells if IRQ happened */
6420424d85SAndreas Larsson generic_handle_irq(p->irq);
655213a780SKonrad Eisele }
665213a780SKonrad Eisele
675213a780SKonrad Eisele /* The extended IRQ controller has been found, this function registers it */
leon_eirq_setup(unsigned int eirq)684007b65aSSam Ravnborg static void leon_eirq_setup(unsigned int eirq)
695213a780SKonrad Eisele {
704c6773c3SDaniel Hellstrom unsigned long mask, oldmask;
714c6773c3SDaniel Hellstrom unsigned int veirq;
725213a780SKonrad Eisele
734c6773c3SDaniel Hellstrom if (eirq < 1 || eirq > 0xf) {
744c6773c3SDaniel Hellstrom printk(KERN_ERR "LEON EXT IRQ NUMBER BAD: %d\n", eirq);
754c6773c3SDaniel Hellstrom return;
765213a780SKonrad Eisele }
775213a780SKonrad Eisele
784c6773c3SDaniel Hellstrom veirq = leon_build_device_irq(eirq, leon_handle_ext_irq, "extirq", 0);
794c6773c3SDaniel Hellstrom
804c6773c3SDaniel Hellstrom /*
814c6773c3SDaniel Hellstrom * Unmask the Extended IRQ, the IRQs routed through the Ext-IRQ
824c6773c3SDaniel Hellstrom * controller have a mask-bit of their own, so this is safe.
834c6773c3SDaniel Hellstrom */
844c6773c3SDaniel Hellstrom irq_link(veirq);
854c6773c3SDaniel Hellstrom mask = 1 << eirq;
8601dae0f0SDaniel Hellstrom oldmask = LEON3_BYPASS_LOAD_PA(LEON_IMASK(boot_cpu_id));
8701dae0f0SDaniel Hellstrom LEON3_BYPASS_STORE_PA(LEON_IMASK(boot_cpu_id), (oldmask | mask));
884c6773c3SDaniel Hellstrom sparc_leon_eirq = eirq;
895213a780SKonrad Eisele }
905213a780SKonrad Eisele
leon_get_irqmask(unsigned int irq)914ba22b16SSam Ravnborg unsigned long leon_get_irqmask(unsigned int irq)
925213a780SKonrad Eisele {
935213a780SKonrad Eisele unsigned long mask;
945213a780SKonrad Eisele
955213a780SKonrad Eisele if (!irq || ((irq > 0xf) && !sparc_leon_eirq)
965213a780SKonrad Eisele || ((irq > 0x1f) && sparc_leon_eirq)) {
975213a780SKonrad Eisele printk(KERN_ERR
985213a780SKonrad Eisele "leon_get_irqmask: false irq number: %d\n", irq);
995213a780SKonrad Eisele mask = 0;
1005213a780SKonrad Eisele } else {
1015213a780SKonrad Eisele mask = LEON_HARD_INT(irq);
1025213a780SKonrad Eisele }
1035213a780SKonrad Eisele return mask;
1045213a780SKonrad Eisele }
1055213a780SKonrad Eisele
1065eb1f4fcSDaniel Hellstrom #ifdef CONFIG_SMP
irq_choose_cpu(const struct cpumask * affinity)1075eb1f4fcSDaniel Hellstrom static int irq_choose_cpu(const struct cpumask *affinity)
1085eb1f4fcSDaniel Hellstrom {
109*1c9e709cSDawei Li unsigned int cpu = cpumask_first_and(affinity, cpu_online_mask);
1105eb1f4fcSDaniel Hellstrom
111*1c9e709cSDawei Li if (cpumask_subset(cpu_online_mask, affinity) || cpu >= nr_cpu_ids)
11201dae0f0SDaniel Hellstrom return boot_cpu_id;
1135eb1f4fcSDaniel Hellstrom else
114*1c9e709cSDawei Li return cpu;
1155eb1f4fcSDaniel Hellstrom }
1165eb1f4fcSDaniel Hellstrom #else
11701dae0f0SDaniel Hellstrom #define irq_choose_cpu(affinity) boot_cpu_id
1185eb1f4fcSDaniel Hellstrom #endif
1195eb1f4fcSDaniel Hellstrom
leon_set_affinity(struct irq_data * data,const struct cpumask * dest,bool force)1205eb1f4fcSDaniel Hellstrom static int leon_set_affinity(struct irq_data *data, const struct cpumask *dest,
1215eb1f4fcSDaniel Hellstrom bool force)
1225eb1f4fcSDaniel Hellstrom {
1235eb1f4fcSDaniel Hellstrom unsigned long mask, oldmask, flags;
1245eb1f4fcSDaniel Hellstrom int oldcpu, newcpu;
1255eb1f4fcSDaniel Hellstrom
1265eb1f4fcSDaniel Hellstrom mask = (unsigned long)data->chip_data;
127d7185a98SJiang Liu oldcpu = irq_choose_cpu(irq_data_get_affinity_mask(data));
1285eb1f4fcSDaniel Hellstrom newcpu = irq_choose_cpu(dest);
1295eb1f4fcSDaniel Hellstrom
1305eb1f4fcSDaniel Hellstrom if (oldcpu == newcpu)
1315eb1f4fcSDaniel Hellstrom goto out;
1325eb1f4fcSDaniel Hellstrom
1335eb1f4fcSDaniel Hellstrom /* unmask on old CPU first before enabling on the selected CPU */
1345eb1f4fcSDaniel Hellstrom spin_lock_irqsave(&leon_irq_lock, flags);
1355eb1f4fcSDaniel Hellstrom oldmask = LEON3_BYPASS_LOAD_PA(LEON_IMASK(oldcpu));
1365eb1f4fcSDaniel Hellstrom LEON3_BYPASS_STORE_PA(LEON_IMASK(oldcpu), (oldmask & ~mask));
1375eb1f4fcSDaniel Hellstrom oldmask = LEON3_BYPASS_LOAD_PA(LEON_IMASK(newcpu));
1385eb1f4fcSDaniel Hellstrom LEON3_BYPASS_STORE_PA(LEON_IMASK(newcpu), (oldmask | mask));
1395eb1f4fcSDaniel Hellstrom spin_unlock_irqrestore(&leon_irq_lock, flags);
1405eb1f4fcSDaniel Hellstrom out:
1415eb1f4fcSDaniel Hellstrom return IRQ_SET_MASK_OK;
1425eb1f4fcSDaniel Hellstrom }
1435eb1f4fcSDaniel Hellstrom
leon_unmask_irq(struct irq_data * data)1446baa9b20SSam Ravnborg static void leon_unmask_irq(struct irq_data *data)
1455213a780SKonrad Eisele {
146a481b5d0SDaniel Hellstrom unsigned long mask, oldmask, flags;
1475eb1f4fcSDaniel Hellstrom int cpu;
1486baa9b20SSam Ravnborg
1496baa9b20SSam Ravnborg mask = (unsigned long)data->chip_data;
150d7185a98SJiang Liu cpu = irq_choose_cpu(irq_data_get_affinity_mask(data));
151d61a38b2SDaniel Hellstrom spin_lock_irqsave(&leon_irq_lock, flags);
1525eb1f4fcSDaniel Hellstrom oldmask = LEON3_BYPASS_LOAD_PA(LEON_IMASK(cpu));
1535eb1f4fcSDaniel Hellstrom LEON3_BYPASS_STORE_PA(LEON_IMASK(cpu), (oldmask | mask));
154d61a38b2SDaniel Hellstrom spin_unlock_irqrestore(&leon_irq_lock, flags);
1555213a780SKonrad Eisele }
1565213a780SKonrad Eisele
leon_mask_irq(struct irq_data * data)1576baa9b20SSam Ravnborg static void leon_mask_irq(struct irq_data *data)
1585213a780SKonrad Eisele {
159a481b5d0SDaniel Hellstrom unsigned long mask, oldmask, flags;
1605eb1f4fcSDaniel Hellstrom int cpu;
1616baa9b20SSam Ravnborg
1626baa9b20SSam Ravnborg mask = (unsigned long)data->chip_data;
163d7185a98SJiang Liu cpu = irq_choose_cpu(irq_data_get_affinity_mask(data));
164d61a38b2SDaniel Hellstrom spin_lock_irqsave(&leon_irq_lock, flags);
1655eb1f4fcSDaniel Hellstrom oldmask = LEON3_BYPASS_LOAD_PA(LEON_IMASK(cpu));
1665eb1f4fcSDaniel Hellstrom LEON3_BYPASS_STORE_PA(LEON_IMASK(cpu), (oldmask & ~mask));
167d61a38b2SDaniel Hellstrom spin_unlock_irqrestore(&leon_irq_lock, flags);
1685213a780SKonrad Eisele }
1695213a780SKonrad Eisele
leon_startup_irq(struct irq_data * data)1706baa9b20SSam Ravnborg static unsigned int leon_startup_irq(struct irq_data *data)
1716baa9b20SSam Ravnborg {
1726baa9b20SSam Ravnborg irq_link(data->irq);
1736baa9b20SSam Ravnborg leon_unmask_irq(data);
1746baa9b20SSam Ravnborg return 0;
1756baa9b20SSam Ravnborg }
1766baa9b20SSam Ravnborg
leon_shutdown_irq(struct irq_data * data)1776baa9b20SSam Ravnborg static void leon_shutdown_irq(struct irq_data *data)
1786baa9b20SSam Ravnborg {
1796baa9b20SSam Ravnborg leon_mask_irq(data);
1806baa9b20SSam Ravnborg irq_unlink(data->irq);
1816baa9b20SSam Ravnborg }
1826baa9b20SSam Ravnborg
1834c6773c3SDaniel Hellstrom /* Used by external level sensitive IRQ handlers on the LEON: ACK IRQ ctrl */
leon_eoi_irq(struct irq_data * data)1844c6773c3SDaniel Hellstrom static void leon_eoi_irq(struct irq_data *data)
1854c6773c3SDaniel Hellstrom {
1864c6773c3SDaniel Hellstrom unsigned long mask = (unsigned long)data->chip_data;
1874c6773c3SDaniel Hellstrom
1884c6773c3SDaniel Hellstrom if (mask & LEON_DO_ACK_HW)
1894c6773c3SDaniel Hellstrom LEON3_BYPASS_STORE_PA(LEON_IACK, mask & ~LEON_DO_ACK_HW);
1904c6773c3SDaniel Hellstrom }
1914c6773c3SDaniel Hellstrom
1926baa9b20SSam Ravnborg static struct irq_chip leon_irq = {
1936baa9b20SSam Ravnborg .name = "leon",
1946baa9b20SSam Ravnborg .irq_startup = leon_startup_irq,
1956baa9b20SSam Ravnborg .irq_shutdown = leon_shutdown_irq,
1966baa9b20SSam Ravnborg .irq_mask = leon_mask_irq,
1976baa9b20SSam Ravnborg .irq_unmask = leon_unmask_irq,
1984c6773c3SDaniel Hellstrom .irq_eoi = leon_eoi_irq,
1995eb1f4fcSDaniel Hellstrom .irq_set_affinity = leon_set_affinity,
2006baa9b20SSam Ravnborg };
2016baa9b20SSam Ravnborg
2024c6773c3SDaniel Hellstrom /*
2034c6773c3SDaniel Hellstrom * Build a LEON IRQ for the edge triggered LEON IRQ controller:
20408f80073SAdam Buchbinder * Edge (normal) IRQ - handle_simple_irq, ack=DON'T-CARE, never ack
2054c6773c3SDaniel Hellstrom * Level IRQ (PCI|Level-GPIO) - handle_fasteoi_irq, ack=1, ack after ISR
2064c6773c3SDaniel Hellstrom * Per-CPU Edge - handle_percpu_irq, ack=0
2074c6773c3SDaniel Hellstrom */
leon_build_device_irq(unsigned int real_irq,irq_flow_handler_t flow_handler,const char * name,int do_ack)2084c6773c3SDaniel Hellstrom unsigned int leon_build_device_irq(unsigned int real_irq,
2094c6773c3SDaniel Hellstrom irq_flow_handler_t flow_handler,
2104c6773c3SDaniel Hellstrom const char *name, int do_ack)
2116baa9b20SSam Ravnborg {
2126baa9b20SSam Ravnborg unsigned int irq;
2136baa9b20SSam Ravnborg unsigned long mask;
2146e4741e7SAndreas Larsson struct irq_desc *desc;
2156baa9b20SSam Ravnborg
2166baa9b20SSam Ravnborg irq = 0;
2174ba22b16SSam Ravnborg mask = leon_get_irqmask(real_irq);
2186baa9b20SSam Ravnborg if (mask == 0)
2196baa9b20SSam Ravnborg goto out;
2206baa9b20SSam Ravnborg
2216baa9b20SSam Ravnborg irq = irq_alloc(real_irq, real_irq);
2226baa9b20SSam Ravnborg if (irq == 0)
2236baa9b20SSam Ravnborg goto out;
2246baa9b20SSam Ravnborg
2254c6773c3SDaniel Hellstrom if (do_ack)
2264c6773c3SDaniel Hellstrom mask |= LEON_DO_ACK_HW;
2274c6773c3SDaniel Hellstrom
2286e4741e7SAndreas Larsson desc = irq_to_desc(irq);
2296e4741e7SAndreas Larsson if (!desc || !desc->handle_irq || desc->handle_irq == handle_bad_irq) {
2306baa9b20SSam Ravnborg irq_set_chip_and_handler_name(irq, &leon_irq,
2314c6773c3SDaniel Hellstrom flow_handler, name);
2326baa9b20SSam Ravnborg irq_set_chip_data(irq, (void *)mask);
2336e4741e7SAndreas Larsson }
2346baa9b20SSam Ravnborg
2356baa9b20SSam Ravnborg out:
2366baa9b20SSam Ravnborg return irq;
2376baa9b20SSam Ravnborg }
2386baa9b20SSam Ravnborg
_leon_build_device_irq(struct platform_device * op,unsigned int real_irq)2394c6773c3SDaniel Hellstrom static unsigned int _leon_build_device_irq(struct platform_device *op,
2404c6773c3SDaniel Hellstrom unsigned int real_irq)
2414c6773c3SDaniel Hellstrom {
2424c6773c3SDaniel Hellstrom return leon_build_device_irq(real_irq, handle_simple_irq, "edge", 0);
2434c6773c3SDaniel Hellstrom }
2444c6773c3SDaniel Hellstrom
leon_update_virq_handling(unsigned int virq,irq_flow_handler_t flow_handler,const char * name,int do_ack)2455d07b786SDaniel Hellstrom void leon_update_virq_handling(unsigned int virq,
2465d07b786SDaniel Hellstrom irq_flow_handler_t flow_handler,
2475d07b786SDaniel Hellstrom const char *name, int do_ack)
2485d07b786SDaniel Hellstrom {
2495d07b786SDaniel Hellstrom unsigned long mask = (unsigned long)irq_get_chip_data(virq);
2505d07b786SDaniel Hellstrom
2515d07b786SDaniel Hellstrom mask &= ~LEON_DO_ACK_HW;
2525d07b786SDaniel Hellstrom if (do_ack)
2535d07b786SDaniel Hellstrom mask |= LEON_DO_ACK_HW;
2545d07b786SDaniel Hellstrom
2555d07b786SDaniel Hellstrom irq_set_chip_and_handler_name(virq, &leon_irq,
2565d07b786SDaniel Hellstrom flow_handler, name);
2575d07b786SDaniel Hellstrom irq_set_chip_data(virq, (void *)mask);
2585d07b786SDaniel Hellstrom }
2595d07b786SDaniel Hellstrom
leon_cycles_offset(void)26062f08283STkhai Kirill static u32 leon_cycles_offset(void)
26162f08283STkhai Kirill {
262384859d2SAndreas Larsson u32 rld, val, ctrl, off;
263384859d2SAndreas Larsson
26462f08283STkhai Kirill rld = LEON3_BYPASS_LOAD_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].rld);
26562f08283STkhai Kirill val = LEON3_BYPASS_LOAD_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].val);
266384859d2SAndreas Larsson ctrl = LEON3_BYPASS_LOAD_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].ctrl);
267384859d2SAndreas Larsson if (LEON3_GPTIMER_CTRL_ISPENDING(ctrl)) {
268384859d2SAndreas Larsson val = LEON3_BYPASS_LOAD_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].val);
269384859d2SAndreas Larsson off = 2 * rld - val;
270384859d2SAndreas Larsson } else {
27162f08283STkhai Kirill off = rld - val;
272384859d2SAndreas Larsson }
273384859d2SAndreas Larsson
274384859d2SAndreas Larsson return off;
27562f08283STkhai Kirill }
27662f08283STkhai Kirill
27762f08283STkhai Kirill #ifdef CONFIG_SMP
27862f08283STkhai Kirill
27962f08283STkhai Kirill /* smp clockevent irq */
leon_percpu_timer_ce_interrupt(int irq,void * unused)2804007b65aSSam Ravnborg static irqreturn_t leon_percpu_timer_ce_interrupt(int irq, void *unused)
28162f08283STkhai Kirill {
28262f08283STkhai Kirill struct clock_event_device *ce;
28362f08283STkhai Kirill int cpu = smp_processor_id();
28462f08283STkhai Kirill
28562f08283STkhai Kirill leon_clear_profile_irq(cpu);
28662f08283STkhai Kirill
2871ffbc51aSAndreas Larsson if (cpu == boot_cpu_id)
2881ffbc51aSAndreas Larsson timer_interrupt(irq, NULL);
2891ffbc51aSAndreas Larsson
29062f08283STkhai Kirill ce = &per_cpu(sparc32_clockevent, cpu);
29162f08283STkhai Kirill
29262f08283STkhai Kirill irq_enter();
29362f08283STkhai Kirill if (ce->event_handler)
29462f08283STkhai Kirill ce->event_handler(ce);
29562f08283STkhai Kirill irq_exit();
29662f08283STkhai Kirill
29762f08283STkhai Kirill return IRQ_HANDLED;
29862f08283STkhai Kirill }
29962f08283STkhai Kirill
30062f08283STkhai Kirill #endif /* CONFIG_SMP */
30162f08283STkhai Kirill
leon_init_timers(void)30262f08283STkhai Kirill void __init leon_init_timers(void)
3035213a780SKonrad Eisele {
3044c6773c3SDaniel Hellstrom int irq, eirq;
3052791c1a4SDaniel Hellstrom struct device_node *rootnp, *np, *nnp;
30653aea7caSDaniel Hellstrom struct property *pp;
30753aea7caSDaniel Hellstrom int len;
30801dae0f0SDaniel Hellstrom int icsel;
3092791c1a4SDaniel Hellstrom int ampopts;
3106baa9b20SSam Ravnborg int err;
3111ffbc51aSAndreas Larsson u32 config;
312384859d2SAndreas Larsson u32 ctrl;
3135213a780SKonrad Eisele
31462f08283STkhai Kirill sparc_config.get_cycles_offset = leon_cycles_offset;
31562f08283STkhai Kirill sparc_config.cs_period = 1000000 / HZ;
31662f08283STkhai Kirill sparc_config.features |= FEAT_L10_CLOCKSOURCE;
31762f08283STkhai Kirill
31862f08283STkhai Kirill #ifndef CONFIG_SMP
31962f08283STkhai Kirill sparc_config.features |= FEAT_L10_CLOCKEVENT;
32062f08283STkhai Kirill #endif
32162f08283STkhai Kirill
3225213a780SKonrad Eisele leondebug_irq_disable = 0;
3235213a780SKonrad Eisele leon_debug_irqout = 0;
324fcea8b27SSam Ravnborg master_l10_counter = (u32 __iomem *)&dummy_master_l10_counter;
3255213a780SKonrad Eisele dummy_master_l10_counter = 0;
3265213a780SKonrad Eisele
32753aea7caSDaniel Hellstrom rootnp = of_find_node_by_path("/ambapp0");
32853aea7caSDaniel Hellstrom if (!rootnp)
32953aea7caSDaniel Hellstrom goto bad;
3307279b82cSDaniel Hellstrom
3317279b82cSDaniel Hellstrom /* Find System ID: GRLIB build ID and optional CHIP ID */
3327279b82cSDaniel Hellstrom pp = of_find_property(rootnp, "systemid", &len);
3337279b82cSDaniel Hellstrom if (pp)
3347279b82cSDaniel Hellstrom amba_system_id = *(unsigned long *)pp->value;
3357279b82cSDaniel Hellstrom
3367279b82cSDaniel Hellstrom /* Find IRQMP IRQ Controller Registers base adr otherwise bail out */
33753aea7caSDaniel Hellstrom np = of_find_node_by_name(rootnp, "GAISLER_IRQMP");
3389742e72cSDaniel Hellstrom if (!np) {
3399742e72cSDaniel Hellstrom np = of_find_node_by_name(rootnp, "01_00d");
34053aea7caSDaniel Hellstrom if (!np)
34153aea7caSDaniel Hellstrom goto bad;
3429742e72cSDaniel Hellstrom }
34353aea7caSDaniel Hellstrom pp = of_find_property(np, "reg", &len);
34453aea7caSDaniel Hellstrom if (!pp)
34553aea7caSDaniel Hellstrom goto bad;
34653aea7caSDaniel Hellstrom leon3_irqctrl_regs = *(struct leon3_irqctrl_regs_map **)pp->value;
34753aea7caSDaniel Hellstrom
34853aea7caSDaniel Hellstrom /* Find GPTIMER Timer Registers base address otherwise bail out. */
3492791c1a4SDaniel Hellstrom nnp = rootnp;
350601e6e3cSDan Carpenter
351601e6e3cSDan Carpenter retry:
3522791c1a4SDaniel Hellstrom np = of_find_node_by_name(nnp, "GAISLER_GPTIMER");
3539742e72cSDaniel Hellstrom if (!np) {
3542791c1a4SDaniel Hellstrom np = of_find_node_by_name(nnp, "01_011");
35553aea7caSDaniel Hellstrom if (!np)
35653aea7caSDaniel Hellstrom goto bad;
3579742e72cSDaniel Hellstrom }
3582791c1a4SDaniel Hellstrom
3592791c1a4SDaniel Hellstrom ampopts = 0;
3602791c1a4SDaniel Hellstrom pp = of_find_property(np, "ampopts", &len);
3612791c1a4SDaniel Hellstrom if (pp) {
3622791c1a4SDaniel Hellstrom ampopts = *(int *)pp->value;
3632791c1a4SDaniel Hellstrom if (ampopts == 0) {
3642791c1a4SDaniel Hellstrom /* Skip this instance, resource already
3652791c1a4SDaniel Hellstrom * allocated by other OS */
3662791c1a4SDaniel Hellstrom nnp = np;
367601e6e3cSDan Carpenter goto retry;
3682791c1a4SDaniel Hellstrom }
3692791c1a4SDaniel Hellstrom }
3702791c1a4SDaniel Hellstrom
3712791c1a4SDaniel Hellstrom /* Select Timer-Instance on Timer Core. Default is zero */
3722791c1a4SDaniel Hellstrom leon3_gptimer_idx = ampopts & 0x7;
3732791c1a4SDaniel Hellstrom
37453aea7caSDaniel Hellstrom pp = of_find_property(np, "reg", &len);
3752791c1a4SDaniel Hellstrom if (pp)
3762791c1a4SDaniel Hellstrom leon3_gptimer_regs = *(struct leon3_gptimer_regs_map **)
3772791c1a4SDaniel Hellstrom pp->value;
37853aea7caSDaniel Hellstrom pp = of_find_property(np, "interrupts", &len);
3792791c1a4SDaniel Hellstrom if (pp)
38053aea7caSDaniel Hellstrom leon3_gptimer_irq = *(unsigned int *)pp->value;
38153aea7caSDaniel Hellstrom
382a481b5d0SDaniel Hellstrom if (!(leon3_gptimer_regs && leon3_irqctrl_regs && leon3_gptimer_irq))
383a481b5d0SDaniel Hellstrom goto bad;
384a481b5d0SDaniel Hellstrom
385384859d2SAndreas Larsson ctrl = LEON3_BYPASS_LOAD_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].ctrl);
386384859d2SAndreas Larsson LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].ctrl,
387384859d2SAndreas Larsson ctrl | LEON3_GPTIMER_CTRL_PENDING);
388384859d2SAndreas Larsson ctrl = LEON3_BYPASS_LOAD_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].ctrl);
389384859d2SAndreas Larsson
390384859d2SAndreas Larsson if ((ctrl & LEON3_GPTIMER_CTRL_PENDING) != 0)
391384859d2SAndreas Larsson leon3_gptimer_ackmask = ~LEON3_GPTIMER_CTRL_PENDING;
392384859d2SAndreas Larsson else
393384859d2SAndreas Larsson leon3_gptimer_ackmask = ~0;
394384859d2SAndreas Larsson
395a481b5d0SDaniel Hellstrom LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].val, 0);
396a481b5d0SDaniel Hellstrom LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].rld,
397b690c425SDaniel Hellstrom (((1000000 / HZ) - 1)));
3982791c1a4SDaniel Hellstrom LEON3_BYPASS_STORE_PA(
3992791c1a4SDaniel Hellstrom &leon3_gptimer_regs->e[leon3_gptimer_idx].ctrl, 0);
4005213a780SKonrad Eisele
401e2305e37SDaniel Hellstrom /*
402e2305e37SDaniel Hellstrom * The IRQ controller may (if implemented) consist of multiple
403e2305e37SDaniel Hellstrom * IRQ controllers, each mapped on a 4Kb boundary.
404e2305e37SDaniel Hellstrom * Each CPU may be routed to different IRQCTRLs, however
405e2305e37SDaniel Hellstrom * we assume that all CPUs (in SMP system) is routed to the
406e2305e37SDaniel Hellstrom * same IRQ Controller, and for non-SMP only one IRQCTRL is
407e2305e37SDaniel Hellstrom * accessed anyway.
408e2305e37SDaniel Hellstrom * In AMP systems, Linux must run on CPU0 for the time being.
409e2305e37SDaniel Hellstrom */
41001dae0f0SDaniel Hellstrom icsel = LEON3_BYPASS_LOAD_PA(&leon3_irqctrl_regs->icsel[boot_cpu_id/8]);
41101dae0f0SDaniel Hellstrom icsel = (icsel >> ((7 - (boot_cpu_id&0x7)) * 4)) & 0xf;
412e2305e37SDaniel Hellstrom leon3_irqctrl_regs += icsel;
4134c6773c3SDaniel Hellstrom
414970def65SDaniel Hellstrom /* Mask all IRQs on boot-cpu IRQ controller */
415970def65SDaniel Hellstrom LEON3_BYPASS_STORE_PA(&leon3_irqctrl_regs->mask[boot_cpu_id], 0);
416970def65SDaniel Hellstrom
4174c6773c3SDaniel Hellstrom /* Probe extended IRQ controller */
4184c6773c3SDaniel Hellstrom eirq = (LEON3_BYPASS_LOAD_PA(&leon3_irqctrl_regs->mpstatus)
4194c6773c3SDaniel Hellstrom >> 16) & 0xf;
4204c6773c3SDaniel Hellstrom if (eirq != 0)
4214c6773c3SDaniel Hellstrom leon_eirq_setup(eirq);
4225213a780SKonrad Eisele
42310f0d07cSDaniel Hellstrom #ifdef CONFIG_SMP
42410f0d07cSDaniel Hellstrom {
42510f0d07cSDaniel Hellstrom unsigned long flags;
42610f0d07cSDaniel Hellstrom
42710f0d07cSDaniel Hellstrom /*
42810f0d07cSDaniel Hellstrom * In SMP, sun4m adds a IPI handler to IRQ trap handler that
42910f0d07cSDaniel Hellstrom * LEON never must take, sun4d and LEON overwrites the branch
43010f0d07cSDaniel Hellstrom * with a NOP.
43110f0d07cSDaniel Hellstrom */
43210f0d07cSDaniel Hellstrom local_irq_save(flags);
43310f0d07cSDaniel Hellstrom patchme_maybe_smp_msg[0] = 0x01000000; /* NOP out the branch */
4345d83d666SDavid S. Miller local_ops->cache_all();
43510f0d07cSDaniel Hellstrom local_irq_restore(flags);
43610f0d07cSDaniel Hellstrom }
43710f0d07cSDaniel Hellstrom #endif
43810f0d07cSDaniel Hellstrom
4391ffbc51aSAndreas Larsson config = LEON3_BYPASS_LOAD_PA(&leon3_gptimer_regs->config);
4401ffbc51aSAndreas Larsson if (config & (1 << LEON3_GPTIMER_SEPIRQ))
4411ffbc51aSAndreas Larsson leon3_gptimer_irq += leon3_gptimer_idx;
4421ffbc51aSAndreas Larsson else if ((config & LEON3_GPTIMER_TIMERS) > 1)
4431ffbc51aSAndreas Larsson pr_warn("GPTIMER uses shared irqs, using other timers of the same core will fail.\n");
4441ffbc51aSAndreas Larsson
4451ffbc51aSAndreas Larsson #ifdef CONFIG_SMP
4461ffbc51aSAndreas Larsson /* Install per-cpu IRQ handler for broadcasted ticker */
4471ffbc51aSAndreas Larsson irq = leon_build_device_irq(leon3_gptimer_irq, handle_percpu_irq,
4481ffbc51aSAndreas Larsson "per-cpu", 0);
4491ffbc51aSAndreas Larsson err = request_irq(irq, leon_percpu_timer_ce_interrupt,
4501ffbc51aSAndreas Larsson IRQF_PERCPU | IRQF_TIMER, "timer", NULL);
4511ffbc51aSAndreas Larsson #else
4521ffbc51aSAndreas Larsson irq = _leon_build_device_irq(NULL, leon3_gptimer_irq);
4531ffbc51aSAndreas Larsson err = request_irq(irq, timer_interrupt, IRQF_TIMER, "timer", NULL);
4541ffbc51aSAndreas Larsson #endif
4551ffbc51aSAndreas Larsson if (err) {
4561ffbc51aSAndreas Larsson pr_err("Unable to attach timer IRQ%d\n", irq);
4571ffbc51aSAndreas Larsson prom_halt();
4581ffbc51aSAndreas Larsson }
4592791c1a4SDaniel Hellstrom LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].ctrl,
4605213a780SKonrad Eisele LEON3_GPTIMER_EN |
4615213a780SKonrad Eisele LEON3_GPTIMER_RL |
462a481b5d0SDaniel Hellstrom LEON3_GPTIMER_LD |
463a481b5d0SDaniel Hellstrom LEON3_GPTIMER_IRQEN);
46453aea7caSDaniel Hellstrom return;
46553aea7caSDaniel Hellstrom bad:
46653aea7caSDaniel Hellstrom printk(KERN_ERR "No Timer/irqctrl found\n");
46753aea7caSDaniel Hellstrom BUG();
46853aea7caSDaniel Hellstrom return;
4695213a780SKonrad Eisele }
4705213a780SKonrad Eisele
leon_clear_clock_irq(void)47108c9388fSSam Ravnborg static void leon_clear_clock_irq(void)
4725213a780SKonrad Eisele {
473384859d2SAndreas Larsson u32 ctrl;
474384859d2SAndreas Larsson
475384859d2SAndreas Larsson ctrl = LEON3_BYPASS_LOAD_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].ctrl);
476384859d2SAndreas Larsson LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].ctrl,
477384859d2SAndreas Larsson ctrl & leon3_gptimer_ackmask);
4785213a780SKonrad Eisele }
4795213a780SKonrad Eisele
leon_load_profile_irq(int cpu,unsigned int limit)48008c9388fSSam Ravnborg static void leon_load_profile_irq(int cpu, unsigned int limit)
4815213a780SKonrad Eisele {
4825213a780SKonrad Eisele }
4835213a780SKonrad Eisele
4848401707fSKonrad Eisele #ifdef CONFIG_SMP
leon_clear_profile_irq(int cpu)4858401707fSKonrad Eisele void leon_clear_profile_irq(int cpu)
4868401707fSKonrad Eisele {
4878401707fSKonrad Eisele }
4888401707fSKonrad Eisele
leon_enable_irq_cpu(unsigned int irq_nr,unsigned int cpu)4898401707fSKonrad Eisele void leon_enable_irq_cpu(unsigned int irq_nr, unsigned int cpu)
4908401707fSKonrad Eisele {
4918401707fSKonrad Eisele unsigned long mask, flags, *addr;
4924ba22b16SSam Ravnborg mask = leon_get_irqmask(irq_nr);
493d61a38b2SDaniel Hellstrom spin_lock_irqsave(&leon_irq_lock, flags);
494a481b5d0SDaniel Hellstrom addr = (unsigned long *)LEON_IMASK(cpu);
495a481b5d0SDaniel Hellstrom LEON3_BYPASS_STORE_PA(addr, (LEON3_BYPASS_LOAD_PA(addr) | mask));
496d61a38b2SDaniel Hellstrom spin_unlock_irqrestore(&leon_irq_lock, flags);
4978401707fSKonrad Eisele }
4988401707fSKonrad Eisele
4998401707fSKonrad Eisele #endif
5008401707fSKonrad Eisele
leon_init_IRQ(void)5015213a780SKonrad Eisele void __init leon_init_IRQ(void)
5025213a780SKonrad Eisele {
503472bc4f2SSam Ravnborg sparc_config.init_timers = leon_init_timers;
504472bc4f2SSam Ravnborg sparc_config.build_device_irq = _leon_build_device_irq;
50562f08283STkhai Kirill sparc_config.clock_rate = 1000000;
50608c9388fSSam Ravnborg sparc_config.clear_clock_irq = leon_clear_clock_irq;
50708c9388fSSam Ravnborg sparc_config.load_profile_irq = leon_load_profile_irq;
5085213a780SKonrad Eisele }
509