Skip to content
Snippets Groups Projects
Commit df2e37c8 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge branch 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull irq updates from Thomas Gleixner:
 "The 4.6 pile of irq updates contains:

   - Support for IPI irqdomains to support proper integration of IPIs to
     and from coprocessors.  The first user of this new facility is
     MIPS.  The relevant MIPS patches come with the core to avoid merge
     ordering issues and have been acked by Ralf.

   - A new command line option to set the default interrupt affinity
     mask at boot time.

   - Support for some more new ARM and MIPS interrupt controllers:
     tango, alpine-msix and bcm6345-l1

   - Two small cleanups for x86/apic which we merged into irq/core to
     avoid yet another branch in x86 with two tiny commits.

   - The usual set of updates, cleanups in drivers/irqchip.  Mostly in
     the area of ARM-GIC, arada-37-xp and atmel chips.  Nothing
     outstanding here"

* 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (56 commits)
  irqchip/irq-alpine-msi: Release the correct domain on error
  irqchip/mxs: Fix error check of of_io_request_and_map()
  irqchip/sunxi-nmi: Fix error check of of_io_request_and_map()
  genirq: Export IRQ functions for module use
  irqchip/gic/realview: Support more RealView DCC variants
  Documentation/bindings: Document the Alpine MSIX driver
  irqchip: Add the Alpine MSIX interrupt controller
  irqchip/gic-v3: Always return IRQ_SET_MASK_OK_DONE in gic_set_affinity
  irqchip/gic-v3-its: Mark its_init() and its children as __init
  irqchip/gic-v3: Remove gic_root_node variable from the ITS code
  irqchip/gic-v3: ACPI: Add redistributor support via GICC structures
  irqchip/gic-v3: Add ACPI support for GICv3/4 initialization
  irqchip/gic-v3: Refactor gic_of_init() for GICv3 driver
  x86/apic: Deinline _flat_send_IPI_mask, save ~150 bytes
  x86/apic: Deinline __default_send_IPI_*, save ~200 bytes
  dt-bindings: interrupt-controller: Add SoC-specific compatible string to Marvell ODMI
  irqchip/mips-gic: Add new DT property to reserve IPIs
  MIPS: Delete smp-gic.c
  MIPS: Make smp CMP, CPS and MT use the new generic IPI functions
  MIPS: Add generic SMP IPI support
  ...
