Merge 397935e3dd ("Merge tag 'smp-core-2024-03-10' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip") into android-mainline
Steps on the way to v6.9-rc1 Signed-off-by: Lee Jones <joneslee@google.com> Change-Id: I922605ec15c24f9f5b7ad1deb4c2c62d6877cc5b
This commit is contained in:
@@ -36,6 +36,7 @@ properties:
|
||||
- amlogic,meson-a1-gpio-intc
|
||||
- amlogic,meson-s4-gpio-intc
|
||||
- amlogic,c3-gpio-intc
|
||||
- amlogic,t7-gpio-intc
|
||||
- const: amlogic,meson-gpio-intc
|
||||
|
||||
reg:
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/interrupt-controller/starfive,jh8100-intc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: StarFive External Interrupt Controller
|
||||
|
||||
description:
|
||||
StarFive SoC JH8100 contain a external interrupt controller. It can be used
|
||||
to handle high-level input interrupt signals. It also send the output
|
||||
interrupt signal to RISC-V PLIC.
|
||||
|
||||
maintainers:
|
||||
- Changhuang Liang <changhuang.liang@starfivetech.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: starfive,jh8100-intc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
description: APB clock for the interrupt controller
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
description: APB reset for the interrupt controller
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
interrupt-controller: true
|
||||
|
||||
"#interrupt-cells":
|
||||
const: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- resets
|
||||
- interrupts
|
||||
- interrupt-controller
|
||||
- "#interrupt-cells"
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
interrupt-controller@12260000 {
|
||||
compatible = "starfive,jh8100-intc";
|
||||
reg = <0x12260000 0x10000>;
|
||||
clocks = <&syscrg_ne 76>;
|
||||
resets = <&syscrg_ne 13>;
|
||||
interrupts = <45>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <1>;
|
||||
};
|
||||
@@ -20959,6 +20959,12 @@ F: Documentation/devicetree/bindings/phy/starfive,jh7110-usb-phy.yaml
|
||||
F: drivers/phy/starfive/phy-jh7110-pcie.c
|
||||
F: drivers/phy/starfive/phy-jh7110-usb.c
|
||||
|
||||
STARFIVE JH8100 EXTERNAL INTERRUPT CONTROLLER DRIVER
|
||||
M: Changhuang Liang <changhuang.liang@starfivetech.com>
|
||||
S: Supported
|
||||
F: Documentation/devicetree/bindings/interrupt-controller/starfive,jh8100-intc.yaml
|
||||
F: drivers/irqchip/irq-starfive-jh8100-intc.c
|
||||
|
||||
STATIC BRANCH/CALL
|
||||
M: Peter Zijlstra <peterz@infradead.org>
|
||||
M: Josh Poimboeuf <jpoimboe@kernel.org>
|
||||
|
||||
@@ -171,6 +171,16 @@
|
||||
};
|
||||
};
|
||||
|
||||
gpio_intc: interrupt-controller@4080 {
|
||||
compatible = "amlogic,t7-gpio-intc",
|
||||
"amlogic,meson-gpio-intc";
|
||||
reg = <0x0 0x4080 0x0 0x20>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
amlogic,channel-interrupts =
|
||||
<10 11 12 13 14 15 16 17 18 19 20 21>;
|
||||
};
|
||||
|
||||
uart_a: serial@78000 {
|
||||
compatible = "amlogic,t7-uart", "amlogic,meson-s4-uart";
|
||||
reg = <0x0 0x78000 0x0 0x18>;
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
#include <asm/irq_vectors.h>
|
||||
|
||||
#define IRQ_MATRIX_BITS NR_VECTORS
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#include <linux/percpu.h>
|
||||
|
||||
@@ -2354,7 +2354,7 @@ static int mp_irqdomain_create(int ioapic)
|
||||
fwspec.param_count = 1;
|
||||
fwspec.param[0] = mpc_ioapic_id(ioapic);
|
||||
|
||||
parent = irq_find_matching_fwspec(&fwspec, DOMAIN_BUS_ANY);
|
||||
parent = irq_find_matching_fwspec(&fwspec, DOMAIN_BUS_GENERIC_MSI);
|
||||
if (!parent) {
|
||||
if (!cfg->dev)
|
||||
irq_domain_free_fwnode(fn);
|
||||
|
||||
@@ -568,7 +568,7 @@ static struct irq_domain *hpet_create_irq_domain(int hpet_id)
|
||||
fwspec.param_count = 1;
|
||||
fwspec.param[0] = hpet_id;
|
||||
|
||||
parent = irq_find_matching_fwspec(&fwspec, DOMAIN_BUS_ANY);
|
||||
parent = irq_find_matching_fwspec(&fwspec, DOMAIN_BUS_GENERIC_MSI);
|
||||
if (!parent) {
|
||||
irq_domain_free_fwnode(fn);
|
||||
kfree(domain_info);
|
||||
|
||||
+105
-14
@@ -13,6 +13,8 @@
|
||||
#include <linux/msi.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/* Begin of removal area. Once everything is converted over. Cleanup the includes too! */
|
||||
|
||||
#define DEV_ID_SHIFT 21
|
||||
#define MAX_DEV_MSIS (1 << (32 - DEV_ID_SHIFT))
|
||||
|
||||
@@ -204,8 +206,8 @@ static void platform_msi_free_priv_data(struct device *dev)
|
||||
* Returns:
|
||||
* Zero for success, or an error code in case of failure
|
||||
*/
|
||||
int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
|
||||
irq_write_msi_msg_t write_msi_msg)
|
||||
static int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
|
||||
irq_write_msi_msg_t write_msi_msg)
|
||||
{
|
||||
int err;
|
||||
|
||||
@@ -219,18 +221,6 @@ int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(platform_msi_domain_alloc_irqs);
|
||||
|
||||
/**
|
||||
* platform_msi_domain_free_irqs - Free MSI interrupts for @dev
|
||||
* @dev: The device for which to free interrupts
|
||||
*/
|
||||
void platform_msi_domain_free_irqs(struct device *dev)
|
||||
{
|
||||
msi_domain_free_irqs_all(dev, MSI_DEFAULT_DOMAIN);
|
||||
platform_msi_free_priv_data(dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(platform_msi_domain_free_irqs);
|
||||
|
||||
/**
|
||||
* platform_msi_get_host_data - Query the private data associated with
|
||||
@@ -350,3 +340,104 @@ int platform_msi_device_domain_alloc(struct irq_domain *domain, unsigned int vir
|
||||
|
||||
return msi_domain_populate_irqs(domain->parent, dev, virq, nr_irqs, &data->arg);
|
||||
}
|
||||
|
||||
/* End of removal area */
|
||||
|
||||
/* Real per device domain interfaces */
|
||||
|
||||
/*
|
||||
* This indirection can go when platform_device_msi_init_and_alloc_irqs()
|
||||
* is switched to a proper irq_chip::irq_write_msi_msg() callback. Keep it
|
||||
* simple for now.
|
||||
*/
|
||||
static void platform_msi_write_msi_msg(struct irq_data *d, struct msi_msg *msg)
|
||||
{
|
||||
irq_write_msi_msg_t cb = d->chip_data;
|
||||
|
||||
cb(irq_data_get_msi_desc(d), msg);
|
||||
}
|
||||
|
||||
static void platform_msi_set_desc_byindex(msi_alloc_info_t *arg, struct msi_desc *desc)
|
||||
{
|
||||
arg->desc = desc;
|
||||
arg->hwirq = desc->msi_index;
|
||||
}
|
||||
|
||||
static const struct msi_domain_template platform_msi_template = {
|
||||
.chip = {
|
||||
.name = "pMSI",
|
||||
.irq_mask = irq_chip_mask_parent,
|
||||
.irq_unmask = irq_chip_unmask_parent,
|
||||
.irq_write_msi_msg = platform_msi_write_msi_msg,
|
||||
/* The rest is filled in by the platform MSI parent */
|
||||
},
|
||||
|
||||
.ops = {
|
||||
.set_desc = platform_msi_set_desc_byindex,
|
||||
},
|
||||
|
||||
.info = {
|
||||
.bus_token = DOMAIN_BUS_DEVICE_MSI,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* platform_device_msi_init_and_alloc_irqs - Initialize platform device MSI
|
||||
* and allocate interrupts for @dev
|
||||
* @dev: The device for which to allocate interrupts
|
||||
* @nvec: The number of interrupts to allocate
|
||||
* @write_msi_msg: Callback to write an interrupt message for @dev
|
||||
*
|
||||
* Returns:
|
||||
* Zero for success, or an error code in case of failure
|
||||
*
|
||||
* This creates a MSI domain on @dev which has @dev->msi.domain as
|
||||
* parent. The parent domain sets up the new domain. The domain has
|
||||
* a fixed size of @nvec. The domain is managed by devres and will
|
||||
* be removed when the device is removed.
|
||||
*
|
||||
* Note: For migration purposes this falls back to the original platform_msi code
|
||||
* up to the point where all platforms have been converted to the MSI
|
||||
* parent model.
|
||||
*/
|
||||
int platform_device_msi_init_and_alloc_irqs(struct device *dev, unsigned int nvec,
|
||||
irq_write_msi_msg_t write_msi_msg)
|
||||
{
|
||||
struct irq_domain *domain = dev->msi.domain;
|
||||
|
||||
if (!domain || !write_msi_msg)
|
||||
return -EINVAL;
|
||||
|
||||
/* Migration support. Will go away once everything is converted */
|
||||
if (!irq_domain_is_msi_parent(domain))
|
||||
return platform_msi_domain_alloc_irqs(dev, nvec, write_msi_msg);
|
||||
|
||||
/*
|
||||
* @write_msi_msg is stored in the resulting msi_domain_info::data.
|
||||
* The underlying domain creation mechanism will assign that
|
||||
* callback to the resulting irq chip.
|
||||
*/
|
||||
if (!msi_create_device_irq_domain(dev, MSI_DEFAULT_DOMAIN,
|
||||
&platform_msi_template,
|
||||
nvec, NULL, write_msi_msg))
|
||||
return -ENODEV;
|
||||
|
||||
return msi_domain_alloc_irqs_range(dev, MSI_DEFAULT_DOMAIN, 0, nvec - 1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(platform_device_msi_init_and_alloc_irqs);
|
||||
|
||||
/**
|
||||
* platform_device_msi_free_irqs_all - Free all interrupts for @dev
|
||||
* @dev: The device for which to free interrupts
|
||||
*/
|
||||
void platform_device_msi_free_irqs_all(struct device *dev)
|
||||
{
|
||||
struct irq_domain *domain = dev->msi.domain;
|
||||
|
||||
msi_domain_free_irqs_all(dev, MSI_DEFAULT_DOMAIN);
|
||||
|
||||
/* Migration support. Will go away once everything is converted */
|
||||
if (!irq_domain_is_msi_parent(domain))
|
||||
platform_msi_free_priv_data(dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(platform_device_msi_free_irqs_all);
|
||||
|
||||
@@ -747,8 +747,8 @@ static int mv_xor_v2_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(xor_dev->clk))
|
||||
return PTR_ERR(xor_dev->clk);
|
||||
|
||||
ret = platform_msi_domain_alloc_irqs(&pdev->dev, 1,
|
||||
mv_xor_v2_set_msi_msg);
|
||||
ret = platform_device_msi_init_and_alloc_irqs(&pdev->dev, 1,
|
||||
mv_xor_v2_set_msi_msg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -851,7 +851,7 @@ free_hw_desq:
|
||||
xor_dev->desc_size * MV_XOR_V2_DESC_NUM,
|
||||
xor_dev->hw_desq_virt, xor_dev->hw_desq);
|
||||
free_msi_irqs:
|
||||
platform_msi_domain_free_irqs(&pdev->dev);
|
||||
platform_device_msi_free_irqs_all(&pdev->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -867,7 +867,7 @@ static void mv_xor_v2_remove(struct platform_device *pdev)
|
||||
|
||||
devm_free_irq(&pdev->dev, xor_dev->irq, xor_dev);
|
||||
|
||||
platform_msi_domain_free_irqs(&pdev->dev);
|
||||
platform_device_msi_free_irqs_all(&pdev->dev);
|
||||
|
||||
tasklet_kill(&xor_dev->irq_tasklet);
|
||||
}
|
||||
|
||||
@@ -696,7 +696,7 @@ static void hidma_free_msis(struct hidma_dev *dmadev)
|
||||
devm_free_irq(dev, virq, &dmadev->lldev);
|
||||
}
|
||||
|
||||
platform_msi_domain_free_irqs(dev);
|
||||
platform_device_msi_free_irqs_all(dev);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -706,8 +706,8 @@ static int hidma_request_msi(struct hidma_dev *dmadev,
|
||||
#ifdef CONFIG_GENERIC_MSI_IRQ
|
||||
int rc, i, virq;
|
||||
|
||||
rc = platform_msi_domain_alloc_irqs(&pdev->dev, HIDMA_MSI_INTS,
|
||||
hidma_write_msi_msg);
|
||||
rc = platform_device_msi_init_and_alloc_irqs(&pdev->dev, HIDMA_MSI_INTS,
|
||||
hidma_write_msi_msg);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
|
||||
@@ -3125,7 +3125,8 @@ static int arm_smmu_update_gbpa(struct arm_smmu_device *smmu, u32 set, u32 clr)
|
||||
static void arm_smmu_free_msis(void *data)
|
||||
{
|
||||
struct device *dev = data;
|
||||
platform_msi_domain_free_irqs(dev);
|
||||
|
||||
platform_device_msi_free_irqs_all(dev);
|
||||
}
|
||||
|
||||
static void arm_smmu_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg)
|
||||
@@ -3166,7 +3167,7 @@ static void arm_smmu_setup_msis(struct arm_smmu_device *smmu)
|
||||
}
|
||||
|
||||
/* Allocate MSIs for evtq, gerror and priq. Ignore cmdq */
|
||||
ret = platform_msi_domain_alloc_irqs(dev, nvec, arm_smmu_write_msi_msg);
|
||||
ret = platform_device_msi_init_and_alloc_irqs(dev, nvec, arm_smmu_write_msi_msg);
|
||||
if (ret) {
|
||||
dev_warn(dev, "failed to allocate MSIs - falling back to wired irqs\n");
|
||||
return;
|
||||
|
||||
@@ -546,6 +546,17 @@ config SIFIVE_PLIC
|
||||
select IRQ_DOMAIN_HIERARCHY
|
||||
select GENERIC_IRQ_EFFECTIVE_AFF_MASK if SMP
|
||||
|
||||
config STARFIVE_JH8100_INTC
|
||||
bool "StarFive JH8100 External Interrupt Controller"
|
||||
depends on ARCH_STARFIVE || COMPILE_TEST
|
||||
default ARCH_STARFIVE
|
||||
select IRQ_DOMAIN_HIERARCHY
|
||||
help
|
||||
This enables support for the INTC chip found in StarFive JH8100
|
||||
SoC.
|
||||
|
||||
If you don't know what to do here, say Y.
|
||||
|
||||
config EXYNOS_IRQ_COMBINER
|
||||
bool "Samsung Exynos IRQ combiner support" if COMPILE_TEST
|
||||
depends on (ARCH_EXYNOS && ARM) || COMPILE_TEST
|
||||
|
||||
@@ -96,6 +96,7 @@ obj-$(CONFIG_CSKY_MPINTC) += irq-csky-mpintc.o
|
||||
obj-$(CONFIG_CSKY_APB_INTC) += irq-csky-apb-intc.o
|
||||
obj-$(CONFIG_RISCV_INTC) += irq-riscv-intc.o
|
||||
obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o
|
||||
obj-$(CONFIG_STARFIVE_JH8100_INTC) += irq-starfive-jh8100-intc.o
|
||||
obj-$(CONFIG_IMX_IRQSTEER) += irq-imx-irqsteer.o
|
||||
obj-$(CONFIG_IMX_INTMUX) += irq-imx-intmux.o
|
||||
obj-$(CONFIG_IMX_MU_MSI) += irq-imx-mu-msi.o
|
||||
|
||||
@@ -242,7 +242,7 @@ static int __init bcm6345_l1_init_one(struct device_node *dn,
|
||||
else if (intc->n_words != n_words)
|
||||
return -EINVAL;
|
||||
|
||||
cpu = intc->cpus[idx] = kzalloc(sizeof(*cpu) + n_words * sizeof(u32),
|
||||
cpu = intc->cpus[idx] = kzalloc(struct_size(cpu, enable_cache, n_words),
|
||||
GFP_KERNEL);
|
||||
if (!cpu)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -249,7 +249,7 @@ static int __init bcm7038_l1_init_one(struct device_node *dn,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cpu = intc->cpus[idx] = kzalloc(sizeof(*cpu) + n_words * sizeof(u32),
|
||||
cpu = intc->cpus[idx] = kzalloc(struct_size(cpu, mask_cache, n_words),
|
||||
GFP_KERNEL);
|
||||
if (!cpu)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -4436,12 +4436,12 @@ static const struct irq_domain_ops its_sgi_domain_ops = {
|
||||
|
||||
static int its_vpe_id_alloc(void)
|
||||
{
|
||||
return ida_simple_get(&its_vpeid_ida, 0, ITS_MAX_VPEID, GFP_KERNEL);
|
||||
return ida_alloc_max(&its_vpeid_ida, ITS_MAX_VPEID - 1, GFP_KERNEL);
|
||||
}
|
||||
|
||||
static void its_vpe_id_free(u16 id)
|
||||
{
|
||||
ida_simple_remove(&its_vpeid_ida, id);
|
||||
ida_free(&its_vpeid_ida, id);
|
||||
}
|
||||
|
||||
static int its_vpe_init(struct its_vpe *vpe)
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/refcount.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <trace/hooks/gic_v3.h>
|
||||
|
||||
#include <linux/irqchip.h>
|
||||
@@ -181,11 +182,6 @@ static enum gic_intid_range get_intid_range(struct irq_data *d)
|
||||
return __get_intid_range(d->hwirq);
|
||||
}
|
||||
|
||||
static inline unsigned int gic_irq(struct irq_data *d)
|
||||
{
|
||||
return d->hwirq;
|
||||
}
|
||||
|
||||
static inline bool gic_irq_in_rdist(struct irq_data *d)
|
||||
{
|
||||
switch (get_intid_range(d)) {
|
||||
@@ -252,17 +248,13 @@ static inline void __iomem *gic_dist_base(struct irq_data *d)
|
||||
|
||||
static void gic_do_wait_for_rwp(void __iomem *base, u32 bit)
|
||||
{
|
||||
u32 count = 1000000; /* 1s! */
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
while (readl_relaxed(base + GICD_CTLR) & bit) {
|
||||
count--;
|
||||
if (!count) {
|
||||
pr_err_ratelimited("RWP timeout, gone fishing\n");
|
||||
return;
|
||||
}
|
||||
cpu_relax();
|
||||
udelay(1);
|
||||
}
|
||||
ret = readl_relaxed_poll_timeout_atomic(base + GICD_CTLR, val, !(val & bit),
|
||||
1, USEC_PER_SEC);
|
||||
if (ret == -ETIMEDOUT)
|
||||
pr_err_ratelimited("RWP timeout, gone fishing\n");
|
||||
}
|
||||
|
||||
/* Wait for completion of a distributor change */
|
||||
@@ -280,8 +272,8 @@ static void gic_redist_wait_for_rwp(void)
|
||||
static void gic_enable_redist(bool enable)
|
||||
{
|
||||
void __iomem *rbase;
|
||||
u32 count = 1000000; /* 1s! */
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
if (gic_data.flags & FLAGS_WORKAROUND_GICR_WAKER_MSM8996)
|
||||
return;
|
||||
@@ -302,16 +294,13 @@ static void gic_enable_redist(bool enable)
|
||||
return; /* No PM support in this redistributor */
|
||||
}
|
||||
|
||||
while (--count) {
|
||||
val = readl_relaxed(rbase + GICR_WAKER);
|
||||
if (enable ^ (bool)(val & GICR_WAKER_ChildrenAsleep))
|
||||
break;
|
||||
cpu_relax();
|
||||
udelay(1);
|
||||
}
|
||||
if (!count)
|
||||
ret = readl_relaxed_poll_timeout_atomic(rbase + GICR_WAKER, val,
|
||||
enable ^ (bool)(val & GICR_WAKER_ChildrenAsleep),
|
||||
1, USEC_PER_SEC);
|
||||
if (ret == -ETIMEDOUT) {
|
||||
pr_err_ratelimited("redistributor failed to %s...\n",
|
||||
enable ? "wakeup" : "sleep");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -549,7 +538,7 @@ static int gic_irq_nmi_setup(struct irq_data *d)
|
||||
* A secondary irq_chip should be in charge of LPI request,
|
||||
* it should not be possible to get there
|
||||
*/
|
||||
if (WARN_ON(gic_irq(d) >= 8192))
|
||||
if (WARN_ON(irqd_to_hwirq(d) >= 8192))
|
||||
return -EINVAL;
|
||||
|
||||
/* desc lock should already be held */
|
||||
@@ -589,7 +578,7 @@ static void gic_irq_nmi_teardown(struct irq_data *d)
|
||||
* A secondary irq_chip should be in charge of LPI request,
|
||||
* it should not be possible to get there
|
||||
*/
|
||||
if (WARN_ON(gic_irq(d) >= 8192))
|
||||
if (WARN_ON(irqd_to_hwirq(d) >= 8192))
|
||||
return;
|
||||
|
||||
/* desc lock should already be held */
|
||||
@@ -627,7 +616,7 @@ static bool gic_arm64_erratum_2941627_needed(struct irq_data *d)
|
||||
|
||||
static void gic_eoi_irq(struct irq_data *d)
|
||||
{
|
||||
write_gicreg(gic_irq(d), ICC_EOIR1_EL1);
|
||||
write_gicreg(irqd_to_hwirq(d), ICC_EOIR1_EL1);
|
||||
isb();
|
||||
|
||||
if (gic_arm64_erratum_2941627_needed(d)) {
|
||||
@@ -647,19 +636,19 @@ static void gic_eoimode1_eoi_irq(struct irq_data *d)
|
||||
* No need to deactivate an LPI, or an interrupt that
|
||||
* is is getting forwarded to a vcpu.
|
||||
*/
|
||||
if (gic_irq(d) >= 8192 || irqd_is_forwarded_to_vcpu(d))
|
||||
if (irqd_to_hwirq(d) >= 8192 || irqd_is_forwarded_to_vcpu(d))
|
||||
return;
|
||||
|
||||
if (!gic_arm64_erratum_2941627_needed(d))
|
||||
gic_write_dir(gic_irq(d));
|
||||
gic_write_dir(irqd_to_hwirq(d));
|
||||
else
|
||||
gic_poke_irq(d, GICD_ICACTIVER);
|
||||
}
|
||||
|
||||
static int gic_set_type(struct irq_data *d, unsigned int type)
|
||||
{
|
||||
irq_hw_number_t irq = irqd_to_hwirq(d);
|
||||
enum gic_intid_range range;
|
||||
unsigned int irq = gic_irq(d);
|
||||
void __iomem *base;
|
||||
u32 offset, index;
|
||||
int ret;
|
||||
@@ -685,7 +674,7 @@ static int gic_set_type(struct irq_data *d, unsigned int type)
|
||||
ret = gic_configure_irq(index, type, base + offset, NULL);
|
||||
if (ret && (range == PPI_RANGE || range == EPPI_RANGE)) {
|
||||
/* Misconfigured PPIs are usually not fatal */
|
||||
pr_warn("GIC: PPI INTID%d is secure or misconfigured\n", irq);
|
||||
pr_warn("GIC: PPI INTID%ld is secure or misconfigured\n", irq);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
@@ -1710,9 +1699,13 @@ static int gic_irq_domain_select(struct irq_domain *d,
|
||||
irq_hw_number_t hwirq;
|
||||
|
||||
/* Not for us */
|
||||
if (fwspec->fwnode != d->fwnode)
|
||||
if (fwspec->fwnode != d->fwnode)
|
||||
return 0;
|
||||
|
||||
/* Handle pure domain searches */
|
||||
if (!fwspec->param_count)
|
||||
return d->bus_token == bus_token;
|
||||
|
||||
/* If this is not DT, then we have a single domain */
|
||||
if (!is_of_node(fwspec->fwnode))
|
||||
return 1;
|
||||
|
||||
+12
-15
@@ -163,11 +163,6 @@ static inline void __iomem *gic_cpu_base(struct irq_data *d)
|
||||
return gic_data_cpu_base(gic_data);
|
||||
}
|
||||
|
||||
static inline unsigned int gic_irq(struct irq_data *d)
|
||||
{
|
||||
return d->hwirq;
|
||||
}
|
||||
|
||||
static inline bool cascading_gic_irq(struct irq_data *d)
|
||||
{
|
||||
void *data = irq_data_get_irq_handler_data(d);
|
||||
@@ -184,14 +179,16 @@ static inline bool cascading_gic_irq(struct irq_data *d)
|
||||
*/
|
||||
static void gic_poke_irq(struct irq_data *d, u32 offset)
|
||||
{
|
||||
u32 mask = 1 << (gic_irq(d) % 32);
|
||||
writel_relaxed(mask, gic_dist_base(d) + offset + (gic_irq(d) / 32) * 4);
|
||||
u32 mask = 1 << (irqd_to_hwirq(d) % 32);
|
||||
|
||||
writel_relaxed(mask, gic_dist_base(d) + offset + (irqd_to_hwirq(d) / 32) * 4);
|
||||
}
|
||||
|
||||
static int gic_peek_irq(struct irq_data *d, u32 offset)
|
||||
{
|
||||
u32 mask = 1 << (gic_irq(d) % 32);
|
||||
return !!(readl_relaxed(gic_dist_base(d) + offset + (gic_irq(d) / 32) * 4) & mask);
|
||||
u32 mask = 1 << (irqd_to_hwirq(d) % 32);
|
||||
|
||||
return !!(readl_relaxed(gic_dist_base(d) + offset + (irqd_to_hwirq(d) / 32) * 4) & mask);
|
||||
}
|
||||
|
||||
static void gic_mask_irq(struct irq_data *d)
|
||||
@@ -221,7 +218,7 @@ static void gic_unmask_irq(struct irq_data *d)
|
||||
|
||||
static void gic_eoi_irq(struct irq_data *d)
|
||||
{
|
||||
u32 hwirq = gic_irq(d);
|
||||
irq_hw_number_t hwirq = irqd_to_hwirq(d);
|
||||
|
||||
if (hwirq < 16)
|
||||
hwirq = this_cpu_read(sgi_intid);
|
||||
@@ -231,7 +228,7 @@ static void gic_eoi_irq(struct irq_data *d)
|
||||
|
||||
static void gic_eoimode1_eoi_irq(struct irq_data *d)
|
||||
{
|
||||
u32 hwirq = gic_irq(d);
|
||||
irq_hw_number_t hwirq = irqd_to_hwirq(d);
|
||||
|
||||
/* Do not deactivate an IRQ forwarded to a vcpu. */
|
||||
if (irqd_is_forwarded_to_vcpu(d))
|
||||
@@ -294,8 +291,8 @@ static int gic_irq_get_irqchip_state(struct irq_data *d,
|
||||
|
||||
static int gic_set_type(struct irq_data *d, unsigned int type)
|
||||
{
|
||||
irq_hw_number_t gicirq = irqd_to_hwirq(d);
|
||||
void __iomem *base = gic_dist_base(d);
|
||||
unsigned int gicirq = gic_irq(d);
|
||||
int ret;
|
||||
|
||||
/* Interrupt configuration for SGIs can't be changed */
|
||||
@@ -310,7 +307,7 @@ static int gic_set_type(struct irq_data *d, unsigned int type)
|
||||
ret = gic_configure_irq(gicirq, type, base + GIC_DIST_CONFIG, NULL);
|
||||
if (ret && gicirq < 32) {
|
||||
/* Misconfigured PPIs are usually not fatal */
|
||||
pr_warn("GIC: PPI%d is secure or misconfigured\n", gicirq - 16);
|
||||
pr_warn("GIC: PPI%ld is secure or misconfigured\n", gicirq - 16);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
@@ -320,7 +317,7 @@ static int gic_set_type(struct irq_data *d, unsigned int type)
|
||||
static int gic_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu)
|
||||
{
|
||||
/* Only interrupts on the primary GIC can be forwarded to a vcpu. */
|
||||
if (cascading_gic_irq(d) || gic_irq(d) < 16)
|
||||
if (cascading_gic_irq(d) || irqd_to_hwirq(d) < 16)
|
||||
return -EINVAL;
|
||||
|
||||
if (vcpu)
|
||||
@@ -797,7 +794,7 @@ static void rmw_writeb(u8 bval, void __iomem *addr)
|
||||
static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
|
||||
bool force)
|
||||
{
|
||||
void __iomem *reg = gic_dist_base(d) + GIC_DIST_TARGET + gic_irq(d);
|
||||
void __iomem *reg = gic_dist_base(d) + GIC_DIST_TARGET + irqd_to_hwirq(d);
|
||||
struct gic_chip_data *gic = irq_data_get_irq_chip_data(d);
|
||||
unsigned int cpu;
|
||||
|
||||
|
||||
@@ -461,12 +461,11 @@ err_generic:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pdc_intc_remove(struct platform_device *pdev)
|
||||
static void pdc_intc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct pdc_intc_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
irq_domain_remove(priv->domain);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id pdc_intc_match[] = {
|
||||
@@ -479,8 +478,8 @@ static struct platform_driver pdc_intc_driver = {
|
||||
.name = "pdc-intc",
|
||||
.of_match_table = pdc_intc_match,
|
||||
},
|
||||
.probe = pdc_intc_probe,
|
||||
.remove = pdc_intc_remove,
|
||||
.probe = pdc_intc_probe,
|
||||
.remove_new = pdc_intc_remove,
|
||||
};
|
||||
|
||||
static int __init pdc_intc_init(void)
|
||||
|
||||
@@ -166,6 +166,10 @@ static int imx_intmux_irq_select(struct irq_domain *d, struct irq_fwspec *fwspec
|
||||
if (fwspec->fwnode != d->fwnode)
|
||||
return false;
|
||||
|
||||
/* Handle pure domain searches */
|
||||
if (!fwspec->param_count)
|
||||
return d->bus_token == bus_token;
|
||||
|
||||
return irqchip_data->chanidx == fwspec->param[1];
|
||||
}
|
||||
|
||||
@@ -282,7 +286,7 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int imx_intmux_remove(struct platform_device *pdev)
|
||||
static void imx_intmux_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct intmux_data *data = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
@@ -298,8 +302,6 @@ static int imx_intmux_remove(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
@@ -354,11 +356,11 @@ static const struct of_device_id imx_intmux_id[] = {
|
||||
|
||||
static struct platform_driver imx_intmux_driver = {
|
||||
.driver = {
|
||||
.name = "imx-intmux",
|
||||
.of_match_table = imx_intmux_id,
|
||||
.pm = &imx_intmux_pm_ops,
|
||||
.name = "imx-intmux",
|
||||
.of_match_table = imx_intmux_id,
|
||||
.pm = &imx_intmux_pm_ops,
|
||||
},
|
||||
.probe = imx_intmux_probe,
|
||||
.remove = imx_intmux_remove,
|
||||
.probe = imx_intmux_probe,
|
||||
.remove_new = imx_intmux_remove,
|
||||
};
|
||||
builtin_platform_driver(imx_intmux_driver);
|
||||
|
||||
@@ -231,7 +231,7 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int imx_irqsteer_remove(struct platform_device *pdev)
|
||||
static void imx_irqsteer_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct irqsteer_data *irqsteer_data = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
@@ -243,8 +243,6 @@ static int imx_irqsteer_remove(struct platform_device *pdev)
|
||||
irq_domain_remove(irqsteer_data->domain);
|
||||
|
||||
clk_disable_unprepare(irqsteer_data->ipg_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
@@ -307,11 +305,11 @@ static const struct of_device_id imx_irqsteer_dt_ids[] = {
|
||||
|
||||
static struct platform_driver imx_irqsteer_driver = {
|
||||
.driver = {
|
||||
.name = "imx-irqsteer",
|
||||
.of_match_table = imx_irqsteer_dt_ids,
|
||||
.pm = &imx_irqsteer_pm_ops,
|
||||
.name = "imx-irqsteer",
|
||||
.of_match_table = imx_irqsteer_dt_ids,
|
||||
.pm = &imx_irqsteer_pm_ops,
|
||||
},
|
||||
.probe = imx_irqsteer_probe,
|
||||
.remove = imx_irqsteer_remove,
|
||||
.probe = imx_irqsteer_probe,
|
||||
.remove_new = imx_irqsteer_remove,
|
||||
};
|
||||
builtin_platform_driver(imx_irqsteer_driver);
|
||||
|
||||
@@ -190,7 +190,7 @@ static int keystone_irq_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int keystone_irq_remove(struct platform_device *pdev)
|
||||
static void keystone_irq_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct keystone_irq_device *kirq = platform_get_drvdata(pdev);
|
||||
int hwirq;
|
||||
@@ -201,7 +201,6 @@ static int keystone_irq_remove(struct platform_device *pdev)
|
||||
irq_dispose_mapping(irq_find_mapping(kirq->irqd, hwirq));
|
||||
|
||||
irq_domain_remove(kirq->irqd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id keystone_irq_dt_ids[] = {
|
||||
@@ -212,7 +211,7 @@ MODULE_DEVICE_TABLE(of, keystone_irq_dt_ids);
|
||||
|
||||
static struct platform_driver keystone_irq_device_driver = {
|
||||
.probe = keystone_irq_probe,
|
||||
.remove = keystone_irq_remove,
|
||||
.remove_new = keystone_irq_remove,
|
||||
.driver = {
|
||||
.name = "keystone_irq",
|
||||
.of_match_table = of_match_ptr(keystone_irq_dt_ids),
|
||||
|
||||
@@ -198,6 +198,12 @@ static void eiointc_irq_dispatch(struct irq_desc *desc)
|
||||
|
||||
for (i = 0; i < eiointc_priv[0]->vec_count / VEC_COUNT_PER_REG; i++) {
|
||||
pending = iocsr_read64(EIOINTC_REG_ISR + (i << 3));
|
||||
|
||||
/* Skip handling if pending bitmap is zero */
|
||||
if (!pending)
|
||||
continue;
|
||||
|
||||
/* Clear the IRQs */
|
||||
iocsr_write64(pending, EIOINTC_REG_ISR + (i << 3));
|
||||
while (pending) {
|
||||
int bit = __ffs(pending);
|
||||
@@ -304,23 +310,7 @@ static int eiointc_suspend(void)
|
||||
|
||||
static void eiointc_resume(void)
|
||||
{
|
||||
int i, j;
|
||||
struct irq_desc *desc;
|
||||
struct irq_data *irq_data;
|
||||
|
||||
eiointc_router_init(0);
|
||||
|
||||
for (i = 0; i < nr_pics; i++) {
|
||||
for (j = 0; j < eiointc_priv[0]->vec_count; j++) {
|
||||
desc = irq_resolve_mapping(eiointc_priv[i]->eiointc_domain, j);
|
||||
if (desc && desc->handle_irq && desc->handle_irq != handle_bad_irq) {
|
||||
raw_spin_lock(&desc->lock);
|
||||
irq_data = irq_domain_get_irq_data(eiointc_priv[i]->eiointc_domain, irq_desc_get_irq(desc));
|
||||
eiointc_set_irq_affinity(irq_data, irq_data->common->affinity, 0);
|
||||
raw_spin_unlock(&desc->lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct syscore_ops eiointc_syscore_ops = {
|
||||
|
||||
@@ -398,7 +398,7 @@ static int ls_scfg_msi_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ls_scfg_msi_remove(struct platform_device *pdev)
|
||||
static void ls_scfg_msi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ls_scfg_msi *msi_data = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
@@ -410,17 +410,15 @@ static int ls_scfg_msi_remove(struct platform_device *pdev)
|
||||
irq_domain_remove(msi_data->parent);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver ls_scfg_msi_driver = {
|
||||
.driver = {
|
||||
.name = "ls-scfg-msi",
|
||||
.of_match_table = ls_scfg_msi_id,
|
||||
.name = "ls-scfg-msi",
|
||||
.of_match_table = ls_scfg_msi_id,
|
||||
},
|
||||
.probe = ls_scfg_msi_probe,
|
||||
.remove = ls_scfg_msi_remove,
|
||||
.probe = ls_scfg_msi_probe,
|
||||
.remove_new = ls_scfg_msi_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(ls_scfg_msi_driver);
|
||||
|
||||
@@ -222,7 +222,7 @@ static int madera_irq_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int madera_irq_remove(struct platform_device *pdev)
|
||||
static void madera_irq_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct madera *madera = dev_get_drvdata(pdev->dev.parent);
|
||||
|
||||
@@ -232,13 +232,11 @@ static int madera_irq_remove(struct platform_device *pdev)
|
||||
*/
|
||||
madera->irq_dev = NULL;
|
||||
regmap_del_irq_chip(madera->irq, madera->irq_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver madera_irq_driver = {
|
||||
.probe = &madera_irq_probe,
|
||||
.remove = &madera_irq_remove,
|
||||
.probe = madera_irq_probe,
|
||||
.remove_new = madera_irq_remove,
|
||||
.driver = {
|
||||
.name = "madera-irq",
|
||||
.pm = &madera_irq_pm_ops,
|
||||
|
||||
@@ -154,6 +154,10 @@ static const struct meson_gpio_irq_params c3_params = {
|
||||
INIT_MESON_S4_COMMON_DATA(55)
|
||||
};
|
||||
|
||||
static const struct meson_gpio_irq_params t7_params = {
|
||||
INIT_MESON_S4_COMMON_DATA(157)
|
||||
};
|
||||
|
||||
static const struct of_device_id meson_irq_gpio_matches[] __maybe_unused = {
|
||||
{ .compatible = "amlogic,meson8-gpio-intc", .data = &meson8_params },
|
||||
{ .compatible = "amlogic,meson8b-gpio-intc", .data = &meson8b_params },
|
||||
@@ -165,6 +169,7 @@ static const struct of_device_id meson_irq_gpio_matches[] __maybe_unused = {
|
||||
{ .compatible = "amlogic,meson-a1-gpio-intc", .data = &a1_params },
|
||||
{ .compatible = "amlogic,meson-s4-gpio-intc", .data = &s4_params },
|
||||
{ .compatible = "amlogic,c3-gpio-intc", .data = &c3_params },
|
||||
{ .compatible = "amlogic,t7-gpio-intc", .data = &t7_params },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
@@ -167,14 +167,12 @@ static int mvebu_pic_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mvebu_pic_remove(struct platform_device *pdev)
|
||||
static void mvebu_pic_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mvebu_pic *pic = platform_get_drvdata(pdev);
|
||||
|
||||
on_each_cpu(mvebu_pic_disable_percpu_irq, pic, 1);
|
||||
irq_domain_remove(pic->domain);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id mvebu_pic_of_match[] = {
|
||||
@@ -184,11 +182,11 @@ static const struct of_device_id mvebu_pic_of_match[] = {
|
||||
MODULE_DEVICE_TABLE(of, mvebu_pic_of_match);
|
||||
|
||||
static struct platform_driver mvebu_pic_driver = {
|
||||
.probe = mvebu_pic_probe,
|
||||
.remove = mvebu_pic_remove,
|
||||
.probe = mvebu_pic_probe,
|
||||
.remove_new = mvebu_pic_remove,
|
||||
.driver = {
|
||||
.name = "mvebu-pic",
|
||||
.of_match_table = mvebu_pic_of_match,
|
||||
.name = "mvebu-pic",
|
||||
.of_match_table = mvebu_pic_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(mvebu_pic_driver);
|
||||
|
||||
@@ -599,7 +599,7 @@ fail_irq:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pruss_intc_remove(struct platform_device *pdev)
|
||||
static void pruss_intc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct pruss_intc *intc = platform_get_drvdata(pdev);
|
||||
u8 max_system_events = intc->soc_config->num_system_events;
|
||||
@@ -616,8 +616,6 @@ static int pruss_intc_remove(struct platform_device *pdev)
|
||||
irq_dispose_mapping(irq_find_mapping(intc->domain, hwirq));
|
||||
|
||||
irq_domain_remove(intc->domain);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct pruss_intc_match_data pruss_intc_data = {
|
||||
@@ -645,12 +643,12 @@ MODULE_DEVICE_TABLE(of, pruss_intc_of_match);
|
||||
|
||||
static struct platform_driver pruss_intc_driver = {
|
||||
.driver = {
|
||||
.name = "pruss-intc",
|
||||
.of_match_table = pruss_intc_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
.name = "pruss-intc",
|
||||
.of_match_table = pruss_intc_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.probe = pruss_intc_probe,
|
||||
.remove = pruss_intc_remove,
|
||||
.probe = pruss_intc_probe,
|
||||
.remove_new = pruss_intc_remove,
|
||||
};
|
||||
module_platform_driver(pruss_intc_driver);
|
||||
|
||||
|
||||
@@ -561,14 +561,13 @@ err0:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int intc_irqpin_remove(struct platform_device *pdev)
|
||||
static void intc_irqpin_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct intc_irqpin_priv *p = platform_get_drvdata(pdev);
|
||||
|
||||
irq_domain_remove(p->irq_domain);
|
||||
pm_runtime_put(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused intc_irqpin_suspend(struct device *dev)
|
||||
@@ -585,11 +584,11 @@ static SIMPLE_DEV_PM_OPS(intc_irqpin_pm_ops, intc_irqpin_suspend, NULL);
|
||||
|
||||
static struct platform_driver intc_irqpin_device_driver = {
|
||||
.probe = intc_irqpin_probe,
|
||||
.remove = intc_irqpin_remove,
|
||||
.remove_new = intc_irqpin_remove,
|
||||
.driver = {
|
||||
.name = "renesas_intc_irqpin",
|
||||
.of_match_table = intc_irqpin_dt_ids,
|
||||
.pm = &intc_irqpin_pm_ops,
|
||||
.name = "renesas_intc_irqpin",
|
||||
.of_match_table = intc_irqpin_dt_ids,
|
||||
.pm = &intc_irqpin_pm_ops,
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -218,14 +218,13 @@ err_runtime_pm_disable:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int irqc_remove(struct platform_device *pdev)
|
||||
static void irqc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct irqc_priv *p = platform_get_drvdata(pdev);
|
||||
|
||||
irq_domain_remove(p->irq_domain);
|
||||
pm_runtime_put(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused irqc_suspend(struct device *dev)
|
||||
@@ -248,11 +247,11 @@ MODULE_DEVICE_TABLE(of, irqc_dt_ids);
|
||||
|
||||
static struct platform_driver irqc_device_driver = {
|
||||
.probe = irqc_probe,
|
||||
.remove = irqc_remove,
|
||||
.remove_new = irqc_remove,
|
||||
.driver = {
|
||||
.name = "renesas_irqc",
|
||||
.name = "renesas_irqc",
|
||||
.of_match_table = irqc_dt_ids,
|
||||
.pm = &irqc_pm_ops,
|
||||
.pm = &irqc_pm_ops,
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -244,12 +244,11 @@ out_put_node:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rza1_irqc_remove(struct platform_device *pdev)
|
||||
static void rza1_irqc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rza1_irqc_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
irq_domain_remove(priv->irq_domain);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id rza1_irqc_dt_ids[] = {
|
||||
@@ -260,9 +259,9 @@ MODULE_DEVICE_TABLE(of, rza1_irqc_dt_ids);
|
||||
|
||||
static struct platform_driver rza1_irqc_device_driver = {
|
||||
.probe = rza1_irqc_probe,
|
||||
.remove = rza1_irqc_remove,
|
||||
.remove_new = rza1_irqc_remove,
|
||||
.driver = {
|
||||
.name = "renesas_rza1_irqc",
|
||||
.name = "renesas_rza1_irqc",
|
||||
.of_match_table = rza1_irqc_dt_ids,
|
||||
}
|
||||
};
|
||||
|
||||
@@ -17,17 +17,29 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/soc/andes/irq.h>
|
||||
|
||||
#include <asm/hwcap.h>
|
||||
|
||||
static struct irq_domain *intc_domain;
|
||||
static unsigned int riscv_intc_nr_irqs __ro_after_init = BITS_PER_LONG;
|
||||
static unsigned int riscv_intc_custom_base __ro_after_init = BITS_PER_LONG;
|
||||
static unsigned int riscv_intc_custom_nr_irqs __ro_after_init;
|
||||
|
||||
static asmlinkage void riscv_intc_irq(struct pt_regs *regs)
|
||||
{
|
||||
unsigned long cause = regs->cause & ~CAUSE_IRQ_FLAG;
|
||||
|
||||
if (unlikely(cause >= BITS_PER_LONG))
|
||||
panic("unexpected interrupt cause");
|
||||
if (generic_handle_domain_irq(intc_domain, cause))
|
||||
pr_warn_ratelimited("Failed to handle interrupt (cause: %ld)\n", cause);
|
||||
}
|
||||
|
||||
generic_handle_domain_irq(intc_domain, cause);
|
||||
static asmlinkage void riscv_intc_aia_irq(struct pt_regs *regs)
|
||||
{
|
||||
unsigned long topi;
|
||||
|
||||
while ((topi = csr_read(CSR_TOPI)))
|
||||
generic_handle_domain_irq(intc_domain, topi >> TOPI_IID_SHIFT);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -39,12 +51,43 @@ static asmlinkage void riscv_intc_irq(struct pt_regs *regs)
|
||||
|
||||
static void riscv_intc_irq_mask(struct irq_data *d)
|
||||
{
|
||||
csr_clear(CSR_IE, BIT(d->hwirq));
|
||||
if (IS_ENABLED(CONFIG_32BIT) && d->hwirq >= BITS_PER_LONG)
|
||||
csr_clear(CSR_IEH, BIT(d->hwirq - BITS_PER_LONG));
|
||||
else
|
||||
csr_clear(CSR_IE, BIT(d->hwirq));
|
||||
}
|
||||
|
||||
static void riscv_intc_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
csr_set(CSR_IE, BIT(d->hwirq));
|
||||
if (IS_ENABLED(CONFIG_32BIT) && d->hwirq >= BITS_PER_LONG)
|
||||
csr_set(CSR_IEH, BIT(d->hwirq - BITS_PER_LONG));
|
||||
else
|
||||
csr_set(CSR_IE, BIT(d->hwirq));
|
||||
}
|
||||
|
||||
static void andes_intc_irq_mask(struct irq_data *d)
|
||||
{
|
||||
/*
|
||||
* Andes specific S-mode local interrupt causes (hwirq)
|
||||
* are defined as (256 + n) and controlled by n-th bit
|
||||
* of SLIE.
|
||||
*/
|
||||
unsigned int mask = BIT(d->hwirq % BITS_PER_LONG);
|
||||
|
||||
if (d->hwirq < ANDES_SLI_CAUSE_BASE)
|
||||
csr_clear(CSR_IE, mask);
|
||||
else
|
||||
csr_clear(ANDES_CSR_SLIE, mask);
|
||||
}
|
||||
|
||||
static void andes_intc_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
unsigned int mask = BIT(d->hwirq % BITS_PER_LONG);
|
||||
|
||||
if (d->hwirq < ANDES_SLI_CAUSE_BASE)
|
||||
csr_set(CSR_IE, mask);
|
||||
else
|
||||
csr_set(ANDES_CSR_SLIE, mask);
|
||||
}
|
||||
|
||||
static void riscv_intc_irq_eoi(struct irq_data *d)
|
||||
@@ -70,12 +113,21 @@ static struct irq_chip riscv_intc_chip = {
|
||||
.irq_eoi = riscv_intc_irq_eoi,
|
||||
};
|
||||
|
||||
static struct irq_chip andes_intc_chip = {
|
||||
.name = "RISC-V INTC",
|
||||
.irq_mask = andes_intc_irq_mask,
|
||||
.irq_unmask = andes_intc_irq_unmask,
|
||||
.irq_eoi = riscv_intc_irq_eoi,
|
||||
};
|
||||
|
||||
static int riscv_intc_domain_map(struct irq_domain *d, unsigned int irq,
|
||||
irq_hw_number_t hwirq)
|
||||
{
|
||||
struct irq_chip *chip = d->host_data;
|
||||
|
||||
irq_set_percpu_devid(irq);
|
||||
irq_domain_set_info(d, irq, hwirq, &riscv_intc_chip, d->host_data,
|
||||
handle_percpu_devid_irq, NULL, NULL);
|
||||
irq_domain_set_info(d, irq, hwirq, chip, NULL, handle_percpu_devid_irq,
|
||||
NULL, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -93,6 +145,14 @@ static int riscv_intc_domain_alloc(struct irq_domain *domain,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Only allow hwirq for which we have corresponding standard or
|
||||
* custom interrupt enable register.
|
||||
*/
|
||||
if ((hwirq >= riscv_intc_nr_irqs && hwirq < riscv_intc_custom_base) ||
|
||||
(hwirq >= riscv_intc_custom_base + riscv_intc_custom_nr_irqs))
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < nr_irqs; i++) {
|
||||
ret = riscv_intc_domain_map(domain, virq + i, hwirq + i);
|
||||
if (ret)
|
||||
@@ -113,18 +173,20 @@ static struct fwnode_handle *riscv_intc_hwnode(void)
|
||||
return intc_domain->fwnode;
|
||||
}
|
||||
|
||||
static int __init riscv_intc_init_common(struct fwnode_handle *fn)
|
||||
static int __init riscv_intc_init_common(struct fwnode_handle *fn, struct irq_chip *chip)
|
||||
{
|
||||
int rc;
|
||||
|
||||
intc_domain = irq_domain_create_linear(fn, BITS_PER_LONG,
|
||||
&riscv_intc_domain_ops, NULL);
|
||||
intc_domain = irq_domain_create_tree(fn, &riscv_intc_domain_ops, chip);
|
||||
if (!intc_domain) {
|
||||
pr_err("unable to add IRQ domain\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
rc = set_handle_irq(&riscv_intc_irq);
|
||||
if (riscv_isa_extension_available(NULL, SxAIA))
|
||||
rc = set_handle_irq(&riscv_intc_aia_irq);
|
||||
else
|
||||
rc = set_handle_irq(&riscv_intc_irq);
|
||||
if (rc) {
|
||||
pr_err("failed to set irq handler\n");
|
||||
return rc;
|
||||
@@ -132,7 +194,11 @@ static int __init riscv_intc_init_common(struct fwnode_handle *fn)
|
||||
|
||||
riscv_set_intc_hwnode_fn(riscv_intc_hwnode);
|
||||
|
||||
pr_info("%d local interrupts mapped\n", BITS_PER_LONG);
|
||||
pr_info("%d local interrupts mapped%s\n",
|
||||
riscv_isa_extension_available(NULL, SxAIA) ? 64 : riscv_intc_nr_irqs,
|
||||
riscv_isa_extension_available(NULL, SxAIA) ? " using AIA" : "");
|
||||
if (riscv_intc_custom_nr_irqs)
|
||||
pr_info("%d custom local interrupts mapped\n", riscv_intc_custom_nr_irqs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -140,8 +206,9 @@ static int __init riscv_intc_init_common(struct fwnode_handle *fn)
|
||||
static int __init riscv_intc_init(struct device_node *node,
|
||||
struct device_node *parent)
|
||||
{
|
||||
int rc;
|
||||
struct irq_chip *chip = &riscv_intc_chip;
|
||||
unsigned long hartid;
|
||||
int rc;
|
||||
|
||||
rc = riscv_of_parent_hartid(node, &hartid);
|
||||
if (rc < 0) {
|
||||
@@ -166,10 +233,17 @@ static int __init riscv_intc_init(struct device_node *node,
|
||||
return 0;
|
||||
}
|
||||
|
||||
return riscv_intc_init_common(of_node_to_fwnode(node));
|
||||
if (of_device_is_compatible(node, "andestech,cpu-intc")) {
|
||||
riscv_intc_custom_base = ANDES_SLI_CAUSE_BASE;
|
||||
riscv_intc_custom_nr_irqs = ANDES_RV_IRQ_LAST;
|
||||
chip = &andes_intc_chip;
|
||||
}
|
||||
|
||||
return riscv_intc_init_common(of_node_to_fwnode(node), chip);
|
||||
}
|
||||
|
||||
IRQCHIP_DECLARE(riscv, "riscv,cpu-intc", riscv_intc_init);
|
||||
IRQCHIP_DECLARE(andes, "andestech,cpu-intc", riscv_intc_init);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
|
||||
@@ -196,7 +270,7 @@ static int __init riscv_intc_acpi_init(union acpi_subtable_headers *header,
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return riscv_intc_init_common(fn);
|
||||
return riscv_intc_init_common(fn, &riscv_intc_chip);
|
||||
}
|
||||
|
||||
IRQCHIP_ACPI_DECLARE(riscv_intc, ACPI_MADT_TYPE_RINTC, NULL,
|
||||
|
||||
+172
-107
@@ -3,7 +3,6 @@
|
||||
* Copyright (C) 2017 SiFive
|
||||
* Copyright (C) 2018 Christoph Hellwig
|
||||
*/
|
||||
#define pr_fmt(fmt) "plic: " fmt
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
@@ -64,6 +63,7 @@
|
||||
#define PLIC_QUIRK_EDGE_INTERRUPT 0
|
||||
|
||||
struct plic_priv {
|
||||
struct device *dev;
|
||||
struct cpumask lmask;
|
||||
struct irq_domain *irqdomain;
|
||||
void __iomem *regs;
|
||||
@@ -103,9 +103,11 @@ static void __plic_toggle(void __iomem *enable_base, int hwirq, int enable)
|
||||
|
||||
static void plic_toggle(struct plic_handler *handler, int hwirq, int enable)
|
||||
{
|
||||
raw_spin_lock(&handler->enable_lock);
|
||||
unsigned long flags;
|
||||
|
||||
raw_spin_lock_irqsave(&handler->enable_lock, flags);
|
||||
__plic_toggle(handler->enable_base, hwirq, enable);
|
||||
raw_spin_unlock(&handler->enable_lock);
|
||||
raw_spin_unlock_irqrestore(&handler->enable_lock, flags);
|
||||
}
|
||||
|
||||
static inline void plic_irq_toggle(const struct cpumask *mask,
|
||||
@@ -242,6 +244,7 @@ static int plic_irq_set_type(struct irq_data *d, unsigned int type)
|
||||
static int plic_irq_suspend(void)
|
||||
{
|
||||
unsigned int i, cpu;
|
||||
unsigned long flags;
|
||||
u32 __iomem *reg;
|
||||
struct plic_priv *priv;
|
||||
|
||||
@@ -259,12 +262,12 @@ static int plic_irq_suspend(void)
|
||||
if (!handler->present)
|
||||
continue;
|
||||
|
||||
raw_spin_lock(&handler->enable_lock);
|
||||
raw_spin_lock_irqsave(&handler->enable_lock, flags);
|
||||
for (i = 0; i < DIV_ROUND_UP(priv->nr_irqs, 32); i++) {
|
||||
reg = handler->enable_base + i * sizeof(u32);
|
||||
handler->enable_save[i] = readl(reg);
|
||||
}
|
||||
raw_spin_unlock(&handler->enable_lock);
|
||||
raw_spin_unlock_irqrestore(&handler->enable_lock, flags);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -273,6 +276,7 @@ static int plic_irq_suspend(void)
|
||||
static void plic_irq_resume(void)
|
||||
{
|
||||
unsigned int i, index, cpu;
|
||||
unsigned long flags;
|
||||
u32 __iomem *reg;
|
||||
struct plic_priv *priv;
|
||||
|
||||
@@ -290,12 +294,12 @@ static void plic_irq_resume(void)
|
||||
if (!handler->present)
|
||||
continue;
|
||||
|
||||
raw_spin_lock(&handler->enable_lock);
|
||||
raw_spin_lock_irqsave(&handler->enable_lock, flags);
|
||||
for (i = 0; i < DIV_ROUND_UP(priv->nr_irqs, 32); i++) {
|
||||
reg = handler->enable_base + i * sizeof(u32);
|
||||
writel(handler->enable_save[i], reg);
|
||||
}
|
||||
raw_spin_unlock(&handler->enable_lock);
|
||||
raw_spin_unlock_irqrestore(&handler->enable_lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,9 +380,10 @@ static void plic_handle_irq(struct irq_desc *desc)
|
||||
while ((hwirq = readl(claim))) {
|
||||
int err = generic_handle_domain_irq(handler->priv->irqdomain,
|
||||
hwirq);
|
||||
if (unlikely(err))
|
||||
pr_warn_ratelimited("can't find mapping for hwirq %lu\n",
|
||||
hwirq);
|
||||
if (unlikely(err)) {
|
||||
dev_warn_ratelimited(handler->priv->dev,
|
||||
"can't find mapping for hwirq %lu\n", hwirq);
|
||||
}
|
||||
}
|
||||
|
||||
chained_irq_exit(chip, desc);
|
||||
@@ -406,63 +411,122 @@ static int plic_starting_cpu(unsigned int cpu)
|
||||
enable_percpu_irq(plic_parent_irq,
|
||||
irq_get_trigger_type(plic_parent_irq));
|
||||
else
|
||||
pr_warn("cpu%d: parent irq not available\n", cpu);
|
||||
dev_warn(handler->priv->dev, "cpu%d: parent irq not available\n", cpu);
|
||||
plic_set_threshold(handler, PLIC_ENABLE_THRESHOLD);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init __plic_init(struct device_node *node,
|
||||
struct device_node *parent,
|
||||
unsigned long plic_quirks)
|
||||
{
|
||||
int error = 0, nr_contexts, nr_handlers = 0, i;
|
||||
u32 nr_irqs;
|
||||
struct plic_priv *priv;
|
||||
struct plic_handler *handler;
|
||||
unsigned int cpu;
|
||||
static const struct of_device_id plic_match[] = {
|
||||
{ .compatible = "sifive,plic-1.0.0" },
|
||||
{ .compatible = "riscv,plic0" },
|
||||
{ .compatible = "andestech,nceplic100",
|
||||
.data = (const void *)BIT(PLIC_QUIRK_EDGE_INTERRUPT) },
|
||||
{ .compatible = "thead,c900-plic",
|
||||
.data = (const void *)BIT(PLIC_QUIRK_EDGE_INTERRUPT) },
|
||||
{}
|
||||
};
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
static int plic_parse_nr_irqs_and_contexts(struct platform_device *pdev,
|
||||
u32 *nr_irqs, u32 *nr_contexts)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
int rc;
|
||||
|
||||
/*
|
||||
* Currently, only OF fwnode is supported so extend this
|
||||
* function for ACPI support.
|
||||
*/
|
||||
if (!is_of_node(dev->fwnode))
|
||||
return -EINVAL;
|
||||
|
||||
rc = of_property_read_u32(to_of_node(dev->fwnode), "riscv,ndev", nr_irqs);
|
||||
if (rc) {
|
||||
dev_err(dev, "riscv,ndev property not available\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
*nr_contexts = of_irq_count(to_of_node(dev->fwnode));
|
||||
if (WARN_ON(!(*nr_contexts))) {
|
||||
dev_err(dev, "no PLIC context available\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int plic_parse_context_parent(struct platform_device *pdev, u32 context,
|
||||
u32 *parent_hwirq, int *parent_cpu)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct of_phandle_args parent;
|
||||
unsigned long hartid;
|
||||
int rc;
|
||||
|
||||
/*
|
||||
* Currently, only OF fwnode is supported so extend this
|
||||
* function for ACPI support.
|
||||
*/
|
||||
if (!is_of_node(dev->fwnode))
|
||||
return -EINVAL;
|
||||
|
||||
rc = of_irq_parse_one(to_of_node(dev->fwnode), context, &parent);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = riscv_of_parent_hartid(parent.np, &hartid);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
*parent_hwirq = parent.args[0];
|
||||
*parent_cpu = riscv_hartid_to_cpuid(hartid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int plic_probe(struct platform_device *pdev)
|
||||
{
|
||||
int error = 0, nr_contexts, nr_handlers = 0, cpu, i;
|
||||
struct device *dev = &pdev->dev;
|
||||
unsigned long plic_quirks = 0;
|
||||
struct plic_handler *handler;
|
||||
u32 nr_irqs, parent_hwirq;
|
||||
struct irq_domain *domain;
|
||||
struct plic_priv *priv;
|
||||
irq_hw_number_t hwirq;
|
||||
bool cpuhp_setup;
|
||||
|
||||
if (is_of_node(dev->fwnode)) {
|
||||
const struct of_device_id *id;
|
||||
|
||||
id = of_match_node(plic_match, to_of_node(dev->fwnode));
|
||||
if (id)
|
||||
plic_quirks = (unsigned long)id->data;
|
||||
}
|
||||
|
||||
error = plic_parse_nr_irqs_and_contexts(pdev, &nr_irqs, &nr_contexts);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->dev = dev;
|
||||
priv->plic_quirks = plic_quirks;
|
||||
|
||||
priv->regs = of_iomap(node, 0);
|
||||
if (WARN_ON(!priv->regs)) {
|
||||
error = -EIO;
|
||||
goto out_free_priv;
|
||||
}
|
||||
|
||||
error = -EINVAL;
|
||||
of_property_read_u32(node, "riscv,ndev", &nr_irqs);
|
||||
if (WARN_ON(!nr_irqs))
|
||||
goto out_iounmap;
|
||||
|
||||
priv->nr_irqs = nr_irqs;
|
||||
|
||||
priv->prio_save = bitmap_alloc(nr_irqs, GFP_KERNEL);
|
||||
priv->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (WARN_ON(!priv->regs))
|
||||
return -EIO;
|
||||
|
||||
priv->prio_save = devm_bitmap_zalloc(dev, nr_irqs, GFP_KERNEL);
|
||||
if (!priv->prio_save)
|
||||
goto out_free_priority_reg;
|
||||
|
||||
nr_contexts = of_irq_count(node);
|
||||
if (WARN_ON(!nr_contexts))
|
||||
goto out_free_priority_reg;
|
||||
|
||||
error = -ENOMEM;
|
||||
priv->irqdomain = irq_domain_add_linear(node, nr_irqs + 1,
|
||||
&plic_irqdomain_ops, priv);
|
||||
if (WARN_ON(!priv->irqdomain))
|
||||
goto out_free_priority_reg;
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < nr_contexts; i++) {
|
||||
struct of_phandle_args parent;
|
||||
irq_hw_number_t hwirq;
|
||||
int cpu;
|
||||
unsigned long hartid;
|
||||
|
||||
if (of_irq_parse_one(node, i, &parent)) {
|
||||
pr_err("failed to parse parent for context %d.\n", i);
|
||||
error = plic_parse_context_parent(pdev, i, &parent_hwirq, &cpu);
|
||||
if (error) {
|
||||
dev_warn(dev, "hwirq for context%d not found\n", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -470,7 +534,7 @@ static int __init __plic_init(struct device_node *node,
|
||||
* Skip contexts other than external interrupts for our
|
||||
* privilege level.
|
||||
*/
|
||||
if (parent.args[0] != RV_IRQ_EXT) {
|
||||
if (parent_hwirq != RV_IRQ_EXT) {
|
||||
/* Disable S-mode enable bits if running in M-mode. */
|
||||
if (IS_ENABLED(CONFIG_RISCV_M_MODE)) {
|
||||
void __iomem *enable_base = priv->regs +
|
||||
@@ -483,24 +547,17 @@ static int __init __plic_init(struct device_node *node,
|
||||
continue;
|
||||
}
|
||||
|
||||
error = riscv_of_parent_hartid(parent.np, &hartid);
|
||||
if (error < 0) {
|
||||
pr_warn("failed to parse hart ID for context %d.\n", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
cpu = riscv_hartid_to_cpuid(hartid);
|
||||
if (cpu < 0) {
|
||||
pr_warn("Invalid cpuid for context %d\n", i);
|
||||
dev_warn(dev, "Invalid cpuid for context %d\n", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Find parent domain and register chained handler */
|
||||
if (!plic_parent_irq && irq_find_host(parent.np)) {
|
||||
plic_parent_irq = irq_of_parse_and_map(node, i);
|
||||
domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(), DOMAIN_BUS_ANY);
|
||||
if (!plic_parent_irq && domain) {
|
||||
plic_parent_irq = irq_create_mapping(domain, RV_IRQ_EXT);
|
||||
if (plic_parent_irq)
|
||||
irq_set_chained_handler(plic_parent_irq,
|
||||
plic_handle_irq);
|
||||
irq_set_chained_handler(plic_parent_irq, plic_handle_irq);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -510,7 +567,7 @@ static int __init __plic_init(struct device_node *node,
|
||||
*/
|
||||
handler = per_cpu_ptr(&plic_handlers, cpu);
|
||||
if (handler->present) {
|
||||
pr_warn("handler already present for context %d.\n", i);
|
||||
dev_warn(dev, "handler already present for context %d.\n", i);
|
||||
plic_set_threshold(handler, PLIC_DISABLE_THRESHOLD);
|
||||
goto done;
|
||||
}
|
||||
@@ -524,10 +581,10 @@ static int __init __plic_init(struct device_node *node,
|
||||
i * CONTEXT_ENABLE_SIZE;
|
||||
handler->priv = priv;
|
||||
|
||||
handler->enable_save = kcalloc(DIV_ROUND_UP(nr_irqs, 32),
|
||||
sizeof(*handler->enable_save), GFP_KERNEL);
|
||||
handler->enable_save = devm_kcalloc(dev, DIV_ROUND_UP(nr_irqs, 32),
|
||||
sizeof(*handler->enable_save), GFP_KERNEL);
|
||||
if (!handler->enable_save)
|
||||
goto out_free_enable_reg;
|
||||
goto fail_cleanup_contexts;
|
||||
done:
|
||||
for (hwirq = 1; hwirq <= nr_irqs; hwirq++) {
|
||||
plic_toggle(handler, hwirq, 0);
|
||||
@@ -537,52 +594,60 @@ done:
|
||||
nr_handlers++;
|
||||
}
|
||||
|
||||
priv->irqdomain = irq_domain_add_linear(to_of_node(dev->fwnode), nr_irqs + 1,
|
||||
&plic_irqdomain_ops, priv);
|
||||
if (WARN_ON(!priv->irqdomain))
|
||||
goto fail_cleanup_contexts;
|
||||
|
||||
/*
|
||||
* We can have multiple PLIC instances so setup cpuhp state
|
||||
* and register syscore operations only when context handler
|
||||
* for current/boot CPU is present.
|
||||
* and register syscore operations only once after context
|
||||
* handlers of all online CPUs are initialized.
|
||||
*/
|
||||
handler = this_cpu_ptr(&plic_handlers);
|
||||
if (handler->present && !plic_cpuhp_setup_done) {
|
||||
cpuhp_setup_state(CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING,
|
||||
"irqchip/sifive/plic:starting",
|
||||
plic_starting_cpu, plic_dying_cpu);
|
||||
register_syscore_ops(&plic_irq_syscore_ops);
|
||||
plic_cpuhp_setup_done = true;
|
||||
if (!plic_cpuhp_setup_done) {
|
||||
cpuhp_setup = true;
|
||||
for_each_online_cpu(cpu) {
|
||||
handler = per_cpu_ptr(&plic_handlers, cpu);
|
||||
if (!handler->present) {
|
||||
cpuhp_setup = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cpuhp_setup) {
|
||||
cpuhp_setup_state(CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING,
|
||||
"irqchip/sifive/plic:starting",
|
||||
plic_starting_cpu, plic_dying_cpu);
|
||||
register_syscore_ops(&plic_irq_syscore_ops);
|
||||
plic_cpuhp_setup_done = true;
|
||||
}
|
||||
}
|
||||
|
||||
pr_info("%pOFP: mapped %d interrupts with %d handlers for"
|
||||
" %d contexts.\n", node, nr_irqs, nr_handlers, nr_contexts);
|
||||
dev_info(dev, "mapped %d interrupts with %d handlers for %d contexts.\n",
|
||||
nr_irqs, nr_handlers, nr_contexts);
|
||||
return 0;
|
||||
|
||||
out_free_enable_reg:
|
||||
for_each_cpu(cpu, cpu_present_mask) {
|
||||
fail_cleanup_contexts:
|
||||
for (i = 0; i < nr_contexts; i++) {
|
||||
if (plic_parse_context_parent(pdev, i, &parent_hwirq, &cpu))
|
||||
continue;
|
||||
if (parent_hwirq != RV_IRQ_EXT || cpu < 0)
|
||||
continue;
|
||||
|
||||
handler = per_cpu_ptr(&plic_handlers, cpu);
|
||||
kfree(handler->enable_save);
|
||||
handler->present = false;
|
||||
handler->hart_base = NULL;
|
||||
handler->enable_base = NULL;
|
||||
handler->enable_save = NULL;
|
||||
handler->priv = NULL;
|
||||
}
|
||||
out_free_priority_reg:
|
||||
kfree(priv->prio_save);
|
||||
out_iounmap:
|
||||
iounmap(priv->regs);
|
||||
out_free_priv:
|
||||
kfree(priv);
|
||||
return error;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int __init plic_init(struct device_node *node,
|
||||
struct device_node *parent)
|
||||
{
|
||||
return __plic_init(node, parent, 0);
|
||||
}
|
||||
|
||||
IRQCHIP_DECLARE(sifive_plic, "sifive,plic-1.0.0", plic_init);
|
||||
IRQCHIP_DECLARE(riscv_plic0, "riscv,plic0", plic_init); /* for legacy systems */
|
||||
|
||||
static int __init plic_edge_init(struct device_node *node,
|
||||
struct device_node *parent)
|
||||
{
|
||||
return __plic_init(node, parent, BIT(PLIC_QUIRK_EDGE_INTERRUPT));
|
||||
}
|
||||
|
||||
IRQCHIP_DECLARE(andestech_nceplic100, "andestech,nceplic100", plic_edge_init);
|
||||
IRQCHIP_DECLARE(thead_c900_plic, "thead,c900-plic", plic_edge_init);
|
||||
static struct platform_driver plic_driver = {
|
||||
.driver = {
|
||||
.name = "riscv-plic",
|
||||
.of_match_table = plic_match,
|
||||
},
|
||||
.probe = plic_probe,
|
||||
};
|
||||
builtin_platform_driver(plic_driver);
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* StarFive JH8100 External Interrupt Controller driver
|
||||
*
|
||||
* Copyright (C) 2023 StarFive Technology Co., Ltd.
|
||||
*
|
||||
* Author: Changhuang Liang <changhuang.liang@starfivetech.com>
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "irq-starfive-jh8100: " fmt
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/clk.h>
|
||||
#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/reset.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#define STARFIVE_INTC_SRC0_CLEAR 0x10
|
||||
#define STARFIVE_INTC_SRC0_MASK 0x14
|
||||
#define STARFIVE_INTC_SRC0_INT 0x1c
|
||||
|
||||
#define STARFIVE_INTC_SRC_IRQ_NUM 32
|
||||
|
||||
struct starfive_irq_chip {
|
||||
void __iomem *base;
|
||||
struct irq_domain *domain;
|
||||
raw_spinlock_t lock;
|
||||
};
|
||||
|
||||
static void starfive_intc_bit_set(struct starfive_irq_chip *irqc,
|
||||
u32 reg, u32 bit_mask)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
value = ioread32(irqc->base + reg);
|
||||
value |= bit_mask;
|
||||
iowrite32(value, irqc->base + reg);
|
||||
}
|
||||
|
||||
static void starfive_intc_bit_clear(struct starfive_irq_chip *irqc,
|
||||
u32 reg, u32 bit_mask)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
value = ioread32(irqc->base + reg);
|
||||
value &= ~bit_mask;
|
||||
iowrite32(value, irqc->base + reg);
|
||||
}
|
||||
|
||||
static void starfive_intc_unmask(struct irq_data *d)
|
||||
{
|
||||
struct starfive_irq_chip *irqc = irq_data_get_irq_chip_data(d);
|
||||
|
||||
raw_spin_lock(&irqc->lock);
|
||||
starfive_intc_bit_clear(irqc, STARFIVE_INTC_SRC0_MASK, BIT(d->hwirq));
|
||||
raw_spin_unlock(&irqc->lock);
|
||||
}
|
||||
|
||||
static void starfive_intc_mask(struct irq_data *d)
|
||||
{
|
||||
struct starfive_irq_chip *irqc = irq_data_get_irq_chip_data(d);
|
||||
|
||||
raw_spin_lock(&irqc->lock);
|
||||
starfive_intc_bit_set(irqc, STARFIVE_INTC_SRC0_MASK, BIT(d->hwirq));
|
||||
raw_spin_unlock(&irqc->lock);
|
||||
}
|
||||
|
||||
static struct irq_chip intc_dev = {
|
||||
.name = "StarFive JH8100 INTC",
|
||||
.irq_unmask = starfive_intc_unmask,
|
||||
.irq_mask = starfive_intc_mask,
|
||||
};
|
||||
|
||||
static int starfive_intc_map(struct irq_domain *d, unsigned int irq,
|
||||
irq_hw_number_t hwirq)
|
||||
{
|
||||
irq_domain_set_info(d, irq, hwirq, &intc_dev, d->host_data,
|
||||
handle_level_irq, NULL, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops starfive_intc_domain_ops = {
|
||||
.xlate = irq_domain_xlate_onecell,
|
||||
.map = starfive_intc_map,
|
||||
};
|
||||
|
||||
static void starfive_intc_irq_handler(struct irq_desc *desc)
|
||||
{
|
||||
struct starfive_irq_chip *irqc = irq_data_get_irq_handler_data(&desc->irq_data);
|
||||
struct irq_chip *chip = irq_desc_get_chip(desc);
|
||||
unsigned long value;
|
||||
int hwirq;
|
||||
|
||||
chained_irq_enter(chip, desc);
|
||||
|
||||
value = ioread32(irqc->base + STARFIVE_INTC_SRC0_INT);
|
||||
while (value) {
|
||||
hwirq = ffs(value) - 1;
|
||||
|
||||
generic_handle_domain_irq(irqc->domain, hwirq);
|
||||
|
||||
starfive_intc_bit_set(irqc, STARFIVE_INTC_SRC0_CLEAR, BIT(hwirq));
|
||||
starfive_intc_bit_clear(irqc, STARFIVE_INTC_SRC0_CLEAR, BIT(hwirq));
|
||||
|
||||
__clear_bit(hwirq, &value);
|
||||
}
|
||||
|
||||
chained_irq_exit(chip, desc);
|
||||
}
|
||||
|
||||
static int __init starfive_intc_init(struct device_node *intc,
|
||||
struct device_node *parent)
|
||||
{
|
||||
struct starfive_irq_chip *irqc;
|
||||
struct reset_control *rst;
|
||||
struct clk *clk;
|
||||
int parent_irq;
|
||||
int ret;
|
||||
|
||||
irqc = kzalloc(sizeof(*irqc), GFP_KERNEL);
|
||||
if (!irqc)
|
||||
return -ENOMEM;
|
||||
|
||||
irqc->base = of_iomap(intc, 0);
|
||||
if (!irqc->base) {
|
||||
pr_err("Unable to map registers\n");
|
||||
ret = -ENXIO;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
rst = of_reset_control_get_exclusive(intc, NULL);
|
||||
if (IS_ERR(rst)) {
|
||||
pr_err("Unable to get reset control %pe\n", rst);
|
||||
ret = PTR_ERR(rst);
|
||||
goto err_unmap;
|
||||
}
|
||||
|
||||
clk = of_clk_get(intc, 0);
|
||||
if (IS_ERR(clk)) {
|
||||
pr_err("Unable to get clock %pe\n", clk);
|
||||
ret = PTR_ERR(clk);
|
||||
goto err_reset_put;
|
||||
}
|
||||
|
||||
ret = reset_control_deassert(rst);
|
||||
if (ret)
|
||||
goto err_clk_put;
|
||||
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret)
|
||||
goto err_reset_assert;
|
||||
|
||||
raw_spin_lock_init(&irqc->lock);
|
||||
|
||||
irqc->domain = irq_domain_add_linear(intc, STARFIVE_INTC_SRC_IRQ_NUM,
|
||||
&starfive_intc_domain_ops, irqc);
|
||||
if (!irqc->domain) {
|
||||
pr_err("Unable to create IRQ domain\n");
|
||||
ret = -EINVAL;
|
||||
goto err_clk_disable;
|
||||
}
|
||||
|
||||
parent_irq = of_irq_get(intc, 0);
|
||||
if (parent_irq < 0) {
|
||||
pr_err("Failed to get main IRQ: %d\n", parent_irq);
|
||||
ret = parent_irq;
|
||||
goto err_remove_domain;
|
||||
}
|
||||
|
||||
irq_set_chained_handler_and_data(parent_irq, starfive_intc_irq_handler,
|
||||
irqc);
|
||||
|
||||
pr_info("Interrupt controller register, nr_irqs %d\n",
|
||||
STARFIVE_INTC_SRC_IRQ_NUM);
|
||||
|
||||
return 0;
|
||||
|
||||
err_remove_domain:
|
||||
irq_domain_remove(irqc->domain);
|
||||
err_clk_disable:
|
||||
clk_disable_unprepare(clk);
|
||||
err_reset_assert:
|
||||
reset_control_assert(rst);
|
||||
err_clk_put:
|
||||
clk_put(clk);
|
||||
err_reset_put:
|
||||
reset_control_put(rst);
|
||||
err_unmap:
|
||||
iounmap(irqc->base);
|
||||
err_free:
|
||||
kfree(irqc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
IRQCHIP_PLATFORM_DRIVER_BEGIN(starfive_intc)
|
||||
IRQCHIP_MATCH("starfive,jh8100-intc", starfive_intc_init)
|
||||
IRQCHIP_PLATFORM_DRIVER_END(starfive_intc)
|
||||
|
||||
MODULE_DESCRIPTION("StarFive JH8100 External Interrupt Controller");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Changhuang Liang <changhuang.liang@starfivetech.com>");
|
||||
@@ -898,10 +898,9 @@ static void stm32_exti_remove_irq(void *data)
|
||||
irq_domain_remove(domain);
|
||||
}
|
||||
|
||||
static int stm32_exti_remove(struct platform_device *pdev)
|
||||
static void stm32_exti_remove(struct platform_device *pdev)
|
||||
{
|
||||
stm32_exti_h_syscore_deinit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_exti_probe(struct platform_device *pdev)
|
||||
@@ -991,10 +990,10 @@ MODULE_DEVICE_TABLE(of, stm32_exti_ids);
|
||||
|
||||
static struct platform_driver stm32_exti_driver = {
|
||||
.probe = stm32_exti_probe,
|
||||
.remove = stm32_exti_remove,
|
||||
.remove_new = stm32_exti_remove,
|
||||
.driver = {
|
||||
.name = "stm32_exti",
|
||||
.of_match_table = stm32_exti_ids,
|
||||
.name = "stm32_exti",
|
||||
.of_match_table = stm32_exti_ids,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -139,13 +139,11 @@ static int ts4800_ic_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ts4800_ic_remove(struct platform_device *pdev)
|
||||
static void ts4800_ic_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ts4800_irq_data *data = platform_get_drvdata(pdev);
|
||||
|
||||
irq_domain_remove(data->domain);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ts4800_ic_of_match[] = {
|
||||
@@ -155,11 +153,11 @@ static const struct of_device_id ts4800_ic_of_match[] = {
|
||||
MODULE_DEVICE_TABLE(of, ts4800_ic_of_match);
|
||||
|
||||
static struct platform_driver ts4800_ic_driver = {
|
||||
.probe = ts4800_ic_probe,
|
||||
.remove = ts4800_ic_remove,
|
||||
.probe = ts4800_ic_probe,
|
||||
.remove_new = ts4800_ic_remove,
|
||||
.driver = {
|
||||
.name = "ts4800-irqc",
|
||||
.of_match_table = ts4800_ic_of_match,
|
||||
.name = "ts4800-irqc",
|
||||
.of_match_table = ts4800_ic_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(ts4800_ic_driver);
|
||||
|
||||
@@ -47,9 +47,8 @@
|
||||
|
||||
/**
|
||||
* struct vic_device - VIC PM device
|
||||
* @parent_irq: The parent IRQ number of the VIC if cascaded, or 0.
|
||||
* @irq: The IRQ number for the base of the VIC.
|
||||
* @base: The register base for the VIC.
|
||||
* @irq: The IRQ number for the base of the VIC.
|
||||
* @valid_sources: A bitmask of valid interrupts
|
||||
* @resume_sources: A bitmask of interrupts for resume.
|
||||
* @resume_irqs: The IRQs enabled for resume.
|
||||
|
||||
@@ -1587,8 +1587,8 @@ static int flexrm_mbox_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
/* Allocate platform MSIs for each ring */
|
||||
ret = platform_msi_domain_alloc_irqs(dev, mbox->num_rings,
|
||||
flexrm_mbox_msi_write);
|
||||
ret = platform_device_msi_init_and_alloc_irqs(dev, mbox->num_rings,
|
||||
flexrm_mbox_msi_write);
|
||||
if (ret)
|
||||
goto fail_destroy_cmpl_pool;
|
||||
|
||||
@@ -1641,7 +1641,7 @@ skip_debugfs:
|
||||
|
||||
fail_free_debugfs_root:
|
||||
debugfs_remove_recursive(mbox->root);
|
||||
platform_msi_domain_free_irqs(dev);
|
||||
platform_device_msi_free_irqs_all(dev);
|
||||
fail_destroy_cmpl_pool:
|
||||
dma_pool_destroy(mbox->cmpl_pool);
|
||||
fail_destroy_bd_pool:
|
||||
@@ -1657,7 +1657,7 @@ static void flexrm_mbox_remove(struct platform_device *pdev)
|
||||
|
||||
debugfs_remove_recursive(mbox->root);
|
||||
|
||||
platform_msi_domain_free_irqs(dev);
|
||||
platform_device_msi_free_irqs_all(dev);
|
||||
|
||||
dma_pool_destroy(mbox->cmpl_pool);
|
||||
dma_pool_destroy(mbox->bd_pool);
|
||||
|
||||
@@ -716,7 +716,7 @@ static void smmu_pmu_free_msis(void *data)
|
||||
{
|
||||
struct device *dev = data;
|
||||
|
||||
platform_msi_domain_free_irqs(dev);
|
||||
platform_device_msi_free_irqs_all(dev);
|
||||
}
|
||||
|
||||
static void smmu_pmu_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg)
|
||||
@@ -746,7 +746,7 @@ static void smmu_pmu_setup_msi(struct smmu_pmu *pmu)
|
||||
if (!(readl(pmu->reg_base + SMMU_PMCG_CFGR) & SMMU_PMCG_CFGR_MSI))
|
||||
return;
|
||||
|
||||
ret = platform_msi_domain_alloc_irqs(dev, 1, smmu_pmu_write_msi_msg);
|
||||
ret = platform_device_msi_init_and_alloc_irqs(dev, 1, smmu_pmu_write_msi_msg);
|
||||
if (ret) {
|
||||
dev_warn(dev, "failed to allocate MSIs\n");
|
||||
return;
|
||||
|
||||
@@ -1712,8 +1712,8 @@ static int ufs_qcom_config_esi(struct ufs_hba *hba)
|
||||
* 2. Poll queues do not need ESI.
|
||||
*/
|
||||
nr_irqs = hba->nr_hw_queues - hba->nr_queues[HCTX_TYPE_POLL];
|
||||
ret = platform_msi_domain_alloc_irqs(hba->dev, nr_irqs,
|
||||
ufs_qcom_write_msi_msg);
|
||||
ret = platform_device_msi_init_and_alloc_irqs(hba->dev, nr_irqs,
|
||||
ufs_qcom_write_msi_msg);
|
||||
if (ret) {
|
||||
dev_err(hba->dev, "Failed to request Platform MSI %d\n", ret);
|
||||
return ret;
|
||||
@@ -1742,7 +1742,7 @@ static int ufs_qcom_config_esi(struct ufs_hba *hba)
|
||||
devm_free_irq(hba->dev, desc->irq, hba);
|
||||
}
|
||||
msi_unlock_descs(hba->dev);
|
||||
platform_msi_domain_free_irqs(hba->dev);
|
||||
platform_device_msi_free_irqs_all(hba->dev);
|
||||
} else {
|
||||
if (host->hw_ver.major == 6 && host->hw_ver.minor == 0 &&
|
||||
host->hw_ver.step == 0)
|
||||
@@ -1818,7 +1818,7 @@ static void ufs_qcom_remove(struct platform_device *pdev)
|
||||
|
||||
pm_runtime_get_sync(&(pdev)->dev);
|
||||
ufshcd_remove(hba);
|
||||
platform_msi_domain_free_irqs(hba->dev);
|
||||
platform_device_msi_free_irqs_all(hba->dev);
|
||||
}
|
||||
|
||||
static const struct of_device_id ufs_qcom_of_match[] __maybe_unused = {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <linux/align.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/find.h>
|
||||
#include <linux/limits.h>
|
||||
@@ -127,6 +128,8 @@ unsigned long *bitmap_alloc_node(unsigned int nbits, gfp_t flags, int node);
|
||||
unsigned long *bitmap_zalloc_node(unsigned int nbits, gfp_t flags, int node);
|
||||
void bitmap_free(const unsigned long *bitmap);
|
||||
|
||||
DEFINE_FREE(bitmap, unsigned long *, if (_T) bitmap_free(_T))
|
||||
|
||||
/* Managed variants of the above. */
|
||||
unsigned long *devm_bitmap_alloc(struct device *dev,
|
||||
unsigned int nbits, gfp_t flags);
|
||||
|
||||
+1
-1
@@ -179,7 +179,7 @@ struct irq_common_data {
|
||||
struct irq_data {
|
||||
u32 mask;
|
||||
unsigned int irq;
|
||||
unsigned long hwirq;
|
||||
irq_hw_number_t hwirq;
|
||||
struct irq_common_data *common;
|
||||
struct irq_chip *chip;
|
||||
struct irq_domain *domain;
|
||||
|
||||
@@ -619,6 +619,23 @@ static inline bool irq_domain_is_msi_device(struct irq_domain *domain)
|
||||
|
||||
#endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */
|
||||
|
||||
#ifdef CONFIG_GENERIC_MSI_IRQ
|
||||
int msi_device_domain_alloc_wired(struct irq_domain *domain, unsigned int hwirq,
|
||||
unsigned int type);
|
||||
void msi_device_domain_free_wired(struct irq_domain *domain, unsigned int virq);
|
||||
#else
|
||||
static inline int msi_device_domain_alloc_wired(struct irq_domain *domain, unsigned int hwirq,
|
||||
unsigned int type)
|
||||
{
|
||||
WARN_ON_ONCE(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
static inline void msi_device_domain_free_wired(struct irq_domain *domain, unsigned int virq)
|
||||
{
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#else /* CONFIG_IRQ_DOMAIN */
|
||||
static inline void irq_dispose_mapping(unsigned int virq) { }
|
||||
static inline struct irq_domain *irq_find_matching_fwnode(
|
||||
|
||||
@@ -26,6 +26,8 @@ enum irq_domain_bus_token {
|
||||
DOMAIN_BUS_DMAR,
|
||||
DOMAIN_BUS_AMDVI,
|
||||
DOMAIN_BUS_PCI_DEVICE_IMS,
|
||||
DOMAIN_BUS_DEVICE_MSI,
|
||||
DOMAIN_BUS_WIRED_TO_MSI,
|
||||
};
|
||||
|
||||
#endif /* _LINUX_IRQDOMAIN_DEFS_H */
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
struct irq_desc;
|
||||
struct irq_data;
|
||||
|
||||
typedef void (*irq_flow_handler_t)(struct irq_desc *desc);
|
||||
|
||||
#endif
|
||||
|
||||
+21
-3
@@ -412,6 +412,7 @@ bool arch_restore_msi_irqs(struct pci_dev *dev);
|
||||
struct irq_domain;
|
||||
struct irq_domain_ops;
|
||||
struct irq_chip;
|
||||
struct irq_fwspec;
|
||||
struct device_node;
|
||||
struct fwnode_handle;
|
||||
struct msi_domain_info;
|
||||
@@ -431,6 +432,8 @@ struct msi_domain_info;
|
||||
* function.
|
||||
* @msi_post_free: Optional function which is invoked after freeing
|
||||
* all interrupts.
|
||||
* @msi_translate: Optional translate callback to support the odd wire to
|
||||
* MSI bridges, e.g. MBIGEN
|
||||
*
|
||||
* @get_hwirq, @msi_init and @msi_free are callbacks used by the underlying
|
||||
* irqdomain.
|
||||
@@ -468,6 +471,8 @@ struct msi_domain_ops {
|
||||
struct device *dev);
|
||||
void (*msi_post_free)(struct irq_domain *domain,
|
||||
struct device *dev);
|
||||
int (*msi_translate)(struct irq_domain *domain, struct irq_fwspec *fwspec,
|
||||
irq_hw_number_t *hwirq, unsigned int *type);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -547,6 +552,10 @@ enum {
|
||||
MSI_FLAG_ALLOC_SIMPLE_MSI_DESCS = (1 << 5),
|
||||
/* Free MSI descriptors */
|
||||
MSI_FLAG_FREE_MSI_DESCS = (1 << 6),
|
||||
/* Use dev->fwnode for MSI device domain creation */
|
||||
MSI_FLAG_USE_DEV_FWNODE = (1 << 7),
|
||||
/* Set parent->dev into domain->pm_dev on device domain creation */
|
||||
MSI_FLAG_PARENT_PM_DEV = (1 << 8),
|
||||
|
||||
/* Mask for the generic functionality */
|
||||
MSI_GENERIC_FLAGS_MASK = GENMASK(15, 0),
|
||||
@@ -572,6 +581,11 @@ enum {
|
||||
* struct msi_parent_ops - MSI parent domain callbacks and configuration info
|
||||
*
|
||||
* @supported_flags: Required: The supported MSI flags of the parent domain
|
||||
* @required_flags: Optional: The required MSI flags of the parent MSI domain
|
||||
* @bus_select_token: Optional: The bus token of the real parent domain for
|
||||
* irq_domain::select()
|
||||
* @bus_select_mask: Optional: A mask of supported BUS_DOMAINs for
|
||||
* irq_domain::select()
|
||||
* @prefix: Optional: Prefix for the domain and chip name
|
||||
* @init_dev_msi_info: Required: Callback for MSI parent domains to setup parent
|
||||
* domain specific domain flags, domain ops and interrupt chip
|
||||
@@ -579,6 +593,9 @@ enum {
|
||||
*/
|
||||
struct msi_parent_ops {
|
||||
u32 supported_flags;
|
||||
u32 required_flags;
|
||||
u32 bus_select_token;
|
||||
u32 bus_select_mask;
|
||||
const char *prefix;
|
||||
bool (*init_dev_msi_info)(struct device *dev, struct irq_domain *domain,
|
||||
struct irq_domain *msi_parent_domain,
|
||||
@@ -627,9 +644,6 @@ struct msi_domain_info *msi_get_domain_info(struct irq_domain *domain);
|
||||
struct irq_domain *platform_msi_create_irq_domain(struct fwnode_handle *fwnode,
|
||||
struct msi_domain_info *info,
|
||||
struct irq_domain *parent);
|
||||
int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
|
||||
irq_write_msi_msg_t write_msi_msg);
|
||||
void platform_msi_domain_free_irqs(struct device *dev);
|
||||
|
||||
/* When an MSI domain is used as an intermediate domain */
|
||||
int msi_domain_prepare_irqs(struct irq_domain *domain, struct device *dev,
|
||||
@@ -656,6 +670,10 @@ int platform_msi_device_domain_alloc(struct irq_domain *domain, unsigned int vir
|
||||
void platform_msi_device_domain_free(struct irq_domain *domain, unsigned int virq,
|
||||
unsigned int nvec);
|
||||
void *platform_msi_get_host_data(struct irq_domain *domain);
|
||||
/* Per device platform MSI */
|
||||
int platform_device_msi_init_and_alloc_irqs(struct device *dev, unsigned int nvec,
|
||||
irq_write_msi_msg_t write_msi_msg);
|
||||
void platform_device_msi_free_irqs_all(struct device *dev);
|
||||
|
||||
bool msi_device_has_isolated_msi(struct device *dev);
|
||||
#else /* CONFIG_GENERIC_MSI_IRQ */
|
||||
|
||||
+1
-1
@@ -261,7 +261,7 @@ static inline int get_boot_cpu_id(void)
|
||||
* regular asm read for the stable.
|
||||
*/
|
||||
#ifndef __smp_processor_id
|
||||
#define __smp_processor_id(x) raw_smp_processor_id(x)
|
||||
#define __smp_processor_id() raw_smp_processor_id()
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DEBUG_PREEMPT
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (C) 2023 Andes Technology Corporation
|
||||
*/
|
||||
#ifndef __ANDES_IRQ_H
|
||||
#define __ANDES_IRQ_H
|
||||
|
||||
/* Andes PMU irq number */
|
||||
#define ANDES_RV_IRQ_PMOVI 18
|
||||
#define ANDES_RV_IRQ_LAST ANDES_RV_IRQ_PMOVI
|
||||
#define ANDES_SLI_CAUSE_BASE 256
|
||||
|
||||
/* Andes PMU related registers */
|
||||
#define ANDES_CSR_SLIE 0x9c4
|
||||
#define ANDES_CSR_SLIP 0x9c5
|
||||
#define ANDES_CSR_SCOUNTEROF 0x9d4
|
||||
|
||||
#endif /* __ANDES_IRQ_H */
|
||||
+4
-5
@@ -54,7 +54,6 @@
|
||||
* @rollback: Perform a rollback
|
||||
* @single: Single callback invocation
|
||||
* @bringup: Single callback bringup or teardown selector
|
||||
* @cpu: CPU number
|
||||
* @node: Remote CPU node; for multi-instance, do a
|
||||
* single entry callback for install/remove
|
||||
* @last: For multi-instance rollback, remember how far we got
|
||||
@@ -3005,7 +3004,7 @@ static ssize_t control_show(struct device *dev,
|
||||
return sysfs_emit(buf, "%d\n", cpu_smt_num_threads);
|
||||
#endif
|
||||
|
||||
return snprintf(buf, PAGE_SIZE - 2, "%s\n", state);
|
||||
return sysfs_emit(buf, "%s\n", state);
|
||||
}
|
||||
|
||||
static ssize_t control_store(struct device *dev, struct device_attribute *attr,
|
||||
@@ -3018,7 +3017,7 @@ static DEVICE_ATTR_RW(control);
|
||||
static ssize_t active_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE - 2, "%d\n", sched_smt_active());
|
||||
return sysfs_emit(buf, "%d\n", sched_smt_active());
|
||||
}
|
||||
static DEVICE_ATTR_RO(active);
|
||||
|
||||
@@ -3107,10 +3106,10 @@ const DECLARE_BITMAP(cpu_all_bits, NR_CPUS) = CPU_BITS_ALL;
|
||||
EXPORT_SYMBOL(cpu_all_bits);
|
||||
|
||||
#ifdef CONFIG_INIT_ALL_POSSIBLE
|
||||
struct cpumask __cpu_possible_mask __read_mostly
|
||||
struct cpumask __cpu_possible_mask __ro_after_init
|
||||
= {CPU_BITS_ALL};
|
||||
#else
|
||||
struct cpumask __cpu_possible_mask __read_mostly;
|
||||
struct cpumask __cpu_possible_mask __ro_after_init;
|
||||
#endif
|
||||
EXPORT_SYMBOL(__cpu_possible_mask);
|
||||
|
||||
|
||||
+11
-17
@@ -4,10 +4,11 @@
|
||||
* Copyright (C) 2020 Bartosz Golaszewski <bgolaszewski@baylibre.com>
|
||||
*/
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irq_sim.h>
|
||||
#include <linux/irq_work.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct irq_sim_work_ctx {
|
||||
@@ -19,7 +20,6 @@ struct irq_sim_work_ctx {
|
||||
};
|
||||
|
||||
struct irq_sim_irq_ctx {
|
||||
int irqnum;
|
||||
bool enabled;
|
||||
struct irq_sim_work_ctx *work_ctx;
|
||||
};
|
||||
@@ -164,33 +164,27 @@ static const struct irq_domain_ops irq_sim_domain_ops = {
|
||||
struct irq_domain *irq_domain_create_sim(struct fwnode_handle *fwnode,
|
||||
unsigned int num_irqs)
|
||||
{
|
||||
struct irq_sim_work_ctx *work_ctx;
|
||||
struct irq_sim_work_ctx *work_ctx __free(kfree) =
|
||||
kmalloc(sizeof(*work_ctx), GFP_KERNEL);
|
||||
|
||||
work_ctx = kmalloc(sizeof(*work_ctx), GFP_KERNEL);
|
||||
if (!work_ctx)
|
||||
goto err_out;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
work_ctx->pending = bitmap_zalloc(num_irqs, GFP_KERNEL);
|
||||
if (!work_ctx->pending)
|
||||
goto err_free_work_ctx;
|
||||
unsigned long *pending __free(bitmap) = bitmap_zalloc(num_irqs, GFP_KERNEL);
|
||||
if (!pending)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
work_ctx->domain = irq_domain_create_linear(fwnode, num_irqs,
|
||||
&irq_sim_domain_ops,
|
||||
work_ctx);
|
||||
if (!work_ctx->domain)
|
||||
goto err_free_bitmap;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
work_ctx->irq_count = num_irqs;
|
||||
work_ctx->work = IRQ_WORK_INIT_HARD(irq_sim_handle_irq);
|
||||
work_ctx->pending = no_free_ptr(pending);
|
||||
|
||||
return work_ctx->domain;
|
||||
|
||||
err_free_bitmap:
|
||||
bitmap_free(work_ctx->pending);
|
||||
err_free_work_ctx:
|
||||
kfree(work_ctx);
|
||||
err_out:
|
||||
return ERR_PTR(-ENOMEM);
|
||||
return no_free_ptr(work_ctx)->domain;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(irq_domain_create_sim);
|
||||
|
||||
|
||||
+64
-48
@@ -92,11 +92,23 @@ static void desc_smp_init(struct irq_desc *desc, int node,
|
||||
#endif
|
||||
}
|
||||
|
||||
static void free_masks(struct irq_desc *desc)
|
||||
{
|
||||
#ifdef CONFIG_GENERIC_PENDING_IRQ
|
||||
free_cpumask_var(desc->pending_mask);
|
||||
#endif
|
||||
free_cpumask_var(desc->irq_common_data.affinity);
|
||||
#ifdef CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK
|
||||
free_cpumask_var(desc->irq_common_data.effective_affinity);
|
||||
#endif
|
||||
}
|
||||
|
||||
#else
|
||||
static inline int
|
||||
alloc_masks(struct irq_desc *desc, int node) { return 0; }
|
||||
static inline void
|
||||
desc_smp_init(struct irq_desc *desc, int node, const struct cpumask *affinity) { }
|
||||
static inline void free_masks(struct irq_desc *desc) { }
|
||||
#endif
|
||||
|
||||
static void desc_set_defaults(unsigned int irq, struct irq_desc *desc, int node,
|
||||
@@ -165,6 +177,39 @@ static void delete_irq_desc(unsigned int irq)
|
||||
mas_erase(&mas);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SPARSE_IRQ
|
||||
static const struct kobj_type irq_kobj_type;
|
||||
#endif
|
||||
|
||||
static int init_desc(struct irq_desc *desc, int irq, int node,
|
||||
unsigned int flags,
|
||||
const struct cpumask *affinity,
|
||||
struct module *owner)
|
||||
{
|
||||
desc->kstat_irqs = alloc_percpu(unsigned int);
|
||||
if (!desc->kstat_irqs)
|
||||
return -ENOMEM;
|
||||
|
||||
if (alloc_masks(desc, node)) {
|
||||
free_percpu(desc->kstat_irqs);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
raw_spin_lock_init(&desc->lock);
|
||||
lockdep_set_class(&desc->lock, &irq_desc_lock_class);
|
||||
mutex_init(&desc->request_mutex);
|
||||
init_waitqueue_head(&desc->wait_for_threads);
|
||||
desc_set_defaults(irq, desc, node, affinity, owner);
|
||||
irqd_set(&desc->irq_data, flags);
|
||||
irq_resend_init(desc);
|
||||
#ifdef CONFIG_SPARSE_IRQ
|
||||
kobject_init(&desc->kobj, &irq_kobj_type);
|
||||
init_rcu_head(&desc->rcu);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SPARSE_IRQ
|
||||
|
||||
static void irq_kobj_release(struct kobject *kobj);
|
||||
@@ -382,21 +427,6 @@ struct irq_desc *irq_to_desc(unsigned int irq)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(irq_to_desc);
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
static void free_masks(struct irq_desc *desc)
|
||||
{
|
||||
#ifdef CONFIG_GENERIC_PENDING_IRQ
|
||||
free_cpumask_var(desc->pending_mask);
|
||||
#endif
|
||||
free_cpumask_var(desc->irq_common_data.affinity);
|
||||
#ifdef CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK
|
||||
free_cpumask_var(desc->irq_common_data.effective_affinity);
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
static inline void free_masks(struct irq_desc *desc) { }
|
||||
#endif
|
||||
|
||||
void irq_lock_sparse(void)
|
||||
{
|
||||
mutex_lock(&sparse_irq_lock);
|
||||
@@ -412,36 +442,19 @@ static struct irq_desc *alloc_desc(int irq, int node, unsigned int flags,
|
||||
struct module *owner)
|
||||
{
|
||||
struct irq_desc *desc;
|
||||
int ret;
|
||||
|
||||
desc = kzalloc_node(sizeof(*desc), GFP_KERNEL, node);
|
||||
if (!desc)
|
||||
return NULL;
|
||||
/* allocate based on nr_cpu_ids */
|
||||
desc->kstat_irqs = alloc_percpu(unsigned int);
|
||||
if (!desc->kstat_irqs)
|
||||
goto err_desc;
|
||||
|
||||
if (alloc_masks(desc, node))
|
||||
goto err_kstat;
|
||||
|
||||
raw_spin_lock_init(&desc->lock);
|
||||
lockdep_set_class(&desc->lock, &irq_desc_lock_class);
|
||||
mutex_init(&desc->request_mutex);
|
||||
init_rcu_head(&desc->rcu);
|
||||
init_waitqueue_head(&desc->wait_for_threads);
|
||||
|
||||
desc_set_defaults(irq, desc, node, affinity, owner);
|
||||
irqd_set(&desc->irq_data, flags);
|
||||
kobject_init(&desc->kobj, &irq_kobj_type);
|
||||
irq_resend_init(desc);
|
||||
ret = init_desc(desc, irq, node, flags, affinity, owner);
|
||||
if (unlikely(ret)) {
|
||||
kfree(desc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return desc;
|
||||
|
||||
err_kstat:
|
||||
free_percpu(desc->kstat_irqs);
|
||||
err_desc:
|
||||
kfree(desc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void irq_kobj_release(struct kobject *kobj)
|
||||
@@ -581,26 +594,29 @@ struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
|
||||
int __init early_irq_init(void)
|
||||
{
|
||||
int count, i, node = first_online_node;
|
||||
struct irq_desc *desc;
|
||||
int ret;
|
||||
|
||||
init_irq_default_affinity();
|
||||
|
||||
printk(KERN_INFO "NR_IRQS: %d\n", NR_IRQS);
|
||||
|
||||
desc = irq_desc;
|
||||
count = ARRAY_SIZE(irq_desc);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
desc[i].kstat_irqs = alloc_percpu(unsigned int);
|
||||
alloc_masks(&desc[i], node);
|
||||
raw_spin_lock_init(&desc[i].lock);
|
||||
lockdep_set_class(&desc[i].lock, &irq_desc_lock_class);
|
||||
mutex_init(&desc[i].request_mutex);
|
||||
init_waitqueue_head(&desc[i].wait_for_threads);
|
||||
desc_set_defaults(i, &desc[i], node, NULL, NULL);
|
||||
irq_resend_init(&desc[i]);
|
||||
ret = init_desc(irq_desc + i, i, node, 0, NULL, NULL);
|
||||
if (unlikely(ret))
|
||||
goto __free_desc_res;
|
||||
}
|
||||
|
||||
return arch_early_irq_init();
|
||||
|
||||
__free_desc_res:
|
||||
while (--i >= 0) {
|
||||
free_masks(irq_desc + i);
|
||||
free_percpu(irq_desc[i].kstat_irqs);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct irq_desc *irq_to_desc(unsigned int irq)
|
||||
|
||||
+21
-7
@@ -29,6 +29,7 @@ static int irq_domain_alloc_irqs_locked(struct irq_domain *domain, int irq_base,
|
||||
unsigned int nr_irqs, int node, void *arg,
|
||||
bool realloc, const struct irq_affinity_desc *affinity);
|
||||
static void irq_domain_check_hierarchy(struct irq_domain *domain);
|
||||
static void irq_domain_free_one_irq(struct irq_domain *domain, unsigned int virq);
|
||||
|
||||
struct irqchip_fwid {
|
||||
struct fwnode_handle fwnode;
|
||||
@@ -448,7 +449,7 @@ struct irq_domain *irq_find_matching_fwspec(struct irq_fwspec *fwspec,
|
||||
*/
|
||||
mutex_lock(&irq_domain_mutex);
|
||||
list_for_each_entry(h, &irq_domain_list, link) {
|
||||
if (h->ops->select && fwspec->param_count)
|
||||
if (h->ops->select && bus_token != DOMAIN_BUS_ANY)
|
||||
rc = h->ops->select(h, fwspec, bus_token);
|
||||
else if (h->ops->match)
|
||||
rc = h->ops->match(h, to_of_node(fwnode), bus_token);
|
||||
@@ -858,8 +859,13 @@ unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec)
|
||||
}
|
||||
|
||||
if (irq_domain_is_hierarchy(domain)) {
|
||||
virq = irq_domain_alloc_irqs_locked(domain, -1, 1, NUMA_NO_NODE,
|
||||
fwspec, false, NULL);
|
||||
if (irq_domain_is_msi_device(domain)) {
|
||||
mutex_unlock(&domain->root->mutex);
|
||||
virq = msi_device_domain_alloc_wired(domain, hwirq, type);
|
||||
mutex_lock(&domain->root->mutex);
|
||||
} else
|
||||
virq = irq_domain_alloc_irqs_locked(domain, -1, 1, NUMA_NO_NODE,
|
||||
fwspec, false, NULL);
|
||||
if (virq <= 0) {
|
||||
virq = 0;
|
||||
goto out;
|
||||
@@ -914,7 +920,7 @@ void irq_dispose_mapping(unsigned int virq)
|
||||
return;
|
||||
|
||||
if (irq_domain_is_hierarchy(domain)) {
|
||||
irq_domain_free_irqs(virq, 1);
|
||||
irq_domain_free_one_irq(domain, virq);
|
||||
} else {
|
||||
irq_domain_disassociate(domain, virq);
|
||||
irq_free_desc(virq);
|
||||
@@ -1755,6 +1761,14 @@ void irq_domain_free_irqs(unsigned int virq, unsigned int nr_irqs)
|
||||
irq_free_descs(virq, nr_irqs);
|
||||
}
|
||||
|
||||
static void irq_domain_free_one_irq(struct irq_domain *domain, unsigned int virq)
|
||||
{
|
||||
if (irq_domain_is_msi_device(domain))
|
||||
msi_device_domain_free_wired(domain, virq);
|
||||
else
|
||||
irq_domain_free_irqs(virq, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* irq_domain_alloc_irqs_parent - Allocate interrupts from parent domain
|
||||
* @domain: Domain below which interrupts must be allocated
|
||||
@@ -1907,9 +1921,9 @@ static int irq_domain_alloc_irqs_locked(struct irq_domain *domain, int irq_base,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void irq_domain_check_hierarchy(struct irq_domain *domain)
|
||||
{
|
||||
}
|
||||
static void irq_domain_check_hierarchy(struct irq_domain *domain) { }
|
||||
static void irq_domain_free_one_irq(struct irq_domain *domain, unsigned int virq) { }
|
||||
|
||||
#endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */
|
||||
|
||||
#ifdef CONFIG_GENERIC_IRQ_DEBUGFS
|
||||
|
||||
+55
-54
@@ -192,10 +192,14 @@ void irq_set_thread_affinity(struct irq_desc *desc)
|
||||
struct irqaction *action;
|
||||
|
||||
for_each_action_of_desc(desc, action) {
|
||||
if (action->thread)
|
||||
if (action->thread) {
|
||||
set_bit(IRQTF_AFFINITY, &action->thread_flags);
|
||||
if (action->secondary && action->secondary->thread)
|
||||
wake_up_process(action->thread);
|
||||
}
|
||||
if (action->secondary && action->secondary->thread) {
|
||||
set_bit(IRQTF_AFFINITY, &action->secondary->thread_flags);
|
||||
wake_up_process(action->secondary->thread);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1049,10 +1053,57 @@ static irqreturn_t irq_forced_secondary_handler(int irq, void *dev_id)
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static int irq_wait_for_interrupt(struct irqaction *action)
|
||||
#ifdef CONFIG_SMP
|
||||
/*
|
||||
* Check whether we need to change the affinity of the interrupt thread.
|
||||
*/
|
||||
static void irq_thread_check_affinity(struct irq_desc *desc, struct irqaction *action)
|
||||
{
|
||||
cpumask_var_t mask;
|
||||
bool valid = false;
|
||||
|
||||
if (!test_and_clear_bit(IRQTF_AFFINITY, &action->thread_flags))
|
||||
return;
|
||||
|
||||
__set_current_state(TASK_RUNNING);
|
||||
|
||||
/*
|
||||
* In case we are out of memory we set IRQTF_AFFINITY again and
|
||||
* try again next time
|
||||
*/
|
||||
if (!alloc_cpumask_var(&mask, GFP_KERNEL)) {
|
||||
set_bit(IRQTF_AFFINITY, &action->thread_flags);
|
||||
return;
|
||||
}
|
||||
|
||||
raw_spin_lock_irq(&desc->lock);
|
||||
/*
|
||||
* This code is triggered unconditionally. Check the affinity
|
||||
* mask pointer. For CPU_MASK_OFFSTACK=n this is optimized out.
|
||||
*/
|
||||
if (cpumask_available(desc->irq_common_data.affinity)) {
|
||||
const struct cpumask *m;
|
||||
|
||||
m = irq_data_get_effective_affinity_mask(&desc->irq_data);
|
||||
cpumask_copy(mask, m);
|
||||
valid = true;
|
||||
}
|
||||
raw_spin_unlock_irq(&desc->lock);
|
||||
|
||||
if (valid)
|
||||
set_cpus_allowed_ptr(current, mask);
|
||||
free_cpumask_var(mask);
|
||||
}
|
||||
#else
|
||||
static inline void irq_thread_check_affinity(struct irq_desc *desc, struct irqaction *action) { }
|
||||
#endif
|
||||
|
||||
static int irq_wait_for_interrupt(struct irq_desc *desc,
|
||||
struct irqaction *action)
|
||||
{
|
||||
for (;;) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
irq_thread_check_affinity(desc, action);
|
||||
|
||||
if (kthread_should_stop()) {
|
||||
/* may need to run one last time */
|
||||
@@ -1129,52 +1180,6 @@ out_unlock:
|
||||
chip_bus_sync_unlock(desc);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/*
|
||||
* Check whether we need to change the affinity of the interrupt thread.
|
||||
*/
|
||||
static void
|
||||
irq_thread_check_affinity(struct irq_desc *desc, struct irqaction *action)
|
||||
{
|
||||
cpumask_var_t mask;
|
||||
bool valid = true;
|
||||
|
||||
if (!test_and_clear_bit(IRQTF_AFFINITY, &action->thread_flags))
|
||||
return;
|
||||
|
||||
/*
|
||||
* In case we are out of memory we set IRQTF_AFFINITY again and
|
||||
* try again next time
|
||||
*/
|
||||
if (!alloc_cpumask_var(&mask, GFP_KERNEL)) {
|
||||
set_bit(IRQTF_AFFINITY, &action->thread_flags);
|
||||
return;
|
||||
}
|
||||
|
||||
raw_spin_lock_irq(&desc->lock);
|
||||
/*
|
||||
* This code is triggered unconditionally. Check the affinity
|
||||
* mask pointer. For CPU_MASK_OFFSTACK=n this is optimized out.
|
||||
*/
|
||||
if (cpumask_available(desc->irq_common_data.affinity)) {
|
||||
const struct cpumask *m;
|
||||
|
||||
m = irq_data_get_effective_affinity_mask(&desc->irq_data);
|
||||
cpumask_copy(mask, m);
|
||||
} else {
|
||||
valid = false;
|
||||
}
|
||||
raw_spin_unlock_irq(&desc->lock);
|
||||
|
||||
if (valid)
|
||||
set_cpus_allowed_ptr(current, mask);
|
||||
free_cpumask_var(mask);
|
||||
}
|
||||
#else
|
||||
static inline void
|
||||
irq_thread_check_affinity(struct irq_desc *desc, struct irqaction *action) { }
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Interrupts which are not explicitly requested as threaded
|
||||
* interrupts rely on the implicit bh/preempt disable of the hard irq
|
||||
@@ -1312,13 +1317,9 @@ static int irq_thread(void *data)
|
||||
init_task_work(&on_exit_work, irq_thread_dtor);
|
||||
task_work_add(current, &on_exit_work, TWA_NONE);
|
||||
|
||||
irq_thread_check_affinity(desc, action);
|
||||
|
||||
while (!irq_wait_for_interrupt(action)) {
|
||||
while (!irq_wait_for_interrupt(desc, action)) {
|
||||
irqreturn_t action_ret;
|
||||
|
||||
irq_thread_check_affinity(desc, action);
|
||||
|
||||
action_ret = handler_fn(desc, action);
|
||||
if (action_ret == IRQ_WAKE_THREAD)
|
||||
irq_wake_secondary(desc, action);
|
||||
|
||||
+17
-11
@@ -8,8 +8,6 @@
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/irq.h>
|
||||
|
||||
#define IRQ_MATRIX_SIZE (BITS_TO_LONGS(IRQ_MATRIX_BITS))
|
||||
|
||||
struct cpumap {
|
||||
unsigned int available;
|
||||
unsigned int allocated;
|
||||
@@ -17,8 +15,8 @@ struct cpumap {
|
||||
unsigned int managed_allocated;
|
||||
bool initialized;
|
||||
bool online;
|
||||
unsigned long alloc_map[IRQ_MATRIX_SIZE];
|
||||
unsigned long managed_map[IRQ_MATRIX_SIZE];
|
||||
unsigned long *managed_map;
|
||||
unsigned long alloc_map[];
|
||||
};
|
||||
|
||||
struct irq_matrix {
|
||||
@@ -32,8 +30,8 @@ struct irq_matrix {
|
||||
unsigned int total_allocated;
|
||||
unsigned int online_maps;
|
||||
struct cpumap __percpu *maps;
|
||||
unsigned long scratch_map[IRQ_MATRIX_SIZE];
|
||||
unsigned long system_map[IRQ_MATRIX_SIZE];
|
||||
unsigned long *system_map;
|
||||
unsigned long scratch_map[];
|
||||
};
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
@@ -50,24 +48,32 @@ __init struct irq_matrix *irq_alloc_matrix(unsigned int matrix_bits,
|
||||
unsigned int alloc_start,
|
||||
unsigned int alloc_end)
|
||||
{
|
||||
unsigned int cpu, matrix_size = BITS_TO_LONGS(matrix_bits);
|
||||
struct irq_matrix *m;
|
||||
|
||||
if (matrix_bits > IRQ_MATRIX_BITS)
|
||||
return NULL;
|
||||
|
||||
m = kzalloc(sizeof(*m), GFP_KERNEL);
|
||||
m = kzalloc(struct_size(m, scratch_map, matrix_size * 2), GFP_KERNEL);
|
||||
if (!m)
|
||||
return NULL;
|
||||
|
||||
m->system_map = &m->scratch_map[matrix_size];
|
||||
|
||||
m->matrix_bits = matrix_bits;
|
||||
m->alloc_start = alloc_start;
|
||||
m->alloc_end = alloc_end;
|
||||
m->alloc_size = alloc_end - alloc_start;
|
||||
m->maps = alloc_percpu(*m->maps);
|
||||
m->maps = __alloc_percpu(struct_size(m->maps, alloc_map, matrix_size * 2),
|
||||
__alignof__(*m->maps));
|
||||
if (!m->maps) {
|
||||
kfree(m);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct cpumap *cm = per_cpu_ptr(m->maps, cpu);
|
||||
|
||||
cm->managed_map = &cm->alloc_map[matrix_size];
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
+150
-42
@@ -726,11 +726,26 @@ static void msi_domain_free(struct irq_domain *domain, unsigned int virq,
|
||||
irq_domain_free_irqs_top(domain, virq, nr_irqs);
|
||||
}
|
||||
|
||||
static int msi_domain_translate(struct irq_domain *domain, struct irq_fwspec *fwspec,
|
||||
irq_hw_number_t *hwirq, unsigned int *type)
|
||||
{
|
||||
struct msi_domain_info *info = domain->host_data;
|
||||
|
||||
/*
|
||||
* This will catch allocations through the regular irqdomain path except
|
||||
* for MSI domains which really support this, e.g. MBIGEN.
|
||||
*/
|
||||
if (!info->ops->msi_translate)
|
||||
return -ENOTSUPP;
|
||||
return info->ops->msi_translate(domain, fwspec, hwirq, type);
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops msi_domain_ops = {
|
||||
.alloc = msi_domain_alloc,
|
||||
.free = msi_domain_free,
|
||||
.activate = msi_domain_activate,
|
||||
.deactivate = msi_domain_deactivate,
|
||||
.translate = msi_domain_translate,
|
||||
};
|
||||
|
||||
static irq_hw_number_t msi_domain_ops_get_hwirq(struct msi_domain_info *info,
|
||||
@@ -830,8 +845,11 @@ static struct irq_domain *__msi_create_irq_domain(struct fwnode_handle *fwnode,
|
||||
domain = irq_domain_create_hierarchy(parent, flags | IRQ_DOMAIN_FLAG_MSI, 0,
|
||||
fwnode, &msi_domain_ops, info);
|
||||
|
||||
if (domain)
|
||||
if (domain) {
|
||||
irq_domain_update_bus_token(domain, info->bus_token);
|
||||
if (info->flags & MSI_FLAG_PARENT_PM_DEV)
|
||||
domain->pm_dev = parent->pm_dev;
|
||||
}
|
||||
|
||||
return domain;
|
||||
}
|
||||
@@ -945,9 +963,9 @@ bool msi_create_device_irq_domain(struct device *dev, unsigned int domid,
|
||||
void *chip_data)
|
||||
{
|
||||
struct irq_domain *domain, *parent = dev->msi.domain;
|
||||
const struct msi_parent_ops *pops;
|
||||
struct fwnode_handle *fwnode, *fwnalloced = NULL;
|
||||
struct msi_domain_template *bundle;
|
||||
struct fwnode_handle *fwnode;
|
||||
const struct msi_parent_ops *pops;
|
||||
|
||||
if (!irq_domain_is_msi_parent(parent))
|
||||
return false;
|
||||
@@ -970,7 +988,19 @@ bool msi_create_device_irq_domain(struct device *dev, unsigned int domid,
|
||||
pops->prefix ? : "", bundle->chip.name, dev_name(dev));
|
||||
bundle->chip.name = bundle->name;
|
||||
|
||||
fwnode = irq_domain_alloc_named_fwnode(bundle->name);
|
||||
/*
|
||||
* Using the device firmware node is required for wire to MSI
|
||||
* device domains so that the existing firmware results in a domain
|
||||
* match.
|
||||
* All other device domains like PCI/MSI use the named firmware
|
||||
* node as they are not guaranteed to have a fwnode. They are never
|
||||
* looked up and always handled in the context of the device.
|
||||
*/
|
||||
if (bundle->info.flags & MSI_FLAG_USE_DEV_FWNODE)
|
||||
fwnode = dev->fwnode;
|
||||
else
|
||||
fwnode = fwnalloced = irq_domain_alloc_named_fwnode(bundle->name);
|
||||
|
||||
if (!fwnode)
|
||||
goto free_bundle;
|
||||
|
||||
@@ -997,7 +1027,7 @@ bool msi_create_device_irq_domain(struct device *dev, unsigned int domid,
|
||||
fail:
|
||||
msi_unlock_descs(dev);
|
||||
free_fwnode:
|
||||
irq_domain_free_fwnode(fwnode);
|
||||
irq_domain_free_fwnode(fwnalloced);
|
||||
free_bundle:
|
||||
kfree(bundle);
|
||||
return false;
|
||||
@@ -1431,6 +1461,51 @@ int msi_domain_alloc_irqs_all_locked(struct device *dev, unsigned int domid, int
|
||||
return msi_domain_alloc_locked(dev, &ctrl);
|
||||
}
|
||||
|
||||
static struct msi_map __msi_domain_alloc_irq_at(struct device *dev, unsigned int domid,
|
||||
unsigned int index,
|
||||
const struct irq_affinity_desc *affdesc,
|
||||
union msi_instance_cookie *icookie)
|
||||
{
|
||||
struct msi_ctrl ctrl = { .domid = domid, .nirqs = 1, };
|
||||
struct irq_domain *domain;
|
||||
struct msi_map map = { };
|
||||
struct msi_desc *desc;
|
||||
int ret;
|
||||
|
||||
domain = msi_get_device_domain(dev, domid);
|
||||
if (!domain) {
|
||||
map.index = -ENODEV;
|
||||
return map;
|
||||
}
|
||||
|
||||
desc = msi_alloc_desc(dev, 1, affdesc);
|
||||
if (!desc) {
|
||||
map.index = -ENOMEM;
|
||||
return map;
|
||||
}
|
||||
|
||||
if (icookie)
|
||||
desc->data.icookie = *icookie;
|
||||
|
||||
ret = msi_insert_desc(dev, desc, domid, index);
|
||||
if (ret) {
|
||||
map.index = ret;
|
||||
return map;
|
||||
}
|
||||
|
||||
ctrl.first = ctrl.last = desc->msi_index;
|
||||
|
||||
ret = __msi_domain_alloc_irqs(dev, domain, &ctrl);
|
||||
if (ret) {
|
||||
map.index = ret;
|
||||
msi_domain_free_locked(dev, &ctrl);
|
||||
} else {
|
||||
map.index = desc->msi_index;
|
||||
map.virq = desc->irq;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* msi_domain_alloc_irq_at - Allocate an interrupt from a MSI interrupt domain at
|
||||
* a given index - or at the next free index
|
||||
@@ -1460,49 +1535,58 @@ struct msi_map msi_domain_alloc_irq_at(struct device *dev, unsigned int domid, u
|
||||
const struct irq_affinity_desc *affdesc,
|
||||
union msi_instance_cookie *icookie)
|
||||
{
|
||||
struct msi_ctrl ctrl = { .domid = domid, .nirqs = 1, };
|
||||
struct irq_domain *domain;
|
||||
struct msi_map map = { };
|
||||
struct msi_desc *desc;
|
||||
int ret;
|
||||
struct msi_map map;
|
||||
|
||||
msi_lock_descs(dev);
|
||||
domain = msi_get_device_domain(dev, domid);
|
||||
if (!domain) {
|
||||
map.index = -ENODEV;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
desc = msi_alloc_desc(dev, 1, affdesc);
|
||||
if (!desc) {
|
||||
map.index = -ENOMEM;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (icookie)
|
||||
desc->data.icookie = *icookie;
|
||||
|
||||
ret = msi_insert_desc(dev, desc, domid, index);
|
||||
if (ret) {
|
||||
map.index = ret;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ctrl.first = ctrl.last = desc->msi_index;
|
||||
|
||||
ret = __msi_domain_alloc_irqs(dev, domain, &ctrl);
|
||||
if (ret) {
|
||||
map.index = ret;
|
||||
msi_domain_free_locked(dev, &ctrl);
|
||||
} else {
|
||||
map.index = desc->msi_index;
|
||||
map.virq = desc->irq;
|
||||
}
|
||||
unlock:
|
||||
map = __msi_domain_alloc_irq_at(dev, domid, index, affdesc, icookie);
|
||||
msi_unlock_descs(dev);
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* msi_device_domain_alloc_wired - Allocate a "wired" interrupt on @domain
|
||||
* @domain: The domain to allocate on
|
||||
* @hwirq: The hardware interrupt number to allocate for
|
||||
* @type: The interrupt type
|
||||
*
|
||||
* This weirdness supports wire to MSI controllers like MBIGEN.
|
||||
*
|
||||
* @hwirq is the hardware interrupt number which is handed in from
|
||||
* irq_create_fwspec_mapping(). As the wire to MSI domain is sparse, but
|
||||
* sized in firmware, the hardware interrupt number cannot be used as MSI
|
||||
* index. For the underlying irq chip the MSI index is irrelevant and
|
||||
* all it needs is the hardware interrupt number.
|
||||
*
|
||||
* To handle this the MSI index is allocated with MSI_ANY_INDEX and the
|
||||
* hardware interrupt number is stored along with the type information in
|
||||
* msi_desc::cookie so the underlying interrupt chip and domain code can
|
||||
* retrieve it.
|
||||
*
|
||||
* Return: The Linux interrupt number (> 0) or an error code
|
||||
*/
|
||||
int msi_device_domain_alloc_wired(struct irq_domain *domain, unsigned int hwirq,
|
||||
unsigned int type)
|
||||
{
|
||||
unsigned int domid = MSI_DEFAULT_DOMAIN;
|
||||
union msi_instance_cookie icookie = { };
|
||||
struct device *dev = domain->dev;
|
||||
struct msi_map map = { };
|
||||
|
||||
if (WARN_ON_ONCE(!dev || domain->bus_token != DOMAIN_BUS_WIRED_TO_MSI))
|
||||
return -EINVAL;
|
||||
|
||||
icookie.value = ((u64)type << 32) | hwirq;
|
||||
|
||||
msi_lock_descs(dev);
|
||||
if (WARN_ON_ONCE(msi_get_device_domain(dev, domid) != domain))
|
||||
map.index = -EINVAL;
|
||||
else
|
||||
map = __msi_domain_alloc_irq_at(dev, domid, MSI_ANY_INDEX, NULL, &icookie);
|
||||
msi_unlock_descs(dev);
|
||||
|
||||
return map.index >= 0 ? map.virq : map.index;
|
||||
}
|
||||
|
||||
static void __msi_domain_free_irqs(struct device *dev, struct irq_domain *domain,
|
||||
struct msi_ctrl *ctrl)
|
||||
{
|
||||
@@ -1628,6 +1712,30 @@ void msi_domain_free_irqs_all(struct device *dev, unsigned int domid)
|
||||
msi_unlock_descs(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* msi_device_domain_free_wired - Free a wired interrupt in @domain
|
||||
* @domain: The domain to free the interrupt on
|
||||
* @virq: The Linux interrupt number to free
|
||||
*
|
||||
* This is the counterpart of msi_device_domain_alloc_wired() for the
|
||||
* weird wired to MSI converting domains.
|
||||
*/
|
||||
void msi_device_domain_free_wired(struct irq_domain *domain, unsigned int virq)
|
||||
{
|
||||
struct msi_desc *desc = irq_get_msi_desc(virq);
|
||||
struct device *dev = domain->dev;
|
||||
|
||||
if (WARN_ON_ONCE(!dev || !desc || domain->bus_token != DOMAIN_BUS_WIRED_TO_MSI))
|
||||
return;
|
||||
|
||||
msi_lock_descs(dev);
|
||||
if (!WARN_ON_ONCE(msi_get_device_domain(dev, MSI_DEFAULT_DOMAIN) != domain)) {
|
||||
msi_domain_free_irqs_range_locked(dev, MSI_DEFAULT_DOMAIN, desc->msi_index,
|
||||
desc->msi_index);
|
||||
}
|
||||
msi_unlock_descs(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* msi_get_domain_info - Get the MSI interrupt domain info for @domain
|
||||
* @domain: The interrupt domain to retrieve data from
|
||||
|
||||
Reference in New Issue
Block a user