/*
* Local APIC related interfaces to support IOAPIC, MSI, etc.
*
* Copyright (C) 1997, 1998, 1999, 2000, 2009 Ingo Molnar, Hajnalka Szabo
* Moved from arch/x86/kernel/apic/io_apic.c.
* Jiang Liu <jiang.liu@linux.intel.com>
* Enable support of hierarchical irqdomains
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/seq_file.h>
#include <linux/init.h>
#include <linux/compiler.h>
#include <linux/slab.h>
#include <asm/irqdomain.h>
#include <asm/hw_irq.h>
#include <asm/apic.h>
#include <asm/i8259.h>
#include <asm/desc.h>
#include <asm/irq_remapping.h>
#include <asm/trace/irq_vectors.h>
struct apic_chip_data {
struct irq_cfg hw_irq_cfg;
unsigned int vector;
unsigned int prev_vector;
unsigned int cpu;
unsigned int prev_cpu;
unsigned int irq;
struct hlist_node clist;
unsigned int move_in_progress : 1,
is_managed : 1,
can_reserve : 1,
has_reserved : 1;
};
struct irq_domain *x86_vector_domain;
EXPORT_SYMBOL_GPL(x86_vector_domain);
static DEFINE_RAW_SPINLOCK(vector_lock);
static cpumask_var_t vector_searchmask;
static struct irq_chip lapic_controller;
static struct irq_matrix *vector_matrix;
#ifdef CONFIG_SMP
static DEFINE_PER_CPU(struct hlist_head, cleanup_list);
#endif
void lock_vector_lock(void)
{
/* Used to the online set of cpus does not change
* during assign_irq_vector.
*/
raw_spin_lock(&vector_lock);
}
void unlock_vector_lock(void)
{
raw_spin_unlock(&vector_lock);
}
void init_irq_alloc_info(struct irq_alloc_info *info,
const struct cpumask *mask)
{
memset(info, 0, sizeof(*info));
info->mask = mask;
}
void copy_irq_alloc_info(struct irq_alloc_info *dst, struct irq_alloc_info *src)
{
if (src)
*dst = *src;
else
memset(dst, 0, sizeof(*dst));
}
static struct apic_chip_data *apic_chip_data(struct irq_data *irqd)
{
if (!irqd)
return NULL;
while (irqd->parent_data)
irqd = irqd->parent_data;
return irqd->chip_data;
}
struct irq_cfg *irqd_cfg(struct irq_data *irqd)
{
struct apic_chip_data *apicd = apic_chip_data(irqd);
return apicd ? &apicd->hw_irq_cfg : NULL;
}
EXPORT_SYMBOL_GPL(irqd_cfg);
struct irq_cfg *irq_cfg(unsigned int irq)
{
return irqd_cfg(irq_get_irq_data(irq));
}
static struct apic_chip_data *alloc_apic_chip_data(int node)
{
struct apic_chip_data *apicd;
apicd = kzalloc_node(sizeof(*apicd), GFP_KERNEL, node);
if (apicd)
INIT_HLIST_NODE(&apicd->clist);
return apicd;
}
static void free_apic_chip_data(struct apic_chip_data *apicd)
{
kfree(apicd);
}
static void apic_update_irq_cfg(struct irq_data *irqd, unsigned int vector,
unsigned int cpu)
{
struct apic_chip_data *apicd = apic_chip_data(irqd);
lockdep_assert_held(&vector_lock);
apicd->hw_irq_cfg.vector = vector;
apicd->hw_irq_cfg.dest_apicid = apic->calc_dest_apicid(cpu);
irq_data_update_effective_affinity(irqd, cpumask_of(cpu));
trace_vector_config(irqd->irq, vector, cpu,
apicd->hw_irq_cfg.dest_apicid