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