[go: nahoru, domu]

15698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan/*
25698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan * Meta internal (HWSTATMETA) interrupt code.
35698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *
45698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan * Copyright (C) 2011-2012 Imagination Technologies Ltd.
55698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *
65698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan * This code is based on the code in SoC/common/irq.c and SoC/comet/irq.c
75698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan * The code base could be generalised/merged as a lot of the functionality is
85698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan * similar. Until this is done, we try to keep the code simple here.
95698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan */
105698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
115698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan#include <linux/interrupt.h>
125698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan#include <linux/io.h>
135698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan#include <linux/irqdomain.h>
145698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
155698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan#include <asm/irq.h>
165698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan#include <asm/hwthread.h>
175698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
185698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan#define PERF0VECINT		0x04820580
195698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan#define PERF1VECINT		0x04820588
205698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan#define PERF0TRIG_OFFSET	16
215698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan#define PERF1TRIG_OFFSET	17
225698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
235698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan/**
245698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan * struct metag_internal_irq_priv - private meta internal interrupt data
255698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan * @domain:		IRQ domain for all internal Meta IRQs (HWSTATMETA)
265698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan * @unmasked:		Record of unmasked IRQs
275698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan */
285698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hoganstruct metag_internal_irq_priv {
295698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	struct irq_domain	*domain;
305698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
315698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	unsigned long		unmasked;
325698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan};
335698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
345698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan/* Private data for the one and only internal interrupt controller */
355698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hoganstatic struct metag_internal_irq_priv metag_internal_irq_priv;
365698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
375698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hoganstatic unsigned int metag_internal_irq_startup(struct irq_data *data);
385698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hoganstatic void metag_internal_irq_shutdown(struct irq_data *data);
395698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hoganstatic void metag_internal_irq_ack(struct irq_data *data);
405698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hoganstatic void metag_internal_irq_mask(struct irq_data *data);
415698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hoganstatic void metag_internal_irq_unmask(struct irq_data *data);
425698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan#ifdef CONFIG_SMP
435698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hoganstatic int metag_internal_irq_set_affinity(struct irq_data *data,
445698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan			const struct cpumask *cpumask, bool force);
455698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan#endif
465698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
475698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hoganstatic struct irq_chip internal_irq_edge_chip = {
485698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	.name = "HWSTATMETA-IRQ",
495698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	.irq_startup = metag_internal_irq_startup,
505698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	.irq_shutdown = metag_internal_irq_shutdown,
515698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	.irq_ack = metag_internal_irq_ack,
525698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	.irq_mask = metag_internal_irq_mask,
535698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	.irq_unmask = metag_internal_irq_unmask,
545698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan#ifdef CONFIG_SMP
555698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	.irq_set_affinity = metag_internal_irq_set_affinity,
565698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan#endif
575698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan};
585698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
595698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan/*
605698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *	metag_hwvec_addr - get the address of *VECINT regs of irq
615698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *
625698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *	This function is a table of supported triggers on HWSTATMETA
635698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *	Could do with a structure, but better keep it simple. Changes
645698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *	in this code should be rare.
655698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan */
665698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hoganstatic inline void __iomem *metag_hwvec_addr(irq_hw_number_t hw)
675698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan{
685698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	void __iomem *addr;
695698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
705698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	switch (hw) {
715698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	case PERF0TRIG_OFFSET:
725698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan		addr = (void __iomem *)PERF0VECINT;
735698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan		break;
745698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	case PERF1TRIG_OFFSET:
755698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan		addr = (void __iomem *)PERF1VECINT;
765698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan		break;
775698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	default:
785698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan		addr = NULL;
795698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan		break;
805698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	}
815698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	return addr;
825698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan}
835698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
845698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan/*
855698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *	metag_internal_startup - setup an internal irq
865698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *	@irq:	the irq to startup
875698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *
885698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *	Multiplex interrupts for @irq onto TR1. Clear any pending
895698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *	interrupts.
905698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan */
915698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hoganstatic unsigned int metag_internal_irq_startup(struct irq_data *data)
925698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan{
935698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	/* Clear (toggle) the bit in HWSTATMETA for our interrupt. */
945698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	metag_internal_irq_ack(data);
955698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
965698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	/* Enable the interrupt by unmasking it */
975698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	metag_internal_irq_unmask(data);
985698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
995698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	return 0;
1005698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan}
1015698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
1025698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan/*
1035698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *	metag_internal_irq_shutdown - turn off the irq
1045698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *	@irq:	the irq number to turn off
1055698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *
1065698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *	Mask @irq and clear any pending interrupts.
1075698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *	Stop muxing @irq onto TR1.
1085698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan */
1095698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hoganstatic void metag_internal_irq_shutdown(struct irq_data *data)
1105698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan{
1115698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	/* Disable the IRQ at the core by masking it. */
1125698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	metag_internal_irq_mask(data);
1135698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
1145698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	/* Clear (toggle) the bit in HWSTATMETA for our interrupt. */
1155698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	metag_internal_irq_ack(data);
1165698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan}
1175698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
1185698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan/*
1195698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *	metag_internal_irq_ack - acknowledge irq
1205698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *	@irq:	the irq to ack
1215698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan */
1225698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hoganstatic void metag_internal_irq_ack(struct irq_data *data)
1235698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan{
1245698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	irq_hw_number_t hw = data->hwirq;
1255698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	unsigned int bit = 1 << hw;
1265698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
1275698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	if (metag_in32(HWSTATMETA) & bit)
1285698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan		metag_out32(bit, HWSTATMETA);
1295698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan}
1305698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
1315698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan/**
1325698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan * metag_internal_irq_mask() - mask an internal irq by unvectoring
1335698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan * @data:	data for the internal irq to mask
1345698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *
1355698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan * HWSTATMETA has no mask register. Instead the IRQ is unvectored from the core
1365698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan * and retriggered if necessary later.
1375698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan */
1385698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hoganstatic void metag_internal_irq_mask(struct irq_data *data)
1395698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan{
1405698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	struct metag_internal_irq_priv *priv = &metag_internal_irq_priv;
1415698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	irq_hw_number_t hw = data->hwirq;
1425698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	void __iomem *vec_addr = metag_hwvec_addr(hw);
1435698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
1445698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	clear_bit(hw, &priv->unmasked);
1455698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
1465698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	/* there is no interrupt mask, so unvector the interrupt */
1475698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	metag_out32(0, vec_addr);
1485698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan}
1495698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
1505698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan/**
1515698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan * meta_intc_unmask_edge_irq_nomask() - unmask an edge irq by revectoring
1525698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan * @data:	data for the internal irq to unmask
1535698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *
1545698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan * HWSTATMETA has no mask register. Instead the IRQ is revectored back to the
1555698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan * core and retriggered if necessary.
1565698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan */
1575698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hoganstatic void metag_internal_irq_unmask(struct irq_data *data)
1585698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan{
1595698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	struct metag_internal_irq_priv *priv = &metag_internal_irq_priv;
1605698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	irq_hw_number_t hw = data->hwirq;
1615698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	unsigned int bit = 1 << hw;
1625698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	void __iomem *vec_addr = metag_hwvec_addr(hw);
1635698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	unsigned int thread = hard_processor_id();
1645698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
1655698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	set_bit(hw, &priv->unmasked);
1665698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
1675698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	/* there is no interrupt mask, so revector the interrupt */
1685698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	metag_out32(TBI_TRIG_VEC(TBID_SIGNUM_TR1(thread)), vec_addr);
1695698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
1705698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	/*
1715698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	 * Re-trigger interrupt
1725698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	 *
1735698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	 * Writing a 1 toggles, and a 0->1 transition triggers. We only
1745698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	 * retrigger if the status bit is already set, which means we
1755698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	 * need to clear it first. Retriggering is fundamentally racy
1765698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	 * because if the interrupt fires again after we clear it we
1775698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	 * could end up clearing it again and the interrupt handler
1785698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	 * thinking it hasn't fired. Therefore we need to keep trying to
1795698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	 * retrigger until the bit is set.
1805698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	 */
1815698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	if (metag_in32(HWSTATMETA) & bit) {
1825698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan		metag_out32(bit, HWSTATMETA);
1835698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan		while (!(metag_in32(HWSTATMETA) & bit))
1845698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan			metag_out32(bit, HWSTATMETA);
1855698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	}
1865698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan}
1875698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
1885698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan#ifdef CONFIG_SMP
1895698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan/*
1905698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *	metag_internal_irq_set_affinity - set the affinity for an interrupt
1915698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan */
1925698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hoganstatic int metag_internal_irq_set_affinity(struct irq_data *data,
1935698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan			const struct cpumask *cpumask, bool force)
1945698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan{
1955698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	unsigned int cpu, thread;
1965698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	irq_hw_number_t hw = data->hwirq;
1975698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	/*
1985698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	 * Wire up this interrupt from *VECINT to the Meta core.
1995698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	 *
2005698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	 * Note that we can't wire up *VECINT to interrupt more than
2015698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	 * one cpu (the interrupt code doesn't support it), so we just
2025698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	 * pick the first cpu we find in 'cpumask'.
2035698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	 */
204f229006ec6beabf7b844653d92fa61f025fe3dcfJames Hogan	cpu = cpumask_any_and(cpumask, cpu_online_mask);
2055698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	thread = cpu_2_hwthread_id[cpu];
2065698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
2075698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	metag_out32(TBI_TRIG_VEC(TBID_SIGNUM_TR1(thread)),
2085698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan		    metag_hwvec_addr(hw));
2095698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
2105698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	return 0;
2115698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan}
2125698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan#endif
2135698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
2145698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan/*
2155698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *	metag_internal_irq_demux - irq de-multiplexer
2165698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *	@irq:	the interrupt number
2175698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *	@desc:	the interrupt description structure for this irq
2185698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *
2195698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *	The cpu receives an interrupt on TR1 when an interrupt has
2205698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *	occurred. It is this function's job to demux this irq and
2215698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *	figure out exactly which trigger needs servicing.
2225698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan */
2235698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hoganstatic void metag_internal_irq_demux(unsigned int irq, struct irq_desc *desc)
2245698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan{
2255698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	struct metag_internal_irq_priv *priv = irq_desc_get_handler_data(desc);
2265698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	irq_hw_number_t hw;
2275698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	unsigned int irq_no;
2285698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	u32 status;
2295698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
2305698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hoganrecalculate:
2315698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	status = metag_in32(HWSTATMETA) & priv->unmasked;
2325698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
2335698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	for (hw = 0; status != 0; status >>= 1, ++hw) {
2345698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan		if (status & 0x1) {
2355698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan			/*
2365698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan			 * Map the hardware IRQ number to a virtual Linux IRQ
2375698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan			 * number.
2385698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan			 */
2395698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan			irq_no = irq_linear_revmap(priv->domain, hw);
2405698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
2415698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan			/*
2425698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan			 * Only fire off interrupts that are
2435698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan			 * registered to be handled by the kernel.
2445698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan			 * Other interrupts are probably being
2455698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan			 * handled by other Meta hardware threads.
2465698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan			 */
2475698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan			generic_handle_irq(irq_no);
2485698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
2495698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan			/*
2505698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan			 * The handler may have re-enabled interrupts
2515698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan			 * which could have caused a nested invocation
2525698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan			 * of this code and make the copy of the
2535698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan			 * status register we are using invalid.
2545698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan			 */
2555698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan			goto recalculate;
2565698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan		}
2575698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	}
2585698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan}
2595698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
2605698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan/**
2615698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan * internal_irq_map() - Map an internal meta IRQ to a virtual IRQ number.
2625698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan * @hw:		Number of the internal IRQ. Must be in range.
2635698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *
2645698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan * Returns:	The virtual IRQ number of the Meta internal IRQ specified by
2655698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *		@hw.
2665698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan */
2675698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hoganint internal_irq_map(unsigned int hw)
2685698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan{
2695698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	struct metag_internal_irq_priv *priv = &metag_internal_irq_priv;
2705698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	if (!priv->domain)
2715698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan		return -ENODEV;
2725698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	return irq_create_mapping(priv->domain, hw);
2735698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan}
2745698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
2755698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan/**
2765698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *	metag_internal_irq_init_cpu - regsister with the Meta cpu
2775698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *	@cpu:	the CPU to register on
2785698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *
2795698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *	Configure @cpu's TR1 irq so that we can demux irqs.
2805698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan */
2815698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hoganstatic void metag_internal_irq_init_cpu(struct metag_internal_irq_priv *priv,
2825698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan					int cpu)
2835698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan{
2845698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	unsigned int thread = cpu_2_hwthread_id[cpu];
2855698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	unsigned int signum = TBID_SIGNUM_TR1(thread);
2865698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	int irq = tbisig_map(signum);
2875698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
2885698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	/* Register the multiplexed IRQ handler */
2895698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	irq_set_handler_data(irq, priv);
2905698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	irq_set_chained_handler(irq, metag_internal_irq_demux);
2915698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW);
2925698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan}
2935698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
2945698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan/**
2955698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan * metag_internal_intc_map() - map an internal irq
2965698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan * @d:		irq domain of internal trigger block
2975698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan * @irq:	virtual irq number
2985698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan * @hw:		hardware irq number within internal trigger block
2995698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *
3005698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan * This sets up a virtual irq for a specified hardware interrupt. The irq chip
3015698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan * and handler is configured.
3025698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan */
3035698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hoganstatic int metag_internal_intc_map(struct irq_domain *d, unsigned int irq,
3045698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan				   irq_hw_number_t hw)
3055698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan{
3065698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	/* only register interrupt if it is mapped */
3075698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	if (!metag_hwvec_addr(hw))
3085698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan		return -EINVAL;
3095698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
3105698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	irq_set_chip_and_handler(irq, &internal_irq_edge_chip,
3115698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan				 handle_edge_irq);
3125698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	return 0;
3135698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan}
3145698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
3155698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hoganstatic const struct irq_domain_ops metag_internal_intc_domain_ops = {
3165698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	.map	= metag_internal_intc_map,
3175698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan};
3185698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
3195698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan/**
3205698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *	metag_internal_irq_register - register internal IRQs
3215698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *
3225698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan *	Register the irq chip and handler function for all internal IRQs
3235698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan */
3245698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hoganint __init init_internal_IRQ(void)
3255698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan{
3265698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	struct metag_internal_irq_priv *priv = &metag_internal_irq_priv;
3275698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	unsigned int cpu;
3285698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
3295698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	/* Set up an IRQ domain */
3305698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	priv->domain = irq_domain_add_linear(NULL, 32,
3315698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan					     &metag_internal_intc_domain_ops,
3325698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan					     priv);
3335698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	if (unlikely(!priv->domain)) {
3345698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan		pr_err("meta-internal-intc: cannot add IRQ domain\n");
3355698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan		return -ENOMEM;
3365698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	}
3375698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
3385698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	/* Setup TR1 for all cpus. */
3395698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	for_each_possible_cpu(cpu)
3405698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan		metag_internal_irq_init_cpu(priv, cpu);
3415698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan
3425698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan	return 0;
3435698c50d9da4ab2f57d98c64ea97675dcaf2a608James Hogan};
344