mirror of
https://github.com/FEX-Emu/linux.git
synced 2024-12-20 16:30:53 +00:00
irqchip updates for v4.13
- support for the new Marvell wire-to-MSI bridge - support for the Aspeed I2C irqchip - Armada XP370 per-cpu interrupt fixes - GICv3 ITS ACPI NUMA support - sunxi-nmi cleanup and updates for new platform support - various GICv3 ITS cleanups and fixes - some constifying in various places -----BEGIN PGP SIGNATURE----- iQJJBAABCAAzFiEEn9UcU+C1Yxj9lZw9I9DQutE9ekMFAllM0+oVHG1hcmMuenlu Z2llckBhcm0uY29tAAoJECPQ0LrRPXpDgdMP/iJB5uKxj/0JfOVfWK3ERyMmdpB5 KEggUvAEZzVx2OpItIRqgMO+YAK4bfTbRn42cz95RgThBpOGgfd7aZCBpJ9OtofB 99jt3ScG8Q38Zh5XrHr7wR/MQyPJbvKjEyuMI9RN9axVacmorSkKXVBcme6DDIUj O9cosxqs3MsALy+G7ALPsMjYl76tYsMvV28pnIbivtd/Gjwfqc6yiiLa9Njd+b2T 47gL6q9MXhkD9g9NwHXvF53tAPR1Zibq93tx10gji7IWjLPuShxOHJYdR6aSMdPP sObQzd6v9uLUme5QQUC2PCRepklfdEPtm8OtMjvWbMs5cNBrK6UcX4lYELUum1YL oR23FicinKW+dytEdsiY/usOhrZUMY95/jwC72258AC8VzzlCAeLd7/XEJkLLFnz TMmSgdQMSlHF8xmYI+kpx2eRG4hSu8pmmOFR56J0i87885lMAeyZm1hA2LRKgEql qOxjS4gT22CtGhiuvW2c7L61i9m/jWzskspjAPCWX45x9HOsP+/3FW0z3D2q3Fe2 fcrBdmba95iquQDFDj4PGMNTqElyjyj2E5McDIuvwtj1d85dYKlBr/WlY4OZaXQk IIe4qU/vW/0a9cqmf/LA8kt0l0+MbyyoUgU023VXDWiNuDY3q5mWkP3K+SbRssiY 0qEGJm3icwmMPNdm =CAfw -----END PGP SIGNATURE----- Merge tag 'irqchip-4.13' of git://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms into irq/core Pull irqchip updates for v4.13 from Marc Zyngier - support for the new Marvell wire-to-MSI bridge - support for the Aspeed I2C irqchip - Armada XP370 per-cpu interrupt fixes - GICv3 ITS ACPI NUMA support - sunxi-nmi cleanup and updates for new platform support - various GICv3 ITS cleanups and fixes - some constifying in various places
This commit is contained in:
commit
8d9d51b62e
@ -3,8 +3,11 @@ Allwinner Sunxi NMI Controller
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : should be "allwinner,sun7i-a20-sc-nmi" or
|
||||
"allwinner,sun6i-a31-sc-nmi" or "allwinner,sun9i-a80-nmi"
|
||||
- compatible : should be one of the following:
|
||||
- "allwinner,sun7i-a20-sc-nmi"
|
||||
- "allwinner,sun6i-a31-sc-nmi" (deprecated)
|
||||
- "allwinner,sun6i-a31-r-intc"
|
||||
- "allwinner,sun9i-a80-nmi"
|
||||
- reg : Specifies base physical address and size of the registers.
|
||||
- interrupt-controller : Identifies the node as an interrupt controller
|
||||
- #interrupt-cells : Specifies the number of cells needed to encode an
|
||||
|
@ -0,0 +1,25 @@
|
||||
Device tree configuration for the I2C Interrupt Controller on the AST24XX and
|
||||
AST25XX SoCs.
|
||||
|
||||
Required Properties:
|
||||
- #address-cells : should be 1
|
||||
- #size-cells : should be 1
|
||||
- #interrupt-cells : should be 1
|
||||
- compatible : should be "aspeed,ast2400-i2c-ic"
|
||||
or "aspeed,ast2500-i2c-ic"
|
||||
- reg : address start and range of controller
|
||||
- interrupts : interrupt number
|
||||
- interrupt-controller : denotes that the controller receives and fires
|
||||
new interrupts for child busses
|
||||
|
||||
Example:
|
||||
|
||||
i2c_ic: interrupt-controller@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
#interrupt-cells = <1>;
|
||||
compatible = "aspeed,ast2400-i2c-ic";
|
||||
reg = <0x0 0x40>;
|
||||
interrupts = <12>;
|
||||
interrupt-controller;
|
||||
};
|
@ -1,12 +1,13 @@
|
||||
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.
|
||||
These bindings are for the Aspeed interrupt controller. The AST2400 and
|
||||
AST2500 SoC families include a legacy register layout before a re-designed
|
||||
layout, but the bindings do not prescribe the use of one or the other.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : should be "aspeed,ast2400-vic".
|
||||
- compatible : "aspeed,ast2400-vic"
|
||||
"aspeed,ast2500-vic"
|
||||
|
||||
- interrupt-controller : Identifies the node as an interrupt controller
|
||||
- #interrupt-cells : Specifies the number of cells needed to encode an
|
||||
|
@ -0,0 +1,27 @@
|
||||
Marvell GICP Controller
|
||||
-----------------------
|
||||
|
||||
GICP is a Marvell extension of the GIC that allows to trigger GIC SPI
|
||||
interrupts by doing a memory transaction. It is used by the ICU
|
||||
located in the Marvell CP110 to turn wired interrupts inside the CP
|
||||
into GIC SPI interrupts.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: Must be "marvell,ap806-gicp"
|
||||
|
||||
- reg: Must be the address and size of the GICP SPI registers
|
||||
|
||||
- marvell,spi-ranges: tuples of GIC SPI interrupts ranges available
|
||||
for this GICP
|
||||
|
||||
- msi-controller: indicates that this is an MSI controller
|
||||
|
||||
Example:
|
||||
|
||||
gicp_spi: gicp-spi@3f0040 {
|
||||
compatible = "marvell,ap806-gicp";
|
||||
reg = <0x3f0040 0x10>;
|
||||
marvell,spi-ranges = <64 64>, <288 64>;
|
||||
msi-controller;
|
||||
};
|
@ -0,0 +1,51 @@
|
||||
Marvell ICU Interrupt Controller
|
||||
--------------------------------
|
||||
|
||||
The Marvell ICU (Interrupt Consolidation Unit) controller is
|
||||
responsible for collecting all wired-interrupt sources in the CP and
|
||||
communicating them to the GIC in the AP, the unit translates interrupt
|
||||
requests on input wires to MSG memory mapped transactions to the GIC.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: Should be "marvell,cp110-icu"
|
||||
|
||||
- reg: Should contain ICU registers location and length.
|
||||
|
||||
- #interrupt-cells: Specifies the number of cells needed to encode an
|
||||
interrupt source. The value shall be 3.
|
||||
|
||||
The 1st cell is the group type of the ICU interrupt. Possible group
|
||||
types are:
|
||||
|
||||
ICU_GRP_NSR (0x0) : Shared peripheral interrupt, non-secure
|
||||
ICU_GRP_SR (0x1) : Shared peripheral interrupt, secure
|
||||
ICU_GRP_SEI (0x4) : System error interrupt
|
||||
ICU_GRP_REI (0x5) : RAM error interrupt
|
||||
|
||||
The 2nd cell is the index of the interrupt in the ICU unit.
|
||||
|
||||
The 3rd cell is the type of the interrupt. See arm,gic.txt for
|
||||
details.
|
||||
|
||||
- interrupt-controller: Identifies the node as an interrupt
|
||||
controller.
|
||||
|
||||
- msi-parent: Should point to the GICP controller, the GIC extension
|
||||
that allows to trigger interrupts using MSG memory mapped
|
||||
transactions.
|
||||
|
||||
Example:
|
||||
|
||||
icu: interrupt-controller@1e0000 {
|
||||
compatible = "marvell,cp110-icu";
|
||||
reg = <0x1e0000 0x10>;
|
||||
#interrupt-cells = <3>;
|
||||
interrupt-controller;
|
||||
msi-parent = <&gicp>;
|
||||
};
|
||||
|
||||
usb3h0: usb3@500000 {
|
||||
interrupt-parent = <&icu>;
|
||||
interrupts = <ICU_GRP_NSR 106 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
@ -268,6 +268,12 @@ config IRQ_MXS
|
||||
select IRQ_DOMAIN
|
||||
select STMP_DEVICE
|
||||
|
||||
config MVEBU_GICP
|
||||
bool
|
||||
|
||||
config MVEBU_ICU
|
||||
bool
|
||||
|
||||
config MVEBU_ODMI
|
||||
bool
|
||||
select GENERIC_MSI_IRQ_DOMAIN
|
||||
|
@ -69,10 +69,12 @@ obj-$(CONFIG_ARCH_SA1100) += irq-sa11x0.o
|
||||
obj-$(CONFIG_INGENIC_IRQ) += irq-ingenic.o
|
||||
obj-$(CONFIG_IMX_GPCV2) += irq-imx-gpcv2.o
|
||||
obj-$(CONFIG_PIC32_EVIC) += irq-pic32-evic.o
|
||||
obj-$(CONFIG_MVEBU_GICP) += irq-mvebu-gicp.o
|
||||
obj-$(CONFIG_MVEBU_ICU) += irq-mvebu-icu.o
|
||||
obj-$(CONFIG_MVEBU_ODMI) += irq-mvebu-odmi.o
|
||||
obj-$(CONFIG_MVEBU_PIC) += irq-mvebu-pic.o
|
||||
obj-$(CONFIG_LS_SCFG_MSI) += irq-ls-scfg-msi.o
|
||||
obj-$(CONFIG_EZNPS_GIC) += irq-eznps.o
|
||||
obj-$(CONFIG_ARCH_ASPEED) += irq-aspeed-vic.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
|
||||
|
@ -34,25 +34,104 @@
|
||||
#include <asm/smp_plat.h>
|
||||
#include <asm/mach/irq.h>
|
||||
|
||||
/* Interrupt Controller Registers Map */
|
||||
#define ARMADA_370_XP_INT_SET_MASK_OFFS (0x48)
|
||||
#define ARMADA_370_XP_INT_CLEAR_MASK_OFFS (0x4C)
|
||||
#define ARMADA_370_XP_INT_FABRIC_MASK_OFFS (0x54)
|
||||
#define ARMADA_370_XP_INT_CAUSE_PERF(cpu) (1 << cpu)
|
||||
/*
|
||||
* Overall diagram of the Armada XP interrupt controller:
|
||||
*
|
||||
* To CPU 0 To CPU 1
|
||||
*
|
||||
* /\ /\
|
||||
* || ||
|
||||
* +---------------+ +---------------+
|
||||
* | | | |
|
||||
* | per-CPU | | per-CPU |
|
||||
* | mask/unmask | | mask/unmask |
|
||||
* | CPU0 | | CPU1 |
|
||||
* | | | |
|
||||
* +---------------+ +---------------+
|
||||
* /\ /\
|
||||
* || ||
|
||||
* \\_______________________//
|
||||
* ||
|
||||
* +-------------------+
|
||||
* | |
|
||||
* | Global interrupt |
|
||||
* | mask/unmask |
|
||||
* | |
|
||||
* +-------------------+
|
||||
* /\
|
||||
* ||
|
||||
* interrupt from
|
||||
* device
|
||||
*
|
||||
* The "global interrupt mask/unmask" is modified using the
|
||||
* ARMADA_370_XP_INT_SET_ENABLE_OFFS and
|
||||
* ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS registers, which are relative
|
||||
* to "main_int_base".
|
||||
*
|
||||
* The "per-CPU mask/unmask" is modified using the
|
||||
* ARMADA_370_XP_INT_SET_MASK_OFFS and
|
||||
* ARMADA_370_XP_INT_CLEAR_MASK_OFFS registers, which are relative to
|
||||
* "per_cpu_int_base". This base address points to a special address,
|
||||
* which automatically accesses the registers of the current CPU.
|
||||
*
|
||||
* The per-CPU mask/unmask can also be adjusted using the global
|
||||
* per-interrupt ARMADA_370_XP_INT_SOURCE_CTL register, which we use
|
||||
* to configure interrupt affinity.
|
||||
*
|
||||
* Due to this model, all interrupts need to be mask/unmasked at two
|
||||
* different levels: at the global level and at the per-CPU level.
|
||||
*
|
||||
* This driver takes the following approach to deal with this:
|
||||
*
|
||||
* - For global interrupts:
|
||||
*
|
||||
* At ->map() time, a global interrupt is unmasked at the per-CPU
|
||||
* mask/unmask level. It is therefore unmasked at this level for
|
||||
* the current CPU, running the ->map() code. This allows to have
|
||||
* the interrupt unmasked at this level in non-SMP
|
||||
* configurations. In SMP configurations, the ->set_affinity()
|
||||
* callback is called, which using the
|
||||
* ARMADA_370_XP_INT_SOURCE_CTL() readjusts the per-CPU mask/unmask
|
||||
* for the interrupt.
|
||||
*
|
||||
* The ->mask() and ->unmask() operations only mask/unmask the
|
||||
* interrupt at the "global" level.
|
||||
*
|
||||
* So, a global interrupt is enabled at the per-CPU level as soon
|
||||
* as it is mapped. At run time, the masking/unmasking takes place
|
||||
* at the global level.
|
||||
*
|
||||
* - For per-CPU interrupts
|
||||
*
|
||||
* At ->map() time, a per-CPU interrupt is unmasked at the global
|
||||
* mask/unmask level.
|
||||
*
|
||||
* The ->mask() and ->unmask() operations mask/unmask the interrupt
|
||||
* at the per-CPU level.
|
||||
*
|
||||
* So, a per-CPU interrupt is enabled at the global level as soon
|
||||
* as it is mapped. At run time, the masking/unmasking takes place
|
||||
* at the per-CPU level.
|
||||
*/
|
||||
|
||||
/* Registers relative to main_int_base */
|
||||
#define ARMADA_370_XP_INT_CONTROL (0x00)
|
||||
#define ARMADA_370_XP_SW_TRIG_INT_OFFS (0x04)
|
||||
#define ARMADA_370_XP_INT_SET_ENABLE_OFFS (0x30)
|
||||
#define ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS (0x34)
|
||||
#define ARMADA_370_XP_INT_SOURCE_CTL(irq) (0x100 + irq*4)
|
||||
#define ARMADA_370_XP_INT_SOURCE_CPU_MASK 0xF
|
||||
#define ARMADA_370_XP_INT_IRQ_FIQ_MASK(cpuid) ((BIT(0) | BIT(8)) << cpuid)
|
||||
|
||||
#define ARMADA_370_XP_CPU_INTACK_OFFS (0x44)
|
||||
/* Registers relative to per_cpu_int_base */
|
||||
#define ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS (0x08)
|
||||
#define ARMADA_370_XP_IN_DRBEL_MSK_OFFS (0x0c)
|
||||
#define ARMADA_375_PPI_CAUSE (0x10)
|
||||
|
||||
#define ARMADA_370_XP_SW_TRIG_INT_OFFS (0x4)
|
||||
#define ARMADA_370_XP_IN_DRBEL_MSK_OFFS (0xc)
|
||||
#define ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS (0x8)
|
||||
#define ARMADA_370_XP_CPU_INTACK_OFFS (0x44)
|
||||
#define ARMADA_370_XP_INT_SET_MASK_OFFS (0x48)
|
||||
#define ARMADA_370_XP_INT_CLEAR_MASK_OFFS (0x4C)
|
||||
#define ARMADA_370_XP_INT_FABRIC_MASK_OFFS (0x54)
|
||||
#define ARMADA_370_XP_INT_CAUSE_PERF(cpu) (1 << cpu)
|
||||
|
||||
#define ARMADA_370_XP_MAX_PER_CPU_IRQS (28)
|
||||
|
||||
@ -281,13 +360,11 @@ static int armada_370_xp_mpic_irq_map(struct irq_domain *h,
|
||||
irq_set_percpu_devid(virq);
|
||||
irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip,
|
||||
handle_percpu_devid_irq);
|
||||
|
||||
} else {
|
||||
irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip,
|
||||
handle_level_irq);
|
||||
}
|
||||
irq_set_probe(virq);
|
||||
irq_clear_status_flags(virq, IRQ_NOAUTOEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -345,16 +422,40 @@ static void armada_mpic_send_doorbell(const struct cpumask *mask,
|
||||
ARMADA_370_XP_SW_TRIG_INT_OFFS);
|
||||
}
|
||||
|
||||
static void armada_xp_mpic_reenable_percpu(void)
|
||||
{
|
||||
unsigned int irq;
|
||||
|
||||
/* Re-enable per-CPU interrupts that were enabled before suspend */
|
||||
for (irq = 0; irq < ARMADA_370_XP_MAX_PER_CPU_IRQS; irq++) {
|
||||
struct irq_data *data;
|
||||
int virq;
|
||||
|
||||
virq = irq_linear_revmap(armada_370_xp_mpic_domain, irq);
|
||||
if (virq == 0)
|
||||
continue;
|
||||
|
||||
data = irq_get_irq_data(virq);
|
||||
|
||||
if (!irq_percpu_is_enabled(virq))
|
||||
continue;
|
||||
|
||||
armada_370_xp_irq_unmask(data);
|
||||
}
|
||||
}
|
||||
|
||||
static int armada_xp_mpic_starting_cpu(unsigned int cpu)
|
||||
{
|
||||
armada_xp_mpic_perf_init();
|
||||
armada_xp_mpic_smp_cpu_init();
|
||||
armada_xp_mpic_reenable_percpu();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpic_cascaded_starting_cpu(unsigned int cpu)
|
||||
{
|
||||
armada_xp_mpic_perf_init();
|
||||
armada_xp_mpic_reenable_percpu();
|
||||
enable_percpu_irq(parent_irq, IRQ_TYPE_NONE);
|
||||
return 0;
|
||||
}
|
||||
@ -502,16 +603,27 @@ static void armada_370_xp_mpic_resume(void)
|
||||
if (virq == 0)
|
||||
continue;
|
||||
|
||||
if (!is_percpu_irq(irq))
|
||||
data = irq_get_irq_data(virq);
|
||||
|
||||
if (!is_percpu_irq(irq)) {
|
||||
/* Non per-CPU interrupts */
|
||||
writel(irq, per_cpu_int_base +
|
||||
ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
|
||||
else
|
||||
if (!irqd_irq_disabled(data))
|
||||
armada_370_xp_irq_unmask(data);
|
||||
} else {
|
||||
/* Per-CPU interrupts */
|
||||
writel(irq, main_int_base +
|
||||
ARMADA_370_XP_INT_SET_ENABLE_OFFS);
|
||||
|
||||
data = irq_get_irq_data(virq);
|
||||
if (!irqd_irq_disabled(data))
|
||||
armada_370_xp_irq_unmask(data);
|
||||
/*
|
||||
* Re-enable on the current CPU,
|
||||
* armada_xp_mpic_reenable_percpu() will take
|
||||
* care of secondary CPUs when they come up.
|
||||
*/
|
||||
if (irq_percpu_is_enabled(virq))
|
||||
armada_370_xp_irq_unmask(data);
|
||||
}
|
||||
}
|
||||
|
||||
/* Reconfigure doorbells for IPIs and MSIs */
|
||||
|
115
drivers/irqchip/irq-aspeed-i2c-ic.c
Normal file
115
drivers/irqchip/irq-aspeed-i2c-ic.c
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Aspeed 24XX/25XX I2C Interrupt Controller.
|
||||
*
|
||||
* Copyright (C) 2012-2017 ASPEED Technology Inc.
|
||||
* Copyright 2017 IBM Corporation
|
||||
* Copyright 2017 Google, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqchip.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
|
||||
#define ASPEED_I2C_IC_NUM_BUS 14
|
||||
|
||||
struct aspeed_i2c_ic {
|
||||
void __iomem *base;
|
||||
int parent_irq;
|
||||
struct irq_domain *irq_domain;
|
||||
};
|
||||
|
||||
/*
|
||||
* The aspeed chip provides a single hardware interrupt for all of the I2C
|
||||
* busses, so we use a dummy interrupt chip to translate this single interrupt
|
||||
* into multiple interrupts, each associated with a single I2C bus.
|
||||
*/
|
||||
static void aspeed_i2c_ic_irq_handler(struct irq_desc *desc)
|
||||
{
|
||||
struct aspeed_i2c_ic *i2c_ic = irq_desc_get_handler_data(desc);
|
||||
struct irq_chip *chip = irq_desc_get_chip(desc);
|
||||
unsigned long bit, status;
|
||||
unsigned int bus_irq;
|
||||
|
||||
chained_irq_enter(chip, desc);
|
||||
status = readl(i2c_ic->base);
|
||||
for_each_set_bit(bit, &status, ASPEED_I2C_IC_NUM_BUS) {
|
||||
bus_irq = irq_find_mapping(i2c_ic->irq_domain, bit);
|
||||
generic_handle_irq(bus_irq);
|
||||
}
|
||||
chained_irq_exit(chip, desc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set simple handler and mark IRQ as valid. Nothing interesting to do here
|
||||
* since we are using a dummy interrupt chip.
|
||||
*/
|
||||
static int aspeed_i2c_ic_map_irq_domain(struct irq_domain *domain,
|
||||
unsigned int irq, irq_hw_number_t hwirq)
|
||||
{
|
||||
irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq);
|
||||
irq_set_chip_data(irq, domain->host_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops aspeed_i2c_ic_irq_domain_ops = {
|
||||
.map = aspeed_i2c_ic_map_irq_domain,
|
||||
};
|
||||
|
||||
static int __init aspeed_i2c_ic_of_init(struct device_node *node,
|
||||
struct device_node *parent)
|
||||
{
|
||||
struct aspeed_i2c_ic *i2c_ic;
|
||||
int ret = 0;
|
||||
|
||||
i2c_ic = kzalloc(sizeof(*i2c_ic), GFP_KERNEL);
|
||||
if (!i2c_ic)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_ic->base = of_iomap(node, 0);
|
||||
if (IS_ERR(i2c_ic->base)) {
|
||||
ret = PTR_ERR(i2c_ic->base);
|
||||
goto err_free_ic;
|
||||
}
|
||||
|
||||
i2c_ic->parent_irq = irq_of_parse_and_map(node, 0);
|
||||
if (i2c_ic->parent_irq < 0) {
|
||||
ret = i2c_ic->parent_irq;
|
||||
goto err_iounmap;
|
||||
}
|
||||
|
||||
i2c_ic->irq_domain = irq_domain_add_linear(node, ASPEED_I2C_IC_NUM_BUS,
|
||||
&aspeed_i2c_ic_irq_domain_ops,
|
||||
NULL);
|
||||
if (!i2c_ic->irq_domain) {
|
||||
ret = -ENOMEM;
|
||||
goto err_iounmap;
|
||||
}
|
||||
|
||||
i2c_ic->irq_domain->name = "aspeed-i2c-domain";
|
||||
|
||||
irq_set_chained_handler_and_data(i2c_ic->parent_irq,
|
||||
aspeed_i2c_ic_irq_handler, i2c_ic);
|
||||
|
||||
pr_info("i2c controller registered, irq %d\n", i2c_ic->parent_irq);
|
||||
|
||||
return 0;
|
||||
|
||||
err_iounmap:
|
||||
iounmap(i2c_ic->base);
|
||||
err_free_ic:
|
||||
kfree(i2c_ic);
|
||||
return ret;
|
||||
}
|
||||
|
||||
IRQCHIP_DECLARE(ast2400_i2c_ic, "aspeed,ast2400-i2c-ic", aspeed_i2c_ic_of_init);
|
||||
IRQCHIP_DECLARE(ast2500_i2c_ic, "aspeed,ast2500-i2c-ic", aspeed_i2c_ic_of_init);
|
@ -186,7 +186,7 @@ static int avic_map(struct irq_domain *d, unsigned int irq,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_domain_ops avic_dom_ops = {
|
||||
static const struct irq_domain_ops avic_dom_ops = {
|
||||
.map = avic_map,
|
||||
.xlate = irq_domain_xlate_onetwocell,
|
||||
};
|
||||
@ -227,4 +227,5 @@ static int __init avic_of_init(struct device_node *node,
|
||||
return 0;
|
||||
}
|
||||
|
||||
IRQCHIP_DECLARE(aspeed_new_vic, "aspeed,ast2400-vic", avic_of_init);
|
||||
IRQCHIP_DECLARE(ast2400_vic, "aspeed,ast2400-vic", avic_of_init);
|
||||
IRQCHIP_DECLARE(ast2500_vic, "aspeed,ast2500-vic", avic_of_init);
|
||||
|
@ -41,27 +41,22 @@ static struct irq_chip its_msi_irq_chip = {
|
||||
.irq_write_msi_msg = pci_msi_domain_write_msg,
|
||||
};
|
||||
|
||||
struct its_pci_alias {
|
||||
struct pci_dev *pdev;
|
||||
u32 count;
|
||||
};
|
||||
|
||||
static int its_pci_msi_vec_count(struct pci_dev *pdev)
|
||||
static int its_pci_msi_vec_count(struct pci_dev *pdev, void *data)
|
||||
{
|
||||
int msi, msix;
|
||||
int msi, msix, *count = data;
|
||||
|
||||
msi = max(pci_msi_vec_count(pdev), 0);
|
||||
msix = max(pci_msix_vec_count(pdev), 0);
|
||||
*count += max(msi, msix);
|
||||
|
||||
return max(msi, msix);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int its_get_pci_alias(struct pci_dev *pdev, u16 alias, void *data)
|
||||
{
|
||||
struct its_pci_alias *dev_alias = data;
|
||||
struct pci_dev **alias_dev = data;
|
||||
|
||||
if (pdev != dev_alias->pdev)
|
||||
dev_alias->count += its_pci_msi_vec_count(pdev);
|
||||
*alias_dev = pdev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -69,9 +64,9 @@ static int its_get_pci_alias(struct pci_dev *pdev, u16 alias, void *data)
|
||||
static int its_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
|
||||
int nvec, msi_alloc_info_t *info)
|
||||
{
|
||||
struct pci_dev *pdev;
|
||||
struct its_pci_alias dev_alias;
|
||||
struct pci_dev *pdev, *alias_dev;
|
||||
struct msi_domain_info *msi_info;
|
||||
int alias_count = 0;
|
||||
|
||||
if (!dev_is_pci(dev))
|
||||
return -EINVAL;
|
||||
@ -79,16 +74,20 @@ static int its_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
|
||||
msi_info = msi_get_domain_info(domain->parent);
|
||||
|
||||
pdev = to_pci_dev(dev);
|
||||
dev_alias.pdev = pdev;
|
||||
dev_alias.count = nvec;
|
||||
|
||||
pci_for_each_dma_alias(pdev, its_get_pci_alias, &dev_alias);
|
||||
/*
|
||||
* If pdev is downstream of any aliasing bridges, take an upper
|
||||
* bound of how many other vectors could map to the same DevID.
|
||||
*/
|
||||
pci_for_each_dma_alias(pdev, its_get_pci_alias, &alias_dev);
|
||||
if (alias_dev != pdev && alias_dev->subordinate)
|
||||
pci_walk_bus(alias_dev->subordinate, its_pci_msi_vec_count,
|
||||
&alias_count);
|
||||
|
||||
/* ITS specific DeviceID, as the core ITS ignores dev. */
|
||||
info->scratchpad[0].ul = pci_msi_domain_get_msi_rid(domain, pdev);
|
||||
|
||||
return msi_info->ops->msi_prepare(domain->parent,
|
||||
dev, dev_alias.count, info);
|
||||
dev, max(nvec, alias_count), info);
|
||||
}
|
||||
|
||||
static struct msi_domain_ops its_pci_msi_ops = {
|
||||
|
@ -86,7 +86,7 @@ static struct msi_domain_info its_pmsi_domain_info = {
|
||||
.chip = &its_pmsi_irq_chip,
|
||||
};
|
||||
|
||||
static struct of_device_id its_device_id[] = {
|
||||
static const struct of_device_id its_device_id[] = {
|
||||
{ .compatible = "arm,gic-v3-its", },
|
||||
{},
|
||||
};
|
||||
|
@ -644,9 +644,12 @@ static int its_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
|
||||
if (cpu >= nr_cpu_ids)
|
||||
return -EINVAL;
|
||||
|
||||
target_col = &its_dev->its->collections[cpu];
|
||||
its_send_movi(its_dev, target_col, id);
|
||||
its_dev->event_map.col_map[id] = cpu;
|
||||
/* don't set the affinity when the target cpu is same as current one */
|
||||
if (cpu != its_dev->event_map.col_map[id]) {
|
||||
target_col = &its_dev->its->collections[cpu];
|
||||
its_send_movi(its_dev, target_col, id);
|
||||
its_dev->event_map.col_map[id] = cpu;
|
||||
}
|
||||
|
||||
return IRQ_SET_MASK_OK_DONE;
|
||||
}
|
||||
@ -688,9 +691,11 @@ static struct irq_chip its_irq_chip = {
|
||||
*/
|
||||
#define IRQS_PER_CHUNK_SHIFT 5
|
||||
#define IRQS_PER_CHUNK (1 << IRQS_PER_CHUNK_SHIFT)
|
||||
#define ITS_MAX_LPI_NRBITS 16 /* 64K LPIs */
|
||||
|
||||
static unsigned long *lpi_bitmap;
|
||||
static u32 lpi_chunks;
|
||||
static u32 lpi_id_bits;
|
||||
static DEFINE_SPINLOCK(lpi_lock);
|
||||
|
||||
static int its_lpi_to_chunk(int lpi)
|
||||
@ -786,17 +791,13 @@ static void its_lpi_free(struct event_lpi_map *map)
|
||||
}
|
||||
|
||||
/*
|
||||
* We allocate 64kB for PROPBASE. That gives us at most 64K LPIs to
|
||||
* We allocate memory for PROPBASE to cover 2 ^ lpi_id_bits LPIs to
|
||||
* deal with (one configuration byte per interrupt). PENDBASE has to
|
||||
* be 64kB aligned (one bit per LPI, plus 8192 bits for SPI/PPI/SGI).
|
||||
*/
|
||||
#define LPI_PROPBASE_SZ SZ_64K
|
||||
#define LPI_PENDBASE_SZ (LPI_PROPBASE_SZ / 8 + SZ_1K)
|
||||
|
||||
/*
|
||||
* This is how many bits of ID we need, including the useless ones.
|
||||
*/
|
||||
#define LPI_NRBITS ilog2(LPI_PROPBASE_SZ + SZ_8K)
|
||||
#define LPI_NRBITS lpi_id_bits
|
||||
#define LPI_PROPBASE_SZ ALIGN(BIT(LPI_NRBITS), SZ_64K)
|
||||
#define LPI_PENDBASE_SZ ALIGN(BIT(LPI_NRBITS) / 8, SZ_64K)
|
||||
|
||||
#define LPI_PROP_DEFAULT_PRIO 0xa0
|
||||
|
||||
@ -804,6 +805,7 @@ static int __init its_alloc_lpi_tables(void)
|
||||
{
|
||||
phys_addr_t paddr;
|
||||
|
||||
lpi_id_bits = min_t(u32, gic_rdists->id_bits, ITS_MAX_LPI_NRBITS);
|
||||
gic_rdists->prop_page = alloc_pages(GFP_NOWAIT,
|
||||
get_order(LPI_PROPBASE_SZ));
|
||||
if (!gic_rdists->prop_page) {
|
||||
@ -822,7 +824,7 @@ static int __init its_alloc_lpi_tables(void)
|
||||
/* Make sure the GIC will observe the written configuration */
|
||||
gic_flush_dcache_to_poc(page_address(gic_rdists->prop_page), LPI_PROPBASE_SZ);
|
||||
|
||||
return 0;
|
||||
return its_lpi_init(lpi_id_bits);
|
||||
}
|
||||
|
||||
static const char *its_base_type_string[] = {
|
||||
@ -1097,7 +1099,7 @@ static void its_cpu_init_lpis(void)
|
||||
* hence the 'max(LPI_PENDBASE_SZ, SZ_64K)' below.
|
||||
*/
|
||||
pend_page = alloc_pages(GFP_NOWAIT | __GFP_ZERO,
|
||||
get_order(max(LPI_PENDBASE_SZ, SZ_64K)));
|
||||
get_order(max_t(u32, LPI_PENDBASE_SZ, SZ_64K)));
|
||||
if (!pend_page) {
|
||||
pr_err("Failed to allocate PENDBASE for CPU%d\n",
|
||||
smp_processor_id());
|
||||
@ -1801,7 +1803,7 @@ int its_cpu_init(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id its_device_id[] = {
|
||||
static const struct of_device_id its_device_id[] = {
|
||||
{ .compatible = "arm,gic-v3-its", },
|
||||
{},
|
||||
};
|
||||
@ -1833,6 +1835,78 @@ static int __init its_of_probe(struct device_node *node)
|
||||
|
||||
#define ACPI_GICV3_ITS_MEM_SIZE (SZ_128K)
|
||||
|
||||
#if defined(CONFIG_ACPI_NUMA) && (ACPI_CA_VERSION >= 0x20170531)
|
||||
struct its_srat_map {
|
||||
/* numa node id */
|
||||
u32 numa_node;
|
||||
/* GIC ITS ID */
|
||||
u32 its_id;
|
||||
};
|
||||
|
||||
static struct its_srat_map its_srat_maps[MAX_NUMNODES] __initdata;
|
||||
static int its_in_srat __initdata;
|
||||
|
||||
static int __init acpi_get_its_numa_node(u32 its_id)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < its_in_srat; i++) {
|
||||
if (its_id == its_srat_maps[i].its_id)
|
||||
return its_srat_maps[i].numa_node;
|
||||
}
|
||||
return NUMA_NO_NODE;
|
||||
}
|
||||
|
||||
static int __init gic_acpi_parse_srat_its(struct acpi_subtable_header *header,
|
||||
const unsigned long end)
|
||||
{
|
||||
int node;
|
||||
struct acpi_srat_gic_its_affinity *its_affinity;
|
||||
|
||||
its_affinity = (struct acpi_srat_gic_its_affinity *)header;
|
||||
if (!its_affinity)
|
||||
return -EINVAL;
|
||||
|
||||
if (its_affinity->header.length < sizeof(*its_affinity)) {
|
||||
pr_err("SRAT: Invalid header length %d in ITS affinity\n",
|
||||
its_affinity->header.length);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (its_in_srat >= MAX_NUMNODES) {
|
||||
pr_err("SRAT: ITS affinity exceeding max count[%d]\n",
|
||||
MAX_NUMNODES);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
node = acpi_map_pxm_to_node(its_affinity->proximity_domain);
|
||||
|
||||
if (node == NUMA_NO_NODE || node >= MAX_NUMNODES) {
|
||||
pr_err("SRAT: Invalid NUMA node %d in ITS affinity\n", node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
its_srat_maps[its_in_srat].numa_node = node;
|
||||
its_srat_maps[its_in_srat].its_id = its_affinity->its_id;
|
||||
its_in_srat++;
|
||||
pr_info("SRAT: PXM %d -> ITS %d -> Node %d\n",
|
||||
its_affinity->proximity_domain, its_affinity->its_id, node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __init acpi_table_parse_srat_its(void)
|
||||
{
|
||||
acpi_table_parse_entries(ACPI_SIG_SRAT,
|
||||
sizeof(struct acpi_table_srat),
|
||||
ACPI_SRAT_TYPE_GIC_ITS_AFFINITY,
|
||||
gic_acpi_parse_srat_its, 0);
|
||||
}
|
||||
#else
|
||||
static void __init acpi_table_parse_srat_its(void) { }
|
||||
static int __init acpi_get_its_numa_node(u32 its_id) { return NUMA_NO_NODE; }
|
||||
#endif
|
||||
|
||||
static int __init gic_acpi_parse_madt_its(struct acpi_subtable_header *header,
|
||||
const unsigned long end)
|
||||
{
|
||||
@ -1861,7 +1935,8 @@ static int __init gic_acpi_parse_madt_its(struct acpi_subtable_header *header,
|
||||
goto dom_err;
|
||||
}
|
||||
|
||||
err = its_probe_one(&res, dom_handle, NUMA_NO_NODE);
|
||||
err = its_probe_one(&res, dom_handle,
|
||||
acpi_get_its_numa_node(its_entry->translation_id));
|
||||
if (!err)
|
||||
return 0;
|
||||
|
||||
@ -1873,6 +1948,7 @@ dom_err:
|
||||
|
||||
static void __init its_acpi_probe(void)
|
||||
{
|
||||
acpi_table_parse_srat_its();
|
||||
acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_TRANSLATOR,
|
||||
gic_acpi_parse_madt_its, 0);
|
||||
}
|
||||
@ -1898,8 +1974,5 @@ int __init its_init(struct fwnode_handle *handle, struct rdists *rdists,
|
||||
}
|
||||
|
||||
gic_rdists = rdists;
|
||||
its_alloc_lpi_tables();
|
||||
its_lpi_init(rdists->id_bits);
|
||||
|
||||
return 0;
|
||||
return its_alloc_lpi_tables();
|
||||
}
|
||||
|
@ -307,7 +307,7 @@ static int i8259A_irq_domain_map(struct irq_domain *d, unsigned int virq,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_domain_ops i8259A_ops = {
|
||||
static const struct irq_domain_ops i8259A_ops = {
|
||||
.map = i8259A_irq_domain_map,
|
||||
.xlate = irq_domain_xlate_onecell,
|
||||
};
|
||||
|
@ -200,7 +200,7 @@ static int imx_gpcv2_domain_alloc(struct irq_domain *domain,
|
||||
&parent_fwspec);
|
||||
}
|
||||
|
||||
static struct irq_domain_ops gpcv2_irqchip_data_domain_ops = {
|
||||
static const struct irq_domain_ops gpcv2_irqchip_data_domain_ops = {
|
||||
.translate = imx_gpcv2_domain_translate,
|
||||
.alloc = imx_gpcv2_domain_alloc,
|
||||
.free = irq_domain_free_irqs_common,
|
||||
|
@ -228,7 +228,7 @@ static int mbigen_irq_domain_alloc(struct irq_domain *domain,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_domain_ops mbigen_domain_ops = {
|
||||
static const struct irq_domain_ops mbigen_domain_ops = {
|
||||
.translate = mbigen_domain_translate,
|
||||
.alloc = mbigen_irq_domain_alloc,
|
||||
.free = irq_domain_free_irqs_common,
|
||||
|
@ -874,7 +874,7 @@ int gic_ipi_domain_match(struct irq_domain *d, struct device_node *node,
|
||||
}
|
||||
}
|
||||
|
||||
static struct irq_domain_ops gic_ipi_domain_ops = {
|
||||
static const struct irq_domain_ops gic_ipi_domain_ops = {
|
||||
.xlate = gic_ipi_domain_xlate,
|
||||
.alloc = gic_ipi_domain_alloc,
|
||||
.free = gic_ipi_domain_free,
|
||||
|
279
drivers/irqchip/irq-mvebu-gicp.c
Normal file
279
drivers/irqchip/irq-mvebu-gicp.c
Normal file
@ -0,0 +1,279 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Marvell
|
||||
*
|
||||
* Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/msi.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
#include "irq-mvebu-gicp.h"
|
||||
|
||||
#define GICP_SETSPI_NSR_OFFSET 0x0
|
||||
#define GICP_CLRSPI_NSR_OFFSET 0x8
|
||||
|
||||
struct mvebu_gicp_spi_range {
|
||||
unsigned int start;
|
||||
unsigned int count;
|
||||
};
|
||||
|
||||
struct mvebu_gicp {
|
||||
struct mvebu_gicp_spi_range *spi_ranges;
|
||||
unsigned int spi_ranges_cnt;
|
||||
unsigned int spi_cnt;
|
||||
unsigned long *spi_bitmap;
|
||||
spinlock_t spi_lock;
|
||||
struct resource *res;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
static int gicp_idx_to_spi(struct mvebu_gicp *gicp, int idx)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < gicp->spi_ranges_cnt; i++) {
|
||||
struct mvebu_gicp_spi_range *r = &gicp->spi_ranges[i];
|
||||
|
||||
if (idx < r->count)
|
||||
return r->start + idx;
|
||||
|
||||
idx -= r->count;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int mvebu_gicp_get_doorbells(struct device_node *dn, phys_addr_t *setspi,
|
||||
phys_addr_t *clrspi)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
struct mvebu_gicp *gicp;
|
||||
|
||||
pdev = of_find_device_by_node(dn);
|
||||
if (!pdev)
|
||||
return -ENODEV;
|
||||
|
||||
gicp = platform_get_drvdata(pdev);
|
||||
if (!gicp)
|
||||
return -ENODEV;
|
||||
|
||||
*setspi = gicp->res->start + GICP_SETSPI_NSR_OFFSET;
|
||||
*clrspi = gicp->res->start + GICP_CLRSPI_NSR_OFFSET;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gicp_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
|
||||
{
|
||||
struct mvebu_gicp *gicp = data->chip_data;
|
||||
phys_addr_t setspi = gicp->res->start + GICP_SETSPI_NSR_OFFSET;
|
||||
|
||||
msg->data = data->hwirq;
|
||||
msg->address_lo = lower_32_bits(setspi);
|
||||
msg->address_hi = upper_32_bits(setspi);
|
||||
}
|
||||
|
||||
static struct irq_chip gicp_irq_chip = {
|
||||
.name = "GICP",
|
||||
.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 = irq_chip_set_type_parent,
|
||||
.irq_compose_msi_msg = gicp_compose_msi_msg,
|
||||
};
|
||||
|
||||
static int gicp_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
|
||||
unsigned int nr_irqs, void *args)
|
||||
{
|
||||
struct mvebu_gicp *gicp = domain->host_data;
|
||||
struct irq_fwspec fwspec;
|
||||
unsigned int hwirq;
|
||||
int ret;
|
||||
|
||||
spin_lock(&gicp->spi_lock);
|
||||
hwirq = find_first_zero_bit(gicp->spi_bitmap, gicp->spi_cnt);
|
||||
if (hwirq == gicp->spi_cnt) {
|
||||
spin_unlock(&gicp->spi_lock);
|
||||
return -ENOSPC;
|
||||
}
|
||||
__set_bit(hwirq, gicp->spi_bitmap);
|
||||
spin_unlock(&gicp->spi_lock);
|
||||
|
||||
fwspec.fwnode = domain->parent->fwnode;
|
||||
fwspec.param_count = 3;
|
||||
fwspec.param[0] = GIC_SPI;
|
||||
fwspec.param[1] = gicp_idx_to_spi(gicp, hwirq) - 32;
|
||||
/*
|
||||
* Assume edge rising for now, it will be properly set when
|
||||
* ->set_type() is called
|
||||
*/
|
||||
fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
|
||||
|
||||
ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
|
||||
if (ret) {
|
||||
dev_err(gicp->dev, "Cannot allocate parent IRQ\n");
|
||||
goto free_hwirq;
|
||||
}
|
||||
|
||||
ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
|
||||
&gicp_irq_chip, gicp);
|
||||
if (ret)
|
||||
goto free_irqs_parent;
|
||||
|
||||
return 0;
|
||||
|
||||
free_irqs_parent:
|
||||
irq_domain_free_irqs_parent(domain, virq, nr_irqs);
|
||||
free_hwirq:
|
||||
spin_lock(&gicp->spi_lock);
|
||||
__clear_bit(hwirq, gicp->spi_bitmap);
|
||||
spin_unlock(&gicp->spi_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void gicp_irq_domain_free(struct irq_domain *domain,
|
||||
unsigned int virq, unsigned int nr_irqs)
|
||||
{
|
||||
struct mvebu_gicp *gicp = domain->host_data;
|
||||
struct irq_data *d = irq_domain_get_irq_data(domain, virq);
|
||||
|
||||
if (d->hwirq >= gicp->spi_cnt) {
|
||||
dev_err(gicp->dev, "Invalid hwirq %lu\n", d->hwirq);
|
||||
return;
|
||||
}
|
||||
|
||||
irq_domain_free_irqs_parent(domain, virq, nr_irqs);
|
||||
|
||||
spin_lock(&gicp->spi_lock);
|
||||
__clear_bit(d->hwirq, gicp->spi_bitmap);
|
||||
spin_unlock(&gicp->spi_lock);
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops gicp_domain_ops = {
|
||||
.alloc = gicp_irq_domain_alloc,
|
||||
.free = gicp_irq_domain_free,
|
||||
};
|
||||
|
||||
static struct irq_chip gicp_msi_irq_chip = {
|
||||
.name = "GICP",
|
||||
.irq_set_type = irq_chip_set_type_parent,
|
||||
};
|
||||
|
||||
static struct msi_domain_ops gicp_msi_ops = {
|
||||
};
|
||||
|
||||
static struct msi_domain_info gicp_msi_domain_info = {
|
||||
.flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
|
||||
.ops = &gicp_msi_ops,
|
||||
.chip = &gicp_msi_irq_chip,
|
||||
};
|
||||
|
||||
static int mvebu_gicp_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mvebu_gicp *gicp;
|
||||
struct irq_domain *inner_domain, *plat_domain, *parent_domain;
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct device_node *irq_parent_dn;
|
||||
int ret, i;
|
||||
|
||||
gicp = devm_kzalloc(&pdev->dev, sizeof(*gicp), GFP_KERNEL);
|
||||
if (!gicp)
|
||||
return -ENOMEM;
|
||||
|
||||
gicp->dev = &pdev->dev;
|
||||
|
||||
gicp->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!gicp->res)
|
||||
return -ENODEV;
|
||||
|
||||
ret = of_property_count_u32_elems(node, "marvell,spi-ranges");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
gicp->spi_ranges_cnt = ret / 2;
|
||||
|
||||
gicp->spi_ranges =
|
||||
devm_kzalloc(&pdev->dev,
|
||||
gicp->spi_ranges_cnt *
|
||||
sizeof(struct mvebu_gicp_spi_range),
|
||||
GFP_KERNEL);
|
||||
if (!gicp->spi_ranges)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < gicp->spi_ranges_cnt; i++) {
|
||||
of_property_read_u32_index(node, "marvell,spi-ranges",
|
||||
i * 2,
|
||||
&gicp->spi_ranges[i].start);
|
||||
|
||||
of_property_read_u32_index(node, "marvell,spi-ranges",
|
||||
i * 2 + 1,
|
||||
&gicp->spi_ranges[i].count);
|
||||
|
||||
gicp->spi_cnt += gicp->spi_ranges[i].count;
|
||||
}
|
||||
|
||||
gicp->spi_bitmap = devm_kzalloc(&pdev->dev,
|
||||
BITS_TO_LONGS(gicp->spi_cnt),
|
||||
GFP_KERNEL);
|
||||
if (!gicp->spi_bitmap)
|
||||
return -ENOMEM;
|
||||
|
||||
irq_parent_dn = of_irq_find_parent(node);
|
||||
if (!irq_parent_dn) {
|
||||
dev_err(&pdev->dev, "failed to find parent IRQ node\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
parent_domain = irq_find_host(irq_parent_dn);
|
||||
if (!parent_domain) {
|
||||
dev_err(&pdev->dev, "failed to find parent IRQ domain\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
inner_domain = irq_domain_create_hierarchy(parent_domain, 0,
|
||||
gicp->spi_cnt,
|
||||
of_node_to_fwnode(node),
|
||||
&gicp_domain_ops, gicp);
|
||||
if (!inner_domain)
|
||||
return -ENOMEM;
|
||||
|
||||
|
||||
plat_domain = platform_msi_create_irq_domain(of_node_to_fwnode(node),
|
||||
&gicp_msi_domain_info,
|
||||
inner_domain);
|
||||
if (!plat_domain) {
|
||||
irq_domain_remove(inner_domain);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, gicp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id mvebu_gicp_of_match[] = {
|
||||
{ .compatible = "marvell,ap806-gicp", },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver mvebu_gicp_driver = {
|
||||
.probe = mvebu_gicp_probe,
|
||||
.driver = {
|
||||
.name = "mvebu-gicp",
|
||||
.of_match_table = mvebu_gicp_of_match,
|
||||
},
|
||||
};
|
||||
builtin_platform_driver(mvebu_gicp_driver);
|
11
drivers/irqchip/irq-mvebu-gicp.h
Normal file
11
drivers/irqchip/irq-mvebu-gicp.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef __MVEBU_GICP_H__
|
||||
#define __MVEBU_GICP_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct device_node;
|
||||
|
||||
int mvebu_gicp_get_doorbells(struct device_node *dn, phys_addr_t *setspi,
|
||||
phys_addr_t *clrspi);
|
||||
|
||||
#endif /* __MVEBU_GICP_H__ */
|
289
drivers/irqchip/irq-mvebu-icu.c
Normal file
289
drivers/irqchip/irq-mvebu-icu.c
Normal file
@ -0,0 +1,289 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Marvell
|
||||
*
|
||||
* Hanna Hawa <hannah@marvell.com>
|
||||
* Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqchip.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/msi.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <dt-bindings/interrupt-controller/mvebu-icu.h>
|
||||
|
||||
#include "irq-mvebu-gicp.h"
|
||||
|
||||
/* ICU registers */
|
||||
#define ICU_SETSPI_NSR_AL 0x10
|
||||
#define ICU_SETSPI_NSR_AH 0x14
|
||||
#define ICU_CLRSPI_NSR_AL 0x18
|
||||
#define ICU_CLRSPI_NSR_AH 0x1c
|
||||
#define ICU_INT_CFG(x) (0x100 + 4 * (x))
|
||||
#define ICU_INT_ENABLE BIT(24)
|
||||
#define ICU_IS_EDGE BIT(28)
|
||||
#define ICU_GROUP_SHIFT 29
|
||||
|
||||
/* ICU definitions */
|
||||
#define ICU_MAX_IRQS 207
|
||||
#define ICU_SATA0_ICU_ID 109
|
||||
#define ICU_SATA1_ICU_ID 107
|
||||
|
||||
struct mvebu_icu {
|
||||
struct irq_chip irq_chip;
|
||||
void __iomem *base;
|
||||
struct irq_domain *domain;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
struct mvebu_icu_irq_data {
|
||||
struct mvebu_icu *icu;
|
||||
unsigned int icu_group;
|
||||
unsigned int type;
|
||||
};
|
||||
|
||||
static void mvebu_icu_write_msg(struct msi_desc *desc, struct msi_msg *msg)
|
||||
{
|
||||
struct irq_data *d = irq_get_irq_data(desc->irq);
|
||||
struct mvebu_icu_irq_data *icu_irqd = d->chip_data;
|
||||
struct mvebu_icu *icu = icu_irqd->icu;
|
||||
unsigned int icu_int;
|
||||
|
||||
if (msg->address_lo || msg->address_hi) {
|
||||
/* Configure the ICU with irq number & type */
|
||||
icu_int = msg->data | ICU_INT_ENABLE;
|
||||
if (icu_irqd->type & IRQ_TYPE_EDGE_RISING)
|
||||
icu_int |= ICU_IS_EDGE;
|
||||
icu_int |= icu_irqd->icu_group << ICU_GROUP_SHIFT;
|
||||
} else {
|
||||
/* De-configure the ICU */
|
||||
icu_int = 0;
|
||||
}
|
||||
|
||||
writel_relaxed(icu_int, icu->base + ICU_INT_CFG(d->hwirq));
|
||||
|
||||
/*
|
||||
* The SATA unit has 2 ports, and a dedicated ICU entry per
|
||||
* port. The ahci sata driver supports only one irq interrupt
|
||||
* per SATA unit. To solve this conflict, we configure the 2
|
||||
* SATA wired interrupts in the south bridge into 1 GIC
|
||||
* interrupt in the north bridge. Even if only a single port
|
||||
* is enabled, if sata node is enabled, both interrupts are
|
||||
* configured (regardless of which port is actually in use).
|
||||
*/
|
||||
if (d->hwirq == ICU_SATA0_ICU_ID || d->hwirq == ICU_SATA1_ICU_ID) {
|
||||
writel_relaxed(icu_int,
|
||||
icu->base + ICU_INT_CFG(ICU_SATA0_ICU_ID));
|
||||
writel_relaxed(icu_int,
|
||||
icu->base + ICU_INT_CFG(ICU_SATA1_ICU_ID));
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
mvebu_icu_irq_domain_translate(struct irq_domain *d, struct irq_fwspec *fwspec,
|
||||
unsigned long *hwirq, unsigned int *type)
|
||||
{
|
||||
struct mvebu_icu *icu = d->host_data;
|
||||
unsigned int icu_group;
|
||||
|
||||
/* Check the count of the parameters in dt */
|
||||
if (WARN_ON(fwspec->param_count < 3)) {
|
||||
dev_err(icu->dev, "wrong ICU parameter count %d\n",
|
||||
fwspec->param_count);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Only ICU group type is handled */
|
||||
icu_group = fwspec->param[0];
|
||||
if (icu_group != ICU_GRP_NSR && icu_group != ICU_GRP_SR &&
|
||||
icu_group != ICU_GRP_SEI && icu_group != ICU_GRP_REI) {
|
||||
dev_err(icu->dev, "wrong ICU group type %x\n", icu_group);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*hwirq = fwspec->param[1];
|
||||
if (*hwirq >= ICU_MAX_IRQS) {
|
||||
dev_err(icu->dev, "invalid interrupt number %ld\n", *hwirq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Mask the type to prevent wrong DT configuration */
|
||||
*type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mvebu_icu_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
|
||||
unsigned int nr_irqs, void *args)
|
||||
{
|
||||
int err;
|
||||
unsigned long hwirq;
|
||||
struct irq_fwspec *fwspec = args;
|
||||
struct mvebu_icu *icu = platform_msi_get_host_data(domain);
|
||||
struct mvebu_icu_irq_data *icu_irqd;
|
||||
|
||||
icu_irqd = kmalloc(sizeof(*icu_irqd), GFP_KERNEL);
|
||||
if (!icu_irqd)
|
||||
return -ENOMEM;
|
||||
|
||||
err = mvebu_icu_irq_domain_translate(domain, fwspec, &hwirq,
|
||||
&icu_irqd->type);
|
||||
if (err) {
|
||||
dev_err(icu->dev, "failed to translate ICU parameters\n");
|
||||
goto free_irqd;
|
||||
}
|
||||
|
||||
icu_irqd->icu_group = fwspec->param[0];
|
||||
icu_irqd->icu = icu;
|
||||
|
||||
err = platform_msi_domain_alloc(domain, virq, nr_irqs);
|
||||
if (err) {
|
||||
dev_err(icu->dev, "failed to allocate ICU interrupt in parent domain\n");
|
||||
goto free_irqd;
|
||||
}
|
||||
|
||||
/* Make sure there is no interrupt left pending by the firmware */
|
||||
err = irq_set_irqchip_state(virq, IRQCHIP_STATE_PENDING, false);
|
||||
if (err)
|
||||
goto free_msi;
|
||||
|
||||
err = irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
|
||||
&icu->irq_chip, icu_irqd);
|
||||
if (err) {
|
||||
dev_err(icu->dev, "failed to set the data to IRQ domain\n");
|
||||
goto free_msi;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
free_msi:
|
||||
platform_msi_domain_free(domain, virq, nr_irqs);
|
||||
free_irqd:
|
||||
kfree(icu_irqd);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void
|
||||
mvebu_icu_irq_domain_free(struct irq_domain *domain, unsigned int virq,
|
||||
unsigned int nr_irqs)
|
||||
{
|
||||
struct irq_data *d = irq_get_irq_data(virq);
|
||||
struct mvebu_icu_irq_data *icu_irqd = d->chip_data;
|
||||
|
||||
kfree(icu_irqd);
|
||||
|
||||
platform_msi_domain_free(domain, virq, nr_irqs);
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops mvebu_icu_domain_ops = {
|
||||
.translate = mvebu_icu_irq_domain_translate,
|
||||
.alloc = mvebu_icu_irq_domain_alloc,
|
||||
.free = mvebu_icu_irq_domain_free,
|
||||
};
|
||||
|
||||
static int mvebu_icu_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mvebu_icu *icu;
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct device_node *gicp_dn;
|
||||
struct resource *res;
|
||||
phys_addr_t setspi, clrspi;
|
||||
u32 i, icu_int;
|
||||
int ret;
|
||||
|
||||
icu = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_icu),
|
||||
GFP_KERNEL);
|
||||
if (!icu)
|
||||
return -ENOMEM;
|
||||
|
||||
icu->dev = &pdev->dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
icu->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(icu->base)) {
|
||||
dev_err(&pdev->dev, "Failed to map icu base address.\n");
|
||||
return PTR_ERR(icu->base);
|
||||
}
|
||||
|
||||
icu->irq_chip.name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
|
||||
"ICU.%x",
|
||||
(unsigned int)res->start);
|
||||
if (!icu->irq_chip.name)
|
||||
return -ENOMEM;
|
||||
|
||||
icu->irq_chip.irq_mask = irq_chip_mask_parent;
|
||||
icu->irq_chip.irq_unmask = irq_chip_unmask_parent;
|
||||
icu->irq_chip.irq_eoi = irq_chip_eoi_parent;
|
||||
icu->irq_chip.irq_set_type = irq_chip_set_type_parent;
|
||||
#ifdef CONFIG_SMP
|
||||
icu->irq_chip.irq_set_affinity = irq_chip_set_affinity_parent;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We're probed after MSI domains have been resolved, so force
|
||||
* resolution here.
|
||||
*/
|
||||
pdev->dev.msi_domain = of_msi_get_domain(&pdev->dev, node,
|
||||
DOMAIN_BUS_PLATFORM_MSI);
|
||||
if (!pdev->dev.msi_domain)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
gicp_dn = irq_domain_get_of_node(pdev->dev.msi_domain);
|
||||
if (!gicp_dn)
|
||||
return -ENODEV;
|
||||
|
||||
ret = mvebu_gicp_get_doorbells(gicp_dn, &setspi, &clrspi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Set Clear/Set ICU SPI message address in AP */
|
||||
writel_relaxed(upper_32_bits(setspi), icu->base + ICU_SETSPI_NSR_AH);
|
||||
writel_relaxed(lower_32_bits(setspi), icu->base + ICU_SETSPI_NSR_AL);
|
||||
writel_relaxed(upper_32_bits(clrspi), icu->base + ICU_CLRSPI_NSR_AH);
|
||||
writel_relaxed(lower_32_bits(clrspi), icu->base + ICU_CLRSPI_NSR_AL);
|
||||
|
||||
/*
|
||||
* Clean all ICU interrupts with type SPI_NSR, required to
|
||||
* avoid unpredictable SPI assignments done by firmware.
|
||||
*/
|
||||
for (i = 0 ; i < ICU_MAX_IRQS ; i++) {
|
||||
icu_int = readl(icu->base + ICU_INT_CFG(i));
|
||||
if ((icu_int >> ICU_GROUP_SHIFT) == ICU_GRP_NSR)
|
||||
writel_relaxed(0x0, icu->base + ICU_INT_CFG(i));
|
||||
}
|
||||
|
||||
icu->domain =
|
||||
platform_msi_create_device_domain(&pdev->dev, ICU_MAX_IRQS,
|
||||
mvebu_icu_write_msg,
|
||||
&mvebu_icu_domain_ops, icu);
|
||||
if (!icu->domain) {
|
||||
dev_err(&pdev->dev, "Failed to create ICU domain\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id mvebu_icu_of_match[] = {
|
||||
{ .compatible = "marvell,cp110-icu", },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver mvebu_icu_driver = {
|
||||
.probe = mvebu_icu_probe,
|
||||
.driver = {
|
||||
.name = "mvebu-icu",
|
||||
.of_match_table = mvebu_icu_of_match,
|
||||
},
|
||||
};
|
||||
builtin_platform_driver(mvebu_icu_driver);
|
@ -67,7 +67,7 @@ static int irq_map(struct irq_domain *h, unsigned int virq,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_domain_ops irq_ops = {
|
||||
static const struct irq_domain_ops irq_ops = {
|
||||
.map = irq_map,
|
||||
.xlate = irq_domain_xlate_onecell,
|
||||
};
|
||||
|
@ -73,7 +73,7 @@ static __init int irq_map(struct irq_domain *h, unsigned int virq,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_domain_ops irq_ops = {
|
||||
static const struct irq_domain_ops irq_ops = {
|
||||
.map = irq_map,
|
||||
.xlate = irq_domain_xlate_onecell,
|
||||
};
|
||||
|
@ -25,6 +25,29 @@
|
||||
|
||||
#define SUNXI_NMI_SRC_TYPE_MASK 0x00000003
|
||||
|
||||
#define SUNXI_NMI_IRQ_BIT BIT(0)
|
||||
|
||||
#define SUN6I_R_INTC_CTRL 0x0c
|
||||
#define SUN6I_R_INTC_PENDING 0x10
|
||||
#define SUN6I_R_INTC_ENABLE 0x40
|
||||
|
||||
/*
|
||||
* For deprecated sun6i-a31-sc-nmi compatible.
|
||||
* Registers are offset by 0x0c.
|
||||
*/
|
||||
#define SUN6I_R_INTC_NMI_OFFSET 0x0c
|
||||
#define SUN6I_NMI_CTRL (SUN6I_R_INTC_CTRL - SUN6I_R_INTC_NMI_OFFSET)
|
||||
#define SUN6I_NMI_PENDING (SUN6I_R_INTC_PENDING - SUN6I_R_INTC_NMI_OFFSET)
|
||||
#define SUN6I_NMI_ENABLE (SUN6I_R_INTC_ENABLE - SUN6I_R_INTC_NMI_OFFSET)
|
||||
|
||||
#define SUN7I_NMI_CTRL 0x00
|
||||
#define SUN7I_NMI_PENDING 0x04
|
||||
#define SUN7I_NMI_ENABLE 0x08
|
||||
|
||||
#define SUN9I_NMI_CTRL 0x00
|
||||
#define SUN9I_NMI_ENABLE 0x04
|
||||
#define SUN9I_NMI_PENDING 0x08
|
||||
|
||||
enum {
|
||||
SUNXI_SRC_TYPE_LEVEL_LOW = 0,
|
||||
SUNXI_SRC_TYPE_EDGE_FALLING,
|
||||
@ -38,22 +61,28 @@ struct sunxi_sc_nmi_reg_offs {
|
||||
u32 enable;
|
||||
};
|
||||
|
||||
static struct sunxi_sc_nmi_reg_offs sun7i_reg_offs = {
|
||||
.ctrl = 0x00,
|
||||
.pend = 0x04,
|
||||
.enable = 0x08,
|
||||
static const struct sunxi_sc_nmi_reg_offs sun6i_r_intc_reg_offs __initconst = {
|
||||
.ctrl = SUN6I_R_INTC_CTRL,
|
||||
.pend = SUN6I_R_INTC_PENDING,
|
||||
.enable = SUN6I_R_INTC_ENABLE,
|
||||
};
|
||||
|
||||
static struct sunxi_sc_nmi_reg_offs sun6i_reg_offs = {
|
||||
.ctrl = 0x00,
|
||||
.pend = 0x04,
|
||||
.enable = 0x34,
|
||||
static const struct sunxi_sc_nmi_reg_offs sun6i_reg_offs __initconst = {
|
||||
.ctrl = SUN6I_NMI_CTRL,
|
||||
.pend = SUN6I_NMI_PENDING,
|
||||
.enable = SUN6I_NMI_ENABLE,
|
||||
};
|
||||
|
||||
static struct sunxi_sc_nmi_reg_offs sun9i_reg_offs = {
|
||||
.ctrl = 0x00,
|
||||
.pend = 0x08,
|
||||
.enable = 0x04,
|
||||
static const struct sunxi_sc_nmi_reg_offs sun7i_reg_offs __initconst = {
|
||||
.ctrl = SUN7I_NMI_CTRL,
|
||||
.pend = SUN7I_NMI_PENDING,
|
||||
.enable = SUN7I_NMI_ENABLE,
|
||||
};
|
||||
|
||||
static const struct sunxi_sc_nmi_reg_offs sun9i_reg_offs __initconst = {
|
||||
.ctrl = SUN9I_NMI_CTRL,
|
||||
.pend = SUN9I_NMI_PENDING,
|
||||
.enable = SUN9I_NMI_ENABLE,
|
||||
};
|
||||
|
||||
static inline void sunxi_sc_nmi_write(struct irq_chip_generic *gc, u32 off,
|
||||
@ -128,7 +157,7 @@ static int sunxi_sc_nmi_set_type(struct irq_data *data, unsigned int flow_type)
|
||||
}
|
||||
|
||||
static int __init sunxi_sc_nmi_irq_init(struct device_node *node,
|
||||
struct sunxi_sc_nmi_reg_offs *reg_offs)
|
||||
const struct sunxi_sc_nmi_reg_offs *reg_offs)
|
||||
{
|
||||
struct irq_domain *domain;
|
||||
struct irq_chip_generic *gc;
|
||||
@ -187,8 +216,11 @@ static int __init sunxi_sc_nmi_irq_init(struct device_node *node,
|
||||
gc->chip_types[1].regs.type = reg_offs->ctrl;
|
||||
gc->chip_types[1].handler = handle_edge_irq;
|
||||
|
||||
/* Disable any active interrupts */
|
||||
sunxi_sc_nmi_write(gc, reg_offs->enable, 0);
|
||||
sunxi_sc_nmi_write(gc, reg_offs->pend, 0x1);
|
||||
|
||||
/* Clear any pending NMI interrupts */
|
||||
sunxi_sc_nmi_write(gc, reg_offs->pend, SUNXI_NMI_IRQ_BIT);
|
||||
|
||||
irq_set_chained_handler_and_data(irq, sunxi_sc_nmi_handle_irq, domain);
|
||||
|
||||
@ -200,6 +232,14 @@ fail_irqd_remove:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __init sun6i_r_intc_irq_init(struct device_node *node,
|
||||
struct device_node *parent)
|
||||
{
|
||||
return sunxi_sc_nmi_irq_init(node, &sun6i_r_intc_reg_offs);
|
||||
}
|
||||
IRQCHIP_DECLARE(sun6i_r_intc, "allwinner,sun6i-a31-r-intc",
|
||||
sun6i_r_intc_irq_init);
|
||||
|
||||
static int __init sun6i_sc_nmi_irq_init(struct device_node *node,
|
||||
struct device_node *parent)
|
||||
{
|
||||
|
@ -288,9 +288,4 @@ static struct platform_driver qcom_irq_combiner_probe = {
|
||||
},
|
||||
.probe = combiner_probe,
|
||||
};
|
||||
|
||||
static int __init register_qcom_irq_combiner(void)
|
||||
{
|
||||
return platform_driver_register(&qcom_irq_combiner_probe);
|
||||
}
|
||||
device_initcall(register_qcom_irq_combiner);
|
||||
builtin_platform_driver(qcom_irq_combiner_probe);
|
||||
|
15
include/dt-bindings/interrupt-controller/mvebu-icu.h
Normal file
15
include/dt-bindings/interrupt-controller/mvebu-icu.h
Normal file
@ -0,0 +1,15 @@
|
||||
/*
|
||||
* This header provides constants for the MVEBU ICU driver.
|
||||
*/
|
||||
|
||||
#ifndef _DT_BINDINGS_INTERRUPT_CONTROLLER_MVEBU_ICU_H
|
||||
#define _DT_BINDINGS_INTERRUPT_CONTROLLER_MVEBU_ICU_H
|
||||
|
||||
/* interrupt specifier cell 0 */
|
||||
|
||||
#define ICU_GRP_NSR 0x0
|
||||
#define ICU_GRP_SR 0x1
|
||||
#define ICU_GRP_SEI 0x4
|
||||
#define ICU_GRP_REI 0x5
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user