You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
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 irq department delivers:
- new core infrastructure to allow better management of multi-queue
devices (interrupt spreading, node aware descriptor allocation ...)
- a new interrupt flow handler to support the new fangled Intel VMD
devices.
- yet another new interrupt controller driver.
- a series of fixes which addresses sparse warnings, missing
includes, missing static declarations etc from Ben Dooks.
- a fix for the error handling in the hierarchical domain allocation
code.
- the usual pile of small updates to core and driver code"
* 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (46 commits)
genirq: Fix missing irq allocation affinity hint
irqdomain: Fix irq_domain_alloc_irqs_recursive() error handling
irq/Documentation: Correct result of echnoing 5 to smp_affinity
MAINTAINERS: Remove Jiang Liu from irq domains
genirq/msi: Fix broken debug output
genirq: Add a helper to spread an affinity mask for MSI/MSI-X vectors
genirq/msi: Make use of affinity aware allocations
genirq: Use affinity hint in irqdesc allocation
genirq: Add affinity hint to irq allocation
genirq: Introduce IRQD_AFFINITY_MANAGED flag
genirq/msi: Remove unused MSI_FLAG_IDENTITY_MAP
irqchip/s3c24xx: Fixup IO accessors for big endian
irqchip/exynos-combiner: Fix usage of __raw IO
irqdomain: Fix disposal of mappings for interrupt hierarchies
irqchip/aspeed-vic: Add irq controller for Aspeed
doc/devicetree: Add Aspeed VIC bindings
x86/PCI/VMD: Use untracked irq handler
genirq: Add untracked irq handler
irqchip/mips-gic: Populate irq_domain names
irqchip/gicv3-its: Implement two-level(indirect) device table support
...
This commit is contained in:
@@ -21,6 +21,7 @@ Main node required properties:
|
|||||||
"arm,pl390"
|
"arm,pl390"
|
||||||
"arm,tc11mp-gic"
|
"arm,tc11mp-gic"
|
||||||
"brcm,brahma-b15-gic"
|
"brcm,brahma-b15-gic"
|
||||||
|
"nvidia,tegra210-agic"
|
||||||
"qcom,msm-8660-qgic"
|
"qcom,msm-8660-qgic"
|
||||||
"qcom,msm-qgic2"
|
"qcom,msm-qgic2"
|
||||||
- interrupt-controller : Identifies the node as an interrupt controller
|
- interrupt-controller : Identifies the node as an interrupt controller
|
||||||
@@ -68,7 +69,7 @@ Optional
|
|||||||
"ic_clk" (for "arm,arm11mp-gic")
|
"ic_clk" (for "arm,arm11mp-gic")
|
||||||
"PERIPHCLKEN" (for "arm,cortex-a15-gic")
|
"PERIPHCLKEN" (for "arm,cortex-a15-gic")
|
||||||
"PERIPHCLK", "PERIPHCLKEN" (for "arm,cortex-a9-gic")
|
"PERIPHCLK", "PERIPHCLKEN" (for "arm,cortex-a9-gic")
|
||||||
"clk" (for "arm,gic-400")
|
"clk" (for "arm,gic-400" and "nvidia,tegra210")
|
||||||
"gclk" (for "arm,pl390")
|
"gclk" (for "arm,pl390")
|
||||||
|
|
||||||
- power-domains : A phandle and PM domain specifier as defined by bindings of
|
- power-domains : A phandle and PM domain specifier as defined by bindings of
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
Aspeed Vectored Interrupt Controller
|
||||||
|
|
||||||
|
These bindings are for the Aspeed AST2400 interrupt controller register layout.
|
||||||
|
The SoC has an legacy register layout, but this driver does not support that
|
||||||
|
mode of operation.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
|
||||||
|
- compatible : should be "aspeed,ast2400-vic".
|
||||||
|
|
||||||
|
- interrupt-controller : Identifies the node as an interrupt controller
|
||||||
|
- #interrupt-cells : Specifies the number of cells needed to encode an
|
||||||
|
interrupt source. The value shall be 1.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
vic: interrupt-controller@1e6c0080 {
|
||||||
|
compatible = "aspeed,ast2400-vic";
|
||||||
|
interrupt-controller;
|
||||||
|
#interrupt-cells = <1>;
|
||||||
|
reg = <0x1e6c0080 0x80>;
|
||||||
|
};
|
||||||
@@ -725,7 +725,7 @@ IRQ, you can set it by doing:
|
|||||||
> echo 1 > /proc/irq/10/smp_affinity
|
> echo 1 > /proc/irq/10/smp_affinity
|
||||||
|
|
||||||
This means that only the first CPU will handle the IRQ, but you can also echo
|
This means that only the first CPU will handle the IRQ, but you can also echo
|
||||||
5 which means that only the first and fourth CPU can handle the IRQ.
|
5 which means that only the first and third CPU can handle the IRQ.
|
||||||
|
|
||||||
The contents of each smp_affinity file is the same by default:
|
The contents of each smp_affinity file is the same by default:
|
||||||
|
|
||||||
|
|||||||
@@ -6235,7 +6235,6 @@ F: Documentation/devicetree/bindings/interrupt-controller/
|
|||||||
F: drivers/irqchip/
|
F: drivers/irqchip/
|
||||||
|
|
||||||
IRQ DOMAINS (IRQ NUMBER MAPPING LIBRARY)
|
IRQ DOMAINS (IRQ NUMBER MAPPING LIBRARY)
|
||||||
M: Jiang Liu <jiang.liu@linux.intel.com>
|
|
||||||
M: Marc Zyngier <marc.zyngier@arm.com>
|
M: Marc Zyngier <marc.zyngier@arm.com>
|
||||||
S: Maintained
|
S: Maintained
|
||||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git irq/core
|
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git irq/core
|
||||||
|
|||||||
@@ -242,7 +242,7 @@ unsigned int irq_alloc(unsigned int dev_handle, unsigned int dev_ino)
|
|||||||
{
|
{
|
||||||
int irq;
|
int irq;
|
||||||
|
|
||||||
irq = __irq_alloc_descs(-1, 1, 1, numa_node_id(), NULL);
|
irq = __irq_alloc_descs(-1, 1, 1, numa_node_id(), NULL, NULL);
|
||||||
if (irq <= 0)
|
if (irq <= 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
|||||||
@@ -981,7 +981,7 @@ static int alloc_irq_from_domain(struct irq_domain *domain, int ioapic, u32 gsi,
|
|||||||
|
|
||||||
return __irq_domain_alloc_irqs(domain, irq, 1,
|
return __irq_domain_alloc_irqs(domain, irq, 1,
|
||||||
ioapic_alloc_attr_node(info),
|
ioapic_alloc_attr_node(info),
|
||||||
info, legacy);
|
info, legacy, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1014,7 +1014,8 @@ static int alloc_isa_irq_from_domain(struct irq_domain *domain,
|
|||||||
info->ioapic_pin))
|
info->ioapic_pin))
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
} else {
|
} else {
|
||||||
irq = __irq_domain_alloc_irqs(domain, irq, 1, node, info, true);
|
irq = __irq_domain_alloc_irqs(domain, irq, 1, node, info, true,
|
||||||
|
NULL);
|
||||||
if (irq >= 0) {
|
if (irq >= 0) {
|
||||||
irq_data = irq_domain_get_irq_data(domain, irq);
|
irq_data = irq_domain_get_irq_data(domain, irq);
|
||||||
data = irq_data->chip_data;
|
data = irq_data->chip_data;
|
||||||
|
|||||||
+1
-1
@@ -195,7 +195,7 @@ static int vmd_msi_init(struct irq_domain *domain, struct msi_domain_info *info,
|
|||||||
vmdirq->virq = virq;
|
vmdirq->virq = virq;
|
||||||
|
|
||||||
irq_domain_set_info(domain, virq, vmdirq->irq->vmd_vector, info->chip,
|
irq_domain_set_info(domain, virq, vmdirq->irq->vmd_vector, info->chip,
|
||||||
vmdirq, handle_simple_irq, vmd, NULL);
|
vmdirq, handle_untracked_irq, vmd, NULL);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,12 @@ config ARM_GIC
|
|||||||
select IRQ_DOMAIN_HIERARCHY
|
select IRQ_DOMAIN_HIERARCHY
|
||||||
select MULTI_IRQ_HANDLER
|
select MULTI_IRQ_HANDLER
|
||||||
|
|
||||||
|
config ARM_GIC_PM
|
||||||
|
bool
|
||||||
|
depends on PM
|
||||||
|
select ARM_GIC
|
||||||
|
select PM_CLK
|
||||||
|
|
||||||
config ARM_GIC_MAX_NR
|
config ARM_GIC_MAX_NR
|
||||||
int
|
int
|
||||||
default 2 if ARCH_REALVIEW
|
default 2 if ARCH_REALVIEW
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o
|
|||||||
obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o
|
obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o
|
||||||
obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o
|
obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o
|
||||||
obj-$(CONFIG_ARM_GIC) += irq-gic.o irq-gic-common.o
|
obj-$(CONFIG_ARM_GIC) += irq-gic.o irq-gic-common.o
|
||||||
|
obj-$(CONFIG_ARM_GIC_PM) += irq-gic-pm.o
|
||||||
obj-$(CONFIG_REALVIEW_DT) += irq-gic-realview.o
|
obj-$(CONFIG_REALVIEW_DT) += irq-gic-realview.o
|
||||||
obj-$(CONFIG_ARM_GIC_V2M) += irq-gic-v2m.o
|
obj-$(CONFIG_ARM_GIC_V2M) += irq-gic-v2m.o
|
||||||
obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-common.o
|
obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-common.o
|
||||||
@@ -69,3 +70,4 @@ obj-$(CONFIG_PIC32_EVIC) += irq-pic32-evic.o
|
|||||||
obj-$(CONFIG_MVEBU_ODMI) += irq-mvebu-odmi.o
|
obj-$(CONFIG_MVEBU_ODMI) += irq-mvebu-odmi.o
|
||||||
obj-$(CONFIG_LS_SCFG_MSI) += irq-ls-scfg-msi.o
|
obj-$(CONFIG_LS_SCFG_MSI) += irq-ls-scfg-msi.o
|
||||||
obj-$(CONFIG_EZNPS_GIC) += irq-eznps.o
|
obj-$(CONFIG_EZNPS_GIC) += irq-eznps.o
|
||||||
|
obj-$(CONFIG_ARCH_ASPEED) += irq-aspeed-vic.o
|
||||||
|
|||||||
@@ -55,14 +55,14 @@ static void combiner_mask_irq(struct irq_data *data)
|
|||||||
{
|
{
|
||||||
u32 mask = 1 << (data->hwirq % 32);
|
u32 mask = 1 << (data->hwirq % 32);
|
||||||
|
|
||||||
__raw_writel(mask, combiner_base(data) + COMBINER_ENABLE_CLEAR);
|
writel_relaxed(mask, combiner_base(data) + COMBINER_ENABLE_CLEAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void combiner_unmask_irq(struct irq_data *data)
|
static void combiner_unmask_irq(struct irq_data *data)
|
||||||
{
|
{
|
||||||
u32 mask = 1 << (data->hwirq % 32);
|
u32 mask = 1 << (data->hwirq % 32);
|
||||||
|
|
||||||
__raw_writel(mask, combiner_base(data) + COMBINER_ENABLE_SET);
|
writel_relaxed(mask, combiner_base(data) + COMBINER_ENABLE_SET);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void combiner_handle_cascade_irq(struct irq_desc *desc)
|
static void combiner_handle_cascade_irq(struct irq_desc *desc)
|
||||||
@@ -75,7 +75,7 @@ static void combiner_handle_cascade_irq(struct irq_desc *desc)
|
|||||||
chained_irq_enter(chip, desc);
|
chained_irq_enter(chip, desc);
|
||||||
|
|
||||||
spin_lock(&irq_controller_lock);
|
spin_lock(&irq_controller_lock);
|
||||||
status = __raw_readl(chip_data->base + COMBINER_INT_STATUS);
|
status = readl_relaxed(chip_data->base + COMBINER_INT_STATUS);
|
||||||
spin_unlock(&irq_controller_lock);
|
spin_unlock(&irq_controller_lock);
|
||||||
status &= chip_data->irq_mask;
|
status &= chip_data->irq_mask;
|
||||||
|
|
||||||
@@ -135,7 +135,7 @@ static void __init combiner_init_one(struct combiner_chip_data *combiner_data,
|
|||||||
combiner_data->parent_irq = irq;
|
combiner_data->parent_irq = irq;
|
||||||
|
|
||||||
/* Disable all interrupts */
|
/* Disable all interrupts */
|
||||||
__raw_writel(combiner_data->irq_mask, base + COMBINER_ENABLE_CLEAR);
|
writel_relaxed(combiner_data->irq_mask, base + COMBINER_ENABLE_CLEAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int combiner_irq_domain_xlate(struct irq_domain *d,
|
static int combiner_irq_domain_xlate(struct irq_domain *d,
|
||||||
@@ -218,7 +218,7 @@ static int combiner_suspend(void)
|
|||||||
|
|
||||||
for (i = 0; i < max_nr; i++)
|
for (i = 0; i < max_nr; i++)
|
||||||
combiner_data[i].pm_save =
|
combiner_data[i].pm_save =
|
||||||
__raw_readl(combiner_data[i].base + COMBINER_ENABLE_SET);
|
readl_relaxed(combiner_data[i].base + COMBINER_ENABLE_SET);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -235,9 +235,9 @@ static void combiner_resume(void)
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < max_nr; i++) {
|
for (i = 0; i < max_nr; i++) {
|
||||||
__raw_writel(combiner_data[i].irq_mask,
|
writel_relaxed(combiner_data[i].irq_mask,
|
||||||
combiner_data[i].base + COMBINER_ENABLE_CLEAR);
|
combiner_data[i].base + COMBINER_ENABLE_CLEAR);
|
||||||
__raw_writel(combiner_data[i].pm_save,
|
writel_relaxed(combiner_data[i].pm_save,
|
||||||
combiner_data[i].base + COMBINER_ENABLE_SET);
|
combiner_data[i].base + COMBINER_ENABLE_SET);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -541,7 +541,7 @@ static void armada_370_xp_mpic_resume(void)
|
|||||||
writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
|
writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct syscore_ops armada_370_xp_mpic_syscore_ops = {
|
static struct syscore_ops armada_370_xp_mpic_syscore_ops = {
|
||||||
.suspend = armada_370_xp_mpic_suspend,
|
.suspend = armada_370_xp_mpic_suspend,
|
||||||
.resume = armada_370_xp_mpic_resume,
|
.resume = armada_370_xp_mpic_resume,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,230 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 - Ben Herrenschmidt, IBM Corp.
|
||||||
|
*
|
||||||
|
* Driver for Aspeed "new" VIC as found in SoC generation 3 and later
|
||||||
|
*
|
||||||
|
* Based on irq-vic.c:
|
||||||
|
*
|
||||||
|
* Copyright (C) 1999 - 2003 ARM Limited
|
||||||
|
* Copyright (C) 2000 Deep Blue Solutions Ltd
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/export.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/irq.h>
|
||||||
|
#include <linux/irqchip.h>
|
||||||
|
#include <linux/irqchip/chained_irq.h>
|
||||||
|
#include <linux/irqdomain.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_address.h>
|
||||||
|
#include <linux/of_irq.h>
|
||||||
|
#include <linux/syscore_ops.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
#include <asm/exception.h>
|
||||||
|
#include <asm/irq.h>
|
||||||
|
|
||||||
|
/* These definitions correspond to the "new mapping" of the
|
||||||
|
* register set that interleaves "high" and "low". The offsets
|
||||||
|
* below are for the "low" register, add 4 to get to the high one
|
||||||
|
*/
|
||||||
|
#define AVIC_IRQ_STATUS 0x00
|
||||||
|
#define AVIC_FIQ_STATUS 0x08
|
||||||
|
#define AVIC_RAW_STATUS 0x10
|
||||||
|
#define AVIC_INT_SELECT 0x18
|
||||||
|
#define AVIC_INT_ENABLE 0x20
|
||||||
|
#define AVIC_INT_ENABLE_CLR 0x28
|
||||||
|
#define AVIC_INT_TRIGGER 0x30
|
||||||
|
#define AVIC_INT_TRIGGER_CLR 0x38
|
||||||
|
#define AVIC_INT_SENSE 0x40
|
||||||
|
#define AVIC_INT_DUAL_EDGE 0x48
|
||||||
|
#define AVIC_INT_EVENT 0x50
|
||||||
|
#define AVIC_EDGE_CLR 0x58
|
||||||
|
#define AVIC_EDGE_STATUS 0x60
|
||||||
|
|
||||||
|
#define NUM_IRQS 64
|
||||||
|
|
||||||
|
struct aspeed_vic {
|
||||||
|
void __iomem *base;
|
||||||
|
u32 edge_sources[2];
|
||||||
|
struct irq_domain *dom;
|
||||||
|
};
|
||||||
|
static struct aspeed_vic *system_avic;
|
||||||
|
|
||||||
|
static void vic_init_hw(struct aspeed_vic *vic)
|
||||||
|
{
|
||||||
|
u32 sense;
|
||||||
|
|
||||||
|
/* Disable all interrupts */
|
||||||
|
writel(0xffffffff, vic->base + AVIC_INT_ENABLE_CLR);
|
||||||
|
writel(0xffffffff, vic->base + AVIC_INT_ENABLE_CLR + 4);
|
||||||
|
|
||||||
|
/* Make sure no soft trigger is on */
|
||||||
|
writel(0xffffffff, vic->base + AVIC_INT_TRIGGER_CLR);
|
||||||
|
writel(0xffffffff, vic->base + AVIC_INT_TRIGGER_CLR + 4);
|
||||||
|
|
||||||
|
/* Set everything to be IRQ */
|
||||||
|
writel(0, vic->base + AVIC_INT_SELECT);
|
||||||
|
writel(0, vic->base + AVIC_INT_SELECT + 4);
|
||||||
|
|
||||||
|
/* Some interrupts have a programable high/low level trigger
|
||||||
|
* (4 GPIO direct inputs), for now we assume this was configured
|
||||||
|
* by firmware. We read which ones are edge now.
|
||||||
|
*/
|
||||||
|
sense = readl(vic->base + AVIC_INT_SENSE);
|
||||||
|
vic->edge_sources[0] = ~sense;
|
||||||
|
sense = readl(vic->base + AVIC_INT_SENSE + 4);
|
||||||
|
vic->edge_sources[1] = ~sense;
|
||||||
|
|
||||||
|
/* Clear edge detection latches */
|
||||||
|
writel(0xffffffff, vic->base + AVIC_EDGE_CLR);
|
||||||
|
writel(0xffffffff, vic->base + AVIC_EDGE_CLR + 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exception_irq_entry avic_handle_irq(struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
struct aspeed_vic *vic = system_avic;
|
||||||
|
u32 stat, irq;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
irq = 0;
|
||||||
|
stat = readl_relaxed(vic->base + AVIC_IRQ_STATUS);
|
||||||
|
if (!stat) {
|
||||||
|
stat = readl_relaxed(vic->base + AVIC_IRQ_STATUS + 4);
|
||||||
|
irq = 32;
|
||||||
|
}
|
||||||
|
if (stat == 0)
|
||||||
|
break;
|
||||||
|
irq += ffs(stat) - 1;
|
||||||
|
handle_domain_irq(vic->dom, irq, regs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void avic_ack_irq(struct irq_data *d)
|
||||||
|
{
|
||||||
|
struct aspeed_vic *vic = irq_data_get_irq_chip_data(d);
|
||||||
|
unsigned int sidx = d->hwirq >> 5;
|
||||||
|
unsigned int sbit = 1u << (d->hwirq & 0x1f);
|
||||||
|
|
||||||
|
/* Clear edge latch for edge interrupts, nop for level */
|
||||||
|
if (vic->edge_sources[sidx] & sbit)
|
||||||
|
writel(sbit, vic->base + AVIC_EDGE_CLR + sidx * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void avic_mask_irq(struct irq_data *d)
|
||||||
|
{
|
||||||
|
struct aspeed_vic *vic = irq_data_get_irq_chip_data(d);
|
||||||
|
unsigned int sidx = d->hwirq >> 5;
|
||||||
|
unsigned int sbit = 1u << (d->hwirq & 0x1f);
|
||||||
|
|
||||||
|
writel(sbit, vic->base + AVIC_INT_ENABLE_CLR + sidx * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void avic_unmask_irq(struct irq_data *d)
|
||||||
|
{
|
||||||
|
struct aspeed_vic *vic = irq_data_get_irq_chip_data(d);
|
||||||
|
unsigned int sidx = d->hwirq >> 5;
|
||||||
|
unsigned int sbit = 1u << (d->hwirq & 0x1f);
|
||||||
|
|
||||||
|
writel(sbit, vic->base + AVIC_INT_ENABLE + sidx * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For level irq, faster than going through a nop "ack" and mask */
|
||||||
|
static void avic_mask_ack_irq(struct irq_data *d)
|
||||||
|
{
|
||||||
|
struct aspeed_vic *vic = irq_data_get_irq_chip_data(d);
|
||||||
|
unsigned int sidx = d->hwirq >> 5;
|
||||||
|
unsigned int sbit = 1u << (d->hwirq & 0x1f);
|
||||||
|
|
||||||
|
/* First mask */
|
||||||
|
writel(sbit, vic->base + AVIC_INT_ENABLE_CLR + sidx * 4);
|
||||||
|
|
||||||
|
/* Then clear edge latch for edge interrupts */
|
||||||
|
if (vic->edge_sources[sidx] & sbit)
|
||||||
|
writel(sbit, vic->base + AVIC_EDGE_CLR + sidx * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct irq_chip avic_chip = {
|
||||||
|
.name = "AVIC",
|
||||||
|
.irq_ack = avic_ack_irq,
|
||||||
|
.irq_mask = avic_mask_irq,
|
||||||
|
.irq_unmask = avic_unmask_irq,
|
||||||
|
.irq_mask_ack = avic_mask_ack_irq,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int avic_map(struct irq_domain *d, unsigned int irq,
|
||||||
|
irq_hw_number_t hwirq)
|
||||||
|
{
|
||||||
|
struct aspeed_vic *vic = d->host_data;
|
||||||
|
unsigned int sidx = hwirq >> 5;
|
||||||
|
unsigned int sbit = 1u << (hwirq & 0x1f);
|
||||||
|
|
||||||
|
/* Check if interrupt exists */
|
||||||
|
if (sidx > 1)
|
||||||
|
return -EPERM;
|
||||||
|
|
||||||
|
if (vic->edge_sources[sidx] & sbit)
|
||||||
|
irq_set_chip_and_handler(irq, &avic_chip, handle_edge_irq);
|
||||||
|
else
|
||||||
|
irq_set_chip_and_handler(irq, &avic_chip, handle_level_irq);
|
||||||
|
irq_set_chip_data(irq, vic);
|
||||||
|
irq_set_probe(irq);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct irq_domain_ops avic_dom_ops = {
|
||||||
|
.map = avic_map,
|
||||||
|
.xlate = irq_domain_xlate_onetwocell,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init avic_of_init(struct device_node *node,
|
||||||
|
struct device_node *parent)
|
||||||
|
{
|
||||||
|
void __iomem *regs;
|
||||||
|
struct aspeed_vic *vic;
|
||||||
|
|
||||||
|
if (WARN(parent, "non-root Aspeed VIC not supported"))
|
||||||
|
return -EINVAL;
|
||||||
|
if (WARN(system_avic, "duplicate Aspeed VIC not supported"))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
regs = of_iomap(node, 0);
|
||||||
|
if (WARN_ON(!regs))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
vic = kzalloc(sizeof(struct aspeed_vic), GFP_KERNEL);
|
||||||
|
if (WARN_ON(!vic)) {
|
||||||
|
iounmap(regs);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
vic->base = regs;
|
||||||
|
|
||||||
|
/* Initialize soures, all masked */
|
||||||
|
vic_init_hw(vic);
|
||||||
|
|
||||||
|
/* Ready to receive interrupts */
|
||||||
|
system_avic = vic;
|
||||||
|
set_handle_irq(avic_handle_irq);
|
||||||
|
|
||||||
|
/* Register our domain */
|
||||||
|
vic->dom = irq_domain_add_simple(node, NUM_IRQS, 0,
|
||||||
|
&avic_dom_ops, vic);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
IRQCHIP_DECLARE(aspeed_new_vic, "aspeed,ast2400-vic", avic_of_init);
|
||||||
@@ -52,7 +52,6 @@
|
|||||||
#include <linux/irqdomain.h>
|
#include <linux/irqdomain.h>
|
||||||
|
|
||||||
#include <asm/exception.h>
|
#include <asm/exception.h>
|
||||||
#include <asm/mach/irq.h>
|
|
||||||
|
|
||||||
/* Put the bank and irq (32 bits) into the hwirq */
|
/* Put the bank and irq (32 bits) into the hwirq */
|
||||||
#define MAKE_HWIRQ(b, n) ((b << 5) | (n))
|
#define MAKE_HWIRQ(b, n) ((b << 5) | (n))
|
||||||
@@ -242,7 +241,7 @@ static void __exception_irq_entry bcm2835_handle_irq(
|
|||||||
u32 hwirq;
|
u32 hwirq;
|
||||||
|
|
||||||
while ((hwirq = get_next_armctrl_hwirq()) != ~0)
|
while ((hwirq = get_next_armctrl_hwirq()) != ~0)
|
||||||
handle_IRQ(irq_linear_revmap(intc.domain, hwirq), regs);
|
handle_domain_irq(intc.domain, hwirq, regs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bcm2836_chained_handle_irq(struct irq_desc *desc)
|
static void bcm2836_chained_handle_irq(struct irq_desc *desc)
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ __exception_irq_entry bcm2836_arm_irqchip_handle_irq(struct pt_regs *regs)
|
|||||||
} else if (stat) {
|
} else if (stat) {
|
||||||
u32 hwirq = ffs(stat) - 1;
|
u32 hwirq = ffs(stat) - 1;
|
||||||
|
|
||||||
handle_IRQ(irq_linear_revmap(intc.domain, hwirq), regs);
|
handle_domain_irq(intc.domain, hwirq, regs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,8 +224,8 @@ static struct notifier_block bcm2836_arm_irqchip_cpu_notifier = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_ARM
|
#ifdef CONFIG_ARM
|
||||||
int __init bcm2836_smp_boot_secondary(unsigned int cpu,
|
static int __init bcm2836_smp_boot_secondary(unsigned int cpu,
|
||||||
struct task_struct *idle)
|
struct task_struct *idle)
|
||||||
{
|
{
|
||||||
unsigned long secondary_startup_phys =
|
unsigned long secondary_startup_phys =
|
||||||
(unsigned long)virt_to_phys((void *)secondary_startup);
|
(unsigned long)virt_to_phys((void *)secondary_startup);
|
||||||
|
|||||||
@@ -215,7 +215,7 @@ static int __init bcm7120_l2_intc_iomap_3380(struct device_node *dn,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int __init bcm7120_l2_intc_probe(struct device_node *dn,
|
static int __init bcm7120_l2_intc_probe(struct device_node *dn,
|
||||||
struct device_node *parent,
|
struct device_node *parent,
|
||||||
int (*iomap_regs_fn)(struct device_node *,
|
int (*iomap_regs_fn)(struct device_node *,
|
||||||
struct bcm7120_l2_intc_data *),
|
struct bcm7120_l2_intc_data *),
|
||||||
@@ -339,15 +339,15 @@ out_unmap:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int __init bcm7120_l2_intc_probe_7120(struct device_node *dn,
|
static int __init bcm7120_l2_intc_probe_7120(struct device_node *dn,
|
||||||
struct device_node *parent)
|
struct device_node *parent)
|
||||||
{
|
{
|
||||||
return bcm7120_l2_intc_probe(dn, parent, bcm7120_l2_intc_iomap_7120,
|
return bcm7120_l2_intc_probe(dn, parent, bcm7120_l2_intc_iomap_7120,
|
||||||
"BCM7120 L2");
|
"BCM7120 L2");
|
||||||
}
|
}
|
||||||
|
|
||||||
int __init bcm7120_l2_intc_probe_3380(struct device_node *dn,
|
static int __init bcm7120_l2_intc_probe_3380(struct device_node *dn,
|
||||||
struct device_node *parent)
|
struct device_node *parent)
|
||||||
{
|
{
|
||||||
return bcm7120_l2_intc_probe(dn, parent, bcm7120_l2_intc_iomap_3380,
|
return bcm7120_l2_intc_probe(dn, parent, bcm7120_l2_intc_iomap_3380,
|
||||||
"BCM3380 L2");
|
"BCM3380 L2");
|
||||||
|
|||||||
@@ -112,8 +112,8 @@ static void brcmstb_l2_intc_resume(struct irq_data *d)
|
|||||||
irq_gc_unlock(gc);
|
irq_gc_unlock(gc);
|
||||||
}
|
}
|
||||||
|
|
||||||
int __init brcmstb_l2_intc_of_init(struct device_node *np,
|
static int __init brcmstb_l2_intc_of_init(struct device_node *np,
|
||||||
struct device_node *parent)
|
struct device_node *parent)
|
||||||
{
|
{
|
||||||
unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
|
unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
|
||||||
struct brcmstb_l2_intc_data *data;
|
struct brcmstb_l2_intc_data *data;
|
||||||
|
|||||||
@@ -90,8 +90,8 @@ int gic_configure_irq(unsigned int irq, unsigned int type,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void __init gic_dist_config(void __iomem *base, int gic_irqs,
|
void gic_dist_config(void __iomem *base, int gic_irqs,
|
||||||
void (*sync_access)(void))
|
void (*sync_access)(void))
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,184 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 NVIDIA CORPORATION, All Rights Reserved.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/of_irq.h>
|
||||||
|
#include <linux/irqchip/arm-gic.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pm_clock.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
struct gic_clk_data {
|
||||||
|
unsigned int num_clocks;
|
||||||
|
const char *const *clocks;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int gic_runtime_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct gic_chip_data *gic = dev_get_drvdata(dev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = pm_clk_resume(dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* On the very first resume, the pointer to the driver data
|
||||||
|
* will be NULL and this is intentional, because we do not
|
||||||
|
* want to restore the GIC on the very first resume. So if
|
||||||
|
* the pointer is not valid just return.
|
||||||
|
*/
|
||||||
|
if (!gic)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
gic_dist_restore(gic);
|
||||||
|
gic_cpu_restore(gic);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gic_runtime_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct gic_chip_data *gic = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
gic_dist_save(gic);
|
||||||
|
gic_cpu_save(gic);
|
||||||
|
|
||||||
|
return pm_clk_suspend(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gic_get_clocks(struct device *dev, const struct gic_clk_data *data)
|
||||||
|
{
|
||||||
|
struct clk *clk;
|
||||||
|
unsigned int i;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!dev || !data)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
ret = pm_clk_create(dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
for (i = 0; i < data->num_clocks; i++) {
|
||||||
|
clk = of_clk_get_by_name(dev->of_node, data->clocks[i]);
|
||||||
|
if (IS_ERR(clk)) {
|
||||||
|
dev_err(dev, "failed to get clock %s\n",
|
||||||
|
data->clocks[i]);
|
||||||
|
ret = PTR_ERR(clk);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = pm_clk_add_clk(dev, clk);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "failed to add clock at index %d\n", i);
|
||||||
|
clk_put(clk);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
pm_clk_destroy(dev);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gic_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
const struct gic_clk_data *data;
|
||||||
|
struct gic_chip_data *gic;
|
||||||
|
int ret, irq;
|
||||||
|
|
||||||
|
data = of_device_get_match_data(&pdev->dev);
|
||||||
|
if (!data) {
|
||||||
|
dev_err(&pdev->dev, "no device match found\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
irq = irq_of_parse_and_map(dev->of_node, 0);
|
||||||
|
if (!irq) {
|
||||||
|
dev_err(dev, "no parent interrupt found!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = gic_get_clocks(dev, data);
|
||||||
|
if (ret)
|
||||||
|
goto irq_dispose;
|
||||||
|
|
||||||
|
pm_runtime_enable(dev);
|
||||||
|
|
||||||
|
ret = pm_runtime_get_sync(dev);
|
||||||
|
if (ret < 0)
|
||||||
|
goto rpm_disable;
|
||||||
|
|
||||||
|
ret = gic_of_init_child(dev, &gic, irq);
|
||||||
|
if (ret)
|
||||||
|
goto rpm_put;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, gic);
|
||||||
|
|
||||||
|
pm_runtime_put(dev);
|
||||||
|
|
||||||
|
dev_info(dev, "GIC IRQ controller registered\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
rpm_put:
|
||||||
|
pm_runtime_put_sync(dev);
|
||||||
|
rpm_disable:
|
||||||
|
pm_runtime_disable(dev);
|
||||||
|
pm_clk_destroy(dev);
|
||||||
|
irq_dispose:
|
||||||
|
irq_dispose_mapping(irq);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct dev_pm_ops gic_pm_ops = {
|
||||||
|
SET_RUNTIME_PM_OPS(gic_runtime_suspend,
|
||||||
|
gic_runtime_resume, NULL)
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char * const gic400_clocks[] = {
|
||||||
|
"clk",
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct gic_clk_data gic400_data = {
|
||||||
|
.num_clocks = ARRAY_SIZE(gic400_clocks),
|
||||||
|
.clocks = gic400_clocks,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct of_device_id gic_match[] = {
|
||||||
|
{ .compatible = "nvidia,tegra210-agic", .data = &gic400_data },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, gic_match);
|
||||||
|
|
||||||
|
static struct platform_driver gic_driver = {
|
||||||
|
.probe = gic_probe,
|
||||||
|
.driver = {
|
||||||
|
.name = "gic",
|
||||||
|
.of_match_table = gic_match,
|
||||||
|
.pm = &gic_pm_ops,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
builtin_platform_driver(gic_driver);
|
||||||
@@ -24,6 +24,7 @@
|
|||||||
#include <linux/of_pci.h>
|
#include <linux/of_pci.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
|
#include <linux/irqchip/arm-gic.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MSI_TYPER:
|
* MSI_TYPER:
|
||||||
|
|||||||
+255
-153
@@ -56,13 +56,14 @@ struct its_collection {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The ITS_BASER structure - contains memory information and cached
|
* The ITS_BASER structure - contains memory information, cached
|
||||||
* value of BASER register configuration.
|
* value of BASER register configuration and ITS page size.
|
||||||
*/
|
*/
|
||||||
struct its_baser {
|
struct its_baser {
|
||||||
void *base;
|
void *base;
|
||||||
u64 val;
|
u64 val;
|
||||||
u32 order;
|
u32 order;
|
||||||
|
u32 psz;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -824,6 +825,182 @@ static const char *its_base_type_string[] = {
|
|||||||
[GITS_BASER_TYPE_RESERVED7] = "Reserved (7)",
|
[GITS_BASER_TYPE_RESERVED7] = "Reserved (7)",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static u64 its_read_baser(struct its_node *its, struct its_baser *baser)
|
||||||
|
{
|
||||||
|
u32 idx = baser - its->tables;
|
||||||
|
|
||||||
|
return readq_relaxed(its->base + GITS_BASER + (idx << 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void its_write_baser(struct its_node *its, struct its_baser *baser,
|
||||||
|
u64 val)
|
||||||
|
{
|
||||||
|
u32 idx = baser - its->tables;
|
||||||
|
|
||||||
|
writeq_relaxed(val, its->base + GITS_BASER + (idx << 3));
|
||||||
|
baser->val = its_read_baser(its, baser);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int its_setup_baser(struct its_node *its, struct its_baser *baser,
|
||||||
|
u64 cache, u64 shr, u32 psz, u32 order,
|
||||||
|
bool indirect)
|
||||||
|
{
|
||||||
|
u64 val = its_read_baser(its, baser);
|
||||||
|
u64 esz = GITS_BASER_ENTRY_SIZE(val);
|
||||||
|
u64 type = GITS_BASER_TYPE(val);
|
||||||
|
u32 alloc_pages;
|
||||||
|
void *base;
|
||||||
|
u64 tmp;
|
||||||
|
|
||||||
|
retry_alloc_baser:
|
||||||
|
alloc_pages = (PAGE_ORDER_TO_SIZE(order) / psz);
|
||||||
|
if (alloc_pages > GITS_BASER_PAGES_MAX) {
|
||||||
|
pr_warn("ITS@%pa: %s too large, reduce ITS pages %u->%u\n",
|
||||||
|
&its->phys_base, its_base_type_string[type],
|
||||||
|
alloc_pages, GITS_BASER_PAGES_MAX);
|
||||||
|
alloc_pages = GITS_BASER_PAGES_MAX;
|
||||||
|
order = get_order(GITS_BASER_PAGES_MAX * psz);
|
||||||
|
}
|
||||||
|
|
||||||
|
base = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, order);
|
||||||
|
if (!base)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
retry_baser:
|
||||||
|
val = (virt_to_phys(base) |
|
||||||
|
(type << GITS_BASER_TYPE_SHIFT) |
|
||||||
|
((esz - 1) << GITS_BASER_ENTRY_SIZE_SHIFT) |
|
||||||
|
((alloc_pages - 1) << GITS_BASER_PAGES_SHIFT) |
|
||||||
|
cache |
|
||||||
|
shr |
|
||||||
|
GITS_BASER_VALID);
|
||||||
|
|
||||||
|
val |= indirect ? GITS_BASER_INDIRECT : 0x0;
|
||||||
|
|
||||||
|
switch (psz) {
|
||||||
|
case SZ_4K:
|
||||||
|
val |= GITS_BASER_PAGE_SIZE_4K;
|
||||||
|
break;
|
||||||
|
case SZ_16K:
|
||||||
|
val |= GITS_BASER_PAGE_SIZE_16K;
|
||||||
|
break;
|
||||||
|
case SZ_64K:
|
||||||
|
val |= GITS_BASER_PAGE_SIZE_64K;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
its_write_baser(its, baser, val);
|
||||||
|
tmp = baser->val;
|
||||||
|
|
||||||
|
if ((val ^ tmp) & GITS_BASER_SHAREABILITY_MASK) {
|
||||||
|
/*
|
||||||
|
* Shareability didn't stick. Just use
|
||||||
|
* whatever the read reported, which is likely
|
||||||
|
* to be the only thing this redistributor
|
||||||
|
* supports. If that's zero, make it
|
||||||
|
* non-cacheable as well.
|
||||||
|
*/
|
||||||
|
shr = tmp & GITS_BASER_SHAREABILITY_MASK;
|
||||||
|
if (!shr) {
|
||||||
|
cache = GITS_BASER_nC;
|
||||||
|
__flush_dcache_area(base, PAGE_ORDER_TO_SIZE(order));
|
||||||
|
}
|
||||||
|
goto retry_baser;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((val ^ tmp) & GITS_BASER_PAGE_SIZE_MASK) {
|
||||||
|
/*
|
||||||
|
* Page size didn't stick. Let's try a smaller
|
||||||
|
* size and retry. If we reach 4K, then
|
||||||
|
* something is horribly wrong...
|
||||||
|
*/
|
||||||
|
free_pages((unsigned long)base, order);
|
||||||
|
baser->base = NULL;
|
||||||
|
|
||||||
|
switch (psz) {
|
||||||
|
case SZ_16K:
|
||||||
|
psz = SZ_4K;
|
||||||
|
goto retry_alloc_baser;
|
||||||
|
case SZ_64K:
|
||||||
|
psz = SZ_16K;
|
||||||
|
goto retry_alloc_baser;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (val != tmp) {
|
||||||
|
pr_err("ITS@%pa: %s doesn't stick: %lx %lx\n",
|
||||||
|
&its->phys_base, its_base_type_string[type],
|
||||||
|
(unsigned long) val, (unsigned long) tmp);
|
||||||
|
free_pages((unsigned long)base, order);
|
||||||
|
return -ENXIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
baser->order = order;
|
||||||
|
baser->base = base;
|
||||||
|
baser->psz = psz;
|
||||||
|
tmp = indirect ? GITS_LVL1_ENTRY_SIZE : esz;
|
||||||
|
|
||||||
|
pr_info("ITS@%pa: allocated %d %s @%lx (%s, esz %d, psz %dK, shr %d)\n",
|
||||||
|
&its->phys_base, (int)(PAGE_ORDER_TO_SIZE(order) / tmp),
|
||||||
|
its_base_type_string[type],
|
||||||
|
(unsigned long)virt_to_phys(base),
|
||||||
|
indirect ? "indirect" : "flat", (int)esz,
|
||||||
|
psz / SZ_1K, (int)shr >> GITS_BASER_SHAREABILITY_SHIFT);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool its_parse_baser_device(struct its_node *its, struct its_baser *baser,
|
||||||
|
u32 psz, u32 *order)
|
||||||
|
{
|
||||||
|
u64 esz = GITS_BASER_ENTRY_SIZE(its_read_baser(its, baser));
|
||||||
|
u64 val = GITS_BASER_InnerShareable | GITS_BASER_WaWb;
|
||||||
|
u32 ids = its->device_ids;
|
||||||
|
u32 new_order = *order;
|
||||||
|
bool indirect = false;
|
||||||
|
|
||||||
|
/* No need to enable Indirection if memory requirement < (psz*2)bytes */
|
||||||
|
if ((esz << ids) > (psz * 2)) {
|
||||||
|
/*
|
||||||
|
* Find out whether hw supports a single or two-level table by
|
||||||
|
* table by reading bit at offset '62' after writing '1' to it.
|
||||||
|
*/
|
||||||
|
its_write_baser(its, baser, val | GITS_BASER_INDIRECT);
|
||||||
|
indirect = !!(baser->val & GITS_BASER_INDIRECT);
|
||||||
|
|
||||||
|
if (indirect) {
|
||||||
|
/*
|
||||||
|
* The size of the lvl2 table is equal to ITS page size
|
||||||
|
* which is 'psz'. For computing lvl1 table size,
|
||||||
|
* subtract ID bits that sparse lvl2 table from 'ids'
|
||||||
|
* which is reported by ITS hardware times lvl1 table
|
||||||
|
* entry size.
|
||||||
|
*/
|
||||||
|
ids -= ilog2(psz / esz);
|
||||||
|
esz = GITS_LVL1_ENTRY_SIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate as many entries as required to fit the
|
||||||
|
* range of device IDs that the ITS can grok... The ID
|
||||||
|
* space being incredibly sparse, this results in a
|
||||||
|
* massive waste of memory if two-level device table
|
||||||
|
* feature is not supported by hardware.
|
||||||
|
*/
|
||||||
|
new_order = max_t(u32, get_order(esz << ids), new_order);
|
||||||
|
if (new_order >= MAX_ORDER) {
|
||||||
|
new_order = MAX_ORDER - 1;
|
||||||
|
ids = ilog2(PAGE_ORDER_TO_SIZE(new_order) / esz);
|
||||||
|
pr_warn("ITS@%pa: Device Table too large, reduce ids %u->%u\n",
|
||||||
|
&its->phys_base, its->device_ids, ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
*order = new_order;
|
||||||
|
|
||||||
|
return indirect;
|
||||||
|
}
|
||||||
|
|
||||||
static void its_free_tables(struct its_node *its)
|
static void its_free_tables(struct its_node *its)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@@ -837,167 +1014,52 @@ static void its_free_tables(struct its_node *its)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int its_alloc_tables(const char *node_name, struct its_node *its)
|
static int its_alloc_tables(struct its_node *its)
|
||||||
{
|
{
|
||||||
int err;
|
u64 typer = readq_relaxed(its->base + GITS_TYPER);
|
||||||
int i;
|
u32 ids = GITS_TYPER_DEVBITS(typer);
|
||||||
int psz = SZ_64K;
|
|
||||||
u64 shr = GITS_BASER_InnerShareable;
|
u64 shr = GITS_BASER_InnerShareable;
|
||||||
u64 cache;
|
u64 cache = GITS_BASER_WaWb;
|
||||||
u64 typer;
|
u32 psz = SZ_64K;
|
||||||
u32 ids;
|
int err, i;
|
||||||
|
|
||||||
if (its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_22375) {
|
if (its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_22375) {
|
||||||
/*
|
/*
|
||||||
* erratum 22375: only alloc 8MB table size
|
* erratum 22375: only alloc 8MB table size
|
||||||
* erratum 24313: ignore memory access type
|
* erratum 24313: ignore memory access type
|
||||||
*/
|
*/
|
||||||
cache = 0;
|
cache = GITS_BASER_nCnB;
|
||||||
ids = 0x14; /* 20 bits, 8MB */
|
ids = 0x14; /* 20 bits, 8MB */
|
||||||
} else {
|
|
||||||
cache = GITS_BASER_WaWb;
|
|
||||||
typer = readq_relaxed(its->base + GITS_TYPER);
|
|
||||||
ids = GITS_TYPER_DEVBITS(typer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
its->device_ids = ids;
|
its->device_ids = ids;
|
||||||
|
|
||||||
for (i = 0; i < GITS_BASER_NR_REGS; i++) {
|
for (i = 0; i < GITS_BASER_NR_REGS; i++) {
|
||||||
u64 val = readq_relaxed(its->base + GITS_BASER + i * 8);
|
struct its_baser *baser = its->tables + i;
|
||||||
|
u64 val = its_read_baser(its, baser);
|
||||||
u64 type = GITS_BASER_TYPE(val);
|
u64 type = GITS_BASER_TYPE(val);
|
||||||
u64 entry_size = GITS_BASER_ENTRY_SIZE(val);
|
u32 order = get_order(psz);
|
||||||
int order = get_order(psz);
|
bool indirect = false;
|
||||||
int alloc_pages;
|
|
||||||
u64 tmp;
|
|
||||||
void *base;
|
|
||||||
|
|
||||||
if (type == GITS_BASER_TYPE_NONE)
|
if (type == GITS_BASER_TYPE_NONE)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/*
|
if (type == GITS_BASER_TYPE_DEVICE)
|
||||||
* Allocate as many entries as required to fit the
|
indirect = its_parse_baser_device(its, baser, psz, &order);
|
||||||
* range of device IDs that the ITS can grok... The ID
|
|
||||||
* space being incredibly sparse, this results in a
|
err = its_setup_baser(its, baser, cache, shr, psz, order, indirect);
|
||||||
* massive waste of memory.
|
if (err < 0) {
|
||||||
*
|
its_free_tables(its);
|
||||||
* For other tables, only allocate a single page.
|
return err;
|
||||||
*/
|
|
||||||
if (type == GITS_BASER_TYPE_DEVICE) {
|
|
||||||
/*
|
|
||||||
* 'order' was initialized earlier to the default page
|
|
||||||
* granule of the the ITS. We can't have an allocation
|
|
||||||
* smaller than that. If the requested allocation
|
|
||||||
* is smaller, round up to the default page granule.
|
|
||||||
*/
|
|
||||||
order = max(get_order((1UL << ids) * entry_size),
|
|
||||||
order);
|
|
||||||
if (order >= MAX_ORDER) {
|
|
||||||
order = MAX_ORDER - 1;
|
|
||||||
pr_warn("%s: Device Table too large, reduce its page order to %u\n",
|
|
||||||
node_name, order);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
retry_alloc_baser:
|
/* Update settings which will be used for next BASERn */
|
||||||
alloc_pages = (PAGE_ORDER_TO_SIZE(order) / psz);
|
psz = baser->psz;
|
||||||
if (alloc_pages > GITS_BASER_PAGES_MAX) {
|
cache = baser->val & GITS_BASER_CACHEABILITY_MASK;
|
||||||
alloc_pages = GITS_BASER_PAGES_MAX;
|
shr = baser->val & GITS_BASER_SHAREABILITY_MASK;
|
||||||
order = get_order(GITS_BASER_PAGES_MAX * psz);
|
|
||||||
pr_warn("%s: Device Table too large, reduce its page order to %u (%u pages)\n",
|
|
||||||
node_name, order, alloc_pages);
|
|
||||||
}
|
|
||||||
|
|
||||||
base = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, order);
|
|
||||||
if (!base) {
|
|
||||||
err = -ENOMEM;
|
|
||||||
goto out_free;
|
|
||||||
}
|
|
||||||
|
|
||||||
its->tables[i].base = base;
|
|
||||||
its->tables[i].order = order;
|
|
||||||
|
|
||||||
retry_baser:
|
|
||||||
val = (virt_to_phys(base) |
|
|
||||||
(type << GITS_BASER_TYPE_SHIFT) |
|
|
||||||
((entry_size - 1) << GITS_BASER_ENTRY_SIZE_SHIFT) |
|
|
||||||
cache |
|
|
||||||
shr |
|
|
||||||
GITS_BASER_VALID);
|
|
||||||
|
|
||||||
switch (psz) {
|
|
||||||
case SZ_4K:
|
|
||||||
val |= GITS_BASER_PAGE_SIZE_4K;
|
|
||||||
break;
|
|
||||||
case SZ_16K:
|
|
||||||
val |= GITS_BASER_PAGE_SIZE_16K;
|
|
||||||
break;
|
|
||||||
case SZ_64K:
|
|
||||||
val |= GITS_BASER_PAGE_SIZE_64K;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
val |= alloc_pages - 1;
|
|
||||||
its->tables[i].val = val;
|
|
||||||
|
|
||||||
writeq_relaxed(val, its->base + GITS_BASER + i * 8);
|
|
||||||
tmp = readq_relaxed(its->base + GITS_BASER + i * 8);
|
|
||||||
|
|
||||||
if ((val ^ tmp) & GITS_BASER_SHAREABILITY_MASK) {
|
|
||||||
/*
|
|
||||||
* Shareability didn't stick. Just use
|
|
||||||
* whatever the read reported, which is likely
|
|
||||||
* to be the only thing this redistributor
|
|
||||||
* supports. If that's zero, make it
|
|
||||||
* non-cacheable as well.
|
|
||||||
*/
|
|
||||||
shr = tmp & GITS_BASER_SHAREABILITY_MASK;
|
|
||||||
if (!shr) {
|
|
||||||
cache = GITS_BASER_nC;
|
|
||||||
__flush_dcache_area(base, PAGE_ORDER_TO_SIZE(order));
|
|
||||||
}
|
|
||||||
goto retry_baser;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((val ^ tmp) & GITS_BASER_PAGE_SIZE_MASK) {
|
|
||||||
/*
|
|
||||||
* Page size didn't stick. Let's try a smaller
|
|
||||||
* size and retry. If we reach 4K, then
|
|
||||||
* something is horribly wrong...
|
|
||||||
*/
|
|
||||||
free_pages((unsigned long)base, order);
|
|
||||||
its->tables[i].base = NULL;
|
|
||||||
|
|
||||||
switch (psz) {
|
|
||||||
case SZ_16K:
|
|
||||||
psz = SZ_4K;
|
|
||||||
goto retry_alloc_baser;
|
|
||||||
case SZ_64K:
|
|
||||||
psz = SZ_16K;
|
|
||||||
goto retry_alloc_baser;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (val != tmp) {
|
|
||||||
pr_err("ITS: %s: GITS_BASER%d doesn't stick: %lx %lx\n",
|
|
||||||
node_name, i,
|
|
||||||
(unsigned long) val, (unsigned long) tmp);
|
|
||||||
err = -ENXIO;
|
|
||||||
goto out_free;
|
|
||||||
}
|
|
||||||
|
|
||||||
pr_info("ITS: allocated %d %s @%lx (psz %dK, shr %d)\n",
|
|
||||||
(int)(PAGE_ORDER_TO_SIZE(order) / entry_size),
|
|
||||||
its_base_type_string[type],
|
|
||||||
(unsigned long)virt_to_phys(base),
|
|
||||||
psz / SZ_1K, (int)shr >> GITS_BASER_SHAREABILITY_SHIFT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_free:
|
|
||||||
its_free_tables(its);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int its_alloc_collections(struct its_node *its)
|
static int its_alloc_collections(struct its_node *its)
|
||||||
@@ -1185,10 +1247,57 @@ static struct its_baser *its_get_baser(struct its_node *its, u32 type)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool its_alloc_device_table(struct its_node *its, u32 dev_id)
|
||||||
|
{
|
||||||
|
struct its_baser *baser;
|
||||||
|
struct page *page;
|
||||||
|
u32 esz, idx;
|
||||||
|
__le64 *table;
|
||||||
|
|
||||||
|
baser = its_get_baser(its, GITS_BASER_TYPE_DEVICE);
|
||||||
|
|
||||||
|
/* Don't allow device id that exceeds ITS hardware limit */
|
||||||
|
if (!baser)
|
||||||
|
return (ilog2(dev_id) < its->device_ids);
|
||||||
|
|
||||||
|
/* Don't allow device id that exceeds single, flat table limit */
|
||||||
|
esz = GITS_BASER_ENTRY_SIZE(baser->val);
|
||||||
|
if (!(baser->val & GITS_BASER_INDIRECT))
|
||||||
|
return (dev_id < (PAGE_ORDER_TO_SIZE(baser->order) / esz));
|
||||||
|
|
||||||
|
/* Compute 1st level table index & check if that exceeds table limit */
|
||||||
|
idx = dev_id >> ilog2(baser->psz / esz);
|
||||||
|
if (idx >= (PAGE_ORDER_TO_SIZE(baser->order) / GITS_LVL1_ENTRY_SIZE))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
table = baser->base;
|
||||||
|
|
||||||
|
/* Allocate memory for 2nd level table */
|
||||||
|
if (!table[idx]) {
|
||||||
|
page = alloc_pages(GFP_KERNEL | __GFP_ZERO, get_order(baser->psz));
|
||||||
|
if (!page)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Flush Lvl2 table to PoC if hw doesn't support coherency */
|
||||||
|
if (!(baser->val & GITS_BASER_SHAREABILITY_MASK))
|
||||||
|
__flush_dcache_area(page_address(page), baser->psz);
|
||||||
|
|
||||||
|
table[idx] = cpu_to_le64(page_to_phys(page) | GITS_BASER_VALID);
|
||||||
|
|
||||||
|
/* Flush Lvl1 entry to PoC if hw doesn't support coherency */
|
||||||
|
if (!(baser->val & GITS_BASER_SHAREABILITY_MASK))
|
||||||
|
__flush_dcache_area(table + idx, GITS_LVL1_ENTRY_SIZE);
|
||||||
|
|
||||||
|
/* Ensure updated table contents are visible to ITS hardware */
|
||||||
|
dsb(sy);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static struct its_device *its_create_device(struct its_node *its, u32 dev_id,
|
static struct its_device *its_create_device(struct its_node *its, u32 dev_id,
|
||||||
int nvecs)
|
int nvecs)
|
||||||
{
|
{
|
||||||
struct its_baser *baser;
|
|
||||||
struct its_device *dev;
|
struct its_device *dev;
|
||||||
unsigned long *lpi_map;
|
unsigned long *lpi_map;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
@@ -1199,14 +1308,7 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id,
|
|||||||
int nr_ites;
|
int nr_ites;
|
||||||
int sz;
|
int sz;
|
||||||
|
|
||||||
baser = its_get_baser(its, GITS_BASER_TYPE_DEVICE);
|
if (!its_alloc_device_table(its, dev_id))
|
||||||
|
|
||||||
/* Don't allow 'dev_id' that exceeds single, flat table limit */
|
|
||||||
if (baser) {
|
|
||||||
if (dev_id >= (PAGE_ORDER_TO_SIZE(baser->order) /
|
|
||||||
GITS_BASER_ENTRY_SIZE(baser->val)))
|
|
||||||
return NULL;
|
|
||||||
} else if (ilog2(dev_id) >= its->device_ids)
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
||||||
@@ -1569,7 +1671,7 @@ static int __init its_probe(struct device_node *node,
|
|||||||
|
|
||||||
its_enable_quirks(its);
|
its_enable_quirks(its);
|
||||||
|
|
||||||
err = its_alloc_tables(node->full_name, its);
|
err = its_alloc_tables(its);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_free_cmd;
|
goto out_free_cmd;
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user