162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Shared interrupt handling code for IPR and INTC2 types of IRQs. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2007, 2008 Magnus Damm 562306a36Sopenharmony_ci * Copyright (C) 2009 - 2012 Paul Mundt 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Based on intc2.c and ipr.c 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Copyright (C) 1999 Niibe Yutaka & Takeshi Yaegashi 1062306a36Sopenharmony_ci * Copyright (C) 2000 Kazumoto Kojima 1162306a36Sopenharmony_ci * Copyright (C) 2001 David J. Mckay (david.mckay@st.com) 1262306a36Sopenharmony_ci * Copyright (C) 2003 Takashi Kusuda <kusuda-takashi@hitachi-ul.co.jp> 1362306a36Sopenharmony_ci * Copyright (C) 2005, 2006 Paul Mundt 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 1662306a36Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 1762306a36Sopenharmony_ci * for more details. 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci#define pr_fmt(fmt) "intc: " fmt 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <linux/init.h> 2262306a36Sopenharmony_ci#include <linux/irq.h> 2362306a36Sopenharmony_ci#include <linux/io.h> 2462306a36Sopenharmony_ci#include <linux/slab.h> 2562306a36Sopenharmony_ci#include <linux/stat.h> 2662306a36Sopenharmony_ci#include <linux/interrupt.h> 2762306a36Sopenharmony_ci#include <linux/sh_intc.h> 2862306a36Sopenharmony_ci#include <linux/irqdomain.h> 2962306a36Sopenharmony_ci#include <linux/device.h> 3062306a36Sopenharmony_ci#include <linux/syscore_ops.h> 3162306a36Sopenharmony_ci#include <linux/list.h> 3262306a36Sopenharmony_ci#include <linux/spinlock.h> 3362306a36Sopenharmony_ci#include <linux/radix-tree.h> 3462306a36Sopenharmony_ci#include <linux/export.h> 3562306a36Sopenharmony_ci#include <linux/sort.h> 3662306a36Sopenharmony_ci#include "internals.h" 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ciLIST_HEAD(intc_list); 3962306a36Sopenharmony_ciDEFINE_RAW_SPINLOCK(intc_big_lock); 4062306a36Sopenharmony_cistatic unsigned int nr_intc_controllers; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* 4362306a36Sopenharmony_ci * Default priority level 4462306a36Sopenharmony_ci * - this needs to be at least 2 for 5-bit priorities on 7780 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_cistatic unsigned int default_prio_level = 2; /* 2 - 16 */ 4762306a36Sopenharmony_cistatic unsigned int intc_prio_level[INTC_NR_IRQS]; /* for now */ 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ciunsigned int intc_get_dfl_prio_level(void) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci return default_prio_level; 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ciunsigned int intc_get_prio_level(unsigned int irq) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci return intc_prio_level[irq]; 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_civoid intc_set_prio_level(unsigned int irq, unsigned int level) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci unsigned long flags; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci raw_spin_lock_irqsave(&intc_big_lock, flags); 6462306a36Sopenharmony_ci intc_prio_level[irq] = level; 6562306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&intc_big_lock, flags); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic void intc_redirect_irq(struct irq_desc *desc) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci generic_handle_irq((unsigned int)irq_desc_get_handler_data(desc)); 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic void __init intc_register_irq(struct intc_desc *desc, 7462306a36Sopenharmony_ci struct intc_desc_int *d, 7562306a36Sopenharmony_ci intc_enum enum_id, 7662306a36Sopenharmony_ci unsigned int irq) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci struct intc_handle_int *hp; 7962306a36Sopenharmony_ci struct irq_data *irq_data; 8062306a36Sopenharmony_ci unsigned int data[2], primary; 8162306a36Sopenharmony_ci unsigned long flags; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci raw_spin_lock_irqsave(&intc_big_lock, flags); 8462306a36Sopenharmony_ci radix_tree_insert(&d->tree, enum_id, intc_irq_xlate_get(irq)); 8562306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&intc_big_lock, flags); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci /* 8862306a36Sopenharmony_ci * Prefer single interrupt source bitmap over other combinations: 8962306a36Sopenharmony_ci * 9062306a36Sopenharmony_ci * 1. bitmap, single interrupt source 9162306a36Sopenharmony_ci * 2. priority, single interrupt source 9262306a36Sopenharmony_ci * 3. bitmap, multiple interrupt sources (groups) 9362306a36Sopenharmony_ci * 4. priority, multiple interrupt sources (groups) 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_ci data[0] = intc_get_mask_handle(desc, d, enum_id, 0); 9662306a36Sopenharmony_ci data[1] = intc_get_prio_handle(desc, d, enum_id, 0); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci primary = 0; 9962306a36Sopenharmony_ci if (!data[0] && data[1]) 10062306a36Sopenharmony_ci primary = 1; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (!data[0] && !data[1]) 10362306a36Sopenharmony_ci pr_warn("missing unique irq mask for irq %d (vect 0x%04x)\n", 10462306a36Sopenharmony_ci irq, irq2evt(irq)); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci data[0] = data[0] ? data[0] : intc_get_mask_handle(desc, d, enum_id, 1); 10762306a36Sopenharmony_ci data[1] = data[1] ? data[1] : intc_get_prio_handle(desc, d, enum_id, 1); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (!data[primary]) 11062306a36Sopenharmony_ci primary ^= 1; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci BUG_ON(!data[primary]); /* must have primary masking method */ 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci irq_data = irq_get_irq_data(irq); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci disable_irq_nosync(irq); 11762306a36Sopenharmony_ci irq_set_chip_and_handler_name(irq, &d->chip, handle_level_irq, 11862306a36Sopenharmony_ci "level"); 11962306a36Sopenharmony_ci irq_set_chip_data(irq, (void *)data[primary]); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* 12262306a36Sopenharmony_ci * set priority level 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_ci intc_set_prio_level(irq, intc_get_dfl_prio_level()); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* enable secondary masking method if present */ 12762306a36Sopenharmony_ci if (data[!primary]) 12862306a36Sopenharmony_ci _intc_enable(irq_data, data[!primary]); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* add irq to d->prio list if priority is available */ 13162306a36Sopenharmony_ci if (data[1]) { 13262306a36Sopenharmony_ci hp = d->prio + d->nr_prio; 13362306a36Sopenharmony_ci hp->irq = irq; 13462306a36Sopenharmony_ci hp->handle = data[1]; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (primary) { 13762306a36Sopenharmony_ci /* 13862306a36Sopenharmony_ci * only secondary priority should access registers, so 13962306a36Sopenharmony_ci * set _INTC_FN(h) = REG_FN_ERR for intc_set_priority() 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_ci hp->handle &= ~_INTC_MK(0x0f, 0, 0, 0, 0, 0); 14262306a36Sopenharmony_ci hp->handle |= _INTC_MK(REG_FN_ERR, 0, 0, 0, 0, 0); 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci d->nr_prio++; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci /* add irq to d->sense list if sense is available */ 14862306a36Sopenharmony_ci data[0] = intc_get_sense_handle(desc, d, enum_id); 14962306a36Sopenharmony_ci if (data[0]) { 15062306a36Sopenharmony_ci (d->sense + d->nr_sense)->irq = irq; 15162306a36Sopenharmony_ci (d->sense + d->nr_sense)->handle = data[0]; 15262306a36Sopenharmony_ci d->nr_sense++; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* irq should be disabled by default */ 15662306a36Sopenharmony_ci d->chip.irq_mask(irq_data); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci intc_set_ack_handle(irq, desc, d, enum_id); 15962306a36Sopenharmony_ci intc_set_dist_handle(irq, desc, d, enum_id); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci activate_irq(irq); 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic unsigned int __init save_reg(struct intc_desc_int *d, 16562306a36Sopenharmony_ci unsigned int cnt, 16662306a36Sopenharmony_ci unsigned long value, 16762306a36Sopenharmony_ci unsigned int smp) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci if (value) { 17062306a36Sopenharmony_ci value = intc_phys_to_virt(d, value); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci d->reg[cnt] = value; 17362306a36Sopenharmony_ci#ifdef CONFIG_SMP 17462306a36Sopenharmony_ci d->smp[cnt] = smp; 17562306a36Sopenharmony_ci#endif 17662306a36Sopenharmony_ci return 1; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return 0; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic bool __init intc_map(struct irq_domain *domain, int irq) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci if (!irq_to_desc(irq) && irq_alloc_desc_at(irq, NUMA_NO_NODE) != irq) { 18562306a36Sopenharmony_ci pr_err("uname to allocate IRQ %d\n", irq); 18662306a36Sopenharmony_ci return false; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (irq_domain_associate(domain, irq, irq)) { 19062306a36Sopenharmony_ci pr_err("domain association failure\n"); 19162306a36Sopenharmony_ci return false; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return true; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ciint __init register_intc_controller(struct intc_desc *desc) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci unsigned int i, k, smp; 20062306a36Sopenharmony_ci struct intc_hw_desc *hw = &desc->hw; 20162306a36Sopenharmony_ci struct intc_desc_int *d; 20262306a36Sopenharmony_ci struct resource *res; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci pr_info("Registered controller '%s' with %u IRQs\n", 20562306a36Sopenharmony_ci desc->name, hw->nr_vectors); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci d = kzalloc(sizeof(*d), GFP_NOWAIT); 20862306a36Sopenharmony_ci if (!d) 20962306a36Sopenharmony_ci goto err0; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci INIT_LIST_HEAD(&d->list); 21262306a36Sopenharmony_ci list_add_tail(&d->list, &intc_list); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci raw_spin_lock_init(&d->lock); 21562306a36Sopenharmony_ci INIT_RADIX_TREE(&d->tree, GFP_ATOMIC); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci d->index = nr_intc_controllers; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (desc->num_resources) { 22062306a36Sopenharmony_ci d->nr_windows = desc->num_resources; 22162306a36Sopenharmony_ci d->window = kcalloc(d->nr_windows, sizeof(*d->window), 22262306a36Sopenharmony_ci GFP_NOWAIT); 22362306a36Sopenharmony_ci if (!d->window) 22462306a36Sopenharmony_ci goto err1; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci for (k = 0; k < d->nr_windows; k++) { 22762306a36Sopenharmony_ci res = desc->resource + k; 22862306a36Sopenharmony_ci WARN_ON(resource_type(res) != IORESOURCE_MEM); 22962306a36Sopenharmony_ci d->window[k].phys = res->start; 23062306a36Sopenharmony_ci d->window[k].size = resource_size(res); 23162306a36Sopenharmony_ci d->window[k].virt = ioremap(res->start, 23262306a36Sopenharmony_ci resource_size(res)); 23362306a36Sopenharmony_ci if (!d->window[k].virt) 23462306a36Sopenharmony_ci goto err2; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci d->nr_reg = hw->mask_regs ? hw->nr_mask_regs * 2 : 0; 23962306a36Sopenharmony_ci#ifdef CONFIG_INTC_BALANCING 24062306a36Sopenharmony_ci if (d->nr_reg) 24162306a36Sopenharmony_ci d->nr_reg += hw->nr_mask_regs; 24262306a36Sopenharmony_ci#endif 24362306a36Sopenharmony_ci d->nr_reg += hw->prio_regs ? hw->nr_prio_regs * 2 : 0; 24462306a36Sopenharmony_ci d->nr_reg += hw->sense_regs ? hw->nr_sense_regs : 0; 24562306a36Sopenharmony_ci d->nr_reg += hw->ack_regs ? hw->nr_ack_regs : 0; 24662306a36Sopenharmony_ci d->nr_reg += hw->subgroups ? hw->nr_subgroups : 0; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci d->reg = kcalloc(d->nr_reg, sizeof(*d->reg), GFP_NOWAIT); 24962306a36Sopenharmony_ci if (!d->reg) 25062306a36Sopenharmony_ci goto err2; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci#ifdef CONFIG_SMP 25362306a36Sopenharmony_ci d->smp = kcalloc(d->nr_reg, sizeof(*d->smp), GFP_NOWAIT); 25462306a36Sopenharmony_ci if (!d->smp) 25562306a36Sopenharmony_ci goto err3; 25662306a36Sopenharmony_ci#endif 25762306a36Sopenharmony_ci k = 0; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (hw->mask_regs) { 26062306a36Sopenharmony_ci for (i = 0; i < hw->nr_mask_regs; i++) { 26162306a36Sopenharmony_ci smp = IS_SMP(hw->mask_regs[i]); 26262306a36Sopenharmony_ci k += save_reg(d, k, hw->mask_regs[i].set_reg, smp); 26362306a36Sopenharmony_ci k += save_reg(d, k, hw->mask_regs[i].clr_reg, smp); 26462306a36Sopenharmony_ci#ifdef CONFIG_INTC_BALANCING 26562306a36Sopenharmony_ci k += save_reg(d, k, hw->mask_regs[i].dist_reg, 0); 26662306a36Sopenharmony_ci#endif 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (hw->prio_regs) { 27162306a36Sopenharmony_ci d->prio = kcalloc(hw->nr_vectors, sizeof(*d->prio), 27262306a36Sopenharmony_ci GFP_NOWAIT); 27362306a36Sopenharmony_ci if (!d->prio) 27462306a36Sopenharmony_ci goto err4; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci for (i = 0; i < hw->nr_prio_regs; i++) { 27762306a36Sopenharmony_ci smp = IS_SMP(hw->prio_regs[i]); 27862306a36Sopenharmony_ci k += save_reg(d, k, hw->prio_regs[i].set_reg, smp); 27962306a36Sopenharmony_ci k += save_reg(d, k, hw->prio_regs[i].clr_reg, smp); 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci sort(d->prio, hw->nr_prio_regs, sizeof(*d->prio), 28362306a36Sopenharmony_ci intc_handle_int_cmp, NULL); 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (hw->sense_regs) { 28762306a36Sopenharmony_ci d->sense = kcalloc(hw->nr_vectors, sizeof(*d->sense), 28862306a36Sopenharmony_ci GFP_NOWAIT); 28962306a36Sopenharmony_ci if (!d->sense) 29062306a36Sopenharmony_ci goto err5; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci for (i = 0; i < hw->nr_sense_regs; i++) 29362306a36Sopenharmony_ci k += save_reg(d, k, hw->sense_regs[i].reg, 0); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci sort(d->sense, hw->nr_sense_regs, sizeof(*d->sense), 29662306a36Sopenharmony_ci intc_handle_int_cmp, NULL); 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (hw->subgroups) 30062306a36Sopenharmony_ci for (i = 0; i < hw->nr_subgroups; i++) 30162306a36Sopenharmony_ci if (hw->subgroups[i].reg) 30262306a36Sopenharmony_ci k+= save_reg(d, k, hw->subgroups[i].reg, 0); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci memcpy(&d->chip, &intc_irq_chip, sizeof(struct irq_chip)); 30562306a36Sopenharmony_ci d->chip.name = desc->name; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (hw->ack_regs) 30862306a36Sopenharmony_ci for (i = 0; i < hw->nr_ack_regs; i++) 30962306a36Sopenharmony_ci k += save_reg(d, k, hw->ack_regs[i].set_reg, 0); 31062306a36Sopenharmony_ci else 31162306a36Sopenharmony_ci d->chip.irq_mask_ack = d->chip.irq_disable; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci /* disable bits matching force_disable before registering irqs */ 31462306a36Sopenharmony_ci if (desc->force_disable) 31562306a36Sopenharmony_ci intc_enable_disable_enum(desc, d, desc->force_disable, 0); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* disable bits matching force_enable before registering irqs */ 31862306a36Sopenharmony_ci if (desc->force_enable) 31962306a36Sopenharmony_ci intc_enable_disable_enum(desc, d, desc->force_enable, 0); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci BUG_ON(k > 256); /* _INTC_ADDR_E() and _INTC_ADDR_D() are 8 bits */ 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci intc_irq_domain_init(d, hw); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* register the vectors one by one */ 32662306a36Sopenharmony_ci for (i = 0; i < hw->nr_vectors; i++) { 32762306a36Sopenharmony_ci struct intc_vect *vect = hw->vectors + i; 32862306a36Sopenharmony_ci unsigned int irq = evt2irq(vect->vect); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (!vect->enum_id) 33162306a36Sopenharmony_ci continue; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (!intc_map(d->domain, irq)) 33462306a36Sopenharmony_ci continue; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci intc_irq_xlate_set(irq, vect->enum_id, d); 33762306a36Sopenharmony_ci intc_register_irq(desc, d, vect->enum_id, irq); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci for (k = i + 1; k < hw->nr_vectors; k++) { 34062306a36Sopenharmony_ci struct intc_vect *vect2 = hw->vectors + k; 34162306a36Sopenharmony_ci unsigned int irq2 = evt2irq(vect2->vect); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (vect->enum_id != vect2->enum_id) 34462306a36Sopenharmony_ci continue; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci /* 34762306a36Sopenharmony_ci * In the case of multi-evt handling and sparse 34862306a36Sopenharmony_ci * IRQ support, each vector still needs to have 34962306a36Sopenharmony_ci * its own backing irq_desc. 35062306a36Sopenharmony_ci */ 35162306a36Sopenharmony_ci if (!intc_map(d->domain, irq2)) 35262306a36Sopenharmony_ci continue; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci vect2->enum_id = 0; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci /* redirect this interrupts to the first one */ 35762306a36Sopenharmony_ci irq_set_chip(irq2, &dummy_irq_chip); 35862306a36Sopenharmony_ci irq_set_chained_handler_and_data(irq2, 35962306a36Sopenharmony_ci intc_redirect_irq, 36062306a36Sopenharmony_ci (void *)irq); 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci intc_subgroup_init(desc, d); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci /* enable bits matching force_enable after registering irqs */ 36762306a36Sopenharmony_ci if (desc->force_enable) 36862306a36Sopenharmony_ci intc_enable_disable_enum(desc, d, desc->force_enable, 1); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci d->skip_suspend = desc->skip_syscore_suspend; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci nr_intc_controllers++; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci return 0; 37562306a36Sopenharmony_cierr5: 37662306a36Sopenharmony_ci kfree(d->prio); 37762306a36Sopenharmony_cierr4: 37862306a36Sopenharmony_ci#ifdef CONFIG_SMP 37962306a36Sopenharmony_ci kfree(d->smp); 38062306a36Sopenharmony_cierr3: 38162306a36Sopenharmony_ci#endif 38262306a36Sopenharmony_ci kfree(d->reg); 38362306a36Sopenharmony_cierr2: 38462306a36Sopenharmony_ci for (k = 0; k < d->nr_windows; k++) 38562306a36Sopenharmony_ci if (d->window[k].virt) 38662306a36Sopenharmony_ci iounmap(d->window[k].virt); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci kfree(d->window); 38962306a36Sopenharmony_cierr1: 39062306a36Sopenharmony_ci kfree(d); 39162306a36Sopenharmony_cierr0: 39262306a36Sopenharmony_ci pr_err("unable to allocate INTC memory\n"); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci return -ENOMEM; 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic int intc_suspend(void) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci struct intc_desc_int *d; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci list_for_each_entry(d, &intc_list, list) { 40262306a36Sopenharmony_ci int irq; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (d->skip_suspend) 40562306a36Sopenharmony_ci continue; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci /* enable wakeup irqs belonging to this intc controller */ 40862306a36Sopenharmony_ci for_each_active_irq(irq) { 40962306a36Sopenharmony_ci struct irq_data *data; 41062306a36Sopenharmony_ci struct irq_chip *chip; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci data = irq_get_irq_data(irq); 41362306a36Sopenharmony_ci chip = irq_data_get_irq_chip(data); 41462306a36Sopenharmony_ci if (chip != &d->chip) 41562306a36Sopenharmony_ci continue; 41662306a36Sopenharmony_ci if (irqd_is_wakeup_set(data)) 41762306a36Sopenharmony_ci chip->irq_enable(data); 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci return 0; 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistatic void intc_resume(void) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci struct intc_desc_int *d; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci list_for_each_entry(d, &intc_list, list) { 42862306a36Sopenharmony_ci int irq; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (d->skip_suspend) 43162306a36Sopenharmony_ci continue; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci for_each_active_irq(irq) { 43462306a36Sopenharmony_ci struct irq_data *data; 43562306a36Sopenharmony_ci struct irq_chip *chip; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci data = irq_get_irq_data(irq); 43862306a36Sopenharmony_ci chip = irq_data_get_irq_chip(data); 43962306a36Sopenharmony_ci /* 44062306a36Sopenharmony_ci * This will catch the redirect and VIRQ cases 44162306a36Sopenharmony_ci * due to the dummy_irq_chip being inserted. 44262306a36Sopenharmony_ci */ 44362306a36Sopenharmony_ci if (chip != &d->chip) 44462306a36Sopenharmony_ci continue; 44562306a36Sopenharmony_ci if (irqd_irq_disabled(data)) 44662306a36Sopenharmony_ci chip->irq_disable(data); 44762306a36Sopenharmony_ci else 44862306a36Sopenharmony_ci chip->irq_enable(data); 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_cistruct syscore_ops intc_syscore_ops = { 45462306a36Sopenharmony_ci .suspend = intc_suspend, 45562306a36Sopenharmony_ci .resume = intc_resume, 45662306a36Sopenharmony_ci}; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistruct bus_type intc_subsys = { 45962306a36Sopenharmony_ci .name = "intc", 46062306a36Sopenharmony_ci .dev_name = "intc", 46162306a36Sopenharmony_ci}; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cistatic ssize_t 46462306a36Sopenharmony_cishow_intc_name(struct device *dev, struct device_attribute *attr, char *buf) 46562306a36Sopenharmony_ci{ 46662306a36Sopenharmony_ci struct intc_desc_int *d; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci d = container_of(dev, struct intc_desc_int, dev); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci return sprintf(buf, "%s\n", d->chip.name); 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cistatic DEVICE_ATTR(name, S_IRUGO, show_intc_name, NULL); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic int __init register_intc_devs(void) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci struct intc_desc_int *d; 47862306a36Sopenharmony_ci int error; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci register_syscore_ops(&intc_syscore_ops); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci error = subsys_system_register(&intc_subsys, NULL); 48362306a36Sopenharmony_ci if (!error) { 48462306a36Sopenharmony_ci list_for_each_entry(d, &intc_list, list) { 48562306a36Sopenharmony_ci d->dev.id = d->index; 48662306a36Sopenharmony_ci d->dev.bus = &intc_subsys; 48762306a36Sopenharmony_ci error = device_register(&d->dev); 48862306a36Sopenharmony_ci if (error == 0) 48962306a36Sopenharmony_ci error = device_create_file(&d->dev, 49062306a36Sopenharmony_ci &dev_attr_name); 49162306a36Sopenharmony_ci if (error) 49262306a36Sopenharmony_ci break; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci if (error) 49762306a36Sopenharmony_ci pr_err("device registration error\n"); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci return error; 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_cidevice_initcall(register_intc_devs); 502