cregit-Linux how code gets into the kernel

Release 4.7 drivers/irqchip/irq-hip04.c

Directory: drivers/irqchip
/*
 * Hisilicon HiP04 INTC
 *
 * Copyright (C) 2002-2014 ARM Limited.
 * Copyright (c) 2013-2014 Hisilicon Ltd.
 * Copyright (c) 2013-2014 Linaro Ltd.
 *
 * 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.
 *
 * Interrupt architecture for the HIP04 INTC:
 *
 * o There is one Interrupt Distributor, which receives interrupts
 *   from system devices and sends them to the Interrupt Controllers.
 *
 * o There is one CPU Interface per CPU, which sends interrupts sent
 *   by the Distributor, and interrupts generated locally, to the
 *   associated CPU. The base address of the CPU interface is usually
 *   aliased so that the same address points to different chips depending
 *   on the CPU it is accessed from.
 *
 * Note that IRQs 0-31 are special - they are local to each CPU.
 * As such, the enable set/clear, pending set/clear and active bit
 * registers are banked per-cpu for these sources.
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/list.h>
#include <linux/smp.h>
#include <linux/cpu.h>
#include <linux/cpu_pm.h>
#include <linux/cpumask.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/irqdomain.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/irqchip.h>
#include <linux/irqchip/arm-gic.h>

#include <asm/irq.h>
#include <asm/exception.h>
#include <asm/smp_plat.h>

#include "irq-gic-common.h"


#define HIP04_MAX_IRQS		510


struct hip04_irq_data {
	
void __iomem *dist_base;
	
void __iomem *cpu_base;
	
struct irq_domain *domain;
	
unsigned int nr_irqs;
};

static DEFINE_RAW_SPINLOCK(irq_controller_lock);

/*
 * The GIC mapping of CPU interfaces does not necessarily match
 * the logical CPU numbering.  Let's use a mapping as returned
 * by the GIC itself.
 */

#define NR_HIP04_CPU_IF 16

static u16 hip04_cpu_map[NR_HIP04_CPU_IF] __read_mostly;


static struct hip04_irq_data hip04_data __read_mostly;


static inline void __iomem *hip04_dist_base(struct irq_data *d) { struct hip04_irq_data *hip04_data = irq_data_get_irq_chip_data(d); return hip04_data->dist_base; }

Contributors

PersonTokensPropCommitsCommitProp
haojian zhuanghaojian zhuang29100.00%1100.00%
Total29100.00%1100.00%


static inline void __iomem *hip04_cpu_base(struct irq_data *d) { struct hip04_irq_data *hip04_data = irq_data_get_irq_chip_data(d); return hip04_data->cpu_base; }

Contributors

PersonTokensPropCommitsCommitProp
haojian zhuanghaojian zhuang29100.00%1100.00%
Total29100.00%1100.00%


static inline unsigned int hip04_irq(struct irq_data *d) { return d->hwirq; }

Contributors

PersonTokensPropCommitsCommitProp
haojian zhuanghaojian zhuang18100.00%1100.00%
Total18100.00%1100.00%

/* * Routines to acknowledge, disable and enable interrupts */
static void hip04_mask_irq(struct irq_data *d) { u32 mask = 1 << (hip04_irq(d) % 32); raw_spin_lock(&irq_controller_lock); writel_relaxed(mask, hip04_dist_base(d) + GIC_DIST_ENABLE_CLEAR + (hip04_irq(d) / 32) * 4); raw_spin_unlock(&irq_controller_lock); }

Contributors

PersonTokensPropCommitsCommitProp
haojian zhuanghaojian zhuang60100.00%1100.00%
Total60100.00%1100.00%


static void hip04_unmask_irq(struct irq_data *d) { u32 mask = 1 << (hip04_irq(d) % 32); raw_spin_lock(&irq_controller_lock); writel_relaxed(mask, hip04_dist_base(d) + GIC_DIST_ENABLE_SET + (hip04_irq(d) / 32) * 4); raw_spin_unlock(&irq_controller_lock); }