parents 8a284c06 8e7fe266
No related branches found
No related tags found
No related merge requests found
Showing
with 323 additions and 298 deletions
Alpine MSIX controller
See arm,gic-v3.txt for SPI and MSI definitions.
Required properties:
- compatible: should be "al,alpine-msix"
- reg: physical base address and size of the registers
- interrupt-parent: specifies the parent interrupt controller.
- interrupt-controller: identifies the node as an interrupt controller
- msi-controller: identifies the node as an PCI Message Signaled Interrupt
controller
- al,msi-base-spi: SPI base of the MSI frame
- al,msi-num-spis: number of SPIs assigned to the MSI frame, relative to SPI0
Example:
msix: msix {
compatible = "al,alpine-msix";
reg = <0x0 0xfbe00000 0x0 0x100000>;
interrupt-parent = <&gic>;
interrupt-controller;
msi-controller;
al,msi-base-spi = <160>;
al,msi-num-spis = <160>;
};
......@@ -16,6 +16,7 @@ Main node required properties:
"arm,cortex-a15-gic"
"arm,cortex-a7-gic"
"arm,cortex-a9-gic"
"arm,eb11mp-gic"
"arm,gic-400"
"arm,pl390"
"arm,tc11mp-gic"
......
* Marvell ODMI for MSI support
Some Marvell SoCs have an On-Die Message Interrupt (ODMI) controller
which can be used by on-board peripheral for MSI interrupts.
Required properties:
- compatible : The value here should contain:
"marvell,ap806-odmi-controller", "marvell,odmi-controller".
- interrupt,controller : Identifies the node as an interrupt controller.
- msi-controller : Identifies the node as an MSI controller.
- marvell,odmi-frames : Number of ODMI frames available. Each frame
provides a number of events.
- reg : List of register definitions, one for each
ODMI frame.
- marvell,spi-base : List of GIC base SPI interrupts, one for each
ODMI frame. Those SPI interrupts are 0-based,
i.e marvell,spi-base = <128> will use SPI #96.
See Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt
for details about the GIC Device Tree binding.
- interrupt-parent : Reference to the parent interrupt controller.
Example:
odmi: odmi@300000 {
compatible = "marvell,ap806-odm-controller",
"marvell,odmi-controller";
interrupt-controller;
msi-controller;
marvell,odmi-frames = <4>;
reg = <0x300000 0x4000>,
<0x304000 0x4000>,
<0x308000 0x4000>,
<0x30C000 0x4000>;
marvell,spi-base = <128>, <136>, <144>, <152>;
};
......@@ -23,6 +23,12 @@ Optional properties:
- mti,reserved-cpu-vectors : Specifies the list of CPU interrupt vectors
to which the GIC may not route interrupts. Valid values are 2 - 7.
This property is ignored if the CPU is started in EIC mode.
- mti,reserved-ipi-vectors : Specifies the range of GIC interrupts that are
reserved for IPIs.
It accepts 2 values, the 1st is the starting interrupt and the 2nd is the size
of the reserved range.
If not specified, the driver will allocate the last 2 * number of VPEs in the
system.
Required properties for timer sub-node:
- compatible : Should be "mti,gic-timer".
......@@ -44,6 +50,7 @@ Example:
#interrupt-cells = <3>;
mti,reserved-cpu-vectors = <7>;
mti,reserved-ipi-vectors = <40 8>;
timer {
compatible = "mti,gic-timer";
......
Sigma Designs SMP86xx/SMP87xx secondary interrupt controller
Required properties:
- compatible: should be "sigma,smp8642-intc"
- reg: physical address of MMIO region
- ranges: address space mapping of child nodes
- interrupt-parent: phandle of parent interrupt controller
- interrupt-controller: boolean
- #address-cells: should be <1>
- #size-cells: should be <1>
One child node per control block with properties:
- reg: address of registers for this control block
- interrupt-controller: boolean
- #interrupt-cells: should be <2>, interrupt index and flags per interrupts.txt
- interrupts: interrupt spec of primary interrupt controller
Example:
interrupt-controller@6e000 {
compatible = "sigma,smp8642-intc";
reg = <0x6e000 0x400>;
ranges = <0x0 0x6e000 0x400>;
interrupt-parent = <&gic>;
interrupt-controller;
#address-cells = <1>;
#size-cells = <1>;
irq0: interrupt-controller@0 {
reg = <0x000 0x100>;
interrupt-controller;
#interrupt-cells = <2>;
interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
};
irq1: interrupt-controller@100 {
reg = <0x100 0x100>;
interrupt-controller;
#interrupt-cells = <2>;
interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>;
};
irq2: interrupt-controller@300 {
reg = <0x300 0x100>;
interrupt-controller;
#interrupt-cells = <2>;
interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>;
};
};
......@@ -1687,6 +1687,15 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
ip= [IP_PNP]
See Documentation/filesystems/nfs/nfsroot.txt.
irqaffinity= [SMP] Set the default irq affinity mask
Format:
<cpu number>,...,<cpu number>
or
<cpu number>-<cpu number>
(must be a positive range in ascending order)
or a mixture
<cpu number>,...,<cpu number>-<cpu number>
irqfixup [HW]
When an interrupt is not handled search all handlers
for it. Intended to get systems with badly broken
......
......@@ -2422,6 +2422,7 @@ F: arch/mips/bmips/*
F: arch/mips/include/asm/mach-bmips/*
F: arch/mips/kernel/*bmips*
F: arch/mips/boot/dts/brcm/bcm*.dts*
F: drivers/irqchip/irq-bcm63*
F: drivers/irqchip/irq-bcm7*
F: drivers/irqchip/irq-brcmstb*
F: include/linux/bcm963xx_nvram.h
......
......@@ -3,7 +3,6 @@ menuconfig ARCH_MVEBU
depends on ARCH_MULTI_V7 || ARCH_MULTI_V5
select ARCH_SUPPORTS_BIG_ENDIAN
select CLKSRC_MMIO
select GENERIC_IRQ_CHIP
select PINCTRL
select PLAT_ORION
select SOC_BUS
......@@ -29,6 +28,7 @@ config MACH_ARMADA_370
bool "Marvell Armada 370 boards"
depends on ARCH_MULTI_V7
select ARMADA_370_CLK
select ARMADA_370_XP_IRQ
select CPU_PJ4B
select MACH_MVEBU_V7
select PINCTRL_ARMADA_370
......@@ -39,6 +39,7 @@ config MACH_ARMADA_370
config MACH_ARMADA_375
bool "Marvell Armada 375 boards"
depends on ARCH_MULTI_V7
select ARMADA_370_XP_IRQ
select ARM_ERRATA_720789
select ARM_ERRATA_753970
select ARM_GIC
......@@ -58,6 +59,7 @@ config MACH_ARMADA_38X
select ARM_ERRATA_720789
select ARM_ERRATA_753970
select ARM_GIC
select ARMADA_370_XP_IRQ
select ARMADA_38X_CLK
select HAVE_ARM_SCU
select HAVE_ARM_TWD if SMP
......@@ -72,6 +74,7 @@ config MACH_ARMADA_39X
bool "Marvell Armada 39x boards"
depends on ARCH_MULTI_V7
select ARM_GIC
select ARMADA_370_XP_IRQ
select ARMADA_39X_CLK
select CACHE_L2X0
select HAVE_ARM_SCU
......@@ -86,6 +89,7 @@ config MACH_ARMADA_39X
config MACH_ARMADA_XP
bool "Marvell Armada XP boards"
depends on ARCH_MULTI_V7
select ARMADA_370_XP_IRQ
select ARMADA_XP_CLK
select CPU_PJ4B
select MACH_MVEBU_V7
......
......@@ -151,6 +151,7 @@ config BMIPS_GENERIC
select CSRC_R4K
select SYNC_R4K
select COMMON_CLK
select BCM6345_L1_IRQ
select BCM7038_L1_IRQ
select BCM7120_L2_IRQ
select BRCMSTB_L2_IRQ
......@@ -2169,7 +2170,6 @@ config MIPS_MT_SMP
select CPU_MIPSR2_IRQ_VI
select CPU_MIPSR2_IRQ_EI
select SYNC_R4K
select MIPS_GIC_IPI if MIPS_GIC
select MIPS_MT
select SMP
select SMP_UP
......@@ -2267,7 +2267,6 @@ config MIPS_VPE_APSP_API_MT
config MIPS_CMP
bool "MIPS CMP framework support (DEPRECATED)"
depends on SYS_SUPPORTS_MIPS_CMP && !CPU_MIPSR6
select MIPS_GIC_IPI if MIPS_GIC
select SMP
select SYNC_R4K
select SYS_SUPPORTS_SMP
......@@ -2287,7 +2286,6 @@ config MIPS_CPS
select MIPS_CM
select MIPS_CPC
select MIPS_CPS_PM if HOTPLUG_CPU
select MIPS_GIC_IPI if MIPS_GIC
select SMP
select SYNC_R4K if (CEVT_R4K || CSRC_R4K)
select SYS_SUPPORTS_HOTPLUG_CPU
......@@ -2305,10 +2303,6 @@ config MIPS_CPS_PM
select MIPS_CPC
bool
config MIPS_GIC_IPI
depends on MIPS_GIC
bool
config MIPS_CM
bool
......
......@@ -26,90 +26,6 @@
#include "common.h"
#include "machtypes.h"
static void __init ath79_misc_intc_domain_init(
struct device_node *node, int irq);
static void ath79_misc_irq_handler(struct irq_desc *desc)
{
struct irq_domain *domain = irq_desc_get_handler_data(desc);
void __iomem *base = domain->host_data;
u32 pending;
pending = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS) &
__raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
if (!pending) {
spurious_interrupt();
return;
}
while (pending) {
int bit = __ffs(pending);
generic_handle_irq(irq_linear_revmap(domain, bit));
pending &= ~BIT(bit);
}
}
static void ar71xx_misc_irq_unmask(struct irq_data *d)
{
void __iomem *base = irq_data_get_irq_chip_data(d);
unsigned int irq = d->hwirq;
u32 t;
t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
__raw_writel(t | (1 << irq), base + AR71XX_RESET_REG_MISC_INT_ENABLE);
/* flush write */
__raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
}
static void ar71xx_misc_irq_mask(struct irq_data *d)
{
void __iomem *base = irq_data_get_irq_chip_data(d);
unsigned int irq = d->hwirq;
u32 t;
t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
__raw_writel(t & ~(1 << irq), base + AR71XX_RESET_REG_MISC_INT_ENABLE);
/* flush write */
__raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
}
static void ar724x_misc_irq_ack(struct irq_data *d)
{
void __iomem *base = irq_data_get_irq_chip_data(d);
unsigned int irq = d->hwirq;
u32 t;
t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS);
__raw_writel(t & ~(1 << irq), base + AR71XX_RESET_REG_MISC_INT_STATUS);
/* flush write */
__raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS);
}
static struct irq_chip ath79_misc_irq_chip = {
.name = "MISC",
.irq_unmask = ar71xx_misc_irq_unmask,
.irq_mask = ar71xx_misc_irq_mask,
};
static void __init ath79_misc_irq_init(void)
{
if (soc_is_ar71xx() || soc_is_ar913x())
ath79_misc_irq_chip.irq_mask_ack = ar71xx_misc_irq_mask;
else if (soc_is_ar724x() ||
soc_is_ar933x() ||
soc_is_ar934x() ||
soc_is_qca955x())
ath79_misc_irq_chip.irq_ack = ar724x_misc_irq_ack;
else
BUG();
ath79_misc_intc_domain_init(NULL, ATH79_CPU_IRQ(6));
}
static void ar934x_ip2_irq_dispatch(struct irq_desc *desc)
{
......@@ -212,142 +128,12 @@ static void qca955x_irq_init(void)
irq_set_chained_handler(ATH79_CPU_IRQ(3), qca955x_ip3_irq_dispatch);
}
/*
* The IP2/IP3 lines are tied to a PCI/WMAC/USB device. Drivers for
* these devices typically allocate coherent DMA memory, however the
* DMA controller may still have some unsynchronized data in the FIFO.
* Issue a flush in the handlers to ensure that the driver sees
* the update.
*
* This array map the interrupt lines to the DDR write buffer channels.
*/
static unsigned irq_wb_chan[8] = {
-1, -1, -1, -1, -1, -1, -1, -1,
};
asmlinkage void plat_irq_dispatch(void)
{
unsigned long pending;
int irq;
pending = read_c0_status() & read_c0_cause() & ST0_IM;
if (!pending) {
spurious_interrupt();
return;
}
pending >>= CAUSEB_IP;
while (pending) {
irq = fls(pending) - 1;
if (irq < ARRAY_SIZE(irq_wb_chan) && irq_wb_chan[irq] != -1)
ath79_ddr_wb_flush(irq_wb_chan[irq]);
do_IRQ(MIPS_CPU_IRQ_BASE + irq);
pending &= ~BIT(irq);
}
}
static int misc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
{
irq_set_chip_and_handler(irq, &ath79_misc_irq_chip, handle_level_irq);
irq_set_chip_data(irq, d->host_data);
return 0;
}
static const struct irq_domain_ops misc_irq_domain_ops = {
.xlate = irq_domain_xlate_onecell,
.map = misc_map,
};
static void __init ath79_misc_intc_domain_init(
struct device_node *node, int irq)
{
void __iomem *base = ath79_reset_base;
struct irq_domain *domain;
domain = irq_domain_add_legacy(node, ATH79_MISC_IRQ_COUNT,
ATH79_MISC_IRQ_BASE, 0, &misc_irq_domain_ops, base);
if (!domain)
panic("Failed to add MISC irqdomain");
/* Disable and clear all interrupts */
__raw_writel(0, base + AR71XX_RESET_REG_MISC_INT_ENABLE);
__raw_writel(0, base + AR71XX_RESET_REG_MISC_INT_STATUS);
irq_set_chained_handler_and_data(irq, ath79_misc_irq_handler, domain);
}
static int __init ath79_misc_intc_of_init(
struct device_node *node, struct device_node *parent)
{
int irq;
irq = irq_of_parse_and_map(node, 0);
if (!irq)
panic("Failed to get MISC IRQ");
ath79_misc_intc_domain_init(node, irq);
return 0;
}
static int __init ar7100_misc_intc_of_init(
struct device_node *node, struct device_node *parent)
{
ath79_misc_irq_chip.irq_mask_ack = ar71xx_misc_irq_mask;
return ath79_misc_intc_of_init(node, parent);
}
IRQCHIP_DECLARE(ar7100_misc_intc, "qca,ar7100-misc-intc",
ar7100_misc_intc_of_init);
static int __init ar7240_misc_intc_of_init(
struct device_node *node, struct device_node *parent)
{
ath79_misc_irq_chip.irq_ack = ar724x_misc_irq_ack;
return ath79_misc_intc_of_init(node, parent);
}
IRQCHIP_DECLARE(ar7240_misc_intc, "qca,ar7240-misc-intc",
ar7240_misc_intc_of_init);
static int __init ar79_cpu_intc_of_init(
struct device_node *node, struct device_node *parent)
{
int err, i, count;
/* Fill the irq_wb_chan table */
count = of_count_phandle_with_args(
node, "qca,ddr-wb-channels", "#qca,ddr-wb-channel-cells");
for (i = 0; i < count; i++) {
struct of_phandle_args args;
u32 irq = i;
of_property_read_u32_index(
node, "qca,ddr-wb-channel-interrupts", i, &irq);
if (irq >= ARRAY_SIZE(irq_wb_chan))
continue;
err = of_parse_phandle_with_args(
node, "qca,ddr-wb-channels",
"#qca,ddr-wb-channel-cells",
i, &args);
if (err)
return err;
irq_wb_chan[irq] = args.args[0];
pr_info("IRQ: Set flush channel of IRQ%d to %d\n",
irq, args.args[0]);
}
return mips_cpu_irq_of_init(node, parent);
}
IRQCHIP_DECLARE(ar79_cpu_intc, "qca,ar7100-cpu-intc",
ar79_cpu_intc_of_init);
void __init arch_init_irq(void)
{
unsigned irq_wb_chan2 = -1;
unsigned irq_wb_chan3 = -1;
bool misc_is_ar71xx;
if (mips_machtype == ATH79_MACH_GENERIC_OF) {
irqchip_init();
return;
......@@ -355,14 +141,26 @@ void __init arch_init_irq(void)
if (soc_is_ar71xx() || soc_is_ar724x() ||
soc_is_ar913x() || soc_is_ar933x()) {
irq_wb_chan[2] = 3;
irq_wb_chan[3] = 2;
irq_wb_chan2 = 3;
irq_wb_chan3 = 2;
} else if (soc_is_ar934x()) {
irq_wb_chan[3] = 2;
irq_wb_chan3 = 2;
}
mips_cpu_irq_init();
ath79_misc_irq_init();
ath79_cpu_irq_init(irq_wb_chan2, irq_wb_chan3);
if (soc_is_ar71xx() || soc_is_ar913x())
misc_is_ar71xx = true;
else if (soc_is_ar724x() ||
soc_is_ar933x() ||
soc_is_ar934x() ||
soc_is_qca955x())
misc_is_ar71xx = false;
else
BUG();
ath79_misc_irq_init(
ath79_reset_base + AR71XX_RESET_REG_MISC_INT_STATUS,
ATH79_CPU_IRQ(6), ATH79_MISC_IRQ_BASE, misc_is_ar71xx);
if (soc_is_ar934x())
ar934x_ip2_irq_init();
......
......@@ -15,6 +15,12 @@
#include <asm/irq_cpu.h>
#include <asm/time.h>
static const struct of_device_id smp_intc_dt_match[] = {
{ .compatible = "brcm,bcm7038-l1-intc" },
{ .compatible = "brcm,bcm6345-l1-intc" },
{}
};
unsigned int get_c0_compare_int(void)
{
return CP0_LEGACY_COMPARE_IRQ;
......@@ -24,8 +30,8 @@ void __init arch_init_irq(void)
{
struct device_node *dn;
/* Only the STB (bcm7038) controller supports SMP IRQ affinity */
dn = of_find_compatible_node(NULL, NULL, "brcm,bcm7038-l1-intc");
/* Only these controllers support SMP IRQ affinity */
dn = of_find_matching_node(NULL, smp_intc_dt_match);
if (dn)
of_node_put(dn);
else
......
......@@ -144,4 +144,8 @@ static inline u32 ath79_reset_rr(unsigned reg)
void ath79_device_reset_set(u32 mask);
void ath79_device_reset_clear(u32 mask);
void ath79_cpu_irq_init(unsigned irq_wb_chan2, unsigned irq_wb_chan3);
void ath79_misc_irq_init(void __iomem *regs, int irq,
int irq_base, bool is_ar71xx);
#endif /* __ASM_MACH_ATH79_H */
......@@ -44,8 +44,9 @@ static inline void plat_smp_setup(void)
mp_ops->smp_setup();
}
extern void gic_send_ipi_single(int cpu, unsigned int action);
extern void gic_send_ipi_mask(const struct cpumask *mask, unsigned int action);
extern void mips_smp_send_ipi_single(int cpu, unsigned int action);
extern void mips_smp_send_ipi_mask(const struct cpumask *mask,
unsigned int action);
#else /* !CONFIG_SMP */
......
......@@ -52,7 +52,6 @@ obj-$(CONFIG_MIPS_MT_SMP) += smp-mt.o
obj-$(CONFIG_MIPS_CMP) += smp-cmp.o
obj-$(CONFIG_MIPS_CPS) += smp-cps.o cps-vec.o
obj-$(CONFIG_MIPS_CPS_NS16550) += cps-vec-ns16550.o
obj-$(CONFIG_MIPS_GIC_IPI) += smp-gic.o
obj-$(CONFIG_MIPS_SPRAM) += spram.o
obj-$(CONFIG_MIPS_VPE_LOADER) += vpe.o
......
......@@ -149,8 +149,8 @@ void __init cmp_prepare_cpus(unsigned int max_cpus)
}
struct plat_smp_ops cmp_smp_ops = {
.send_ipi_single = gic_send_ipi_single,
.send_ipi_mask = gic_send_ipi_mask,
.send_ipi_single = mips_smp_send_ipi_single,
.send_ipi_mask = mips_smp_send_ipi_mask,
.init_secondary = cmp_init_secondary,
.smp_finish = cmp_smp_finish,
.boot_secondary = cmp_boot_secondary,
......
......@@ -472,8 +472,8 @@ static struct plat_smp_ops cps_smp_ops = {
.boot_secondary = cps_boot_secondary,
.init_secondary = cps_init_secondary,
.smp_finish = cps_smp_finish,
.send_ipi_single = gic_send_ipi_single,
.send_ipi_mask = gic_send_ipi_mask,
.send_ipi_single = mips_smp_send_ipi_single,
.send_ipi_mask = mips_smp_send_ipi_mask,
#ifdef CONFIG_HOTPLUG_CPU
.cpu_disable = cps_cpu_disable,
.cpu_die = cps_cpu_die,
......
......@@ -121,7 +121,7 @@ static void vsmp_send_ipi_single(int cpu, unsigned int action)
#ifdef CONFIG_MIPS_GIC
if (gic_present) {
gic_send_ipi_single(cpu, action);
mips_smp_send_ipi_single(cpu, action);
return;
}
#endif
......
......@@ -33,12 +33,16 @@
#include <linux/cpu.h>
#include <linux/err.h>
#include <linux/ftrace.h>
#include <linux/irqdomain.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/atomic.h>
#include <asm/cpu.h>
#include <asm/processor.h>
#include <asm/idle.h>
#include <asm/r4k-timer.h>
#include <asm/mips-cpc.h>
#include <asm/mmu_context.h>
#include <asm/time.h>
#include <asm/setup.h>
......@@ -79,6 +83,11 @@ static cpumask_t cpu_core_setup_map;
cpumask_t cpu_coherent_mask;
#ifdef CONFIG_GENERIC_IRQ_IPI
static struct irq_desc *call_desc;
static struct irq_desc *sched_desc;
#endif
static inline void set_cpu_sibling_map(int cpu)
{
int i;
......@@ -146,6 +155,133 @@ void register_smp_ops(struct plat_smp_ops *ops)
mp_ops = ops;
}
#ifdef CONFIG_GENERIC_IRQ_IPI
void mips_smp_send_ipi_single(int cpu, unsigned int action)
{
mips_smp_send_ipi_mask(cpumask_of(cpu), action);
}
void mips_smp_send_ipi_mask(const struct cpumask *mask, unsigned int action)
{
unsigned long flags;
unsigned int core;
int cpu;
local_irq_save(flags);
switch (action) {
case SMP_CALL_FUNCTION:
__ipi_send_mask(call_desc, mask);
break;
case SMP_RESCHEDULE_YOURSELF:
__ipi_send_mask(sched_desc, mask);
break;
default:
BUG();
}
if (mips_cpc_present()) {
for_each_cpu(cpu, mask) {
core = cpu_data[cpu].core;
if (core == current_cpu_data.core)
continue;
while (!cpumask_test_cpu(cpu, &cpu_coherent_mask)) {
mips_cpc_lock_other(core);
write_cpc_co_cmd(CPC_Cx_CMD_PWRUP);
mips_cpc_unlock_other();
}
}
}
local_irq_restore(flags);
}
static irqreturn_t ipi_resched_interrupt(int irq, void *dev_id)
{
scheduler_ipi();
return IRQ_HANDLED;
}
static irqreturn_t ipi_call_interrupt(int irq, void *dev_id)
{
generic_smp_call_function_interrupt();
return IRQ_HANDLED;
}
static struct irqaction irq_resched = {
.handler = ipi_resched_interrupt,
.flags = IRQF_PERCPU,
.name = "IPI resched"
};
static struct irqaction irq_call = {
.handler = ipi_call_interrupt,
.flags = IRQF_PERCPU,
.name = "IPI call"
};
static __init void smp_ipi_init_one(unsigned int virq,
struct irqaction *action)
{
int ret;
irq_set_handler(virq, handle_percpu_irq);
ret = setup_irq(virq, action);
BUG_ON(ret);
}
static int __init mips_smp_ipi_init(void)
{
unsigned int call_virq, sched_virq;
struct irq_domain *ipidomain;
struct device_node *node;
node = of_irq_find_parent(of_root);
ipidomain = irq_find_matching_host(node, DOMAIN_BUS_IPI);
/*
* Some platforms have half DT setup. So if we found irq node but
* didn't find an ipidomain, try to search for one that is not in the
* DT.
*/
if (node && !ipidomain)
ipidomain = irq_find_matching_host(NULL, DOMAIN_BUS_IPI);
BUG_ON(!ipidomain);
call_virq = irq_reserve_ipi(ipidomain, cpu_possible_mask);
BUG_ON(!call_virq);
sched_virq = irq_reserve_ipi(ipidomain, cpu_possible_mask);
BUG_ON(!sched_virq);
if (irq_domain_is_ipi_per_cpu(ipidomain)) {
int cpu;
for_each_cpu(cpu, cpu_possible_mask) {
smp_ipi_init_one(call_virq + cpu, &irq_call);
smp_ipi_init_one(sched_virq + cpu, &irq_resched);
}
} else {
smp_ipi_init_one(call_virq, &irq_call);
smp_ipi_init_one(sched_virq, &irq_resched);
}
call_desc = irq_to_desc(call_virq);
sched_desc = irq_to_desc(sched_virq);
return 0;
}
early_initcall(mips_smp_ipi_init);
#endif
/*
* First C code run on the secondary CPUs after being started up by
* the master.
......
......@@ -57,67 +57,13 @@ static inline void __xapic_wait_icr_idle(void)
cpu_relax();
}
static inline void
__default_send_IPI_shortcut(unsigned int shortcut, int vector, unsigned int dest)
{
/*
* Subtle. In the case of the 'never do double writes' workaround
* we have to lock out interrupts to be safe. As we don't care
* of the value read we use an atomic rmw access to avoid costly
* cli/sti. Otherwise we use an even cheaper single atomic write
* to the APIC.
*/
unsigned int cfg;
/*
* Wait for idle.
*/
__xapic_wait_icr_idle();
/*
* No need to touch the target chip field
*/
cfg = __prepare_ICR(shortcut, vector, dest);
/*
* Send the IPI. The write to APIC_ICR fires this off.
*/
native_apic_mem_write(APIC_ICR, cfg);
}
void __default_send_IPI_shortcut(unsigned int shortcut, int vector, unsigned int dest);
/*
* This is used to send an IPI with no shorthand notation (the destination is
* specified in bits 56 to 63 of the ICR).
*/
static inline void
__default_send_IPI_dest_field(unsigned int mask, int vector, unsigned int dest)
{
unsigned long cfg;
/*
* Wait for idle.
*/
if (unlikely(vector == NMI_VECTOR))
safe_apic_wait_icr_idle();
else
__xapic_wait_icr_idle();
/*
* prepare target chip field
*/
cfg = __prepare_ICR2(mask);
native_apic_mem_write(APIC_ICR2, cfg);
/*
* program the ICR
*/
cfg = __prepare_ICR(0, vector, dest);
/*
* Send the IPI. The write to APIC_ICR fires this off.
*/
native_apic_mem_write(APIC_ICR, cfg);
}
void __default_send_IPI_dest_field(unsigned int mask, int vector, unsigned int dest);
extern void default_send_IPI_single(int cpu, int vector);
extern void default_send_IPI_single_phys(int cpu, int vector);
......
......@@ -53,7 +53,7 @@ void flat_init_apic_ldr(void)
apic_write(APIC_LDR, val);
}
static inline void _flat_send_IPI_mask(unsigned long mask, int vector)
static void _flat_send_IPI_mask(unsigned long mask, int vector)
{
unsigned long flags;
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment