18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Shared interrupt handling code for IPR and INTC2 types of IRQs. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2007, 2008 Magnus Damm 58c2ecf20Sopenharmony_ci * Copyright (C) 2009 - 2012 Paul Mundt 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Based on intc2.c and ipr.c 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Copyright (C) 1999 Niibe Yutaka & Takeshi Yaegashi 108c2ecf20Sopenharmony_ci * Copyright (C) 2000 Kazumoto Kojima 118c2ecf20Sopenharmony_ci * Copyright (C) 2001 David J. Mckay (david.mckay@st.com) 128c2ecf20Sopenharmony_ci * Copyright (C) 2003 Takashi Kusuda <kusuda-takashi@hitachi-ul.co.jp> 138c2ecf20Sopenharmony_ci * Copyright (C) 2005, 2006 Paul Mundt 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 168c2ecf20Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 178c2ecf20Sopenharmony_ci * for more details. 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "intc: " fmt 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <linux/init.h> 228c2ecf20Sopenharmony_ci#include <linux/irq.h> 238c2ecf20Sopenharmony_ci#include <linux/io.h> 248c2ecf20Sopenharmony_ci#include <linux/slab.h> 258c2ecf20Sopenharmony_ci#include <linux/stat.h> 268c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 278c2ecf20Sopenharmony_ci#include <linux/sh_intc.h> 288c2ecf20Sopenharmony_ci#include <linux/irqdomain.h> 298c2ecf20Sopenharmony_ci#include <linux/device.h> 308c2ecf20Sopenharmony_ci#include <linux/syscore_ops.h> 318c2ecf20Sopenharmony_ci#include <linux/list.h> 328c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 338c2ecf20Sopenharmony_ci#include <linux/radix-tree.h> 348c2ecf20Sopenharmony_ci#include <linux/export.h> 358c2ecf20Sopenharmony_ci#include <linux/sort.h> 368c2ecf20Sopenharmony_ci#include "internals.h" 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ciLIST_HEAD(intc_list); 398c2ecf20Sopenharmony_ciDEFINE_RAW_SPINLOCK(intc_big_lock); 408c2ecf20Sopenharmony_cistatic unsigned int nr_intc_controllers; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* 438c2ecf20Sopenharmony_ci * Default priority level 448c2ecf20Sopenharmony_ci * - this needs to be at least 2 for 5-bit priorities on 7780 458c2ecf20Sopenharmony_ci */ 468c2ecf20Sopenharmony_cistatic unsigned int default_prio_level = 2; /* 2 - 16 */ 478c2ecf20Sopenharmony_cistatic unsigned int intc_prio_level[INTC_NR_IRQS]; /* for now */ 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ciunsigned int intc_get_dfl_prio_level(void) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci return default_prio_level; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ciunsigned int intc_get_prio_level(unsigned int irq) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci return intc_prio_level[irq]; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_civoid intc_set_prio_level(unsigned int irq, unsigned int level) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci unsigned long flags; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&intc_big_lock, flags); 648c2ecf20Sopenharmony_ci intc_prio_level[irq] = level; 658c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&intc_big_lock, flags); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic void intc_redirect_irq(struct irq_desc *desc) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci generic_handle_irq((unsigned int)irq_desc_get_handler_data(desc)); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic void __init intc_register_irq(struct intc_desc *desc, 748c2ecf20Sopenharmony_ci struct intc_desc_int *d, 758c2ecf20Sopenharmony_ci intc_enum enum_id, 768c2ecf20Sopenharmony_ci unsigned int irq) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct intc_handle_int *hp; 798c2ecf20Sopenharmony_ci struct irq_data *irq_data; 808c2ecf20Sopenharmony_ci unsigned int data[2], primary; 818c2ecf20Sopenharmony_ci unsigned long flags; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&intc_big_lock, flags); 848c2ecf20Sopenharmony_ci radix_tree_insert(&d->tree, enum_id, intc_irq_xlate_get(irq)); 858c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&intc_big_lock, flags); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci /* 888c2ecf20Sopenharmony_ci * Prefer single interrupt source bitmap over other combinations: 898c2ecf20Sopenharmony_ci * 908c2ecf20Sopenharmony_ci * 1. bitmap, single interrupt source 918c2ecf20Sopenharmony_ci * 2. priority, single interrupt source 928c2ecf20Sopenharmony_ci * 3. bitmap, multiple interrupt sources (groups) 938c2ecf20Sopenharmony_ci * 4. priority, multiple interrupt sources (groups) 948c2ecf20Sopenharmony_ci */ 958c2ecf20Sopenharmony_ci data[0] = intc_get_mask_handle(desc, d, enum_id, 0); 968c2ecf20Sopenharmony_ci data[1] = intc_get_prio_handle(desc, d, enum_id, 0); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci primary = 0; 998c2ecf20Sopenharmony_ci if (!data[0] && data[1]) 1008c2ecf20Sopenharmony_ci primary = 1; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci if (!data[0] && !data[1]) 1038c2ecf20Sopenharmony_ci pr_warn("missing unique irq mask for irq %d (vect 0x%04x)\n", 1048c2ecf20Sopenharmony_ci irq, irq2evt(irq)); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci data[0] = data[0] ? data[0] : intc_get_mask_handle(desc, d, enum_id, 1); 1078c2ecf20Sopenharmony_ci data[1] = data[1] ? data[1] : intc_get_prio_handle(desc, d, enum_id, 1); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (!data[primary]) 1108c2ecf20Sopenharmony_ci primary ^= 1; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci BUG_ON(!data[primary]); /* must have primary masking method */ 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci irq_data = irq_get_irq_data(irq); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci disable_irq_nosync(irq); 1178c2ecf20Sopenharmony_ci irq_set_chip_and_handler_name(irq, &d->chip, handle_level_irq, 1188c2ecf20Sopenharmony_ci "level"); 1198c2ecf20Sopenharmony_ci irq_set_chip_data(irq, (void *)data[primary]); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci /* 1228c2ecf20Sopenharmony_ci * set priority level 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_ci intc_set_prio_level(irq, intc_get_dfl_prio_level()); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* enable secondary masking method if present */ 1278c2ecf20Sopenharmony_ci if (data[!primary]) 1288c2ecf20Sopenharmony_ci _intc_enable(irq_data, data[!primary]); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci /* add irq to d->prio list if priority is available */ 1318c2ecf20Sopenharmony_ci if (data[1]) { 1328c2ecf20Sopenharmony_ci hp = d->prio + d->nr_prio; 1338c2ecf20Sopenharmony_ci hp->irq = irq; 1348c2ecf20Sopenharmony_ci hp->handle = data[1]; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (primary) { 1378c2ecf20Sopenharmony_ci /* 1388c2ecf20Sopenharmony_ci * only secondary priority should access registers, so 1398c2ecf20Sopenharmony_ci * set _INTC_FN(h) = REG_FN_ERR for intc_set_priority() 1408c2ecf20Sopenharmony_ci */ 1418c2ecf20Sopenharmony_ci hp->handle &= ~_INTC_MK(0x0f, 0, 0, 0, 0, 0); 1428c2ecf20Sopenharmony_ci hp->handle |= _INTC_MK(REG_FN_ERR, 0, 0, 0, 0, 0); 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci d->nr_prio++; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* add irq to d->sense list if sense is available */ 1488c2ecf20Sopenharmony_ci data[0] = intc_get_sense_handle(desc, d, enum_id); 1498c2ecf20Sopenharmony_ci if (data[0]) { 1508c2ecf20Sopenharmony_ci (d->sense + d->nr_sense)->irq = irq; 1518c2ecf20Sopenharmony_ci (d->sense + d->nr_sense)->handle = data[0]; 1528c2ecf20Sopenharmony_ci d->nr_sense++; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci /* irq should be disabled by default */ 1568c2ecf20Sopenharmony_ci d->chip.irq_mask(irq_data); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci intc_set_ack_handle(irq, desc, d, enum_id); 1598c2ecf20Sopenharmony_ci intc_set_dist_handle(irq, desc, d, enum_id); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci activate_irq(irq); 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic unsigned int __init save_reg(struct intc_desc_int *d, 1658c2ecf20Sopenharmony_ci unsigned int cnt, 1668c2ecf20Sopenharmony_ci unsigned long value, 1678c2ecf20Sopenharmony_ci unsigned int smp) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci if (value) { 1708c2ecf20Sopenharmony_ci value = intc_phys_to_virt(d, value); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci d->reg[cnt] = value; 1738c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 1748c2ecf20Sopenharmony_ci d->smp[cnt] = smp; 1758c2ecf20Sopenharmony_ci#endif 1768c2ecf20Sopenharmony_ci return 1; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci return 0; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ciint __init register_intc_controller(struct intc_desc *desc) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci unsigned int i, k, smp; 1858c2ecf20Sopenharmony_ci struct intc_hw_desc *hw = &desc->hw; 1868c2ecf20Sopenharmony_ci struct intc_desc_int *d; 1878c2ecf20Sopenharmony_ci struct resource *res; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci pr_info("Registered controller '%s' with %u IRQs\n", 1908c2ecf20Sopenharmony_ci desc->name, hw->nr_vectors); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci d = kzalloc(sizeof(*d), GFP_NOWAIT); 1938c2ecf20Sopenharmony_ci if (!d) 1948c2ecf20Sopenharmony_ci goto err0; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&d->list); 1978c2ecf20Sopenharmony_ci list_add_tail(&d->list, &intc_list); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci raw_spin_lock_init(&d->lock); 2008c2ecf20Sopenharmony_ci INIT_RADIX_TREE(&d->tree, GFP_ATOMIC); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci d->index = nr_intc_controllers; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci if (desc->num_resources) { 2058c2ecf20Sopenharmony_ci d->nr_windows = desc->num_resources; 2068c2ecf20Sopenharmony_ci d->window = kcalloc(d->nr_windows, sizeof(*d->window), 2078c2ecf20Sopenharmony_ci GFP_NOWAIT); 2088c2ecf20Sopenharmony_ci if (!d->window) 2098c2ecf20Sopenharmony_ci goto err1; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci for (k = 0; k < d->nr_windows; k++) { 2128c2ecf20Sopenharmony_ci res = desc->resource + k; 2138c2ecf20Sopenharmony_ci WARN_ON(resource_type(res) != IORESOURCE_MEM); 2148c2ecf20Sopenharmony_ci d->window[k].phys = res->start; 2158c2ecf20Sopenharmony_ci d->window[k].size = resource_size(res); 2168c2ecf20Sopenharmony_ci d->window[k].virt = ioremap(res->start, 2178c2ecf20Sopenharmony_ci resource_size(res)); 2188c2ecf20Sopenharmony_ci if (!d->window[k].virt) 2198c2ecf20Sopenharmony_ci goto err2; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci d->nr_reg = hw->mask_regs ? hw->nr_mask_regs * 2 : 0; 2248c2ecf20Sopenharmony_ci#ifdef CONFIG_INTC_BALANCING 2258c2ecf20Sopenharmony_ci if (d->nr_reg) 2268c2ecf20Sopenharmony_ci d->nr_reg += hw->nr_mask_regs; 2278c2ecf20Sopenharmony_ci#endif 2288c2ecf20Sopenharmony_ci d->nr_reg += hw->prio_regs ? hw->nr_prio_regs * 2 : 0; 2298c2ecf20Sopenharmony_ci d->nr_reg += hw->sense_regs ? hw->nr_sense_regs : 0; 2308c2ecf20Sopenharmony_ci d->nr_reg += hw->ack_regs ? hw->nr_ack_regs : 0; 2318c2ecf20Sopenharmony_ci d->nr_reg += hw->subgroups ? hw->nr_subgroups : 0; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci d->reg = kcalloc(d->nr_reg, sizeof(*d->reg), GFP_NOWAIT); 2348c2ecf20Sopenharmony_ci if (!d->reg) 2358c2ecf20Sopenharmony_ci goto err2; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 2388c2ecf20Sopenharmony_ci d->smp = kcalloc(d->nr_reg, sizeof(*d->smp), GFP_NOWAIT); 2398c2ecf20Sopenharmony_ci if (!d->smp) 2408c2ecf20Sopenharmony_ci goto err3; 2418c2ecf20Sopenharmony_ci#endif 2428c2ecf20Sopenharmony_ci k = 0; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (hw->mask_regs) { 2458c2ecf20Sopenharmony_ci for (i = 0; i < hw->nr_mask_regs; i++) { 2468c2ecf20Sopenharmony_ci smp = IS_SMP(hw->mask_regs[i]); 2478c2ecf20Sopenharmony_ci k += save_reg(d, k, hw->mask_regs[i].set_reg, smp); 2488c2ecf20Sopenharmony_ci k += save_reg(d, k, hw->mask_regs[i].clr_reg, smp); 2498c2ecf20Sopenharmony_ci#ifdef CONFIG_INTC_BALANCING 2508c2ecf20Sopenharmony_ci k += save_reg(d, k, hw->mask_regs[i].dist_reg, 0); 2518c2ecf20Sopenharmony_ci#endif 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (hw->prio_regs) { 2568c2ecf20Sopenharmony_ci d->prio = kcalloc(hw->nr_vectors, sizeof(*d->prio), 2578c2ecf20Sopenharmony_ci GFP_NOWAIT); 2588c2ecf20Sopenharmony_ci if (!d->prio) 2598c2ecf20Sopenharmony_ci goto err4; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci for (i = 0; i < hw->nr_prio_regs; i++) { 2628c2ecf20Sopenharmony_ci smp = IS_SMP(hw->prio_regs[i]); 2638c2ecf20Sopenharmony_ci k += save_reg(d, k, hw->prio_regs[i].set_reg, smp); 2648c2ecf20Sopenharmony_ci k += save_reg(d, k, hw->prio_regs[i].clr_reg, smp); 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci sort(d->prio, hw->nr_prio_regs, sizeof(*d->prio), 2688c2ecf20Sopenharmony_ci intc_handle_int_cmp, NULL); 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if (hw->sense_regs) { 2728c2ecf20Sopenharmony_ci d->sense = kcalloc(hw->nr_vectors, sizeof(*d->sense), 2738c2ecf20Sopenharmony_ci GFP_NOWAIT); 2748c2ecf20Sopenharmony_ci if (!d->sense) 2758c2ecf20Sopenharmony_ci goto err5; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci for (i = 0; i < hw->nr_sense_regs; i++) 2788c2ecf20Sopenharmony_ci k += save_reg(d, k, hw->sense_regs[i].reg, 0); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci sort(d->sense, hw->nr_sense_regs, sizeof(*d->sense), 2818c2ecf20Sopenharmony_ci intc_handle_int_cmp, NULL); 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci if (hw->subgroups) 2858c2ecf20Sopenharmony_ci for (i = 0; i < hw->nr_subgroups; i++) 2868c2ecf20Sopenharmony_ci if (hw->subgroups[i].reg) 2878c2ecf20Sopenharmony_ci k+= save_reg(d, k, hw->subgroups[i].reg, 0); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci memcpy(&d->chip, &intc_irq_chip, sizeof(struct irq_chip)); 2908c2ecf20Sopenharmony_ci d->chip.name = desc->name; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci if (hw->ack_regs) 2938c2ecf20Sopenharmony_ci for (i = 0; i < hw->nr_ack_regs; i++) 2948c2ecf20Sopenharmony_ci k += save_reg(d, k, hw->ack_regs[i].set_reg, 0); 2958c2ecf20Sopenharmony_ci else 2968c2ecf20Sopenharmony_ci d->chip.irq_mask_ack = d->chip.irq_disable; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci /* disable bits matching force_disable before registering irqs */ 2998c2ecf20Sopenharmony_ci if (desc->force_disable) 3008c2ecf20Sopenharmony_ci intc_enable_disable_enum(desc, d, desc->force_disable, 0); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci /* disable bits matching force_enable before registering irqs */ 3038c2ecf20Sopenharmony_ci if (desc->force_enable) 3048c2ecf20Sopenharmony_ci intc_enable_disable_enum(desc, d, desc->force_enable, 0); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci BUG_ON(k > 256); /* _INTC_ADDR_E() and _INTC_ADDR_D() are 8 bits */ 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci intc_irq_domain_init(d, hw); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci /* register the vectors one by one */ 3118c2ecf20Sopenharmony_ci for (i = 0; i < hw->nr_vectors; i++) { 3128c2ecf20Sopenharmony_ci struct intc_vect *vect = hw->vectors + i; 3138c2ecf20Sopenharmony_ci unsigned int irq = evt2irq(vect->vect); 3148c2ecf20Sopenharmony_ci int res; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (!vect->enum_id) 3178c2ecf20Sopenharmony_ci continue; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci res = irq_create_identity_mapping(d->domain, irq); 3208c2ecf20Sopenharmony_ci if (unlikely(res)) { 3218c2ecf20Sopenharmony_ci if (res == -EEXIST) { 3228c2ecf20Sopenharmony_ci res = irq_domain_associate(d->domain, irq, irq); 3238c2ecf20Sopenharmony_ci if (unlikely(res)) { 3248c2ecf20Sopenharmony_ci pr_err("domain association failure\n"); 3258c2ecf20Sopenharmony_ci continue; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci } else { 3288c2ecf20Sopenharmony_ci pr_err("can't identity map IRQ %d\n", irq); 3298c2ecf20Sopenharmony_ci continue; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci intc_irq_xlate_set(irq, vect->enum_id, d); 3348c2ecf20Sopenharmony_ci intc_register_irq(desc, d, vect->enum_id, irq); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci for (k = i + 1; k < hw->nr_vectors; k++) { 3378c2ecf20Sopenharmony_ci struct intc_vect *vect2 = hw->vectors + k; 3388c2ecf20Sopenharmony_ci unsigned int irq2 = evt2irq(vect2->vect); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci if (vect->enum_id != vect2->enum_id) 3418c2ecf20Sopenharmony_ci continue; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci /* 3448c2ecf20Sopenharmony_ci * In the case of multi-evt handling and sparse 3458c2ecf20Sopenharmony_ci * IRQ support, each vector still needs to have 3468c2ecf20Sopenharmony_ci * its own backing irq_desc. 3478c2ecf20Sopenharmony_ci */ 3488c2ecf20Sopenharmony_ci res = irq_create_identity_mapping(d->domain, irq2); 3498c2ecf20Sopenharmony_ci if (unlikely(res)) { 3508c2ecf20Sopenharmony_ci if (res == -EEXIST) { 3518c2ecf20Sopenharmony_ci res = irq_domain_associate(d->domain, 3528c2ecf20Sopenharmony_ci irq2, irq2); 3538c2ecf20Sopenharmony_ci if (unlikely(res)) { 3548c2ecf20Sopenharmony_ci pr_err("domain association " 3558c2ecf20Sopenharmony_ci "failure\n"); 3568c2ecf20Sopenharmony_ci continue; 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci } else { 3598c2ecf20Sopenharmony_ci pr_err("can't identity map IRQ %d\n", 3608c2ecf20Sopenharmony_ci irq); 3618c2ecf20Sopenharmony_ci continue; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci vect2->enum_id = 0; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci /* redirect this interrupts to the first one */ 3688c2ecf20Sopenharmony_ci irq_set_chip(irq2, &dummy_irq_chip); 3698c2ecf20Sopenharmony_ci irq_set_chained_handler_and_data(irq2, 3708c2ecf20Sopenharmony_ci intc_redirect_irq, 3718c2ecf20Sopenharmony_ci (void *)irq); 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci intc_subgroup_init(desc, d); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci /* enable bits matching force_enable after registering irqs */ 3788c2ecf20Sopenharmony_ci if (desc->force_enable) 3798c2ecf20Sopenharmony_ci intc_enable_disable_enum(desc, d, desc->force_enable, 1); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci d->skip_suspend = desc->skip_syscore_suspend; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci nr_intc_controllers++; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci return 0; 3868c2ecf20Sopenharmony_cierr5: 3878c2ecf20Sopenharmony_ci kfree(d->prio); 3888c2ecf20Sopenharmony_cierr4: 3898c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 3908c2ecf20Sopenharmony_ci kfree(d->smp); 3918c2ecf20Sopenharmony_cierr3: 3928c2ecf20Sopenharmony_ci#endif 3938c2ecf20Sopenharmony_ci kfree(d->reg); 3948c2ecf20Sopenharmony_cierr2: 3958c2ecf20Sopenharmony_ci for (k = 0; k < d->nr_windows; k++) 3968c2ecf20Sopenharmony_ci if (d->window[k].virt) 3978c2ecf20Sopenharmony_ci iounmap(d->window[k].virt); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci kfree(d->window); 4008c2ecf20Sopenharmony_cierr1: 4018c2ecf20Sopenharmony_ci kfree(d); 4028c2ecf20Sopenharmony_cierr0: 4038c2ecf20Sopenharmony_ci pr_err("unable to allocate INTC memory\n"); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci return -ENOMEM; 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic int intc_suspend(void) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci struct intc_desc_int *d; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci list_for_each_entry(d, &intc_list, list) { 4138c2ecf20Sopenharmony_ci int irq; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci if (d->skip_suspend) 4168c2ecf20Sopenharmony_ci continue; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci /* enable wakeup irqs belonging to this intc controller */ 4198c2ecf20Sopenharmony_ci for_each_active_irq(irq) { 4208c2ecf20Sopenharmony_ci struct irq_data *data; 4218c2ecf20Sopenharmony_ci struct irq_chip *chip; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci data = irq_get_irq_data(irq); 4248c2ecf20Sopenharmony_ci chip = irq_data_get_irq_chip(data); 4258c2ecf20Sopenharmony_ci if (chip != &d->chip) 4268c2ecf20Sopenharmony_ci continue; 4278c2ecf20Sopenharmony_ci if (irqd_is_wakeup_set(data)) 4288c2ecf20Sopenharmony_ci chip->irq_enable(data); 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci return 0; 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic void intc_resume(void) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci struct intc_desc_int *d; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci list_for_each_entry(d, &intc_list, list) { 4398c2ecf20Sopenharmony_ci int irq; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci if (d->skip_suspend) 4428c2ecf20Sopenharmony_ci continue; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci for_each_active_irq(irq) { 4458c2ecf20Sopenharmony_ci struct irq_data *data; 4468c2ecf20Sopenharmony_ci struct irq_chip *chip; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci data = irq_get_irq_data(irq); 4498c2ecf20Sopenharmony_ci chip = irq_data_get_irq_chip(data); 4508c2ecf20Sopenharmony_ci /* 4518c2ecf20Sopenharmony_ci * This will catch the redirect and VIRQ cases 4528c2ecf20Sopenharmony_ci * due to the dummy_irq_chip being inserted. 4538c2ecf20Sopenharmony_ci */ 4548c2ecf20Sopenharmony_ci if (chip != &d->chip) 4558c2ecf20Sopenharmony_ci continue; 4568c2ecf20Sopenharmony_ci if (irqd_irq_disabled(data)) 4578c2ecf20Sopenharmony_ci chip->irq_disable(data); 4588c2ecf20Sopenharmony_ci else 4598c2ecf20Sopenharmony_ci chip->irq_enable(data); 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_cistruct syscore_ops intc_syscore_ops = { 4658c2ecf20Sopenharmony_ci .suspend = intc_suspend, 4668c2ecf20Sopenharmony_ci .resume = intc_resume, 4678c2ecf20Sopenharmony_ci}; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_cistruct bus_type intc_subsys = { 4708c2ecf20Sopenharmony_ci .name = "intc", 4718c2ecf20Sopenharmony_ci .dev_name = "intc", 4728c2ecf20Sopenharmony_ci}; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic ssize_t 4758c2ecf20Sopenharmony_cishow_intc_name(struct device *dev, struct device_attribute *attr, char *buf) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci struct intc_desc_int *d; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci d = container_of(dev, struct intc_desc_int, dev); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", d->chip.name); 4828c2ecf20Sopenharmony_ci} 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cistatic DEVICE_ATTR(name, S_IRUGO, show_intc_name, NULL); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_cistatic int __init register_intc_devs(void) 4878c2ecf20Sopenharmony_ci{ 4888c2ecf20Sopenharmony_ci struct intc_desc_int *d; 4898c2ecf20Sopenharmony_ci int error; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci register_syscore_ops(&intc_syscore_ops); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci error = subsys_system_register(&intc_subsys, NULL); 4948c2ecf20Sopenharmony_ci if (!error) { 4958c2ecf20Sopenharmony_ci list_for_each_entry(d, &intc_list, list) { 4968c2ecf20Sopenharmony_ci d->dev.id = d->index; 4978c2ecf20Sopenharmony_ci d->dev.bus = &intc_subsys; 4988c2ecf20Sopenharmony_ci error = device_register(&d->dev); 4998c2ecf20Sopenharmony_ci if (error == 0) 5008c2ecf20Sopenharmony_ci error = device_create_file(&d->dev, 5018c2ecf20Sopenharmony_ci &dev_attr_name); 5028c2ecf20Sopenharmony_ci if (error) 5038c2ecf20Sopenharmony_ci break; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci if (error) 5088c2ecf20Sopenharmony_ci pr_err("device registration error\n"); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci return error; 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_cidevice_initcall(register_intc_devs); 513