Contributors

PersonTokensPropCommitsCommitProp
haojian zhuanghaojian zhuang60100.00%1100.00%
Total60100.00%1100.00%


static void hip04_eoi_irq(struct irq_data *d) { writel_relaxed(hip04_irq(d), hip04_cpu_base(d) + GIC_CPU_EOI); }

Contributors

PersonTokensPropCommitsCommitProp
haojian zhuanghaojian zhuang26100.00%1100.00%
Total26100.00%1100.00%


static int hip04_irq_set_type(struct irq_data *d, unsigned int type) { void __iomem *base = hip04_dist_base(d); unsigned int irq = hip04_irq(d); int ret; /* Interrupt configuration for SGIs can't be changed */ if (irq < 16) return -EINVAL; /* SPIs have restrictions on the supported types */ if (irq >= 32 && type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING) return -EINVAL; raw_spin_lock(&irq_controller_lock); ret = gic_configure_irq(irq, type, base, NULL); raw_spin_unlock(&irq_controller_lock); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
haojian zhuanghaojian zhuang8488.42%150.00%
liviu dudauliviu dudau1111.58%150.00%
Total95100.00%2100.00%

#ifdef CONFIG_SMP
static int hip04_irq_set_affinity(struct irq_data *d, const struct cpumask *mask_val, bool force) { void __iomem *reg; unsigned int cpu, shift = (hip04_irq(d) % 2) * 16; u32 val, mask, bit; if (!force) cpu = cpumask_any_and(mask_val, cpu_online_mask); else cpu = cpumask_first(mask_val); if (cpu >= NR_HIP04_CPU_IF || cpu >= nr_cpu_ids) return -EINVAL; raw_spin_lock(&irq_controller_lock); reg = hip04_dist_base(d) + GIC_DIST_TARGET + ((hip04_irq(d) * 2) & ~3); mask = 0xffff << shift; bit = hip04_cpu_map[cpu] << shift; val = readl_relaxed(reg) & ~mask; writel_relaxed(val | bit, reg); raw_spin_unlock(&irq_controller_lock); return IRQ_SET_MASK_OK; }

Contributors

PersonTokensPropCommitsCommitProp
haojian zhuanghaojian zhuang157100.00%1100.00%
Total157100.00%1100.00%

#endif
static void __exception_irq_entry hip04_handle_irq(struct pt_regs *regs) { u32 irqstat, irqnr; void __iomem *cpu_base = hip04_data.cpu_base; do { irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK); irqnr = irqstat & GICC_IAR_INT_ID_MASK; if (likely(irqnr > 15 && irqnr <= HIP04_MAX_IRQS)) { handle_domain_irq(hip04_data.domain, irqnr, regs); continue; } if (irqnr < 16) { writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI); #ifdef CONFIG_SMP handle_IPI(irqnr, regs); #endif continue; } break; } while (1); }

Contributors

PersonTokensPropCommitsCommitProp
haojian zhuanghaojian zhuang10699.07%150.00%
marc zyngiermarc zyngier10.93%150.00%
Total107100.00%2100.00%

static struct irq_chip hip04_irq_chip = { .name = "HIP04 INTC", .irq_mask = hip04_mask_irq, .irq_unmask = hip04_unmask_irq, .irq_eoi = hip04_eoi_irq, .irq_set_type = hip04_irq_set_type, #ifdef CONFIG_SMP .irq_set_affinity = hip04_irq_set_affinity, #endif .flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND, };
static u16 hip04_get_cpumask(struct hip04_irq_data *intc) { void __iomem *base = intc->dist_base; u32 mask, i; for (i = mask = 0; i < 32; i += 2) { mask = readl_relaxed(base + GIC_DIST_TARGET + i * 2); mask |= mask >> 16; if (mask) break; } if (!mask) pr_crit("GIC CPU mask not found - kernel will fail to boot.\n"); return mask; }

Contributors

PersonTokensPropCommitsCommitProp
haojian zhuanghaojian zhuang80100.00%1100.00%
Total80100.00%1100.00%


static void __init hip04_irq_dist_init(struct hip04_irq_data *intc) { unsigned int i; u32 cpumask; unsigned int nr_irqs = intc->nr_irqs; void __iomem *base = intc->dist_base; writel_relaxed(0, base + GIC_DIST_CTRL); /* * Set all global interrupts to this CPU only. */ cpumask = hip04_get_cpumask(intc); cpumask |= cpumask << 16; for (i = 32; i < nr_irqs; i += 2) writel_relaxed(cpumask, base + GIC_DIST_TARGET + ((i * 2) & ~3)); gic_dist_config(base, nr_irqs, NULL); writel_relaxed(1, base + GIC_DIST_CTRL); }

Contributors

PersonTokensPropCommitsCommitProp
haojian zhuanghaojian zhuang111100.00%1100.00%
Total111100.00%1100.00%


static void hip04_irq_cpu_init(struct hip04_irq_data *intc) { void __iomem *dist_base = intc->dist_base; void __iomem *base = intc->cpu_base; unsigned int cpu_mask, cpu = smp_processor_id(); int i; /* * Get what the GIC says our CPU mask is. */ BUG_ON(cpu >= NR_HIP04_CPU_IF); cpu_mask = hip04_get_cpumask(intc); hip04_cpu_map[cpu] = cpu_mask; /* * Clear our mask from the other map entries in case they're * still undefined. */ for (i = 0; i < NR_HIP04_CPU_IF; i++) if (i != cpu) hip04_cpu_map[i] &= ~cpu_mask; gic_cpu_config(dist_base, NULL); writel_relaxed(0xf0, base + GIC_CPU_PRIMASK); writel_relaxed(1, base + GIC_CPU_CTRL); }

Contributors

PersonTokensPropCommitsCommitProp
haojian zhuanghaojian zhuang116100.00%1100.00%
Total116100.00%1100.00%

#ifdef CONFIG_SMP
static void hip04_raise_softirq(const struct cpumask *mask, unsigned int irq) { int cpu; unsigned long flags, map = 0; raw_spin_lock_irqsave(&irq_controller_lock, flags); /* Convert our logical CPU mask into a physical one. */ for_each_cpu(cpu, mask) map |= hip04_cpu_map[cpu]; /* * Ensure that stores to Normal memory are visible to the * other CPUs before they observe us issuing the IPI. */ dmb(ishst); /* this always happens on GIC0 */ writel_relaxed(map << 8 | irq, hip04_data.dist_base + GIC_DIST_SOFTINT); raw_spin_unlock_irqrestore(&irq_controller_lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
haojian zhuanghaojian zhuang79100.00%1100.00%
Total79100.00%1100.00%

#endif
static int hip04_irq_domain_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) { if (hw < 32) { irq_set_percpu_devid(irq); irq_set_chip_and_handler(irq, &hip04_irq_chip, handle_percpu_devid_irq); irq_set_status_flags(irq, IRQ_NOAUTOEN); } else { irq_set_chip_and_handler(irq, &hip04_irq_chip, handle_fasteoi_irq); irq_set_probe(irq); } irq_set_chip_data(irq, d->host_data); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
haojian zhuanghaojian zhuang7596.15%150.00%
rob herringrob herring33.85%150.00%
Total78100.00%2100.00%


static int hip04_irq_domain_xlate(struct irq_domain *d, struct device_node *controller, const u32 *intspec, unsigned int intsize, unsigned long *out_hwirq, unsigned int *out_type) { unsigned long ret = 0; if (irq_domain_get_of_node(d) != controller) return -EINVAL; if (intsize < 3) return -EINVAL; /* Get the interrupt number and add 16 to skip over SGIs */ *out_hwirq = intspec[1] + 16; /* For SPIs, we need to add 16 more to get the irq ID number */ if (!intspec[0]) *out_hwirq += 16; *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK; return ret; }

Contributors

PersonTokensPropCommitsCommitProp
haojian zhuanghaojian zhuang9997.06%150.00%
marc zyngiermarc zyngier32.94%150.00%
Total102100.00%2100.00%

#ifdef CONFIG_SMP
static int hip04_irq_secondary_init(struct notifier_block *nfb, unsigned long action, void *hcpu) { if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) hip04_irq_cpu_init(&hip04_data); return NOTIFY_OK; }

Contributors

PersonTokensPropCommitsCommitProp
haojian zhuanghaojian zhuang38100.00%1100.00%
Total38100.00%1100.00%

/* * Notifier for enabling the INTC CPU interface. Set an arbitrarily high * priority because the GIC needs to be up before the ARM generic timers. */ static struct notifier_block hip04_irq_cpu_notifier = { .notifier_call = hip04_irq_secondary_init, .priority = 100, }; #endif static const struct irq_domain_ops hip04_irq_domain_ops = { .map = hip04_irq_domain_map, .xlate = hip04_irq_domain_xlate, };
static int __init hip04_of_init(struct device_node *node, struct device_node *parent) { irq_hw_number_t hwirq_base = 16; int nr_irqs, irq_base, i; if (WARN_ON(!node)) return -ENODEV; hip04_data.dist_base = of_iomap(node, 0); WARN(!hip04_data.dist_base, "fail to map hip04 intc dist registers\n"); hip04_data.cpu_base = of_iomap(node, 1); WARN(!hip04_data.cpu_base, "unable to map hip04 intc cpu registers\n"); /* * Initialize the CPU interface map to all CPUs. * It will be refined as each CPU probes its ID. */ for (i = 0; i < NR_HIP04_CPU_IF; i++) hip04_cpu_map[i] = 0xffff; /* * Find out how many interrupts are supported. * The HIP04 INTC only supports up to 510 interrupt sources. */ nr_irqs = readl_relaxed(hip04_data.dist_base + GIC_DIST_CTR) & 0x1f; nr_irqs = (nr_irqs + 1) * 32; if (nr_irqs > HIP04_MAX_IRQS) nr_irqs = HIP04_MAX_IRQS; hip04_data.nr_irqs = nr_irqs; nr_irqs -= hwirq_base; /* calculate # of irqs to allocate */ irq_base = irq_alloc_descs(-1, hwirq_base, nr_irqs, numa_node_id()); if (irq_base < 0) { pr_err("failed to allocate IRQ numbers\n"); return -EINVAL; } hip04_data.domain = irq_domain_add_legacy(node, nr_irqs, irq_base, hwirq_base, &hip04_irq_domain_ops, &hip04_data); if (WARN_ON(!hip04_data.domain)) return -EINVAL; #ifdef CONFIG_SMP set_smp_cross_call(hip04_raise_softirq); register_cpu_notifier(&hip04_irq_cpu_notifier); #endif set_handle_irq(hip04_handle_irq); hip04_irq_dist_init(&hip04_data); hip04_irq_cpu_init(&hip04_data); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
haojian zhuanghaojian zhuang24998.81%133.33%
arnd bergmannarnd bergmann20.79%133.33%
wang longwang long10.40%133.33%
Total252100.00%3100.00%

IRQCHIP_DECLARE(hip04_intc, "hisilicon,hip04-intc", hip04_of_init);

Overall Contributors

PersonTokensPropCommitsCommitProp
haojian zhuanghaojian zhuang163698.02%110.00%
liviu dudauliviu dudau110.66%110.00%
sudeep hollasudeep holla90.54%220.00%
marc zyngiermarc zyngier40.24%220.00%
joel porquetjoel porquet30.18%110.00%
rob herringrob herring30.18%110.00%
arnd bergmannarnd bergmann20.12%110.00%
wang longwang long10.06%110.00%
Total1669100.00%10100.00%
Directory: drivers/irqchip
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}