irqchip updates for 4.14
- irqchip-specific part of the monster GICv4 series - new UniPhier AIDET irqchip driver - new variants of some Freescale MSI widget - blanket removal of of_node->full_name in printk - random collection of fixes -----BEGIN PGP SIGNATURE----- iQJJBAABCAAzFiEEn9UcU+C1Yxj9lZw9I9DQutE9ekMFAlmoQxkVHG1hcmMuenlu Z2llckBhcm0uY29tAAoJECPQ0LrRPXpDVUMQAIyE1q3fjSNZ+EkfK8+mbcWC80Wc suklgcqVbHahu6FHuHALlR7rgJIPSaFYFpDIwybA9A0Pwia/5Jf2mOL3RGVF4f97 nyHlSS16kocZz8lKn+NtgcaUiFRma3y7GNek0pnsSlm+Vu+Syw3xssN+yYcGujTu jWRocvIqIJlScpzHG/Ulx3tZTXYfipQFfIQ3+9gm/i+KYqTwGDH/MsdxI7uAbctx YJGwLVtv4MGGmNHaq4iS64d55yrG/4Yqv+q92zFaaxj+V0di+Ds01+MDhdq8X7N/ fhLGY/Yh/I3FiIIdIO/O1sj1EPO6lLbg4DPYXIMdjzwhBdKhu8i66/ttH/Kx//Aa 1hhLZSN6rYiJM3lWcTxej45bs8MR/3MBm4gKpZxTgJ12YRIwgY8lRyoqXTlto5ls w10yi5wFsJaAO1E/HdEs/dyndV1jpvGo9KIRnfh7E5+Hw7PCYs9kZa4MUtq9RYT8 Civyppi2sMfKYtGvwm+FS6sIigoFCh4DJ5MmUbM5CLh5imnggyYJlTsJdBuxVDZM 1RoDnX/YebpVceezIZ/oCKq60Utck0Oqge2pc+NjVQupAp/x/13R/7DQPnFCq/OL Avx9kBtSzdYmYgE3EWt9n+h4LT23JpOym2OEUF3fhpPE96BKAJkMEPB/QlBi39fo 0cZEX8M7xq5KjRJy =3ZS3 -----END PGP SIGNATURE----- Merge tag 'irqchip-4.14' of git://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms into irq/core Pull irqchip updates for 4.14 from Marc Zyngier: - irqchip-specific part of the monster GICv4 series - new UniPhier AIDET irqchip driver - new variants of some Freescale MSI widget - blanket removal of of_node->full_name in printk - random collection of fixes
This commit is contained in:
commit
9fbd7fd28d
|
@ -4,8 +4,10 @@ Required properties:
|
|||
|
||||
- compatible: should be "fsl,<soc-name>-msi" to identify
|
||||
Layerscape PCIe MSI controller block such as:
|
||||
"fsl,1s1021a-msi"
|
||||
"fsl,1s1043a-msi"
|
||||
"fsl,ls1021a-msi"
|
||||
"fsl,ls1043a-msi"
|
||||
"fsl,ls1046a-msi"
|
||||
"fsl,ls1043a-v1.1-msi"
|
||||
- msi-controller: indicates that this is a PCIe MSI controller node
|
||||
- reg: physical base address of the controller and length of memory mapped.
|
||||
- interrupts: an interrupt to the parent interrupt controller.
|
||||
|
@ -23,7 +25,7 @@ MSI controller node
|
|||
Examples:
|
||||
|
||||
msi1: msi-controller@1571000 {
|
||||
compatible = "fsl,1s1043a-msi";
|
||||
compatible = "fsl,ls1043a-msi";
|
||||
reg = <0x0 0x1571000 0x0 0x8>,
|
||||
msi-controller;
|
||||
interrupts = <0 116 0x4>;
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
UniPhier AIDET
|
||||
|
||||
UniPhier AIDET (ARM Interrupt Detector) is an add-on block for ARM GIC (Generic
|
||||
Interrupt Controller). GIC itself can handle only high level and rising edge
|
||||
interrupts. The AIDET provides logic inverter to support low level and falling
|
||||
edge interrupts.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be one of the following:
|
||||
"socionext,uniphier-ld4-aidet" - for LD4 SoC
|
||||
"socionext,uniphier-pro4-aidet" - for Pro4 SoC
|
||||
"socionext,uniphier-sld8-aidet" - for sLD8 SoC
|
||||
"socionext,uniphier-pro5-aidet" - for Pro5 SoC
|
||||
"socionext,uniphier-pxs2-aidet" - for PXs2/LD6b SoC
|
||||
"socionext,uniphier-ld11-aidet" - for LD11 SoC
|
||||
"socionext,uniphier-ld20-aidet" - for LD20 SoC
|
||||
"socionext,uniphier-pxs3-aidet" - for PXs3 SoC
|
||||
- reg: Specifies offset and length of the register set for the device.
|
||||
- interrupt-controller: Identifies the node as an interrupt controller
|
||||
- #interrupt-cells : Specifies the number of cells needed to encode an interrupt
|
||||
source. The value should be 2. The first cell defines the interrupt number
|
||||
(corresponds to the SPI interrupt number of GIC). The second cell specifies
|
||||
the trigger type as defined in interrupts.txt in this directory.
|
||||
|
||||
Example:
|
||||
|
||||
aidet: aidet@5fc20000 {
|
||||
compatible = "socionext,uniphier-pro4-aidet";
|
||||
reg = <0x5fc20000 0x200>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
};
|
|
@ -1993,6 +1993,7 @@ F: arch/arm64/boot/dts/socionext/
|
|||
F: drivers/bus/uniphier-system-bus.c
|
||||
F: drivers/clk/uniphier/
|
||||
F: drivers/i2c/busses/i2c-uniphier*
|
||||
F: drivers/irqchip/irq-uniphier-aidet.c
|
||||
F: drivers/pinctrl/uniphier/
|
||||
F: drivers/reset/reset-uniphier.c
|
||||
F: drivers/tty/serial/8250/8250_uniphier.c
|
||||
|
|
|
@ -129,14 +129,14 @@
|
|||
};
|
||||
|
||||
msi1: msi-controller@1570e00 {
|
||||
compatible = "fsl,1s1021a-msi";
|
||||
compatible = "fsl,ls1021a-msi";
|
||||
reg = <0x0 0x1570e00 0x0 0x8>;
|
||||
msi-controller;
|
||||
interrupts = <GIC_SPI 179 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
|
||||
msi2: msi-controller@1570e08 {
|
||||
compatible = "fsl,1s1021a-msi";
|
||||
compatible = "fsl,ls1021a-msi";
|
||||
reg = <0x0 0x1570e08 0x0 0x8>;
|
||||
msi-controller;
|
||||
interrupts = <GIC_SPI 180 IRQ_TYPE_LEVEL_HIGH>;
|
||||
|
@ -699,7 +699,7 @@
|
|||
bus-range = <0x0 0xff>;
|
||||
ranges = <0x81000000 0x0 0x00000000 0x40 0x00010000 0x0 0x00010000 /* downstream I/O */
|
||||
0x82000000 0x0 0x40000000 0x40 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */
|
||||
msi-parent = <&msi1>;
|
||||
msi-parent = <&msi1>, <&msi2>;
|
||||
#interrupt-cells = <1>;
|
||||
interrupt-map-mask = <0 0 0 7>;
|
||||
interrupt-map = <0000 0 0 1 &gic GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>,
|
||||
|
@ -722,7 +722,7 @@
|
|||
bus-range = <0x0 0xff>;
|
||||
ranges = <0x81000000 0x0 0x00000000 0x48 0x00010000 0x0 0x00010000 /* downstream I/O */
|
||||
0x82000000 0x0 0x40000000 0x48 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */
|
||||
msi-parent = <&msi2>;
|
||||
msi-parent = <&msi1>, <&msi2>;
|
||||
#interrupt-cells = <1>;
|
||||
interrupt-map-mask = <0 0 0 7>;
|
||||
interrupt-map = <0000 0 0 1 &gic GIC_SPI 92 IRQ_TYPE_LEVEL_HIGH>,
|
||||
|
|
|
@ -275,6 +275,12 @@ static inline u64 __gic_readq_nonatomic(const volatile void __iomem *addr)
|
|||
#define gicr_read_pendbaser(c) __gic_readq_nonatomic(c)
|
||||
#define gicr_write_pendbaser(v, c) __gic_writeq_nonatomic(v, c)
|
||||
|
||||
/*
|
||||
* GICR_xLPIR - only the lower bits are significant
|
||||
*/
|
||||
#define gic_read_lpir(c) readl_relaxed(c)
|
||||
#define gic_write_lpir(v, c) writel_relaxed(lower_32_bits(v), c)
|
||||
|
||||
/*
|
||||
* GITS_TYPER is an ID register and doesn't need atomicity.
|
||||
*/
|
||||
|
@ -291,5 +297,33 @@ static inline u64 __gic_readq_nonatomic(const volatile void __iomem *addr)
|
|||
*/
|
||||
#define gits_write_cwriter(v, c) __gic_writeq_nonatomic(v, c)
|
||||
|
||||
/*
|
||||
* GITS_VPROPBASER - hi and lo bits may be accessed independently.
|
||||
*/
|
||||
#define gits_write_vpropbaser(v, c) __gic_writeq_nonatomic(v, c)
|
||||
|
||||
/*
|
||||
* GITS_VPENDBASER - the Valid bit must be cleared before changing
|
||||
* anything else.
|
||||
*/
|
||||
static inline void gits_write_vpendbaser(u64 val, void * __iomem addr)
|
||||
{
|
||||
u32 tmp;
|
||||
|
||||
tmp = readl_relaxed(addr + 4);
|
||||
if (tmp & (GICR_VPENDBASER_Valid >> 32)) {
|
||||
tmp &= ~(GICR_VPENDBASER_Valid >> 32);
|
||||
writel_relaxed(tmp, addr + 4);
|
||||
}
|
||||
|
||||
/*
|
||||
* Use the fact that __gic_writeq_nonatomic writes the second
|
||||
* half of the 64bit quantity after the first.
|
||||
*/
|
||||
__gic_writeq_nonatomic(val, addr);
|
||||
}
|
||||
|
||||
#define gits_read_vpendbaser(c) __gic_readq_nonatomic(c)
|
||||
|
||||
#endif /* !__ASSEMBLY__ */
|
||||
#endif /* !__ASM_ARCH_GICV3_H */
|
||||
|
|
|
@ -653,21 +653,21 @@
|
|||
};
|
||||
|
||||
msi1: msi-controller1@1571000 {
|
||||
compatible = "fsl,1s1043a-msi";
|
||||
compatible = "fsl,ls1043a-msi";
|
||||
reg = <0x0 0x1571000 0x0 0x8>;
|
||||
msi-controller;
|
||||
interrupts = <0 116 0x4>;
|
||||
};
|
||||
|
||||
msi2: msi-controller2@1572000 {
|
||||
compatible = "fsl,1s1043a-msi";
|
||||
compatible = "fsl,ls1043a-msi";
|
||||
reg = <0x0 0x1572000 0x0 0x8>;
|
||||
msi-controller;
|
||||
interrupts = <0 126 0x4>;
|
||||
};
|
||||
|
||||
msi3: msi-controller3@1573000 {
|
||||
compatible = "fsl,1s1043a-msi";
|
||||
compatible = "fsl,ls1043a-msi";
|
||||
reg = <0x0 0x1573000 0x0 0x8>;
|
||||
msi-controller;
|
||||
interrupts = <0 160 0x4>;
|
||||
|
@ -689,7 +689,7 @@
|
|||
bus-range = <0x0 0xff>;
|
||||
ranges = <0x81000000 0x0 0x00000000 0x40 0x00010000 0x0 0x00010000 /* downstream I/O */
|
||||
0x82000000 0x0 0x40000000 0x40 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */
|
||||
msi-parent = <&msi1>;
|
||||
msi-parent = <&msi1>, <&msi2>, <&msi3>;
|
||||
#interrupt-cells = <1>;
|
||||
interrupt-map-mask = <0 0 0 7>;
|
||||
interrupt-map = <0000 0 0 1 &gic 0 110 0x4>,
|
||||
|
@ -714,7 +714,7 @@
|
|||
bus-range = <0x0 0xff>;
|
||||
ranges = <0x81000000 0x0 0x00000000 0x48 0x00010000 0x0 0x00010000 /* downstream I/O */
|
||||
0x82000000 0x0 0x40000000 0x48 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */
|
||||
msi-parent = <&msi2>;
|
||||
msi-parent = <&msi1>, <&msi2>, <&msi3>;
|
||||
#interrupt-cells = <1>;
|
||||
interrupt-map-mask = <0 0 0 7>;
|
||||
interrupt-map = <0000 0 0 1 &gic 0 120 0x4>,
|
||||
|
@ -739,7 +739,7 @@
|
|||
bus-range = <0x0 0xff>;
|
||||
ranges = <0x81000000 0x0 0x00000000 0x50 0x00010000 0x0 0x00010000 /* downstream I/O */
|
||||
0x82000000 0x0 0x40000000 0x50 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */
|
||||
msi-parent = <&msi3>;
|
||||
msi-parent = <&msi1>, <&msi2>, <&msi3>;
|
||||
#interrupt-cells = <1>;
|
||||
interrupt-map-mask = <0 0 0 7>;
|
||||
interrupt-map = <0000 0 0 1 &gic 0 154 0x4>,
|
||||
|
|
|
@ -630,6 +630,37 @@
|
|||
interrupts = <GIC_SPI 69 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clockgen 4 1>;
|
||||
};
|
||||
|
||||
msi1: msi-controller@1580000 {
|
||||
compatible = "fsl,ls1046a-msi";
|
||||
msi-controller;
|
||||
reg = <0x0 0x1580000 0x0 0x10000>;
|
||||
interrupts = <GIC_SPI 116 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 111 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
|
||||
msi2: msi-controller@1590000 {
|
||||
compatible = "fsl,ls1046a-msi";
|
||||
msi-controller;
|
||||
reg = <0x0 0x1590000 0x0 0x10000>;
|
||||
interrupts = <GIC_SPI 126 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 122 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
|
||||
msi3: msi-controller@15a0000 {
|
||||
compatible = "fsl,ls1046a-msi";
|
||||
msi-controller;
|
||||
reg = <0x0 0x15a0000 0x0 0x10000>;
|
||||
interrupts = <GIC_SPI 160 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 155 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 156 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 157 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
reserved-memory {
|
||||
|
|
|
@ -116,6 +116,8 @@ static inline void gic_write_bpr1(u32 val)
|
|||
|
||||
#define gic_read_typer(c) readq_relaxed(c)
|
||||
#define gic_write_irouter(v, c) writeq_relaxed(v, c)
|
||||
#define gic_read_lpir(c) readq_relaxed(c)
|
||||
#define gic_write_lpir(v, c) writeq_relaxed(v, c)
|
||||
|
||||
#define gic_flush_dcache_to_poc(a,l) __flush_dcache_area((a), (l))
|
||||
|
||||
|
@ -133,5 +135,10 @@ static inline void gic_write_bpr1(u32 val)
|
|||
#define gicr_write_pendbaser(v, c) writeq_relaxed(v, c)
|
||||
#define gicr_read_pendbaser(c) readq_relaxed(c)
|
||||
|
||||
#define gits_write_vpropbaser(v, c) writeq_relaxed(v, c)
|
||||
|
||||
#define gits_write_vpendbaser(v, c) writeq_relaxed(v, c)
|
||||
#define gits_read_vpendbaser(c) readq_relaxed(c)
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
#endif /* __ASM_ARCH_GICV3_H */
|
||||
|
|
|
@ -313,3 +313,11 @@ config QCOM_IRQ_COMBINER
|
|||
help
|
||||
Say yes here to add support for the IRQ combiner devices embedded
|
||||
in Qualcomm Technologies chips.
|
||||
|
||||
config IRQ_UNIPHIER_AIDET
|
||||
bool "UniPhier AIDET support" if COMPILE_TEST
|
||||
depends on ARCH_UNIPHIER || COMPILE_TEST
|
||||
default ARCH_UNIPHIER
|
||||
select IRQ_DOMAIN_HIERARCHY
|
||||
help
|
||||
Support for the UniPhier AIDET (ARM Interrupt Detector).
|
||||
|
|
|
@ -28,7 +28,7 @@ obj-$(CONFIG_ARM_GIC_PM) += irq-gic-pm.o
|
|||
obj-$(CONFIG_ARCH_REALVIEW) += irq-gic-realview.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_ITS) += irq-gic-v3-its.o irq-gic-v3-its-pci-msi.o irq-gic-v3-its-platform-msi.o
|
||||
obj-$(CONFIG_ARM_GIC_V3_ITS) += irq-gic-v3-its.o irq-gic-v3-its-pci-msi.o irq-gic-v3-its-platform-msi.o irq-gic-v4.o
|
||||
obj-$(CONFIG_PARTITION_PERCPU) += irq-partition-percpu.o
|
||||
obj-$(CONFIG_HISILICON_IRQ_MBIGEN) += irq-mbigen.o
|
||||
obj-$(CONFIG_ARM_NVIC) += irq-nvic.o
|
||||
|
@ -78,3 +78,4 @@ obj-$(CONFIG_EZNPS_GIC) += irq-eznps.o
|
|||
obj-$(CONFIG_ARCH_ASPEED) += irq-aspeed-vic.o irq-aspeed-i2c-ic.o
|
||||
obj-$(CONFIG_STM32_EXTI) += irq-stm32-exti.o
|
||||
obj-$(CONFIG_QCOM_IRQ_COMBINER) += qcom-irq-combiner.o
|
||||
obj-$(CONFIG_IRQ_UNIPHIER_AIDET) += irq-uniphier-aidet.o
|
||||
|
|
|
@ -203,7 +203,7 @@ static struct irq_chip armada_370_xp_msi_irq_chip = {
|
|||
|
||||
static struct msi_domain_info armada_370_xp_msi_domain_info = {
|
||||
.flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
|
||||
MSI_FLAG_MULTI_PCI_MSI),
|
||||
MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX),
|
||||
.chip = &armada_370_xp_msi_irq_chip,
|
||||
};
|
||||
|
||||
|
|
|
@ -147,13 +147,12 @@ static int __init armctrl_of_init(struct device_node *node,
|
|||
|
||||
base = of_iomap(node, 0);
|
||||
if (!base)
|
||||
panic("%s: unable to map IC registers\n",
|
||||
node->full_name);
|
||||
panic("%pOF: unable to map IC registers\n", node);
|
||||
|
||||
intc.domain = irq_domain_add_linear(node, MAKE_HWIRQ(NR_BANKS, 0),
|
||||
&armctrl_ops, NULL);
|
||||
if (!intc.domain)
|
||||
panic("%s: unable to create IRQ domain\n", node->full_name);
|
||||
panic("%pOF: unable to create IRQ domain\n", node);
|
||||
|
||||
for (b = 0; b < NR_BANKS; b++) {
|
||||
intc.pending[b] = base + reg_pending[b];
|
||||
|
@ -173,8 +172,8 @@ static int __init armctrl_of_init(struct device_node *node,
|
|||
int parent_irq = irq_of_parse_and_map(node, 0);
|
||||
|
||||
if (!parent_irq) {
|
||||
panic("%s: unable to get parent interrupt.\n",
|
||||
node->full_name);
|
||||
panic("%pOF: unable to get parent interrupt.\n",
|
||||
node);
|
||||
}
|
||||
irq_set_chained_handler(parent_irq, bcm2836_chained_handle_irq);
|
||||
} else {
|
||||
|
|
|
@ -282,8 +282,7 @@ static int __init bcm2836_arm_irqchip_l1_intc_of_init(struct device_node *node,
|
|||
{
|
||||
intc.base = of_iomap(node, 0);
|
||||
if (!intc.base) {
|
||||
panic("%s: unable to map local interrupt registers\n",
|
||||
node->full_name);
|
||||
panic("%pOF: unable to map local interrupt registers\n", node);
|
||||
}
|
||||
|
||||
bcm2835_init_local_timer_frequency();
|
||||
|
@ -292,7 +291,7 @@ static int __init bcm2836_arm_irqchip_l1_intc_of_init(struct device_node *node,
|
|||
&bcm2836_arm_irqchip_intc_ops,
|
||||
NULL);
|
||||
if (!intc.domain)
|
||||
panic("%s: unable to create IRQ domain\n", node->full_name);
|
||||
panic("%pOF: unable to create IRQ domain\n", node);
|
||||
|
||||
bcm2836_arm_irqchip_register_irq(LOCAL_IRQ_CNTPSIRQ,
|
||||
&bcm2836_arm_irqchip_timer);
|
||||
|
|
|
@ -250,12 +250,6 @@ static int __init bcm7120_l2_intc_probe(struct device_node *dn,
|
|||
if (ret < 0)
|
||||
goto out_free_l1_data;
|
||||
|
||||
for (idx = 0; idx < data->n_words; idx++) {
|
||||
__raw_writel(data->irq_fwd_mask[idx],
|
||||
data->pair_base[idx] +
|
||||
data->en_offset[idx]);
|
||||
}
|
||||
|
||||
for (irq = 0; irq < data->num_parent_irqs; irq++) {
|
||||
ret = bcm7120_l2_intc_init_one(dn, data, irq, valid_mask);
|
||||
if (ret)
|
||||
|
@ -297,6 +291,10 @@ static int __init bcm7120_l2_intc_probe(struct device_node *dn,
|
|||
gc->reg_base = data->pair_base[idx];
|
||||
ct->regs.mask = data->en_offset[idx];
|
||||
|
||||
/* gc->reg_base is defined and so is gc->writel */
|
||||
irq_reg_writel(gc, data->irq_fwd_mask[idx],
|
||||
data->en_offset[idx]);
|
||||
|
||||
ct->chip.irq_mask = irq_gc_mask_clr_bit;
|
||||
ct->chip.irq_unmask = irq_gc_mask_set_bit;
|
||||
ct->chip.irq_ack = irq_gc_noop;
|
||||
|
|
|
@ -341,13 +341,13 @@ static int __init irqcrossbar_init(struct device_node *node,
|
|||
int err;
|
||||
|
||||
if (!parent) {
|
||||
pr_err("%s: no parent, giving up\n", node->full_name);
|
||||
pr_err("%pOF: no parent, giving up\n", node);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
parent_domain = irq_find_host(parent);
|
||||
if (!parent_domain) {
|
||||
pr_err("%s: unable to obtain parent domain\n", node->full_name);
|
||||
pr_err("%pOF: unable to obtain parent domain\n", node);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
|
@ -360,7 +360,7 @@ static int __init irqcrossbar_init(struct device_node *node,
|
|||
node, &crossbar_domain_ops,
|
||||
NULL);
|
||||
if (!domain) {
|
||||
pr_err("%s: failed to allocated domain\n", node->full_name);
|
||||
pr_err("%pOF: failed to allocated domain\n", node);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ static int __init digicolor_of_init(struct device_node *node,
|
|||
|
||||
reg_base = of_iomap(node, 0);
|
||||
if (!reg_base) {
|
||||
pr_err("%s: unable to map IC registers\n", node->full_name);
|
||||
pr_err("%pOF: unable to map IC registers\n", node);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
|
@ -88,7 +88,7 @@ static int __init digicolor_of_init(struct device_node *node,
|
|||
|
||||
ucregs = syscon_regmap_lookup_by_phandle(node, "syscon");
|
||||
if (IS_ERR(ucregs)) {
|
||||
pr_err("%s: unable to map UC registers\n", node->full_name);
|
||||
pr_err("%pOF: unable to map UC registers\n", node);
|
||||
return PTR_ERR(ucregs);
|
||||
}
|
||||
/* channel 1, regular IRQs */
|
||||
|
@ -97,7 +97,7 @@ static int __init digicolor_of_init(struct device_node *node,
|
|||
digicolor_irq_domain =
|
||||
irq_domain_add_linear(node, 64, &irq_generic_chip_ops, NULL);
|
||||
if (!digicolor_irq_domain) {
|
||||
pr_err("%s: unable to create IRQ domain\n", node->full_name);
|
||||
pr_err("%pOF: unable to create IRQ domain\n", node);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,7 @@ static int __init digicolor_of_init(struct device_node *node,
|
|||
"digicolor_irq", handle_level_irq,
|
||||
clr, 0, 0);
|
||||
if (ret) {
|
||||
pr_err("%s: unable to allocate IRQ gc\n", node->full_name);
|
||||
pr_err("%pOF: unable to allocate IRQ gc\n", node);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -79,24 +79,24 @@ static int __init dw_apb_ictl_init(struct device_node *np,
|
|||
/* Map the parent interrupt for the chained handler */
|
||||
irq = irq_of_parse_and_map(np, 0);
|
||||
if (irq <= 0) {
|
||||
pr_err("%s: unable to parse irq\n", np->full_name);
|
||||
pr_err("%pOF: unable to parse irq\n", np);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = of_address_to_resource(np, 0, &r);
|
||||
if (ret) {
|
||||
pr_err("%s: unable to get resource\n", np->full_name);
|
||||
pr_err("%pOF: unable to get resource\n", np);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!request_mem_region(r.start, resource_size(&r), np->full_name)) {
|
||||
pr_err("%s: unable to request mem region\n", np->full_name);
|
||||
pr_err("%pOF: unable to request mem region\n", np);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
iobase = ioremap(r.start, resource_size(&r));
|
||||
if (!iobase) {
|
||||
pr_err("%s: unable to map resource\n", np->full_name);
|
||||
pr_err("%pOF: unable to map resource\n", np);
|
||||
ret = -ENOMEM;
|
||||
goto err_release;
|
||||
}
|
||||
|
@ -123,7 +123,7 @@ static int __init dw_apb_ictl_init(struct device_node *np,
|
|||
domain = irq_domain_add_linear(np, nrirqs,
|
||||
&irq_generic_chip_ops, NULL);
|
||||
if (!domain) {
|
||||
pr_err("%s: unable to add irq domain\n", np->full_name);
|
||||
pr_err("%pOF: unable to add irq domain\n", np);
|
||||
ret = -ENOMEM;
|
||||
goto err_unmap;
|
||||
}
|
||||
|
@ -132,7 +132,7 @@ static int __init dw_apb_ictl_init(struct device_node *np,
|
|||
handle_level_irq, clr, 0,
|
||||
IRQ_GC_INIT_MASK_CACHE);
|
||||
if (ret) {
|
||||
pr_err("%s: unable to alloc irq domain gc\n", np->full_name);
|
||||
pr_err("%pOF: unable to alloc irq domain gc\n", np);
|
||||
goto err_unmap;
|
||||
}
|
||||
|
||||
|
|
|
@ -138,7 +138,7 @@ static int __init its_pci_of_msi_init(void)
|
|||
if (its_pci_msi_init_one(of_node_to_fwnode(np), np->full_name))
|
||||
continue;
|
||||
|
||||
pr_info("PCI/MSI: %s domain created\n", np->full_name);
|
||||
pr_info("PCI/MSI: %pOF domain created\n", np);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2013, 2014 ARM Limited, All Rights Reserved.
|
||||
* Copyright (C) 2013-2017 ARM Limited, All Rights Reserved.
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@ -421,24 +421,14 @@ static void __init gic_dist_init(void)
|
|||
gic_write_irouter(affinity, base + GICD_IROUTER + i * 8);
|
||||
}
|
||||
|
||||
static int gic_populate_rdist(void)
|
||||
static int gic_iterate_rdists(int (*fn)(struct redist_region *, void __iomem *))
|
||||
{
|
||||
unsigned long mpidr = cpu_logical_map(smp_processor_id());
|
||||
u64 typer;
|
||||
u32 aff;
|
||||
int ret = -ENODEV;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Convert affinity to a 32bit value that can be matched to
|
||||
* GICR_TYPER bits [63:32].
|
||||
*/
|
||||
aff = (MPIDR_AFFINITY_LEVEL(mpidr, 3) << 24 |
|
||||
MPIDR_AFFINITY_LEVEL(mpidr, 2) << 16 |
|
||||
MPIDR_AFFINITY_LEVEL(mpidr, 1) << 8 |
|
||||
MPIDR_AFFINITY_LEVEL(mpidr, 0));
|
||||
|
||||
for (i = 0; i < gic_data.nr_redist_regions; i++) {
|
||||
void __iomem *ptr = gic_data.redist_regions[i].redist_base;
|
||||
u64 typer;
|
||||
u32 reg;
|
||||
|
||||
reg = readl_relaxed(ptr + GICR_PIDR2) & GIC_PIDR2_ARCH_MASK;
|
||||
|
@ -450,15 +440,9 @@ static int gic_populate_rdist(void)
|
|||
|
||||
do {
|
||||
typer = gic_read_typer(ptr + GICR_TYPER);
|
||||
if ((typer >> 32) == aff) {
|
||||
u64 offset = ptr - gic_data.redist_regions[i].redist_base;
|
||||
gic_data_rdist_rd_base() = ptr;
|
||||
gic_data_rdist()->phys_base = gic_data.redist_regions[i].phys_base + offset;
|
||||
pr_info("CPU%d: found redistributor %lx region %d:%pa\n",
|
||||
smp_processor_id(), mpidr, i,
|
||||
&gic_data_rdist()->phys_base);
|
||||
ret = fn(gic_data.redist_regions + i, ptr);
|
||||
if (!ret)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (gic_data.redist_regions[i].single_redist)
|
||||
break;
|
||||
|
@ -473,12 +457,71 @@ static int gic_populate_rdist(void)
|
|||
} while (!(typer & GICR_TYPER_LAST));
|
||||
}
|
||||
|
||||
return ret ? -ENODEV : 0;
|
||||
}
|
||||
|
||||
static int __gic_populate_rdist(struct redist_region *region, void __iomem *ptr)
|
||||
{
|
||||
unsigned long mpidr = cpu_logical_map(smp_processor_id());
|
||||
u64 typer;
|
||||
u32 aff;
|
||||
|
||||
/*
|
||||
* Convert affinity to a 32bit value that can be matched to
|
||||
* GICR_TYPER bits [63:32].
|
||||
*/
|
||||
aff = (MPIDR_AFFINITY_LEVEL(mpidr, 3) << 24 |
|
||||
MPIDR_AFFINITY_LEVEL(mpidr, 2) << 16 |
|
||||
MPIDR_AFFINITY_LEVEL(mpidr, 1) << 8 |
|
||||
MPIDR_AFFINITY_LEVEL(mpidr, 0));
|
||||
|
||||
typer = gic_read_typer(ptr + GICR_TYPER);
|
||||
if ((typer >> 32) == aff) {
|
||||
u64 offset = ptr - region->redist_base;
|
||||
gic_data_rdist_rd_base() = ptr;
|
||||
gic_data_rdist()->phys_base = region->phys_base + offset;
|
||||
|
||||
pr_info("CPU%d: found redistributor %lx region %d:%pa\n",
|
||||
smp_processor_id(), mpidr,
|
||||
(int)(region - gic_data.redist_regions),
|
||||
&gic_data_rdist()->phys_base);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Try next one */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int gic_populate_rdist(void)
|
||||
{
|
||||
if (gic_iterate_rdists(__gic_populate_rdist) == 0)
|
||||
return 0;
|
||||
|
||||
/* We couldn't even deal with ourselves... */
|
||||
WARN(true, "CPU%d: mpidr %lx has no re-distributor!\n",
|
||||
smp_processor_id(), mpidr);
|
||||
smp_processor_id(),
|
||||
(unsigned long)cpu_logical_map(smp_processor_id()));
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int __gic_update_vlpi_properties(struct redist_region *region,
|
||||
void __iomem *ptr)
|
||||
{
|
||||
u64 typer = gic_read_typer(ptr + GICR_TYPER);
|
||||
gic_data.rdists.has_vlpis &= !!(typer & GICR_TYPER_VLPIS);
|
||||
gic_data.rdists.has_direct_lpi &= !!(typer & GICR_TYPER_DirectLPIS);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void gic_update_vlpi_properties(void)
|
||||
{
|
||||
gic_iterate_rdists(__gic_update_vlpi_properties);
|
||||
pr_info("%sVLPI support, %sdirect LPI support\n",
|
||||
!gic_data.rdists.has_vlpis ? "no " : "",
|
||||
!gic_data.rdists.has_direct_lpi ? "no " : "");
|
||||
}
|
||||
|
||||
static void gic_cpu_sys_reg_init(void)
|
||||
{
|
||||
/*
|
||||
|
@ -946,6 +989,8 @@ static int __init gic_init_bases(void __iomem *dist_base,
|
|||
gic_data.domain = irq_domain_create_tree(handle, &gic_irq_domain_ops,
|
||||
&gic_data);
|
||||
gic_data.rdists.rdist = alloc_percpu(typeof(*gic_data.rdists.rdist));
|
||||
gic_data.rdists.has_vlpis = true;
|
||||
gic_data.rdists.has_direct_lpi = true;
|
||||
|
||||
if (WARN_ON(!gic_data.domain) || WARN_ON(!gic_data.rdists.rdist)) {
|
||||
err = -ENOMEM;
|
||||
|
@ -954,6 +999,8 @@ static int __init gic_init_bases(void __iomem *dist_base,
|
|||
|
||||
set_handle_irq(gic_handle_irq);
|
||||
|
||||
gic_update_vlpi_properties();
|
||||
|
||||
if (IS_ENABLED(CONFIG_ARM_GIC_V3_ITS) && gic_dist_supports_lpis())
|
||||
its_init(handle, &gic_data.rdists, gic_data.domain);
|
||||
|
||||
|
@ -1060,7 +1107,7 @@ static void __init gic_populate_ppi_partitions(struct device_node *gic_node)
|
|||
if (WARN_ON(cpu == -1))
|
||||
continue;
|
||||
|
||||
pr_cont("%s[%d] ", cpu_node->full_name, cpu);
|
||||
pr_cont("%pOF[%d] ", cpu_node, cpu);
|
||||
|
||||
cpumask_set_cpu(cpu, &part->mask);
|
||||
}
|
||||
|
@ -1115,6 +1162,7 @@ static void __init gic_of_setup_kvm_info(struct device_node *node)
|
|||
if (!ret)
|
||||
gic_v3_kvm_info.vcpu = r;
|
||||
|
||||
gic_v3_kvm_info.has_v4 = gic_data.rdists.has_vlpis;
|
||||
gic_set_kvm_info(&gic_v3_kvm_info);
|
||||
}
|
||||
|
||||
|
@ -1128,15 +1176,13 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare
|
|||
|
||||
dist_base = of_iomap(node, 0);
|
||||
if (!dist_base) {
|
||||
pr_err("%s: unable to map gic dist registers\n",
|
||||
node->full_name);
|
||||
pr_err("%pOF: unable to map gic dist registers\n", node);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
err = gic_validate_dist_version(dist_base);
|
||||
if (err) {
|
||||
pr_err("%s: no distributor detected, giving up\n",
|
||||
node->full_name);
|
||||
pr_err("%pOF: no distributor detected, giving up\n", node);
|
||||
goto out_unmap_dist;
|
||||
}
|
||||
|
||||
|
@ -1156,8 +1202,7 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare
|
|||
ret = of_address_to_resource(node, 1 + i, &res);
|
||||
rdist_regs[i].redist_base = of_iomap(node, 1 + i);
|
||||
if (ret || !rdist_regs[i].redist_base) {
|
||||
pr_err("%s: couldn't map region %d\n",
|
||||
node->full_name, i);
|
||||
pr_err("%pOF: couldn't map region %d\n", node, i);
|
||||
err = -ENODEV;
|
||||
goto out_unmap_rdist;
|
||||
}
|
||||
|
@ -1411,6 +1456,7 @@ static void __init gic_acpi_setup_kvm_info(void)
|
|||
vcpu->end = vcpu->start + ACPI_GICV2_VCPU_MEM_SIZE - 1;
|
||||
}
|
||||
|
||||
gic_v3_kvm_info.has_v4 = gic_data.rdists.has_vlpis;
|
||||
gic_set_kvm_info(&gic_v3_kvm_info);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
* Copyright (C) 2016,2017 ARM Limited, All Rights Reserved.
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*
|
||||
* 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/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/msi.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include <linux/irqchip/arm-gic-v4.h>
|
||||
|
||||
/*
|
||||
* WARNING: The blurb below assumes that you understand the
|
||||
* intricacies of GICv3, GICv4, and how a guest's view of a GICv3 gets
|
||||
* translated into GICv4 commands. So it effectively targets at most
|
||||
* two individuals. You know who you are.
|
||||
*
|
||||
* The core GICv4 code is designed to *avoid* exposing too much of the
|
||||
* core GIC code (that would in turn leak into the hypervisor code),
|
||||
* and instead provide a hypervisor agnostic interface to the HW (of
|
||||
* course, the astute reader will quickly realize that hypervisor
|
||||
* agnostic actually means KVM-specific - what were you thinking?).
|
||||
*
|
||||
* In order to achieve a modicum of isolation, we try to hide most of
|
||||
* the GICv4 "stuff" behind normal irqchip operations:
|
||||
*
|
||||
* - Any guest-visible VLPI is backed by a Linux interrupt (and a
|
||||
* physical LPI which gets unmapped when the guest maps the
|
||||
* VLPI). This allows the same DevID/EventID pair to be either
|
||||
* mapped to the LPI (host) or the VLPI (guest). Note that this is
|
||||
* exclusive, and you cannot have both.
|
||||
*
|
||||
* - Enabling/disabling a VLPI is done by issuing mask/unmask calls.
|
||||
*
|
||||
* - Guest INT/CLEAR commands are implemented through
|
||||
* irq_set_irqchip_state().
|
||||
*
|
||||
* - The *bizarre* stuff (mapping/unmapping an interrupt to a VLPI, or
|
||||
* issuing an INV after changing a priority) gets shoved into the
|
||||
* irq_set_vcpu_affinity() method. While this is quite horrible
|
||||
* (let's face it, this is the irqchip version of an ioctl), it
|
||||
* confines the crap to a single location. And map/unmap really is
|
||||
* about setting the affinity of a VLPI to a vcpu, so only INV is
|
||||
* majorly out of place. So there.
|
||||
*
|
||||
* A number of commands are simply not provided by this interface, as
|
||||
* they do not make direct sense. For example, MAPD is purely local to
|
||||
* the virtual ITS (because it references a virtual device, and the
|
||||
* physical ITS is still very much in charge of the physical
|
||||
* device). Same goes for things like MAPC (the physical ITS deals
|
||||
* with the actual vPE affinity, and not the braindead concept of
|
||||
* collection). SYNC is not provided either, as each and every command
|
||||
* is followed by a VSYNC. This could be relaxed in the future, should
|
||||
* this be seen as a bottleneck (yes, this means *never*).
|
||||
*
|
||||
* But handling VLPIs is only one side of the job of the GICv4
|
||||
* code. The other (darker) side is to take care of the doorbell
|
||||
* interrupts which are delivered when a VLPI targeting a non-running
|
||||
* vcpu is being made pending.
|
||||
*
|
||||
* The choice made here is that each vcpu (VPE in old northern GICv4
|
||||
* dialect) gets a single doorbell LPI, no matter how many interrupts
|
||||
* are targeting it. This has a nice property, which is that the
|
||||
* interrupt becomes a handle for the VPE, and that the hypervisor
|
||||
* code can manipulate it through the normal interrupt API:
|
||||
*
|
||||
* - VMs (or rather the VM abstraction that matters to the GIC)
|
||||
* contain an irq domain where each interrupt maps to a VPE. In
|
||||
* turn, this domain sits on top of the normal LPI allocator, and a
|
||||
* specially crafted irq_chip implementation.
|
||||
*
|
||||
* - mask/unmask do what is expected on the doorbell interrupt.
|
||||
*
|
||||
* - irq_set_affinity is used to move a VPE from one redistributor to
|
||||
* another.
|
||||
*
|
||||
* - irq_set_vcpu_affinity once again gets hijacked for the purpose of
|
||||
* creating a new sub-API, namely scheduling/descheduling a VPE
|
||||
* (which involves programming GICR_V{PROP,PEND}BASER) and
|
||||
* performing INVALL operations.
|
||||
*/
|
||||
|
||||
static struct irq_domain *gic_domain;
|
||||
static const struct irq_domain_ops *vpe_domain_ops;
|
||||
|
||||
int its_alloc_vcpu_irqs(struct its_vm *vm)
|
||||
{
|
||||
int vpe_base_irq, i;
|
||||
|
||||
vm->fwnode = irq_domain_alloc_named_id_fwnode("GICv4-vpe",
|
||||
task_pid_nr(current));
|
||||
if (!vm->fwnode)
|
||||
goto err;
|
||||
|
||||
vm->domain = irq_domain_create_hierarchy(gic_domain, 0, vm->nr_vpes,
|
||||
vm->fwnode, vpe_domain_ops,
|
||||
vm);
|
||||
if (!vm->domain)
|
||||
goto err;
|
||||
|
||||
for (i = 0; i < vm->nr_vpes; i++) {
|
||||
vm->vpes[i]->its_vm = vm;
|
||||
vm->vpes[i]->idai = true;
|
||||
}
|
||||
|
||||
vpe_base_irq = __irq_domain_alloc_irqs(vm->domain, -1, vm->nr_vpes,
|
||||
NUMA_NO_NODE, vm,
|
||||
false, NULL);
|
||||
if (vpe_base_irq <= 0)
|
||||
goto err;
|
||||
|
||||
for (i = 0; i < vm->nr_vpes; i++)
|
||||
vm->vpes[i]->irq = vpe_base_irq + i;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
if (vm->domain)
|
||||
irq_domain_remove(vm->domain);
|
||||
if (vm->fwnode)
|
||||
irq_domain_free_fwnode(vm->fwnode);
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
void its_free_vcpu_irqs(struct its_vm *vm)
|
||||
{
|
||||
irq_domain_free_irqs(vm->vpes[0]->irq, vm->nr_vpes);
|
||||
irq_domain_remove(vm->domain);
|
||||
irq_domain_free_fwnode(vm->fwnode);
|
||||
}
|
||||
|
||||
static int its_send_vpe_cmd(struct its_vpe *vpe, struct its_cmd_info *info)
|
||||
{
|
||||
return irq_set_vcpu_affinity(vpe->irq, info);
|
||||
}
|
||||
|
||||
int its_schedule_vpe(struct its_vpe *vpe, bool on)
|
||||
{
|
||||
struct its_cmd_info info;
|
||||
|
||||
WARN_ON(preemptible());
|
||||
|
||||
info.cmd_type = on ? SCHEDULE_VPE : DESCHEDULE_VPE;
|
||||
|
||||
return its_send_vpe_cmd(vpe, &info);
|
||||
}
|
||||
|
||||
int its_invall_vpe(struct its_vpe *vpe)
|
||||
{
|
||||
struct its_cmd_info info = {
|
||||
.cmd_type = INVALL_VPE,
|
||||
};
|
||||
|
||||
return its_send_vpe_cmd(vpe, &info);
|
||||
}
|
||||
|
||||
int its_map_vlpi(int irq, struct its_vlpi_map *map)
|
||||
{
|
||||
struct its_cmd_info info = {
|
||||
.cmd_type = MAP_VLPI,
|
||||
.map = map,
|
||||
};
|
||||
|
||||
/*
|
||||
* The host will never see that interrupt firing again, so it
|
||||
* is vital that we don't do any lazy masking.
|
||||
*/
|
||||
irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY);
|
||||
|
||||
return irq_set_vcpu_affinity(irq, &info);
|
||||
}
|
||||
|
||||
int its_get_vlpi(int irq, struct its_vlpi_map *map)
|
||||
{
|
||||
struct its_cmd_info info = {
|
||||
.cmd_type = GET_VLPI,
|
||||
.map = map,
|
||||
};
|
||||
|
||||
return irq_set_vcpu_affinity(irq, &info);
|
||||
}
|
||||
|
||||
int its_unmap_vlpi(int irq)
|
||||
{
|
||||
irq_clear_status_flags(irq, IRQ_DISABLE_UNLAZY);
|
||||
return irq_set_vcpu_affinity(irq, NULL);
|
||||
}
|
||||
|
||||
int its_prop_update_vlpi(int irq, u8 config, bool inv)
|
||||
{
|
||||
struct its_cmd_info info = {
|
||||
.cmd_type = inv ? PROP_UPDATE_AND_INV_VLPI : PROP_UPDATE_VLPI,
|
||||
.config = config,
|
||||
};
|
||||
|
||||
return irq_set_vcpu_affinity(irq, &info);
|
||||
}
|
||||
|
||||
int its_init_v4(struct irq_domain *domain, const struct irq_domain_ops *ops)
|
||||
{
|
||||
if (domain) {
|
||||
pr_info("ITS: Enabling GICv4 support\n");
|
||||
gic_domain = domain;
|
||||
vpe_domain_ops = ops;
|
||||
return 0;
|
||||
}
|
||||
|
||||
pr_err("ITS: No GICv4 VPE domain allocated\n");
|
||||
return -ENODEV;
|
||||
}
|
|
@ -412,7 +412,7 @@ static void gic_handle_cascade_irq(struct irq_desc *desc)
|
|||
chained_irq_exit(chip, desc);
|
||||
}
|
||||
|
||||
static struct irq_chip gic_chip = {
|
||||
static const struct irq_chip gic_chip = {
|
||||
.irq_mask = gic_mask_irq,
|
||||
.irq_unmask = gic_unmask_irq,
|
||||
.irq_eoi = gic_eoi_irq,
|
||||
|
|
|
@ -214,13 +214,13 @@ static int __init imx_gpcv2_irqchip_init(struct device_node *node,
|
|||
int i;
|
||||
|
||||
if (!parent) {
|
||||
pr_err("%s: no parent, giving up\n", node->full_name);
|
||||
pr_err("%pOF: no parent, giving up\n", node);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
parent_domain = irq_find_host(parent);
|
||||
if (!parent_domain) {
|
||||
pr_err("%s: unable to get parent domain\n", node->full_name);
|
||||
pr_err("%pOF: unable to get parent domain\n", node);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
|
|
|
@ -191,7 +191,7 @@ static int __init lpc32xx_of_ic_init(struct device_node *node,
|
|||
|
||||
irqc->base = of_iomap(node, 0);
|
||||
if (!irqc->base) {
|
||||
pr_err("%s: unable to map registers\n", node->full_name);
|
||||
pr_err("%pOF: unable to map registers\n", node);
|
||||
kfree(irqc);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
|
@ -17,13 +17,32 @@
|
|||
#include <linux/irq.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_pci.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#define MSI_MAX_IRQS 32
|
||||
#define MSI_IBS_SHIFT 3
|
||||
#define MSIR 4
|
||||
#define MSI_IRQS_PER_MSIR 32
|
||||
#define MSI_MSIR_OFFSET 4
|
||||
|
||||
#define MSI_LS1043V1_1_IRQS_PER_MSIR 8
|
||||
#define MSI_LS1043V1_1_MSIR_OFFSET 0x10
|
||||
|
||||
struct ls_scfg_msi_cfg {
|
||||
u32 ibs_shift; /* Shift of interrupt bit select */
|
||||
u32 msir_irqs; /* The irq number per MSIR */
|
||||
u32 msir_base; /* The base address of MSIR */
|
||||
};
|
||||
|
||||
struct ls_scfg_msir {
|
||||
struct ls_scfg_msi *msi_data;
|
||||
unsigned int index;
|
||||
unsigned int gic_irq;
|
||||
unsigned int bit_start;
|
||||
unsigned int bit_end;
|
||||
unsigned int srs; /* Shared interrupt register select */
|
||||
void __iomem *reg;
|
||||
};
|
||||
|
||||
struct ls_scfg_msi {
|
||||
spinlock_t lock;
|
||||
|
@ -32,8 +51,11 @@ struct ls_scfg_msi {
|
|||
struct irq_domain *msi_domain;
|
||||
void __iomem *regs;
|
||||
phys_addr_t msiir_addr;
|
||||
int irq;
|
||||
DECLARE_BITMAP(used, MSI_MAX_IRQS);
|
||||
struct ls_scfg_msi_cfg *cfg;
|
||||
u32 msir_num;
|
||||
struct ls_scfg_msir *msir;
|
||||
u32 irqs_num;
|
||||
unsigned long *used;
|
||||
};
|
||||
|
||||
static struct irq_chip ls_scfg_msi_irq_chip = {
|
||||
|
@ -49,19 +71,56 @@ static struct msi_domain_info ls_scfg_msi_domain_info = {
|
|||
.chip = &ls_scfg_msi_irq_chip,
|
||||
};
|
||||
|
||||
static int msi_affinity_flag = 1;
|
||||
|
||||
static int __init early_parse_ls_scfg_msi(char *p)
|
||||
{
|
||||
if (p && strncmp(p, "no-affinity", 11) == 0)
|
||||
msi_affinity_flag = 0;
|
||||
else
|
||||
msi_affinity_flag = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
early_param("lsmsi", early_parse_ls_scfg_msi);
|
||||
|
||||
static void ls_scfg_msi_compose_msg(struct irq_data *data, struct msi_msg *msg)
|
||||
{
|
||||
struct ls_scfg_msi *msi_data = irq_data_get_irq_chip_data(data);
|
||||
|
||||
msg->address_hi = upper_32_bits(msi_data->msiir_addr);
|
||||
msg->address_lo = lower_32_bits(msi_data->msiir_addr);
|
||||
msg->data = data->hwirq << MSI_IBS_SHIFT;
|
||||
msg->data = data->hwirq;
|
||||
|
||||
if (msi_affinity_flag)
|
||||
msg->data |= cpumask_first(data->common->affinity);
|
||||
}
|
||||
|
||||
static int ls_scfg_msi_set_affinity(struct irq_data *irq_data,
|
||||
const struct cpumask *mask, bool force)
|
||||
{
|
||||
return -EINVAL;
|
||||
struct ls_scfg_msi *msi_data = irq_data_get_irq_chip_data(irq_data);
|
||||
u32 cpu;
|
||||
|
||||
if (!msi_affinity_flag)
|
||||
return -EINVAL;
|
||||
|
||||
if (!force)
|
||||
cpu = cpumask_any_and(mask, cpu_online_mask);
|
||||
else
|
||||
cpu = cpumask_first(mask);
|
||||
|
||||
if (cpu >= msi_data->msir_num)
|
||||
return -EINVAL;
|
||||
|
||||
if (msi_data->msir[cpu].gic_irq <= 0) {
|
||||
pr_warn("cannot bind the irq to cpu%d\n", cpu);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cpumask_copy(irq_data->common->affinity, mask);
|
||||
|
||||
return IRQ_SET_MASK_OK;
|
||||
}
|
||||
|
||||
static struct irq_chip ls_scfg_msi_parent_chip = {
|
||||
|
@ -81,8 +140,8 @@ static int ls_scfg_msi_domain_irq_alloc(struct irq_domain *domain,
|
|||
WARN_ON(nr_irqs != 1);
|
||||
|
||||
spin_lock(&msi_data->lock);
|
||||
pos = find_first_zero_bit(msi_data->used, MSI_MAX_IRQS);
|
||||
if (pos < MSI_MAX_IRQS)
|
||||
pos = find_first_zero_bit(msi_data->used, msi_data->irqs_num);
|
||||
if (pos < msi_data->irqs_num)
|
||||
__set_bit(pos, msi_data->used);
|
||||
else
|
||||
err = -ENOSPC;
|
||||
|
@ -106,7 +165,7 @@ static void ls_scfg_msi_domain_irq_free(struct irq_domain *domain,
|
|||
int pos;
|
||||
|
||||
pos = d->hwirq;
|
||||
if (pos < 0 || pos >= MSI_MAX_IRQS) {
|
||||
if (pos < 0 || pos >= msi_data->irqs_num) {
|
||||
pr_err("failed to teardown msi. Invalid hwirq %d\n", pos);
|
||||
return;
|
||||
}
|
||||
|
@ -123,15 +182,22 @@ static const struct irq_domain_ops ls_scfg_msi_domain_ops = {
|
|||
|
||||
static void ls_scfg_msi_irq_handler(struct irq_desc *desc)
|
||||
{
|
||||
struct ls_scfg_msi *msi_data = irq_desc_get_handler_data(desc);
|
||||
struct ls_scfg_msir *msir = irq_desc_get_handler_data(desc);
|
||||
struct ls_scfg_msi *msi_data = msir->msi_data;
|
||||
unsigned long val;
|
||||
int pos, virq;
|
||||
int pos, size, virq, hwirq;
|
||||
|
||||
chained_irq_enter(irq_desc_get_chip(desc), desc);
|
||||
|
||||
val = ioread32be(msi_data->regs + MSIR);
|
||||
for_each_set_bit(pos, &val, MSI_MAX_IRQS) {
|
||||
virq = irq_find_mapping(msi_data->parent, (31 - pos));
|
||||
val = ioread32be(msir->reg);
|
||||
|
||||
pos = msir->bit_start;
|
||||
size = msir->bit_end + 1;
|
||||
|
||||
for_each_set_bit_from(pos, &val, size) {
|
||||
hwirq = ((msir->bit_end - pos) << msi_data->cfg->ibs_shift) |
|
||||
msir->srs;
|
||||
virq = irq_find_mapping(msi_data->parent, hwirq);
|
||||
if (virq)
|
||||
generic_handle_irq(virq);
|
||||
}
|
||||
|
@ -143,7 +209,7 @@ static int ls_scfg_msi_domains_init(struct ls_scfg_msi *msi_data)
|
|||
{
|
||||
/* Initialize MSI domain parent */
|
||||
msi_data->parent = irq_domain_add_linear(NULL,
|
||||
MSI_MAX_IRQS,
|
||||
msi_data->irqs_num,
|
||||
&ls_scfg_msi_domain_ops,
|
||||
msi_data);
|
||||
if (!msi_data->parent) {
|
||||
|
@ -164,16 +230,117 @@ static int ls_scfg_msi_domains_init(struct ls_scfg_msi *msi_data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ls_scfg_msi_setup_hwirq(struct ls_scfg_msi *msi_data, int index)
|
||||
{
|
||||
struct ls_scfg_msir *msir;
|
||||
int virq, i, hwirq;
|
||||
|
||||
virq = platform_get_irq(msi_data->pdev, index);
|
||||
if (virq <= 0)
|
||||
return -ENODEV;
|
||||
|
||||
msir = &msi_data->msir[index];
|
||||
msir->index = index;
|
||||
msir->msi_data = msi_data;
|
||||
msir->gic_irq = virq;
|
||||
msir->reg = msi_data->regs + msi_data->cfg->msir_base + 4 * index;
|
||||
|
||||
if (msi_data->cfg->msir_irqs == MSI_LS1043V1_1_IRQS_PER_MSIR) {
|
||||
msir->bit_start = 32 - ((msir->index + 1) *
|
||||
MSI_LS1043V1_1_IRQS_PER_MSIR);
|
||||
msir->bit_end = msir->bit_start +
|
||||
MSI_LS1043V1_1_IRQS_PER_MSIR - 1;
|
||||
} else {
|
||||
msir->bit_start = 0;
|
||||
msir->bit_end = msi_data->cfg->msir_irqs - 1;
|
||||
}
|
||||
|
||||
irq_set_chained_handler_and_data(msir->gic_irq,
|
||||
ls_scfg_msi_irq_handler,
|
||||
msir);
|
||||
|
||||
if (msi_affinity_flag) {
|
||||
/* Associate MSIR interrupt to the cpu */
|
||||
irq_set_affinity(msir->gic_irq, get_cpu_mask(index));
|
||||
msir->srs = 0; /* This value is determined by the CPU */
|
||||
} else
|
||||
msir->srs = index;
|
||||
|
||||
/* Release the hwirqs corresponding to this MSIR */
|
||||
if (!msi_affinity_flag || msir->index == 0) {
|
||||
for (i = 0; i < msi_data->cfg->msir_irqs; i++) {
|
||||
hwirq = i << msi_data->cfg->ibs_shift | msir->index;
|
||||
bitmap_clear(msi_data->used, hwirq, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ls_scfg_msi_teardown_hwirq(struct ls_scfg_msir *msir)
|
||||
{
|
||||
struct ls_scfg_msi *msi_data = msir->msi_data;
|
||||
int i, hwirq;
|
||||
|
||||
if (msir->gic_irq > 0)
|
||||
irq_set_chained_handler_and_data(msir->gic_irq, NULL, NULL);
|
||||
|
||||
for (i = 0; i < msi_data->cfg->msir_irqs; i++) {
|
||||
hwirq = i << msi_data->cfg->ibs_shift | msir->index;
|
||||
bitmap_set(msi_data->used, hwirq, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ls_scfg_msi_cfg ls1021_msi_cfg = {
|
||||
.ibs_shift = 3,
|
||||
.msir_irqs = MSI_IRQS_PER_MSIR,
|
||||
.msir_base = MSI_MSIR_OFFSET,
|
||||
};
|
||||
|
||||
static struct ls_scfg_msi_cfg ls1046_msi_cfg = {
|
||||
.ibs_shift = 2,
|
||||
.msir_irqs = MSI_IRQS_PER_MSIR,
|
||||
.msir_base = MSI_MSIR_OFFSET,
|
||||
};
|
||||
|
||||
static struct ls_scfg_msi_cfg ls1043_v1_1_msi_cfg = {
|
||||
.ibs_shift = 2,
|
||||
.msir_irqs = MSI_LS1043V1_1_IRQS_PER_MSIR,
|
||||
.msir_base = MSI_LS1043V1_1_MSIR_OFFSET,
|
||||
};
|
||||
|
||||
static const struct of_device_id ls_scfg_msi_id[] = {
|
||||
/* The following two misspelled compatibles are obsolete */
|
||||
{ .compatible = "fsl,1s1021a-msi", .data = &ls1021_msi_cfg},
|
||||
{ .compatible = "fsl,1s1043a-msi", .data = &ls1021_msi_cfg},
|
||||
|
||||
{ .compatible = "fsl,ls1021a-msi", .data = &ls1021_msi_cfg },
|
||||
{ .compatible = "fsl,ls1043a-msi", .data = &ls1021_msi_cfg },
|
||||
{ .compatible = "fsl,ls1043a-v1.1-msi", .data = &ls1043_v1_1_msi_cfg },
|
||||
{ .compatible = "fsl,ls1046a-msi", .data = &ls1046_msi_cfg },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ls_scfg_msi_id);
|
||||
|
||||
static int ls_scfg_msi_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
struct ls_scfg_msi *msi_data;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
int i, ret;
|
||||
|
||||
match = of_match_device(ls_scfg_msi_id, &pdev->dev);
|
||||
if (!match)
|
||||
return -ENODEV;
|
||||
|
||||
msi_data = devm_kzalloc(&pdev->dev, sizeof(*msi_data), GFP_KERNEL);
|
||||
if (!msi_data)
|
||||
return -ENOMEM;
|
||||
|
||||
msi_data->cfg = (struct ls_scfg_msi_cfg *) match->data;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
msi_data->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(msi_data->regs)) {
|
||||
|
@ -182,23 +349,48 @@ static int ls_scfg_msi_probe(struct platform_device *pdev)
|
|||
}
|
||||
msi_data->msiir_addr = res->start;
|
||||
|
||||
msi_data->irq = platform_get_irq(pdev, 0);
|
||||
if (msi_data->irq <= 0) {
|
||||
dev_err(&pdev->dev, "failed to get MSI irq\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
msi_data->pdev = pdev;
|
||||
spin_lock_init(&msi_data->lock);
|
||||
|
||||
msi_data->irqs_num = MSI_IRQS_PER_MSIR *
|
||||
(1 << msi_data->cfg->ibs_shift);
|
||||
msi_data->used = devm_kcalloc(&pdev->dev,
|
||||
BITS_TO_LONGS(msi_data->irqs_num),
|
||||
sizeof(*msi_data->used),
|
||||
GFP_KERNEL);
|
||||
if (!msi_data->used)
|
||||
return -ENOMEM;
|
||||
/*
|
||||
* Reserve all the hwirqs
|
||||
* The available hwirqs will be released in ls1_msi_setup_hwirq()
|
||||
*/
|
||||
bitmap_set(msi_data->used, 0, msi_data->irqs_num);
|
||||
|
||||
msi_data->msir_num = of_irq_count(pdev->dev.of_node);
|
||||
|
||||
if (msi_affinity_flag) {
|
||||
u32 cpu_num;
|
||||
|
||||
cpu_num = num_possible_cpus();
|
||||
if (msi_data->msir_num >= cpu_num)
|
||||
msi_data->msir_num = cpu_num;
|
||||
else
|
||||
msi_affinity_flag = 0;
|
||||
}
|
||||
|
||||
msi_data->msir = devm_kcalloc(&pdev->dev, msi_data->msir_num,
|
||||
sizeof(*msi_data->msir),
|
||||
GFP_KERNEL);
|
||||
if (!msi_data->msir)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < msi_data->msir_num; i++)
|
||||
ls_scfg_msi_setup_hwirq(msi_data, i);
|
||||
|
||||
ret = ls_scfg_msi_domains_init(msi_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
irq_set_chained_handler_and_data(msi_data->irq,
|
||||
ls_scfg_msi_irq_handler,
|
||||
msi_data);
|
||||
|
||||
platform_set_drvdata(pdev, msi_data);
|
||||
|
||||
return 0;
|
||||
|
@ -207,8 +399,10 @@ static int ls_scfg_msi_probe(struct platform_device *pdev)
|
|||
static int ls_scfg_msi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ls_scfg_msi *msi_data = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
|
||||
irq_set_chained_handler_and_data(msi_data->irq, NULL, NULL);
|
||||
for (i = 0; i < msi_data->msir_num; i++)
|
||||
ls_scfg_msi_teardown_hwirq(&msi_data->msir[i]);
|
||||
|
||||
irq_domain_remove(msi_data->msi_domain);
|
||||
irq_domain_remove(msi_data->parent);
|
||||
|
@ -218,12 +412,6 @@ static int ls_scfg_msi_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ls_scfg_msi_id[] = {
|
||||
{ .compatible = "fsl,1s1021a-msi", },
|
||||
{ .compatible = "fsl,1s1043a-msi", },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver ls_scfg_msi_driver = {
|
||||
.driver = {
|
||||
.name = "ls-scfg-msi",
|
||||
|
|
|
@ -181,13 +181,13 @@ const struct irq_domain_ops mmp_irq_domain_ops = {
|
|||
.xlate = mmp_irq_domain_xlate,
|
||||
};
|
||||
|
||||
static struct mmp_intc_conf mmp_conf = {
|
||||
static const struct mmp_intc_conf mmp_conf = {
|
||||
.conf_enable = 0x51,
|
||||
.conf_disable = 0x0,
|
||||
.conf_mask = 0x7f,
|
||||
};
|
||||
|
||||
static struct mmp_intc_conf mmp2_conf = {
|
||||
static const struct mmp_intc_conf mmp2_conf = {
|
||||
.conf_enable = 0x20,
|
||||
.conf_disable = 0x0,
|
||||
.conf_mask = 0x7f,
|
||||
|
|
|
@ -178,8 +178,7 @@ static int __init mtk_sysirq_of_init(struct device_node *node,
|
|||
chip_data->intpol_words[i] = size / 4;
|
||||
chip_data->intpol_bases[i] = of_iomap(node, i);
|
||||
if (ret || !chip_data->intpol_bases[i]) {
|
||||
pr_err("%s: couldn't map region %d\n",
|
||||
node->full_name, i);
|
||||
pr_err("%pOF: couldn't map region %d\n", node, i);
|
||||
ret = -ENODEV;
|
||||
goto out_free_intpol;
|
||||
}
|
||||
|
|
|
@ -179,7 +179,7 @@ static void __init icoll_add_domain(struct device_node *np,
|
|||
&icoll_irq_domain_ops, NULL);
|
||||
|
||||
if (!icoll_domain)
|
||||
panic("%s: unable to create irq domain", np->full_name);
|
||||
panic("%pOF: unable to create irq domain", np);
|
||||
}
|
||||
|
||||
static void __iomem * __init icoll_init_iobase(struct device_node *np)
|
||||
|
@ -188,7 +188,7 @@ static void __iomem * __init icoll_init_iobase(struct device_node *np)
|
|||
|
||||
icoll_base = of_io_request_and_map(np, 0, np->name);
|
||||
if (IS_ERR(icoll_base))
|
||||
panic("%s: unable to map resource", np->full_name);
|
||||
panic("%pOF: unable to map resource", np);
|
||||
return icoll_base;
|
||||
}
|
||||
|
||||
|
|
|
@ -140,7 +140,7 @@ static int __init stm32_exti_init(struct device_node *node,
|
|||
|
||||
base = of_iomap(node, 0);
|
||||
if (!base) {
|
||||
pr_err("%s: Unable to map registers\n", node->full_name);
|
||||
pr_err("%pOF: Unable to map registers\n", node);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
@ -149,7 +149,7 @@ static int __init stm32_exti_init(struct device_node *node,
|
|||
nr_exti = fls(readl_relaxed(base + EXTI_RTSR));
|
||||
writel_relaxed(0, base + EXTI_RTSR);
|
||||
|
||||
pr_info("%s: %d External IRQs detected\n", node->full_name, nr_exti);
|
||||
pr_info("%pOF: %d External IRQs detected\n", node, nr_exti);
|
||||
|
||||
domain = irq_domain_add_linear(node, nr_exti,
|
||||
&irq_exti_domain_ops, NULL);
|
||||
|
@ -163,8 +163,8 @@ static int __init stm32_exti_init(struct device_node *node,
|
|||
ret = irq_alloc_domain_generic_chips(domain, nr_exti, 1, "exti",
|
||||
handle_edge_irq, clr, 0, 0);
|
||||
if (ret) {
|
||||
pr_err("%s: Could not allocate generic interrupt chip.\n",
|
||||
node->full_name);
|
||||
pr_err("%pOF: Could not allocate generic interrupt chip.\n",
|
||||
node);
|
||||
goto out_free_domain;
|
||||
}
|
||||
|
||||
|
|
|
@ -97,8 +97,8 @@ static int __init sun4i_of_init(struct device_node *node,
|
|||
{
|
||||
sun4i_irq_base = of_iomap(node, 0);
|
||||
if (!sun4i_irq_base)
|
||||
panic("%s: unable to map IC registers\n",
|
||||
node->full_name);
|
||||
panic("%pOF: unable to map IC registers\n",
|
||||
node);
|
||||
|
||||
/* Disable all interrupts */
|
||||
writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(0));
|
||||
|
@ -124,7 +124,7 @@ static int __init sun4i_of_init(struct device_node *node,
|
|||
sun4i_irq_domain = irq_domain_add_linear(node, 3 * 32,
|
||||
&sun4i_irq_ops, NULL);
|
||||
if (!sun4i_irq_domain)
|
||||
panic("%s: unable to create IRQ domain\n", node->full_name);
|
||||
panic("%pOF: unable to create IRQ domain\n", node);
|
||||
|
||||
set_handle_irq(sun4i_handle_irq);
|
||||
|
||||
|
|
|
@ -291,13 +291,13 @@ static int __init tegra_ictlr_init(struct device_node *node,
|
|||
int err;
|
||||
|
||||
if (!parent) {
|
||||
pr_err("%s: no parent, giving up\n", node->full_name);
|
||||
pr_err("%pOF: no parent, giving up\n", node);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
parent_domain = irq_find_host(parent);
|
||||
if (!parent_domain) {
|
||||
pr_err("%s: unable to obtain parent domain\n", node->full_name);
|
||||
pr_err("%pOF: unable to obtain parent domain\n", node);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
|
@ -329,29 +329,29 @@ static int __init tegra_ictlr_init(struct device_node *node,
|
|||
}
|
||||
|
||||
if (!num_ictlrs) {
|
||||
pr_err("%s: no valid regions, giving up\n", node->full_name);
|
||||
pr_err("%pOF: no valid regions, giving up\n", node);
|
||||
err = -ENOMEM;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
WARN(num_ictlrs != soc->num_ictlrs,
|
||||
"%s: Found %u interrupt controllers in DT; expected %u.\n",
|
||||
node->full_name, num_ictlrs, soc->num_ictlrs);
|
||||
"%pOF: Found %u interrupt controllers in DT; expected %u.\n",
|
||||
node, num_ictlrs, soc->num_ictlrs);
|
||||
|
||||
|
||||
domain = irq_domain_add_hierarchy(parent_domain, 0, num_ictlrs * 32,
|
||||
node, &tegra_ictlr_domain_ops,
|
||||
lic);
|
||||
if (!domain) {
|
||||
pr_err("%s: failed to allocated domain\n", node->full_name);
|
||||
pr_err("%pOF: failed to allocated domain\n", node);
|
||||
err = -ENOMEM;
|
||||
goto out_unmap;
|
||||
}
|
||||
|
||||
tegra_ictlr_syscore_init();
|
||||
|
||||
pr_info("%s: %d interrupts forwarded to %s\n",
|
||||
node->full_name, num_ictlrs * 32, parent->full_name);
|
||||
pr_info("%pOF: %d interrupts forwarded to %pOF\n",
|
||||
node, num_ictlrs * 32, parent);
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
* Driver for UniPhier AIDET (ARM Interrupt Detector)
|
||||
*
|
||||
* Copyright (C) 2017 Socionext Inc.
|
||||
* Author: Masahiro Yamada <yamada.masahiro@socionext.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#define UNIPHIER_AIDET_NR_IRQS 256
|
||||
|
||||
#define UNIPHIER_AIDET_DETCONF 0x04 /* inverter register base */
|
||||
|
||||
struct uniphier_aidet_priv {
|
||||
struct irq_domain *domain;
|
||||
void __iomem *reg_base;
|
||||
spinlock_t lock;
|
||||
u32 saved_vals[UNIPHIER_AIDET_NR_IRQS / 32];
|
||||
};
|
||||
|
||||
static void uniphier_aidet_reg_update(struct uniphier_aidet_priv *priv,
|
||||
unsigned int reg, u32 mask, u32 val)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 tmp;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
tmp = readl_relaxed(priv->reg_base + reg);
|
||||
tmp &= ~mask;
|
||||
tmp |= mask & val;
|
||||
writel_relaxed(tmp, priv->reg_base + reg);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
}
|
||||
|
||||
static void uniphier_aidet_detconf_update(struct uniphier_aidet_priv *priv,
|
||||
unsigned long index, unsigned int val)
|
||||
{
|
||||
unsigned int reg;
|
||||
u32 mask;
|
||||
|
||||
reg = UNIPHIER_AIDET_DETCONF + index / 32 * 4;
|
||||
mask = BIT(index % 32);
|
||||
|
||||
uniphier_aidet_reg_update(priv, reg, mask, val ? mask : 0);
|
||||
}
|
||||
|
||||
static int uniphier_aidet_irq_set_type(struct irq_data *data, unsigned int type)
|
||||
{
|
||||
struct uniphier_aidet_priv *priv = data->chip_data;
|
||||
unsigned int val;
|
||||
|
||||
/* enable inverter for active low triggers */
|
||||
switch (type) {
|
||||
case IRQ_TYPE_EDGE_RISING:
|
||||
case IRQ_TYPE_LEVEL_HIGH:
|
||||
val = 0;
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_FALLING:
|
||||
val = 1;
|
||||
type = IRQ_TYPE_EDGE_RISING;
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_LOW:
|
||||
val = 1;
|
||||
type = IRQ_TYPE_LEVEL_HIGH;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
uniphier_aidet_detconf_update(priv, data->hwirq, val);
|
||||
|
||||
return irq_chip_set_type_parent(data, type);
|
||||
}
|
||||
|
||||
static struct irq_chip uniphier_aidet_irq_chip = {
|
||||
.name = "AIDET",
|
||||
.irq_mask = irq_chip_mask_parent,
|
||||
.irq_unmask = irq_chip_unmask_parent,
|
||||
.irq_eoi = irq_chip_eoi_parent,
|
||||
.irq_set_affinity = irq_chip_set_affinity_parent,
|
||||
.irq_set_type = uniphier_aidet_irq_set_type,
|
||||
};
|
||||
|
||||
static int uniphier_aidet_domain_translate(struct irq_domain *domain,
|
||||
struct irq_fwspec *fwspec,
|
||||
unsigned long *out_hwirq,
|
||||
unsigned int *out_type)
|
||||
{
|
||||
if (WARN_ON(fwspec->param_count < 2))
|
||||
return -EINVAL;
|
||||
|
||||
*out_hwirq = fwspec->param[0];
|
||||
*out_type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uniphier_aidet_domain_alloc(struct irq_domain *domain,
|
||||
unsigned int virq, unsigned int nr_irqs,
|
||||
void *arg)
|
||||
{
|
||||
struct irq_fwspec parent_fwspec;
|
||||
irq_hw_number_t hwirq;
|
||||
unsigned int type;
|
||||
int ret;
|
||||
|
||||
if (nr_irqs != 1)
|
||||
return -EINVAL;
|
||||
|
||||
ret = uniphier_aidet_domain_translate(domain, arg, &hwirq, &type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (type) {
|
||||
case IRQ_TYPE_EDGE_RISING:
|
||||
case IRQ_TYPE_LEVEL_HIGH:
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_FALLING:
|
||||
type = IRQ_TYPE_EDGE_RISING;
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_LOW:
|
||||
type = IRQ_TYPE_LEVEL_HIGH;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (hwirq >= UNIPHIER_AIDET_NR_IRQS)
|
||||
return -ENXIO;
|
||||
|
||||
ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
|
||||
&uniphier_aidet_irq_chip,
|
||||
domain->host_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* parent is GIC */
|
||||
parent_fwspec.fwnode = domain->parent->fwnode;
|
||||
parent_fwspec.param_count = 3;
|
||||
parent_fwspec.param[0] = 0; /* SPI */
|
||||
parent_fwspec.param[1] = hwirq;
|
||||
parent_fwspec.param[2] = type;
|
||||
|
||||
return irq_domain_alloc_irqs_parent(domain, virq, 1, &parent_fwspec);
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops uniphier_aidet_domain_ops = {
|
||||
.alloc = uniphier_aidet_domain_alloc,
|
||||
.free = irq_domain_free_irqs_common,
|
||||
.translate = uniphier_aidet_domain_translate,
|
||||
};
|
||||
|
||||
static int uniphier_aidet_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *parent_np;
|
||||
struct irq_domain *parent_domain;
|
||||
struct uniphier_aidet_priv *priv;
|
||||
struct resource *res;
|
||||
|
||||
parent_np = of_irq_find_parent(dev->of_node);
|
||||
if (!parent_np)
|
||||
return -ENXIO;
|
||||
|
||||
parent_domain = irq_find_host(parent_np);
|
||||
of_node_put(parent_np);
|
||||
if (!parent_domain)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
priv->reg_base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(priv->reg_base))
|
||||
return PTR_ERR(priv->reg_base);
|
||||
|
||||
spin_lock_init(&priv->lock);
|
||||
|
||||
priv->domain = irq_domain_create_hierarchy(
|
||||
parent_domain, 0,
|
||||
UNIPHIER_AIDET_NR_IRQS,
|
||||
of_node_to_fwnode(dev->of_node),
|
||||
&uniphier_aidet_domain_ops, priv);
|
||||
if (!priv->domain)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused uniphier_aidet_suspend(struct device *dev)
|
||||
{
|
||||
struct uniphier_aidet_priv *priv = dev_get_drvdata(dev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(priv->saved_vals); i++)
|
||||
priv->saved_vals[i] = readl_relaxed(
|
||||
priv->reg_base + UNIPHIER_AIDET_DETCONF + i * 4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused uniphier_aidet_resume(struct device *dev)
|
||||
{
|
||||
struct uniphier_aidet_priv *priv = dev_get_drvdata(dev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(priv->saved_vals); i++)
|
||||
writel_relaxed(priv->saved_vals[i],
|
||||
priv->reg_base + UNIPHIER_AIDET_DETCONF + i * 4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops uniphier_aidet_pm_ops = {
|
||||
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(uniphier_aidet_suspend,
|
||||
uniphier_aidet_resume)
|
||||
};
|
||||
|
||||
static const struct of_device_id uniphier_aidet_match[] = {
|
||||
{ .compatible = "socionext,uniphier-ld4-aidet" },
|
||||
{ .compatible = "socionext,uniphier-pro4-aidet" },
|
||||
{ .compatible = "socionext,uniphier-sld8-aidet" },
|
||||
{ .compatible = "socionext,uniphier-pro5-aidet" },
|
||||
{ .compatible = "socionext,uniphier-pxs2-aidet" },
|
||||
{ .compatible = "socionext,uniphier-ld11-aidet" },
|
||||
{ .compatible = "socionext,uniphier-ld20-aidet" },
|
||||
{ .compatible = "socionext,uniphier-pxs3-aidet" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static struct platform_driver uniphier_aidet_driver = {
|
||||
.probe = uniphier_aidet_probe,
|
||||
.driver = {
|
||||
.name = "uniphier-aidet",
|
||||
.of_match_table = uniphier_aidet_match,
|
||||
.pm = &uniphier_aidet_pm_ops,
|
||||
},
|
||||
};
|
||||
builtin_platform_driver(uniphier_aidet_driver);
|
|
@ -186,8 +186,8 @@ static int __init xilinx_intc_of_init(struct device_node *intc,
|
|||
if (irqc->intr_mask >> nr_irq)
|
||||
pr_warn("irq-xilinx: mismatch in kind-of-intr param\n");
|
||||
|
||||
pr_info("irq-xilinx: %s: num_irq=%d, edge=0x%x\n",
|
||||
intc->full_name, nr_irq, irqc->intr_mask);
|
||||
pr_info("irq-xilinx: %pOF: num_irq=%d, edge=0x%x\n",
|
||||
intc, nr_irq, irqc->intr_mask);
|
||||
|
||||
|
||||
/*
|
||||
|
|
|
@ -27,6 +27,8 @@ struct gic_kvm_info {
|
|||
unsigned int maint_irq;
|
||||
/* Virtual control interface */
|
||||
struct resource vctrl;
|
||||
/* vlpi support */
|
||||
bool has_v4;
|
||||
};
|
||||
|
||||
const struct gic_kvm_info *gic_get_kvm_info(void);
|
||||
|
|
|
@ -204,6 +204,7 @@
|
|||
|
||||
#define GICR_TYPER_PLPIS (1U << 0)
|
||||
#define GICR_TYPER_VLPIS (1U << 1)
|
||||
#define GICR_TYPER_DirectLPIS (1U << 3)
|
||||
#define GICR_TYPER_LAST (1U << 4)
|
||||
|
||||
#define GIC_V3_REDIST_SIZE 0x20000
|
||||
|
@ -211,6 +212,69 @@
|
|||
#define LPI_PROP_GROUP1 (1 << 1)
|
||||
#define LPI_PROP_ENABLED (1 << 0)
|
||||
|
||||
/*
|
||||
* Re-Distributor registers, offsets from VLPI_base
|
||||
*/
|
||||
#define GICR_VPROPBASER 0x0070
|
||||
|
||||
#define GICR_VPROPBASER_IDBITS_MASK 0x1f
|
||||
|
||||
#define GICR_VPROPBASER_SHAREABILITY_SHIFT (10)
|
||||
#define GICR_VPROPBASER_INNER_CACHEABILITY_SHIFT (7)
|
||||
#define GICR_VPROPBASER_OUTER_CACHEABILITY_SHIFT (56)
|
||||
|
||||
#define GICR_VPROPBASER_SHAREABILITY_MASK \
|
||||
GIC_BASER_SHAREABILITY(GICR_VPROPBASER, SHAREABILITY_MASK)
|
||||
#define GICR_VPROPBASER_INNER_CACHEABILITY_MASK \
|
||||
GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, MASK)
|
||||
#define GICR_VPROPBASER_OUTER_CACHEABILITY_MASK \
|
||||
GIC_BASER_CACHEABILITY(GICR_VPROPBASER, OUTER, MASK)
|
||||
#define GICR_VPROPBASER_CACHEABILITY_MASK \
|
||||
GICR_VPROPBASER_INNER_CACHEABILITY_MASK
|
||||
|
||||
#define GICR_VPROPBASER_InnerShareable \
|
||||
GIC_BASER_SHAREABILITY(GICR_VPROPBASER, InnerShareable)
|
||||
|
||||
#define GICR_VPROPBASER_nCnB GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, nCnB)
|
||||
#define GICR_VPROPBASER_nC GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, nC)
|
||||
#define GICR_VPROPBASER_RaWt GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, RaWt)
|
||||
#define GICR_VPROPBASER_RaWb GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, RaWt)
|
||||
#define GICR_VPROPBASER_WaWt GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, WaWt)
|
||||
#define GICR_VPROPBASER_WaWb GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, WaWb)
|
||||
#define GICR_VPROPBASER_RaWaWt GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, RaWaWt)
|
||||
#define GICR_VPROPBASER_RaWaWb GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, RaWaWb)
|
||||
|
||||
#define GICR_VPENDBASER 0x0078
|
||||
|
||||
#define GICR_VPENDBASER_SHAREABILITY_SHIFT (10)
|
||||
#define GICR_VPENDBASER_INNER_CACHEABILITY_SHIFT (7)
|
||||
#define GICR_VPENDBASER_OUTER_CACHEABILITY_SHIFT (56)
|
||||
#define GICR_VPENDBASER_SHAREABILITY_MASK \
|
||||
GIC_BASER_SHAREABILITY(GICR_VPENDBASER, SHAREABILITY_MASK)
|
||||
#define GICR_VPENDBASER_INNER_CACHEABILITY_MASK \
|
||||
GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, MASK)
|
||||
#define GICR_VPENDBASER_OUTER_CACHEABILITY_MASK \
|
||||
GIC_BASER_CACHEABILITY(GICR_VPENDBASER, OUTER, MASK)
|
||||
#define GICR_VPENDBASER_CACHEABILITY_MASK \
|
||||
GICR_VPENDBASER_INNER_CACHEABILITY_MASK
|
||||
|
||||
#define GICR_VPENDBASER_NonShareable \
|
||||
GIC_BASER_SHAREABILITY(GICR_VPENDBASER, NonShareable)
|
||||
|
||||
#define GICR_VPENDBASER_nCnB GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, nCnB)
|
||||
#define GICR_VPENDBASER_nC GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, nC)
|
||||
#define GICR_VPENDBASER_RaWt GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, RaWt)
|
||||
#define GICR_VPENDBASER_RaWb GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, RaWt)
|
||||
#define GICR_VPENDBASER_WaWt GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, WaWt)
|
||||
#define GICR_VPENDBASER_WaWb GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, WaWb)
|
||||
#define GICR_VPENDBASER_RaWaWt GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, RaWaWt)
|
||||
#define GICR_VPENDBASER_RaWaWb GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, RaWaWb)
|
||||
|
||||
#define GICR_VPENDBASER_Dirty (1ULL << 60)
|
||||
#define GICR_VPENDBASER_PendingLast (1ULL << 61)
|
||||
#define GICR_VPENDBASER_IDAI (1ULL << 62)
|
||||
#define GICR_VPENDBASER_Valid (1ULL << 63)
|
||||
|
||||
/*
|
||||
* ITS registers, offsets from ITS_base
|
||||
*/
|
||||
|
@ -234,15 +298,21 @@
|
|||
#define GITS_TRANSLATER 0x10040
|
||||
|
||||
#define GITS_CTLR_ENABLE (1U << 0)
|
||||
#define GITS_CTLR_ImDe (1U << 1)
|
||||
#define GITS_CTLR_ITS_NUMBER_SHIFT 4
|
||||
#define GITS_CTLR_ITS_NUMBER (0xFU << GITS_CTLR_ITS_NUMBER_SHIFT)
|
||||
#define GITS_CTLR_QUIESCENT (1U << 31)
|
||||
|
||||
#define GITS_TYPER_PLPIS (1UL << 0)
|
||||
#define GITS_TYPER_VLPIS (1UL << 1)
|
||||
#define GITS_TYPER_ITT_ENTRY_SIZE_SHIFT 4
|
||||
#define GITS_TYPER_ITT_ENTRY_SIZE(r) ((((r) >> GITS_TYPER_ITT_ENTRY_SIZE_SHIFT) & 0x1f) + 1)
|
||||
#define GITS_TYPER_IDBITS_SHIFT 8
|
||||
#define GITS_TYPER_DEVBITS_SHIFT 13
|
||||
#define GITS_TYPER_DEVBITS(r) ((((r) >> GITS_TYPER_DEVBITS_SHIFT) & 0x1f) + 1)
|
||||
#define GITS_TYPER_PTA (1UL << 19)
|
||||
#define GITS_TYPER_HWCOLLCNT_SHIFT 24
|
||||
#define GITS_TYPER_VMOVP (1ULL << 37)
|
||||
|
||||
#define GITS_IIDR_REV_SHIFT 12
|
||||
#define GITS_IIDR_REV_MASK (0xf << GITS_IIDR_REV_SHIFT)
|
||||
|
@ -341,6 +411,18 @@
|
|||
#define GITS_CMD_CLEAR 0x04
|
||||
#define GITS_CMD_SYNC 0x05
|
||||
|
||||
/*
|
||||
* GICv4 ITS specific commands
|
||||
*/
|
||||
#define GITS_CMD_GICv4(x) ((x) | 0x20)
|
||||
#define GITS_CMD_VINVALL GITS_CMD_GICv4(GITS_CMD_INVALL)
|
||||
#define GITS_CMD_VMAPP GITS_CMD_GICv4(GITS_CMD_MAPC)
|
||||
#define GITS_CMD_VMAPTI GITS_CMD_GICv4(GITS_CMD_MAPTI)
|
||||
#define GITS_CMD_VMOVI GITS_CMD_GICv4(GITS_CMD_MOVI)
|
||||
#define GITS_CMD_VSYNC GITS_CMD_GICv4(GITS_CMD_SYNC)
|
||||
/* VMOVP is the odd one, as it doesn't have a physical counterpart */
|
||||
#define GITS_CMD_VMOVP GITS_CMD_GICv4(2)
|
||||
|
||||
/*
|
||||
* ITS error numbers
|
||||
*/
|
||||
|
@ -487,6 +569,8 @@ struct rdists {
|
|||
struct page *prop_page;
|
||||
int id_bits;
|
||||
u64 flags;
|
||||
bool has_vlpis;
|
||||
bool has_direct_lpi;
|
||||
};
|
||||
|
||||
struct irq_domain;
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright (C) 2016,2017 ARM Limited, All Rights Reserved.
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_IRQCHIP_ARM_GIC_V4_H
|
||||
#define __LINUX_IRQCHIP_ARM_GIC_V4_H
|
||||
|
||||
struct its_vpe;
|
||||
|
||||
/* Embedded in kvm.arch */
|
||||
struct its_vm {
|
||||
struct fwnode_handle *fwnode;
|
||||
struct irq_domain *domain;
|
||||
struct page *vprop_page;
|
||||
struct its_vpe **vpes;
|
||||
int nr_vpes;
|
||||
irq_hw_number_t db_lpi_base;
|
||||
unsigned long *db_bitmap;
|
||||
int nr_db_lpis;
|
||||
};
|
||||
|
||||
/* Embedded in kvm_vcpu.arch */
|
||||
struct its_vpe {
|
||||
struct page *vpt_page;
|
||||
struct its_vm *its_vm;
|
||||
/* Doorbell interrupt */
|
||||
int irq;
|
||||
irq_hw_number_t vpe_db_lpi;
|
||||
/* VPE proxy mapping */
|
||||
int vpe_proxy_event;
|
||||
/*
|
||||
* This collection ID is used to indirect the target
|
||||
* redistributor for this VPE. The ID itself isn't involved in
|
||||
* programming of the ITS.
|
||||
*/
|
||||
u16 col_idx;
|
||||
/* Unique (system-wide) VPE identifier */
|
||||
u16 vpe_id;
|
||||
/* Implementation Defined Area Invalid */
|
||||
bool idai;
|
||||
/* Pending VLPIs on schedule out? */
|
||||
bool pending_last;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct its_vlpi_map: structure describing the mapping of a
|
||||
* VLPI. Only to be interpreted in the context of a physical interrupt
|
||||
* it complements. To be used as the vcpu_info passed to
|
||||
* irq_set_vcpu_affinity().
|
||||
*
|
||||
* @vm: Pointer to the GICv4 notion of a VM
|
||||
* @vpe: Pointer to the GICv4 notion of a virtual CPU (VPE)
|
||||
* @vintid: Virtual LPI number
|
||||
* @db_enabled: Is the VPE doorbell to be generated?
|
||||
*/
|
||||
struct its_vlpi_map {
|
||||
struct its_vm *vm;
|
||||
struct its_vpe *vpe;
|
||||
u32 vintid;
|
||||
bool db_enabled;
|
||||
};
|
||||
|
||||
enum its_vcpu_info_cmd_type {
|
||||
MAP_VLPI,
|
||||
GET_VLPI,
|
||||
PROP_UPDATE_VLPI,
|
||||
PROP_UPDATE_AND_INV_VLPI,
|
||||
SCHEDULE_VPE,
|
||||
DESCHEDULE_VPE,
|
||||
INVALL_VPE,
|
||||
};
|
||||
|
||||
struct its_cmd_info {
|
||||
enum its_vcpu_info_cmd_type cmd_type;
|
||||
union {
|
||||
struct its_vlpi_map *map;
|
||||
u8 config;
|
||||
};
|
||||
};
|
||||
|
||||
int its_alloc_vcpu_irqs(struct its_vm *vm);
|
||||
void its_free_vcpu_irqs(struct its_vm *vm);
|
||||
int its_schedule_vpe(struct its_vpe *vpe, bool on);
|
||||
int its_invall_vpe(struct its_vpe *vpe);
|
||||
int its_map_vlpi(int irq, struct its_vlpi_map *map);
|
||||
int its_get_vlpi(int irq, struct its_vlpi_map *map);
|
||||
int its_unmap_vlpi(int irq);
|
||||
int its_prop_update_vlpi(int irq, u8 config, bool inv);
|
||||
|
||||
int its_init_v4(struct irq_domain *domain, const struct irq_domain_ops *ops);
|
||||
|
||||
#endif
|
|
@ -400,8 +400,18 @@ int irq_set_vcpu_affinity(unsigned int irq, void *vcpu_info)
|
|||
return -EINVAL;
|
||||
|
||||
data = irq_desc_get_irq_data(desc);
|
||||
chip = irq_data_get_irq_chip(data);
|
||||
if (chip && chip->irq_set_vcpu_affinity)
|
||||
do {
|
||||
chip = irq_data_get_irq_chip(data);
|
||||
if (chip && chip->irq_set_vcpu_affinity)
|
||||
break;
|
||||
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
|
||||
data = data->parent_data;
|
||||
#else
|
||||
data = NULL;
|
||||
#endif
|
||||
} while (data);
|
||||
|
||||
if (data)
|
||||
ret = chip->irq_set_vcpu_affinity(data, vcpu_info);
|
||||
irq_put_desc_unlock(desc, flags);
|
||||
|
||||
|
|
Loading…
Reference in New Issue