Commit e4844ded authored by Thomas Gleixner's avatar Thomas Gleixner

Merge tag 'irqchip-4.15' of...

Merge tag 'irqchip-4.15' of git://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms into irq/core

Pull irqchip updates for 4.15 from Marc Zyngier

- GICv4 updates (improved performance, errata workarounds)
- Workaround for Socionext's pre-ITS erratum
- Meson GPIO interrupt controller
- BCM7271 L2 interrupt controller
- GICv3 range selector support
- various cleanups
parents c94fb639 7bdeb7f5
......@@ -70,6 +70,7 @@ stable kernels.
| | | | |
| Hisilicon | Hip0{5,6,7} | #161010101 | HISILICON_ERRATUM_161010101 |
| Hisilicon | Hip0{6,7} | #161010701 | N/A |
| Hisilicon | Hip07 | #161600802 | HISILICON_ERRATUM_161600802 |
| | | | |
| Qualcomm Tech. | Falkor v1 | E1003 | QCOM_FALKOR_ERRATUM_1003 |
| Qualcomm Tech. | Falkor v1 | E1009 | QCOM_FALKOR_ERRATUM_1009 |
......
Amlogic meson GPIO interrupt controller
Meson SoCs contains an interrupt controller which is able to watch the SoC
pads and generate an interrupt on edge or level. The controller is essentially
a 256 pads to 8 GIC interrupt multiplexer, with a filter block to select edge
or level and polarity. It does not expose all 256 mux inputs because the
documentation shows that the upper part is not mapped to any pad. The actual
number of interrupt exposed depends on the SoC.
Required properties:
- compatible : must have "amlogic,meson8-gpio-intc” and either
“amlogic,meson8b-gpio-intc” for meson8b SoCs (S805) or
“amlogic,meson-gxbb-gpio-intc” for GXBB SoCs (S905) or
“amlogic,meson-gxl-gpio-intc” for GXL SoCs (S905X, S912)
- interrupt-parent : a phandle to the GIC the interrupts are routed to.
Usually this is provided at the root level of the device tree as it is
common to most of the SoC.
- reg : Specifies base physical address and size of the registers.
- interrupt-controller : Identifies the node as an interrupt controller.
- #interrupt-cells : Specifies the number of cells needed to encode an
interrupt source. The value must be 2.
- meson,channel-interrupts: Array with the 8 upstream hwirq numbers. These
are the hwirqs used on the parent interrupt controller.
Example:
gpio_interrupt: interrupt-controller@9880 {
compatible = "amlogic,meson-gxbb-gpio-intc",
"amlogic,meson-gpio-intc";
reg = <0x0 0x9880 0x0 0x10>;
interrupt-controller;
#interrupt-cells = <2>;
meson,channel-interrupts = <64 65 66 67 68 69 70 71>;
};
......@@ -75,6 +75,10 @@ These nodes must have the following properties:
- reg: Specifies the base physical address and size of the ITS
registers.
Optional:
- socionext,synquacer-pre-its: (u32, u32) tuple describing the untranslated
address and size of the pre-ITS window.
The main GIC node must contain the appropriate #address-cells,
#size-cells and ranges properties for the reg property of all ITS
nodes.
......
......@@ -2,7 +2,8 @@ Broadcom Generic Level 2 Interrupt Controller
Required properties:
- compatible: should be "brcm,l2-intc"
- compatible: should be "brcm,l2-intc" for latched interrupt controllers
should be "brcm,bcm7271-l2-intc" for level interrupt controllers
- reg: specifies the base physical address and size of the registers
- interrupt-controller: identifies the node as an interrupt controller
- #interrupt-cells: specifies the number of cells needed to encode an
......
......@@ -13,6 +13,9 @@ Required properties:
- "renesas,irqc-r8a7793" (R-Car M2-N)
- "renesas,irqc-r8a7794" (R-Car E2)
- "renesas,intc-ex-r8a7795" (R-Car H3)
- "renesas,intc-ex-r8a7796" (R-Car M3-W)
- "renesas,intc-ex-r8a77970" (R-Car V3M)
- "renesas,intc-ex-r8a77995" (R-Car D3)
- #interrupt-cells: has to be <2>: an interrupt index and flags, as defined in
interrupts.txt in this directory
- clocks: Must contain a reference to the functional clock.
......
......@@ -196,6 +196,11 @@ static inline void gic_write_ctlr(u32 val)
isb();
}
static inline u32 gic_read_ctlr(void)
{
return read_sysreg(ICC_CTLR);
}
static inline void gic_write_grpen1(u32 val)
{
write_sysreg(val, ICC_IGRPEN1);
......
......@@ -539,6 +539,25 @@ config QCOM_QDF2400_ERRATUM_0065
If unsure, say Y.
config SOCIONEXT_SYNQUACER_PREITS
bool "Socionext Synquacer: Workaround for GICv3 pre-ITS"
default y
help
Socionext Synquacer SoCs implement a separate h/w block to generate
MSI doorbell writes with non-zero values for the device ID.
If unsure, say Y.
config HISILICON_ERRATUM_161600802
bool "Hip07 161600802: Erroneous redistributor VLPI base"
default y
help
The HiSilicon Hip07 SoC usees the wrong redistributor base
when issued ITS commands such as VMOVP and VMAPP, and requires
a 128kB offset to be applied to the target address in this commands.
If unsure, say Y.
endmenu
......
......@@ -87,6 +87,11 @@ static inline void gic_write_ctlr(u32 val)
isb();
}
static inline u32 gic_read_ctlr(void)
{
return read_sysreg_s(SYS_ICC_CTLR_EL1);
}
static inline void gic_write_grpen1(u32 val)
{
write_sysreg_s(val, SYS_ICC_IGRPEN1_EL1);
......
......@@ -324,4 +324,12 @@ config IRQ_UNIPHIER_AIDET
help
Support for the UniPhier AIDET (ARM Interrupt Detector).
config MESON_IRQ_GPIO
bool "Meson GPIO Interrupt Multiplexer"
depends on ARCH_MESON || COMPILE_TEST
select IRQ_DOMAIN
select IRQ_DOMAIN_HIERARCHY
help
Support Meson SoC Family GPIO Interrupt Multiplexer
endmenu
......@@ -79,3 +79,4 @@ obj-$(CONFIG_ARCH_ASPEED) += irq-aspeed-vic.o irq-aspeed-i2c-ic.o
obj-$(CONFIG_STM32_EXTI) += irq-stm32-exti.o
obj-$(CONFIG_QCOM_IRQ_COMBINER) += qcom-irq-combiner.o
obj-$(CONFIG_IRQ_UNIPHIER_AIDET) += irq-uniphier-aidet.o
obj-$(CONFIG_MESON_IRQ_GPIO) += irq-meson-gpio.o
......@@ -76,8 +76,8 @@ static int __init aspeed_i2c_ic_of_init(struct device_node *node,
return -ENOMEM;
i2c_ic->base = of_iomap(node, 0);
if (IS_ERR(i2c_ic->base)) {
ret = PTR_ERR(i2c_ic->base);
if (!i2c_ic->base) {
ret = -ENOMEM;
goto err_free_ic;
}
......
/*
* Generic Broadcom Set Top Box Level 2 Interrupt controller driver
*
* Copyright (C) 2014 Broadcom Corporation
* Copyright (C) 2014-2017 Broadcom
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
......@@ -31,35 +31,82 @@
#include <linux/irqchip.h>
#include <linux/irqchip/chained_irq.h>
/* Register offsets in the L2 interrupt controller */
#define CPU_STATUS 0x00
#define CPU_SET 0x04
#define CPU_CLEAR 0x08
#define CPU_MASK_STATUS 0x0c
#define CPU_MASK_SET 0x10
#define CPU_MASK_CLEAR 0x14
struct brcmstb_intc_init_params {
irq_flow_handler_t handler;
int cpu_status;
int cpu_clear;
int cpu_mask_status;
int cpu_mask_set;
int cpu_mask_clear;
};
/* Register offsets in the L2 latched interrupt controller */
static const struct brcmstb_intc_init_params l2_edge_intc_init = {
.handler = handle_edge_irq,
.cpu_status = 0x00,
.cpu_clear = 0x08,
.cpu_mask_status = 0x0c,
.cpu_mask_set = 0x10,
.cpu_mask_clear = 0x14
};
/* Register offsets in the L2 level interrupt controller */
static const struct brcmstb_intc_init_params l2_lvl_intc_init = {
.handler = handle_level_irq,
.cpu_status = 0x00,
.cpu_clear = -1, /* Register not present */
.cpu_mask_status = 0x04,
.cpu_mask_set = 0x08,
.cpu_mask_clear = 0x0C
};
/* L2 intc private data structure */
struct brcmstb_l2_intc_data {
int parent_irq;
void __iomem *base;
struct irq_domain *domain;
struct irq_chip_generic *gc;
int status_offset;
int mask_offset;
bool can_wake;
u32 saved_mask; /* for suspend/resume */
};
/**
* brcmstb_l2_mask_and_ack - Mask and ack pending interrupt
* @d: irq_data
*
* Chip has separate enable/disable registers instead of a single mask
* register and pending interrupt is acknowledged by setting a bit.
*
* Note: This function is generic and could easily be added to the
* generic irqchip implementation if there ever becomes a will to do so.
* Perhaps with a name like irq_gc_mask_disable_and_ack_set().
*
* e.g.: https://patchwork.kernel.org/patch/9831047/
*/
static void brcmstb_l2_mask_and_ack(struct irq_data *d)
{
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
struct irq_chip_type *ct = irq_data_get_chip_type(d);
u32 mask = d->mask;
irq_gc_lock(gc);
irq_reg_writel(gc, mask, ct->regs.disable);
*ct->mask_cache &= ~mask;
irq_reg_writel(gc, mask, ct->regs.ack);
irq_gc_unlock(gc);
}
static void brcmstb_l2_intc_irq_handle(struct irq_desc *desc)
{
struct brcmstb_l2_intc_data *b = irq_desc_get_handler_data(desc);
struct irq_chip_generic *gc = irq_get_domain_generic_chip(b->domain, 0);
struct irq_chip *chip = irq_desc_get_chip(desc);
unsigned int irq;
u32 status;
chained_irq_enter(chip, desc);
status = irq_reg_readl(gc, CPU_STATUS) &
~(irq_reg_readl(gc, CPU_MASK_STATUS));
status = irq_reg_readl(b->gc, b->status_offset) &
~(irq_reg_readl(b->gc, b->mask_offset));
if (status == 0) {
raw_spin_lock(&desc->lock);
......@@ -70,10 +117,8 @@ static void brcmstb_l2_intc_irq_handle(struct irq_desc *desc)
do {
irq = ffs(status) - 1;
/* ack at our level */
irq_reg_writel(gc, 1 << irq, CPU_CLEAR);
status &= ~(1 << irq);
generic_handle_irq(irq_find_mapping(b->domain, irq));
generic_handle_irq(irq_linear_revmap(b->domain, irq));
} while (status);
out:
chained_irq_exit(chip, desc);
......@@ -82,16 +127,17 @@ static void brcmstb_l2_intc_irq_handle(struct irq_desc *desc)
static void brcmstb_l2_intc_suspend(struct irq_data *d)
{
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
struct irq_chip_type *ct = irq_data_get_chip_type(d);
struct brcmstb_l2_intc_data *b = gc->private;
irq_gc_lock(gc);
/* Save the current mask */
b->saved_mask = irq_reg_readl(gc, CPU_MASK_STATUS);
b->saved_mask = irq_reg_readl(gc, ct->regs.mask);
if (b->can_wake) {
/* Program the wakeup mask */
irq_reg_writel(gc, ~gc->wake_active, CPU_MASK_SET);
irq_reg_writel(gc, gc->wake_active, CPU_MASK_CLEAR);
irq_reg_writel(gc, ~gc->wake_active, ct->regs.disable);
irq_reg_writel(gc, gc->wake_active, ct->regs.enable);
}
irq_gc_unlock(gc);
}
......@@ -99,49 +145,56 @@ static void brcmstb_l2_intc_suspend(struct irq_data *d)
static void brcmstb_l2_intc_resume(struct irq_data *d)
{
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
struct irq_chip_type *ct = irq_data_get_chip_type(d);
struct brcmstb_l2_intc_data *b = gc->private;
irq_gc_lock(gc);
/* Clear unmasked non-wakeup interrupts */
irq_reg_writel(gc, ~b->saved_mask & ~gc->wake_active, CPU_CLEAR);
if (ct->chip.irq_ack) {
/* Clear unmasked non-wakeup interrupts */
irq_reg_writel(gc, ~b->saved_mask & ~gc->wake_active,
ct->regs.ack);
}
/* Restore the saved mask */
irq_reg_writel(gc, b->saved_mask, CPU_MASK_SET);
irq_reg_writel(gc, ~b->saved_mask, CPU_MASK_CLEAR);
irq_reg_writel(gc, b->saved_mask, ct->regs.disable);
irq_reg_writel(gc, ~b->saved_mask, ct->regs.enable);
irq_gc_unlock(gc);
}
static int __init brcmstb_l2_intc_of_init(struct device_node *np,
struct device_node *parent)
struct device_node *parent,
const struct brcmstb_intc_init_params
*init_params)
{
unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
struct brcmstb_l2_intc_data *data;
struct irq_chip_generic *gc;
struct irq_chip_type *ct;
int ret;
unsigned int flags;
int parent_irq;
void __iomem *base;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->base = of_iomap(np, 0);
if (!data->base) {
base = of_iomap(np, 0);
if (!base) {
pr_err("failed to remap intc L2 registers\n");
ret = -ENOMEM;
goto out_free;
}
/* Disable all interrupts by default */
writel(0xffffffff, data->base + CPU_MASK_SET);
writel(0xffffffff, base + init_params->cpu_mask_set);
/* Wakeup interrupts may be retained from S5 (cold boot) */
data->can_wake = of_property_read_bool(np, "brcm,irq-can-wake");
if (!data->can_wake)
writel(0xffffffff, data->base + CPU_CLEAR);
if (!data->can_wake && (init_params->cpu_clear >= 0))
writel(0xffffffff, base + init_params->cpu_clear);
data->parent_irq = irq_of_parse_and_map(np, 0);
if (!data->parent_irq) {
parent_irq = irq_of_parse_and_map(np, 0);
if (!parent_irq) {
pr_err("failed to find parent interrupt\n");
ret = -EINVAL;
goto out_unmap;
......@@ -163,29 +216,39 @@ static int __init brcmstb_l2_intc_of_init(struct device_node *np,
/* Allocate a single Generic IRQ chip for this node */
ret = irq_alloc_domain_generic_chips(data->domain, 32, 1,
np->full_name, handle_edge_irq, clr, 0, flags);
np->full_name, init_params->handler, clr, 0, flags);
if (ret) {
pr_err("failed to allocate generic irq chip\n");
goto out_free_domain;
}
/* Set the IRQ chaining logic */
irq_set_chained_handler_and_data(data->parent_irq,
irq_set_chained_handler_and_data(parent_irq,
brcmstb_l2_intc_irq_handle, data);
gc = irq_get_domain_generic_chip(data->domain, 0);
gc->reg_base = data->base;
gc->private = data;
ct = gc->chip_types;
ct->chip.irq_ack = irq_gc_ack_set_bit;
ct->regs.ack = CPU_CLEAR;
data->gc = irq_get_domain_generic_chip(data->domain, 0);
data->gc->reg_base = base;
data->gc->private = data;
data->status_offset = init_params->cpu_status;
data->mask_offset = init_params->cpu_mask_status;
ct = data->gc->chip_types;
if (init_params->cpu_clear >= 0) {
ct->regs.ack = init_params->cpu_clear;
ct->chip.irq_ack = irq_gc_ack_set_bit;
ct->chip.irq_mask_ack = brcmstb_l2_mask_and_ack;
} else {
/* No Ack - but still slightly more efficient to define this */
ct->chip.irq_mask_ack = irq_gc_mask_disable_reg;
}
ct->chip.irq_mask = irq_gc_mask_disable_reg;
ct->regs.disable = CPU_MASK_SET;
ct->regs.disable = init_params->cpu_mask_set;
ct->regs.mask = init_params->cpu_mask_status;
ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
ct->regs.enable = CPU_MASK_CLEAR;
ct->regs.enable = init_params->cpu_mask_clear;
ct->chip.irq_suspend = brcmstb_l2_intc_suspend;
ct->chip.irq_resume = brcmstb_l2_intc_resume;
......@@ -195,21 +258,35 @@ static int __init brcmstb_l2_intc_of_init(struct device_node *np,
/* This IRQ chip can wake the system, set all child interrupts
* in wake_enabled mask
*/
gc->wake_enabled = 0xffffffff;
data->gc->wake_enabled = 0xffffffff;
ct->chip.irq_set_wake = irq_gc_set_wake;
}
pr_info("registered L2 intc (mem: 0x%p, parent irq: %d)\n",
data->base, data->parent_irq);
base, parent_irq);
return 0;
out_free_domain:
irq_domain_remove(data->domain);
out_unmap:
iounmap(data->base);
iounmap(base);
out_free:
kfree(data);
return ret;
}
IRQCHIP_DECLARE(brcmstb_l2_intc, "brcm,l2-intc", brcmstb_l2_intc_of_init);
int __init brcmstb_l2_edge_intc_of_init(struct device_node *np,
struct device_node *parent)
{
return brcmstb_l2_intc_of_init(np, parent, &l2_edge_intc_init);
}
IRQCHIP_DECLARE(brcmstb_l2_intc, "brcm,l2-intc", brcmstb_l2_edge_intc_of_init);
int __init brcmstb_l2_lvl_intc_of_init(struct device_node *np,
struct device_node *parent)
{
return brcmstb_l2_intc_of_init(np, parent, &l2_lvl_intc_init);
}
IRQCHIP_DECLARE(bcm7271_l2_intc, "brcm,bcm7271-l2-intc",
brcmstb_l2_lvl_intc_of_init);
......@@ -40,8 +40,9 @@ void gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks,
for (; quirks->desc; quirks++) {
if (quirks->iidr != (quirks->mask & iidr))
continue;
quirks->init(data);
pr_info("GIC: enabling workaround for %s\n", quirks->desc);
if (quirks->init(data))
pr_info("GIC: enabling workaround for %s\n",
quirks->desc);
}
}
......
......@@ -23,7 +23,7 @@
struct gic_quirk {
const char *desc;
void (*init)(void *data);
bool (*init)(void *data);
u32 iidr;
u32 mask;
};
......
......@@ -83,6 +83,8 @@ struct its_baser {
u32 psz;
};
struct its_device;
/*
* The ITS structure - contains most of the infrastructure, with the
* top-level MSI domain, the command queue, the collections, and the
......@@ -97,12 +99,18 @@ struct its_node {
struct its_cmd_block *cmd_write;
struct its_baser tables[GITS_BASER_NR_REGS];
struct its_collection *collections;
struct fwnode_handle *fwnode_handle;
u64 (*get_msi_base)(struct its_device *its_dev);
struct list_head its_device_list;
u64 flags;
unsigned long list_nr;
u32 ite_size;
u32 device_ids;
int numa_node;
unsigned int msi_domain_flags;
u32 pre_its_base; /* for Socionext Synquacer */
bool is_v4;
int vlpi_redist_offset;
};
#define ITS_ITT_ALIGN SZ_256
......@@ -148,12 +156,6 @@ static DEFINE_SPINLOCK(its_lock);
static struct rdists *gic_rdists;
static struct irq_domain *its_parent;
/*
* We have a maximum number of 16 ITSs in the whole system if we're
* using the ITSList mechanism
*/
#define ITS_LIST_MAX 16
static unsigned long its_list_map;
static u16 vmovp_seq_num;
static DEFINE_RAW_SPINLOCK(vmovp_lock);
......@@ -268,10 +270,12 @@ struct its_cmd_block {
#define ITS_CMD_QUEUE_SZ SZ_64K
#define ITS_CMD_QUEUE_NR_ENTRIES (ITS_CMD_QUEUE_SZ / sizeof(struct its_cmd_block))
typedef struct its_collection *(*its_cmd_builder_t)(struct its_cmd_block *,
typedef struct its_collection *(*its_cmd_builder_t)(struct its_node *,
struct its_cmd_block *,
struct its_cmd_desc *);
typedef struct its_vpe *(*its_cmd_vbuilder_t)(struct its_cmd_block *,
typedef struct its_vpe *(*its_cmd_vbuilder_t)(struct its_node *,
struct its_cmd_block *,
struct its_cmd_desc *);
static void its_mask_encode(u64 *raw_cmd, u64 val, int h, int l)
......@@ -375,7 +379,8 @@ static inline void its_fixup_cmd(struct its_cmd_block *cmd)
cmd->raw_cmd[3] = cpu_to_le64(cmd->raw_cmd[3]);
}
static struct its_collection *its_build_mapd_cmd(struct its_cmd_block *cmd,
static struct its_collection *its_build_mapd_cmd(struct its_node *its,
struct its_cmd_block *cmd,
struct its_cmd_desc *desc)
{
unsigned long itt_addr;
......@@ -395,7 +400,8 @@ static struct its_collection *its_build_mapd_cmd(struct its_cmd_block *cmd,
return NULL;
}
static struct its_collection *its_build_mapc_cmd(struct its_cmd_block *cmd,
static struct its_collection *its_build_mapc_cmd(struct its_node *its,
struct its_cmd_block *cmd,
struct its_cmd_desc *desc)
{
its_encode_cmd(cmd, GITS_CMD_MAPC);
......@@ -408,7 +414,8 @@ static struct its_collection *its_build_mapc_cmd(struct its_cmd_block *cmd,
return desc->its_mapc_cmd.col;
}
static struct its_collection *its_build_mapti_cmd(struct its_cmd_block *cmd,
static struct its_collection *its_build_mapti_cmd(struct its_node *its,
struct its_cmd_block *cmd,
struct its_cmd_desc *desc)
{
struct its_collection *col;
......@@ -427,7 +434,8 @@ static struct its_collection *its_build_mapti_cmd(struct its_cmd_block *cmd,
return col;
}
static struct its_collection *its_build_movi_cmd(struct its_cmd_block *cmd,
static struct its_collection *its_build_movi_cmd(struct its_node *its,
struct its_cmd_block *cmd,
struct its_cmd_desc *desc)
{
struct its_collection *col;
......@@ -445,7 +453,8 @@ static struct its_collection *its_build_movi_cmd(struct its_cmd_block *cmd,
return col;
}
static struct its_collection *its_build_discard_cmd(struct its_cmd_block *cmd,
static struct its_collection *its_build_discard_cmd(struct its_node *its,
struct its_cmd_block *cmd,
struct its_cmd_desc *desc)
{
struct its_collection *col;
......@@ -462,7 +471,8 @@ static struct its_collection *its_build_discard_cmd(struct its_cmd_block *cmd,
return col;
}
static struct its_collection *its_build_inv_cmd(struct its_cmd_block *cmd,
static struct its_collection *its_build_inv_cmd(struct its_node *its,
struct its_cmd_block *cmd,
struct its_cmd_desc *desc)
{
struct its_collection *col;
......@@ -479,7 +489,8 @@ static struct its_collection *its_build_inv_cmd(struct its_cmd_block *cmd,
return col;
}
static struct its_collection *its_build_int_cmd(struct its_cmd_block *cmd,
static struct its_collection *its_build_int_cmd(struct its_node *its,
struct its_cmd_block *cmd,
struct its_cmd_desc *desc)
{
struct its_collection *col;
......@@ -496,7 +507,8 @@ static struct its_collection *its_build_int_cmd(struct its_cmd_block *cmd,
return col;
}
static struct its_collection *its_build_clear_cmd(struct its_cmd_block *cmd,
static struct its_collection *its_build_clear_cmd(struct its_node *its,
struct its_cmd_block *cmd,
struct its_cmd_desc *desc)
{
struct its_collection *col;
......@@ -513,7 +525,8 @@ static struct its_collection *its_build_clear_cmd(struct its_cmd_block *cmd,
return col;
}
static struct its_collection *its_build_invall_cmd(struct its_cmd_block *cmd,
static struct its_collection *its_build_invall_cmd(struct its_node *its,
struct its_cmd_block *cmd,
struct its_cmd_desc *desc)
{
its_encode_cmd(cmd, GITS_CMD_INVALL);
......@@ -524,7 +537,8 @@ static struct its_collection *its_build_invall_cmd(struct its_cmd_block *cmd,
return NULL;
}
static struct its_vpe *its_build_vinvall_cmd(struct its_cmd_block *cmd,
static struct its_vpe *its_build_vinvall_cmd(struct its_node *its,
struct its_cmd_block *cmd,
struct its_cmd_desc *desc)
{
its_encode_cmd(cmd, GITS_CMD_VINVALL);
......@@ -535,17 +549,20 @@ static struct its_vpe *its_build_vinvall_cmd(struct its_cmd_block *cmd,
return desc->its_vinvall_cmd.vpe;
}
static struct its_vpe *its_build_vmapp_cmd(struct its_cmd_block *cmd,
static struct its_vpe *its_build_vmapp_cmd(struct its_node *its,
struct its_cmd_block *cmd,
struct its_cmd_desc *desc)
{
unsigned long vpt_addr;
u64 target;
vpt_addr = virt_to_phys(page_address(desc->its_vmapp_cmd.vpe->vpt_page));
target = desc->its_vmapp_cmd.col->target_address + its->vlpi_redist_offset;
its_encode_cmd(cmd, GITS_CMD_VMAPP);
its_encode_vpeid(cmd, desc->its_vmapp_cmd.vpe->vpe_id);
its_encode_valid(cmd, desc->its_vmapp_cmd.valid);
its_encode_target(cmd, desc->its_vmapp_cmd.col->target_address);
its_encode_target(cmd, target);
its_encode_vpt_addr(cmd, vpt_addr);
its_encode_vpt_size(cmd, LPI_NRBITS - 1);
......@@ -554,7 +571,8 @@ static struct its_vpe *its_build_vmapp_cmd(struct its_cmd_block *cmd,
return desc->its_vmapp_cmd.vpe;
}
static struct its_vpe *its_build_vmapti_cmd(struct its_cmd_block *cmd,
static struct its_vpe *its_build_vmapti_cmd(struct its_node *its,
struct its_cmd_block *cmd,
struct its_cmd_desc *desc)
{
u32 db;
......@@ -576,7 +594,8 @@ static struct its_vpe *its_build_vmapti_cmd(struct its_cmd_block *cmd,
return desc->its_vmapti_cmd.vpe;
}
static struct its_vpe *its_build_vmovi_cmd(struct its_cmd_block *cmd,
static struct its_vpe *its_build_vmovi_cmd(struct its_node *its,
struct its_cmd_block *cmd,
struct its_cmd_desc *desc)
{
u32 db;
......@@ -598,14 +617,18 @@ static struct its_vpe *its_build_vmovi_cmd(struct its_cmd_block *cmd,
return desc->its_vmovi_cmd.vpe;
}
static struct its_vpe *its_build_vmovp_cmd(struct its_cmd_block *cmd,
static struct its_vpe *its_build_vmovp_cmd(struct its_node *its,
struct its_cmd_block *cmd,
struct its_cmd_desc *desc)
{
u64 target;
target = desc->its_vmovp_cmd.col->target_address + its->vlpi_redist_offset;
its_encode_cmd(cmd, GITS_CMD_VMOVP);
its_encode_seq_num(cmd, desc->its_vmovp_cmd.seq_num);
its_encode_its_list(cmd, desc->its_vmovp_cmd.its_list);
its_encode_vpeid(cmd, desc->its_vmovp_cmd.vpe->vpe_id);
its_encode_target(cmd, desc->its_vmovp_cmd.col->target_address);
its_encode_target(cmd, target);
its_fixup_cmd(cmd);
......@@ -684,9 +707,9 @@ static void its_flush_cmd(struct its_node *its, struct its_cmd_block *cmd)
dsb(ishst);
}
static void its_wait_for_range_completion(struct its_node *its,
struct its_cmd_block *from,
struct its_cmd_block *to)
static int its_wait_for_range_completion(struct its_node *its,
struct its_cmd_block *from,
struct its_cmd_block *to)
{
u64 rd_idx, from_idx, to_idx;
u32 count = 1000000; /* 1s! */
......@@ -707,12 +730,15 @@ static void its_wait_for_range_completion(struct its_node *its,
count--;
if (!count) {
pr_err_ratelimited("ITS queue timeout\n");
return;
pr_err_ratelimited("ITS queue timeout (%llu %llu %llu)\n",
from_idx, to_idx, rd_idx);
return -1;
}
cpu_relax();
udelay(1);
}
return 0;
}
/* Warning, macro hell follows */
......@@ -732,7 +758,7 @@ void name(struct its_node *its, \
raw_spin_unlock_irqrestore(&its->lock, flags); \
return; \
} \
sync_obj = builder(cmd, desc); \
sync_obj = builder(its, cmd, desc); \
its_flush_cmd(its, cmd); \
\
if (sync_obj) { \
......@@ -740,7 +766,7 @@ void name(struct its_node *its, \
if (!sync_cmd) \
goto post; \
\
buildfn(sync_cmd, sync_obj); \
buildfn(its, sync_cmd, sync_obj); \
its_flush_cmd(its, sync_cmd); \
} \
\
......@@ -748,10 +774,12 @@ post: \
next_cmd = its_post_commands(its); \
raw_spin_unlock_irqrestore(&its->lock, flags); \
\
its_wait_for_range_completion(its, cmd, next_cmd); \
if (its_wait_for_range_completion(its, cmd, next_cmd)) \
pr_err_ratelimited("ITS cmd %ps failed\n", builder); \
}
static void its_build_sync_cmd(struct its_cmd_block *sync_cmd,
static void its_build_sync_cmd(struct its_node *its,
struct its_cmd_block *sync_cmd,
struct its_collection *sync_col)
{
its_encode_cmd(sync_cmd, GITS_CMD_SYNC);
......@@ -763,7 +791,8 @@ static void its_build_sync_cmd(struct its_cmd_block *sync_cmd,
static BUILD_SINGLE_CMD_FUNC(its_send_single_command, its_cmd_builder_t,
struct its_collection, its_build_sync_cmd)
static void its_build_vsync_cmd(struct its_cmd_block *sync_cmd,
static void its_build_vsync_cmd(struct its_node *its,
struct its_cmd_block *sync_cmd,
struct its_vpe *sync_vpe)
{
its_encode_cmd(sync_cmd, GITS_CMD_VSYNC);
......@@ -895,21 +924,16 @@ static void its_send_vmovi(struct its_device *dev, u32 id)
its_send_single_vcommand(dev->its, its_build_vmovi_cmd, &desc);
}
static void its_send_vmapp(struct its_vpe *vpe, bool valid)
static void its_send_vmapp(struct its_node *its,
struct its_vpe *vpe, bool valid)
{
struct its_cmd_desc desc;
struct its_node *its;
desc.its_vmapp_cmd.vpe = vpe;
desc.its_vmapp_cmd.valid = valid;
desc.its_vmapp_cmd.col = &its->collections[vpe->col_idx];
list_for_each_entry(its, &its_nodes, entry) {
if (!its->is_v4)
continue;
desc.its_vmapp_cmd.col = &its->collections[vpe->col_idx];
its_send_single_vcommand(its, its_build_vmapp_cmd, &desc);
}
its_send_single_vcommand(its, its_build_vmapp_cmd, &desc);
}
static void its_send_vmovp(struct its_vpe *vpe)
......@@ -947,6 +971,9 @@ static void its_send_vmovp(struct its_vpe *vpe)
if (!its->is_v4)
continue;
if (!vpe->its_vm->vlpi_count[its->list_nr])
continue;
desc.its_vmovp_cmd.col = &its->collections[col_id];
its_send_single_vcommand(its, its_build_vmovp_cmd, &desc);
}
......@@ -954,18 +981,12 @@ static void its_send_vmovp(struct its_vpe *vpe)
raw_spin_unlock_irqrestore(&vmovp_lock, flags);
}
static void its_send_vinvall(struct its_vpe *vpe)
static void its_send_vinvall(struct its_node *its, struct its_vpe *vpe)
{
struct its_cmd_desc desc;
struct its_node *its;
desc.its_vinvall_cmd.vpe = vpe;
list_for_each_entry(its, &its_nodes, entry) {
if (!its->is_v4)
continue;
its_send_single_vcommand(its, its_build_vinvall_cmd, &desc);
}
its_send_single_vcommand(its, its_build_vinvall_cmd, &desc);
}
/*
......@@ -1095,6 +1116,13 @@ static int its_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
return IRQ_SET_MASK_OK_DONE;
}
static u64 its_irq_get_msi_base(struct its_device *its_dev)
{
struct its_node *its = its_dev->its;
return its->phys_base + GITS_TRANSLATER;
}
static void its_irq_compose_msi_msg(struct irq_data *d, struct msi_msg *msg)
{
struct its_device *its_dev = irq_data_get_irq_chip_data(d);
......@@ -1102,7 +1130,7 @@ static void its_irq_compose_msi_msg(struct irq_data *d, struct msi_msg *msg)
u64 addr;
its = its_dev->its;
addr = its->phys_base + GITS_TRANSLATER;
addr = its->get_msi_base(its_dev);
msg->address_lo = lower_32_bits(addr);
msg->address_hi = upper_32_bits(addr);
......@@ -1129,6 +1157,60 @@ static int its_irq_set_irqchip_state(struct irq_data *d,
return 0;
}
static void its_map_vm(struct its_node *its, struct its_vm *vm)
{
unsigned long flags;
/* Not using the ITS list? Everything is always mapped. */
if (!its_list_map)
return;
raw_spin_lock_irqsave(&vmovp_lock, flags);
/*
* If the VM wasn't mapped yet, iterate over the vpes and get
* them mapped now.
*/
vm->vlpi_count[its->list_nr]++;
if (vm->vlpi_count[its->list_nr] == 1) {
int i;
for (i = 0; i < vm->nr_vpes; i++) {
struct its_vpe *vpe = vm->vpes[i];
struct irq_data *d = irq_get_irq_data(vpe->irq);
/* Map the VPE to the first possible CPU */
vpe->col_idx = cpumask_first(cpu_online_mask);
its_send_vmapp(its, vpe, true);
its_send_vinvall(its, vpe);
irq_data_update_effective_affinity(d, cpumask_of(vpe->col_idx));
}
}
raw_spin_unlock_irqrestore(&vmovp_lock, flags);
}
static void its_unmap_vm(struct its_node *its, struct its_vm *vm)
{
unsigned long flags;
/* Not using the ITS list? Everything is always mapped. */
if (!its_list_map)
return;
raw_spin_lock_irqsave(&vmovp_lock, flags);
if (!--vm->vlpi_count[its->list_nr]) {
int i;
for (i = 0; i < vm->nr_vpes; i++)
its_send_vmapp(its, vm->vpes[i], false);
}
raw_spin_unlock_irqrestore(&vmovp_lock, flags);
}
static int its_vlpi_map(struct irq_data *d, struct its_cmd_info *info)
{
struct its_device *its_dev = irq_data_get_irq_chip_data(d);
......@@ -1164,6 +1246,9 @@ static int its_vlpi_map(struct irq_data *d, struct its_cmd_info *info)
/* Already mapped, move it around */
its_send_vmovi(its_dev, event);
} else {
/* Ensure all the VPEs are mapped on this ITS */
its_map_vm(its_dev->its, info->map->vm);
/* Drop the physical mapping */
its_send_discard(its_dev, event);
......@@ -1225,6 +1310,9 @@ static int its_vlpi_unmap(struct irq_data *d)
LPI_PROP_ENABLED |
LPI_PROP_GROUP1));
/* Potentially unmap the VM from this ITS */
its_unmap_vm(its_dev->its, its_dev->event_map.vm);
/*
* Drop the refcount and make the device available again if
* this was the last VLPI.
......@@ -1650,23 +1738,14 @@ static void its_free_tables(struct its_node *its)
static int its_alloc_tables(struct its_node *its)
{
u64 typer = gic_read_typer(its->base + GITS_TYPER);
u32 ids = GITS_TYPER_DEVBITS(typer);
u64 shr = GITS_BASER_InnerShareable;
u64 cache = GITS_BASER_RaWaWb;
u32 psz = SZ_64K;
int err, i;
if (its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_22375) {
/*
* erratum 22375: only alloc 8MB table size
* erratum 24313: ignore memory access type
*/
cache = GITS_BASER_nCnB;
ids = 0x14; /* 20 bits, 8MB */
}
its->device_ids = ids;
if (its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_22375)
/* erratum 24313: ignore memory access type */
cache = GITS_BASER_nCnB;
for (i = 0; i < GITS_BASER_NR_REGS; i++) {
struct its_baser *baser = its->tables + i;
......@@ -2372,6 +2451,8 @@ static int its_vpe_set_affinity(struct irq_data *d,
its_vpe_db_proxy_move(vpe, from, cpu);
}
irq_data_update_effective_affinity(d, cpumask_of(cpu));
return IRQ_SET_MASK_OK_DONE;
}
......@@ -2439,6 +2520,26 @@ static void its_vpe_deschedule(struct its_vpe *vpe)
}
}
static void its_vpe_invall(struct its_vpe *vpe)
{
struct its_node *its;
list_for_each_entry(its, &its_nodes, entry) {
if (!its->is_v4)
continue;
if (its_list_map && !vpe->its_vm->vlpi_count[its->list_nr])
continue;
/*
* Sending a VINVALL to a single ITS is enough, as all
* we need is to reach the redistributors.
*/
its_send_vinvall(its, vpe);
return;
}
}
static int its_vpe_set_vcpu_affinity(struct irq_data *d, void *vcpu_info)
{
struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
......@@ -2454,7 +2555,7 @@ static int its_vpe_set_vcpu_affinity(struct irq_data *d, void *vcpu_info)
return 0;
case INVALL_VPE:
its_send_vinvall(vpe);
its_vpe_invall(vpe);
return 0;
default:
......@@ -2683,11 +2784,25 @@ static int its_vpe_irq_domain_activate(struct irq_domain *domain,
struct irq_data *d, bool early)
{
struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
struct its_node *its;
/* If we use the list map, we issue VMAPP on demand... */
if (its_list_map)
return true;
/* Map the VPE to the first possible CPU */
vpe->col_idx = cpumask_first(cpu_online_mask);
its_send_vmapp(vpe, true);
its_send_vinvall(vpe);
list_for_each_entry(its, &its_nodes, entry) {
if (!its->is_v4)
continue;
its_send_vmapp(its, vpe, true);
its_send_vinvall(its, vpe);
}
irq_data_update_effective_affinity(d, cpumask_of(vpe->col_idx));
return 0;
}
......@@ -2695,8 +2810,21 @@ static void its_vpe_irq_domain_deactivate(struct irq_domain *domain,
struct irq_data *d)
{
struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
struct its_node *its;
its_send_vmapp(vpe, false);
/*
* If we use the list map, we unmap the VPE once no VLPIs are
* associated with the VM.
*/
if (its_list_map)
return;
list_for_each_entry(its, &its_nodes, entry) {
if (!its->is_v4)
continue;
its_send_vmapp(its, vpe, false);
}
}
static const struct irq_domain_ops its_vpe_domain_ops = {
......@@ -2739,26 +2867,85 @@ static int its_force_quiescent(void __iomem *base)
}
}
static void __maybe_unused its_enable_quirk_cavium_22375(void *data)
static bool __maybe_unused its_enable_quirk_cavium_22375(void *data)
{
struct its_node *its = data;
/* erratum 22375: only alloc 8MB table size */
its->device_ids = 0x14; /* 20 bits, 8MB */
its->flags |= ITS_FLAGS_WORKAROUND_CAVIUM_22375;
return true;
}
static void __maybe_unused its_enable_quirk_cavium_23144(void *data)
static bool __maybe_unused its_enable_quirk_cavium_23144(void *data)
{
struct its_node *its = data;
its->flags |= ITS_FLAGS_WORKAROUND_CAVIUM_23144;
return true;
}
static void __maybe_unused its_enable_quirk_qdf2400_e0065(void *data)
static bool __maybe_unused its_enable_quirk_qdf2400_e0065(void *data)
{
struct its_node *its = data;
/* On QDF2400, the size of the ITE is 16Bytes */
its->ite_size = 16;
return true;
}
static u64 its_irq_get_msi_base_pre_its(struct its_device *its_dev)
{
struct its_node *its = its_dev->its;
/*
* The Socionext Synquacer SoC has a so-called 'pre-ITS',
* which maps 32-bit writes targeted at a separate window of
* size '4 << device_id_bits' onto writes to GITS_TRANSLATER
* with device ID taken from bits [device_id_bits + 1:2] of
* the window offset.
*/
return its->pre_its_base + (its_dev->device_id << 2);
}
static bool __maybe_unused its_enable_quirk_socionext_synquacer(void *data)
{
struct its_node *its = data;
u32 pre_its_window[2];
u32 ids;
if (!fwnode_property_read_u32_array(its->fwnode_handle,
"socionext,synquacer-pre-its",
pre_its_window,
ARRAY_SIZE(pre_its_window))) {
its->pre_its_base = pre_its_window[0];
its->get_msi_base = its_irq_get_msi_base_pre_its;
ids = ilog2(pre_its_window[1]) - 2;
if (its->device_ids > ids)
its->device_ids = ids;
/* the pre-ITS breaks isolation, so disable MSI remapping */
its->msi_domain_flags &= ~IRQ_DOMAIN_FLAG_MSI_REMAP;
return true;
}
return false;
}
static bool __maybe_unused its_enable_quirk_hip07_161600802(void *data)
{
struct its_node *its = data;
/*
* Hip07 insists on using the wrong address for the VLPI
* page. Trick it into doing the right thing...
*/
its->vlpi_redist_offset = SZ_128K;
return true;
}
static const struct gic_quirk its_quirks[] = {
......@@ -2785,6 +2972,27 @@ static const struct gic_quirk its_quirks[] = {
.mask = 0xffffffff,
.init = its_enable_quirk_qdf2400_e0065,
},
#endif
#ifdef CONFIG_SOCIONEXT_SYNQUACER_PREITS
{
/*
* The Socionext Synquacer SoC incorporates ARM's own GIC-500
* implementation, but with a 'pre-ITS' added that requires
* special handling in software.
*/
.desc = "ITS: Socionext Synquacer pre-ITS",
.iidr = 0x0001143b,
.mask = 0xffffffff,
.init = its_enable_quirk_socionext_synquacer,
},
#endif
#ifdef CONFIG_HISILICON_ERRATUM_161600802
{
.desc = "ITS: Hip07 erratum 161600802",
.iidr = 0x00000004,
.mask = 0xffffffff,
.init = its_enable_quirk_hip07_161600802,
},
#endif
{
}
......@@ -2814,7 +3022,7 @@ static int its_init_domain(struct fwnode_handle *handle, struct its_node *its)
inner_domain->parent = its_parent;
irq_domain_update_bus_token(inner_domain, DOMAIN_BUS_NEXUS);
inner_domain->flags |= IRQ_DOMAIN_FLAG_MSI_REMAP;
inner_domain->flags |= its->msi_domain_flags;
info->ops = &its_msi_domain_ops;
info->data = its;
inner_domain->host_data = info;
......@@ -2875,8 +3083,8 @@ static int __init its_compute_its_list_map(struct resource *res,
* locking. Should this change, we should address
* this.
*/
its_number = find_first_zero_bit(&its_list_map, ITS_LIST_MAX);
if (its_number >= ITS_LIST_MAX) {
its_number = find_first_zero_bit(&its_list_map, GICv4_ITS_LIST_MAX);
if (its_number >= GICv4_ITS_LIST_MAX) {
pr_err("ITS@%pa: No ITSList entry available!\n",
&res->start);
return -EINVAL;
......@@ -2944,6 +3152,7 @@ static int __init its_probe_one(struct resource *res,
its->base = its_base;
its->phys_base = res->start;
its->ite_size = GITS_TYPER_ITT_ENTRY_SIZE(typer);
its->device_ids = GITS_TYPER_DEVBITS(typer);
its->is_v4 = !!(typer & GITS_TYPER_VLPIS);
if (its->is_v4) {
if (!(typer & GITS_TYPER_VMOVP)) {
......@@ -2951,6 +3160,8 @@ static int __init its_probe_one(struct resource *res,
if (err < 0)
goto out_free_its;
its->list_nr = err;
pr_info("ITS@%pa: Using ITS number %d\n",
&res->start, err);
} else {
......@@ -2967,6 +3178,9 @@ static int __init its_probe_one(struct resource *res,
goto out_free_its;
}
its->cmd_write = its->cmd_base;
its->fwnode_handle = handle;
its->get_msi_base = its_irq_get_msi_base;
its->msi_domain_flags = IRQ_DOMAIN_FLAG_MSI_REMAP;
its_enable_quirks(its);
......
......@@ -55,6 +55,7 @@ struct gic_chip_data {
struct irq_domain *domain;
u64 redist_stride;
u32 nr_redist_regions;
bool has_rss;
unsigned int irq_nr;
struct partition_desc *ppi_descs[16];
};
......@@ -63,7 +64,9 @@ static struct gic_chip_data gic_data __read_mostly;
static struct static_key supports_deactivate = STATIC_KEY_INIT_TRUE;
static struct gic_kvm_info gic_v3_kvm_info;
static DEFINE_PER_CPU(bool, has_rss);
#define MPIDR_RS(mpidr) (((mpidr) & 0xF0UL) >> 4)
#define gic_data_rdist() (this_cpu_ptr(gic_data.rdists.rdist))
#define gic_data_rdist_rd_base() (gic_data_rdist()->rd_base)
#define gic_data_rdist_sgi_base() (gic_data_rdist_rd_base() + SZ_64K)
......@@ -526,6 +529,10 @@ static void gic_update_vlpi_properties(void)
static void gic_cpu_sys_reg_init(void)
{
int i, cpu = smp_processor_id();
u64 mpidr = cpu_logical_map(cpu);
u64 need_rss = MPIDR_RS(mpidr);
/*
* Need to check that the SRE bit has actually been set. If
* not, it means that SRE is disabled at EL2. We're going to
......@@ -557,6 +564,30 @@ static void gic_cpu_sys_reg_init(void)
/* ... and let's hit the road... */
gic_write_grpen1(1);
/* Keep the RSS capability status in per_cpu variable */
per_cpu(has_rss, cpu) = !!(gic_read_ctlr() & ICC_CTLR_EL1_RSS);
/* Check all the CPUs have capable of sending SGIs to other CPUs */
for_each_online_cpu(i) {
bool have_rss = per_cpu(has_rss, i) && per_cpu(has_rss, cpu);
need_rss |= MPIDR_RS(cpu_logical_map(i));
if (need_rss && (!have_rss))
pr_crit("CPU%d (%lx) can't SGI CPU%d (%lx), no RSS\n",
cpu, (unsigned long)mpidr,
i, (unsigned long)cpu_logical_map(i));
}
/**
* GIC spec says, when ICC_CTLR_EL1.RSS==1 and GICD_TYPER.RSS==0,
* writing ICC_ASGI1R_EL1 register with RS != 0 is a CONSTRAINED
* UNPREDICTABLE choice of :
* - The write is ignored.
* - The RS field is treated as 0.
*/
if (need_rss && (!gic_data.has_rss))
pr_crit_once("RSS is required but GICD doesn't support it\n");
}
static int gic_dist_supports_lpis(void)
......@@ -591,6 +622,9 @@ static void gic_cpu_init(void)
#ifdef CONFIG_SMP
#define MPIDR_TO_SGI_RS(mpidr) (MPIDR_RS(mpidr) << ICC_SGI1R_RS_SHIFT)
#define MPIDR_TO_SGI_CLUSTER_ID(mpidr) ((mpidr) & ~0xFUL)
static int gic_starting_cpu(unsigned int cpu)
{
gic_cpu_init();
......@@ -605,13 +639,6 @@ static u16 gic_compute_target_list(int *base_cpu, const struct cpumask *mask,
u16 tlist = 0;
while (cpu < nr_cpu_ids) {
/*
* If we ever get a cluster of more than 16 CPUs, just
* scream and skip that CPU.
*/
if (WARN_ON((mpidr & 0xff) >= 16))
goto out;
tlist |= 1 << (mpidr & 0xf);
next_cpu = cpumask_next(cpu, mask);
......@@ -621,7 +648,7 @@ static u16 gic_compute_target_list(int *base_cpu, const struct cpumask *mask,
mpidr = cpu_logical_map(cpu);
if (cluster_id != (mpidr & ~0xffUL)) {
if (cluster_id != MPIDR_TO_SGI_CLUSTER_ID(mpidr)) {
cpu--;
goto out;
}
......@@ -643,6 +670,7 @@ static void gic_send_sgi(u64 cluster_id, u16 tlist, unsigned int irq)
MPIDR_TO_SGI_AFFINITY(cluster_id, 2) |
irq << ICC_SGI1R_SGI_ID_SHIFT |
MPIDR_TO_SGI_AFFINITY(cluster_id, 1) |
MPIDR_TO_SGI_RS(cluster_id) |
tlist << ICC_SGI1R_TARGET_LIST_SHIFT);
pr_debug("CPU%d: ICC_SGI1R_EL1 %llx\n", smp_processor_id(), val);
......@@ -663,7 +691,7 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
smp_wmb();
for_each_cpu(cpu, mask) {
unsigned long cluster_id = cpu_logical_map(cpu) & ~0xffUL;
u64 cluster_id = MPIDR_TO_SGI_CLUSTER_ID(cpu_logical_map(cpu));
u16 tlist;
tlist = gic_compute_target_list(&cpu, mask, cluster_id);
......@@ -1007,6 +1035,10 @@ static int __init gic_init_bases(void __iomem *dist_base,
goto out_free;
}
gic_data.has_rss = !!(typer & GICD_TYPER_RSS);
pr_info("Distributor has %sRange Selector support\n",
gic_data.has_rss ? "" : "no ");
set_handle_irq(gic_handle_irq);
gic_update_vlpi_properties();
......
/*
* Copyright (c) 2015 Endless Mobile, Inc.
* Author: Carlo Caione <carlo@endlessm.com>
* Copyright (c) 2016 BayLibre, SAS.
* Author: Jerome Brunet <jbrunet@baylibre.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/io.h>
#include <linux/module.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/irqchip.h>
#include <linux/of.h>
#include <linux/of_address.h>
#define NUM_CHANNEL 8
#define MAX_INPUT_MUX 256
#define REG_EDGE_POL 0x00
#define REG_PIN_03_SEL 0x04
#define REG_PIN_47_SEL 0x08
#define REG_FILTER_SEL 0x0c
#define REG_EDGE_POL_MASK(x) (BIT(x) | BIT(16 + (x)))
#define REG_EDGE_POL_EDGE(x) BIT(x)
#define REG_EDGE_POL_LOW(x) BIT(16 + (x))
#define REG_PIN_SEL_SHIFT(x) (((x) % 4) * 8)
#define REG_FILTER_SEL_SHIFT(x) ((x) * 4)
struct meson_gpio_irq_params {
unsigned int nr_hwirq;
};
static const struct meson_gpio_irq_params meson8b_params = {
.nr_hwirq = 119,
};
static const struct meson_gpio_irq_params gxbb_params = {
.nr_hwirq = 133,
};
static const struct meson_gpio_irq_params gxl_params = {
.nr_hwirq = 110,
};
static const struct of_device_id meson_irq_gpio_matches[] = {
{ .compatible = "amlogic,meson8b-gpio-intc", .data = &meson8b_params },
{ .compatible = "amlogic,meson-gxbb-gpio-intc", .data = &gxbb_params },
{ .compatible = "amlogic,meson-gxl-gpio-intc", .data = &gxl_params },
{ }
};
struct meson_gpio_irq_controller {
unsigned int nr_hwirq;
void __iomem *base;
u32 channel_irqs[NUM_CHANNEL];
DECLARE_BITMAP(channel_map, NUM_CHANNEL);
spinlock_t lock;
};
static void meson_gpio_irq_update_bits(struct meson_gpio_irq_controller *ctl,
unsigned int reg, u32 mask, u32 val)
{
u32 tmp;
tmp = readl_relaxed(ctl->base + reg);
tmp &= ~mask;
tmp |= val;
writel_relaxed(tmp, ctl->base + reg);
}
static unsigned int meson_gpio_irq_channel_to_reg(unsigned int channel)
{
return (channel < 4) ? REG_PIN_03_SEL : REG_PIN_47_SEL;
}
static int
meson_gpio_irq_request_channel(struct meson_gpio_irq_controller *ctl,
unsigned long hwirq,
u32 **channel_hwirq)
{
unsigned int reg, idx;
spin_lock(&ctl->lock);
/* Find a free channel */
idx = find_first_zero_bit(ctl->channel_map, NUM_CHANNEL);
if (idx >= NUM_CHANNEL) {
spin_unlock(&ctl->lock);
pr_err("No channel available\n");
return -ENOSPC;
}
/* Mark the channel as used */
set_bit(idx, ctl->channel_map);
/*
* Setup the mux of the channel to route the signal of the pad
* to the appropriate input of the GIC
*/
reg = meson_gpio_irq_channel_to_reg(idx);
meson_gpio_irq_update_bits(ctl, reg,
0xff << REG_PIN_SEL_SHIFT(idx),
hwirq << REG_PIN_SEL_SHIFT(idx));
/*
* Get the hwirq number assigned to this channel through
* a pointer the channel_irq table. The added benifit of this
* method is that we can also retrieve the channel index with
* it, using the table base.
*/
*channel_hwirq = &(ctl->channel_irqs[idx]);
spin_unlock(&ctl->lock);
pr_debug("hwirq %lu assigned to channel %d - irq %u\n",
hwirq, idx, **channel_hwirq);
return 0;
}
static unsigned int
meson_gpio_irq_get_channel_idx(struct meson_gpio_irq_controller *ctl,
u32 *channel_hwirq)
{
return channel_hwirq - ctl->channel_irqs;
}
static void
meson_gpio_irq_release_channel(struct meson_gpio_irq_controller *ctl,
u32 *channel_hwirq)
{
unsigned int idx;
idx = meson_gpio_irq_get_channel_idx(ctl, channel_hwirq);
clear_bit(idx, ctl->channel_map);
}
static int meson_gpio_irq_type_setup(struct meson_gpio_irq_controller *ctl,
unsigned int type,
u32 *channel_hwirq)
{
u32 val = 0;
unsigned int idx;
idx = meson_gpio_irq_get_channel_idx(ctl, channel_hwirq);
/*
* The controller has a filter block to operate in either LEVEL or
* EDGE mode, then signal is sent to the GIC. To enable LEVEL_LOW and
* EDGE_FALLING support (which the GIC does not support), the filter
* block is also able to invert the input signal it gets before
* providing it to the GIC.
*/
type &= IRQ_TYPE_SENSE_MASK;
if (type == IRQ_TYPE_EDGE_BOTH)
return -EINVAL;
if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING))
val |= REG_EDGE_POL_EDGE(idx);
if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_EDGE_FALLING))
val |= REG_EDGE_POL_LOW(idx);
spin_lock(&ctl->lock);
meson_gpio_irq_update_bits(ctl, REG_EDGE_POL,
REG_EDGE_POL_MASK(idx), val);
spin_unlock(&ctl->lock);
return 0;
}
static unsigned int meson_gpio_irq_type_output(unsigned int type)
{
unsigned int sense = type & IRQ_TYPE_SENSE_MASK;
type &= ~IRQ_TYPE_SENSE_MASK;
/*
* The polarity of the signal provided to the GIC should always
* be high.
*/
if (sense & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
type |= IRQ_TYPE_LEVEL_HIGH;
else if (sense & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING))
type |= IRQ_TYPE_EDGE_RISING;
return type;
}
static int meson_gpio_irq_set_type(struct irq_data *data, unsigned int type)
{
struct meson_gpio_irq_controller *ctl = data->domain->host_data;
u32 *channel_hwirq = irq_data_get_irq_chip_data(data);
int ret;
ret = meson_gpio_irq_type_setup(ctl, type, channel_hwirq);
if (ret)
return ret;
return irq_chip_set_type_parent(data,
meson_gpio_irq_type_output(type));
}
static struct irq_chip meson_gpio_irq_chip = {
.name = "meson-gpio-irqchip",
.irq_mask = irq_chip_mask_parent,
.irq_unmask = irq_chip_unmask_parent,
.irq_eoi = irq_chip_eoi_parent,
.irq_set_type = meson_gpio_irq_set_type,
.irq_retrigger = irq_chip_retrigger_hierarchy,
#ifdef CONFIG_SMP
.irq_set_affinity = irq_chip_set_affinity_parent,
#endif
.flags = IRQCHIP_SET_TYPE_MASKED,
};
static int meson_gpio_irq_domain_translate(struct irq_domain *domain,
struct irq_fwspec *fwspec,
unsigned long *hwirq,
unsigned int *type)
{
if (is_of_node(fwspec->fwnode) && fwspec->param_count == 2) {
*hwirq = fwspec->param[0];
*type = fwspec->param[1];
return 0;
}
return -EINVAL;
}
static int meson_gpio_irq_allocate_gic_irq(struct irq_domain *domain,
unsigned int virq,
u32 hwirq,
unsigned int type)
{
struct irq_fwspec fwspec;
fwspec.fwnode = domain->parent->fwnode;
fwspec.param_count = 3;
fwspec.param[0] = 0; /* SPI */
fwspec.param[1] = hwirq;
fwspec.param[2] = meson_gpio_irq_type_output(type);
return irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
}
static int meson_gpio_irq_domain_alloc(struct irq_domain *domain,
unsigned int virq,
unsigned int nr_irqs,
void *data)
{
struct irq_fwspec *fwspec = data;
struct meson_gpio_irq_controller *ctl = domain->host_data;
unsigned long hwirq;
u32 *channel_hwirq;
unsigned int type;
int ret;
if (WARN_ON(nr_irqs != 1))
return -EINVAL;
ret = meson_gpio_irq_domain_translate(domain, fwspec, &hwirq, &type);
if (ret)
return ret;
ret = meson_gpio_irq_request_channel(ctl, hwirq, &channel_hwirq);
if (ret)
return ret;
ret = meson_gpio_irq_allocate_gic_irq(domain, virq,
*channel_hwirq, type);
if (ret < 0) {
pr_err("failed to allocate gic irq %u\n", *channel_hwirq);
meson_gpio_irq_release_channel(ctl, channel_hwirq);
return ret;
}
irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
&meson_gpio_irq_chip, channel_hwirq);
return 0;
}
static void meson_gpio_irq_domain_free(struct irq_domain *domain,
unsigned int virq,
unsigned int nr_irqs)
{
struct meson_gpio_irq_controller *ctl = domain->host_data;
struct irq_data *irq_data;
u32 *channel_hwirq;
if (WARN_ON(nr_irqs != 1))
return;
irq_domain_free_irqs_parent(domain, virq, 1);
irq_data = irq_domain_get_irq_data(domain, virq);
channel_hwirq = irq_data_get_irq_chip_data(irq_data);
meson_gpio_irq_release_channel(ctl, channel_hwirq);
}
static const struct irq_domain_ops meson_gpio_irq_domain_ops = {
.alloc = meson_gpio_irq_domain_alloc,
.free = meson_gpio_irq_domain_free,
.translate = meson_gpio_irq_domain_translate,
};
static int __init meson_gpio_irq_parse_dt(struct device_node *node,
struct meson_gpio_irq_controller *ctl)
{
const struct of_device_id *match;
const struct meson_gpio_irq_params *params;
int ret;
match = of_match_node(meson_irq_gpio_matches, node);
if (!match)
return -ENODEV;
params = match->data;
ctl->nr_hwirq = params->nr_hwirq;
ret = of_property_read_variable_u32_array(node,
"amlogic,channel-interrupts",
ctl->channel_irqs,
NUM_CHANNEL,
NUM_CHANNEL);
if (ret < 0) {
pr_err("can't get %d channel interrupts\n", NUM_CHANNEL);
return ret;
}
return 0;
}
static int __init meson_gpio_irq_of_init(struct device_node *node,
struct device_node *parent)
{
struct irq_domain *domain, *parent_domain;
struct meson_gpio_irq_controller *ctl;
int ret;
if (!parent) {
pr_err("missing parent interrupt node\n");
return -ENODEV;
}
parent_domain = irq_find_host(parent);
if (!parent_domain) {
pr_err("unable to obtain parent domain\n");
return -ENXIO;
}
ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
if (!ctl)
return -ENOMEM;
spin_lock_init(&ctl->lock);
ctl->base = of_iomap(node, 0);
if (!ctl->base) {
ret = -ENOMEM;
goto free_ctl;
}
ret = meson_gpio_irq_parse_dt(node, ctl);
if (ret)
goto free_channel_irqs;
domain = irq_domain_create_hierarchy(parent_domain, 0, ctl->nr_hwirq,
of_node_to_fwnode(node),
&meson_gpio_irq_domain_ops,
ctl);
if (!domain) {
pr_err("failed to add domain\n");
ret = -ENODEV;
goto free_channel_irqs;
}
pr_info("%d to %d gpio interrupt mux initialized\n",
ctl->nr_hwirq, NUM_CHANNEL);
return 0;
free_channel_irqs:
iounmap(ctl->base);
free_ctl:
kfree(ctl);
return ret;
}
IRQCHIP_DECLARE(meson_gpio_intc, "amlogic,meson-gpio-intc",
meson_gpio_irq_of_init);
......@@ -389,9 +389,8 @@ MODULE_DEVICE_TABLE(of, intc_irqpin_dt_ids);
static int intc_irqpin_probe(struct platform_device *pdev)
{
const struct intc_irqpin_config *config = NULL;
const struct intc_irqpin_config *config;
struct device *dev = &pdev->dev;
const struct of_device_id *of_id;
struct intc_irqpin_priv *p;
struct intc_irqpin_iomem *i;
struct resource *io[INTC_IRQPIN_REG_NR];
......@@ -422,11 +421,9 @@ static int intc_irqpin_probe(struct platform_device *pdev)
p->pdev = pdev;
platform_set_drvdata(pdev, p);
of_id = of_match_device(intc_irqpin_dt_ids, dev);
if (of_id && of_id->data) {
config = of_id->data;
config = of_device_get_match_data(dev);
if (config)
p->needs_clk = config->needs_clk;
}
p->clk = devm_clk_get(dev, NULL);
if (IS_ERR(p->clk)) {
......
......@@ -68,6 +68,7 @@
#define GICD_CTLR_ENABLE_SS_G1 (1U << 1)
#define GICD_CTLR_ENABLE_SS_G0 (1U << 0)
#define GICD_TYPER_RSS (1U << 26)
#define GICD_TYPER_LPIS (1U << 17)
#define GICD_TYPER_MBIS (1U << 16)
......@@ -459,6 +460,7 @@
#define ICC_CTLR_EL1_SEIS_MASK (0x1 << ICC_CTLR_EL1_SEIS_SHIFT)
#define ICC_CTLR_EL1_A3V_SHIFT 15
#define ICC_CTLR_EL1_A3V_MASK (0x1 << ICC_CTLR_EL1_A3V_SHIFT)
#define ICC_CTLR_EL1_RSS (0x1 << 18)
#define ICC_PMR_EL1_SHIFT 0
#define ICC_PMR_EL1_MASK (0xff << ICC_PMR_EL1_SHIFT)
#define ICC_BPR0_EL1_SHIFT 0
......@@ -547,6 +549,8 @@
#define ICC_SGI1R_AFFINITY_2_SHIFT 32
#define ICC_SGI1R_AFFINITY_2_MASK (0xffULL << ICC_SGI1R_AFFINITY_2_SHIFT)
#define ICC_SGI1R_IRQ_ROUTING_MODE_BIT 40
#define ICC_SGI1R_RS_SHIFT 44
#define ICC_SGI1R_RS_MASK (0xfULL << ICC_SGI1R_RS_SHIFT)
#define ICC_SGI1R_AFFINITY_3_SHIFT 48
#define ICC_SGI1R_AFFINITY_3_MASK (0xffULL << ICC_SGI1R_AFFINITY_3_SHIFT)
......
......@@ -20,6 +20,12 @@
struct its_vpe;
/*
* Maximum number of ITTs when GITS_TYPER.VMOVP == 0, using the
* ITSList mechanism to perform inter-ITS synchronization.
*/
#define GICv4_ITS_LIST_MAX 16
/* Embedded in kvm.arch */
struct its_vm {
struct fwnode_handle *fwnode;
......@@ -30,6 +36,7 @@ struct its_vm {
irq_hw_number_t db_lpi_base;
unsigned long *db_bitmap;
int nr_db_lpis;
u32 vlpi_count[GICv4_ITS_LIST_MAX];
};
/* Embedded in kvm_vcpu.arch */
......
......@@ -32,6 +32,7 @@
#include <linux/types.h>
#include <linux/irqhandler.h>
#include <linux/of.h>
#include <linux/mutex.h>
#include <linux/radix-tree.h>
struct device_node;
......@@ -176,6 +177,7 @@ struct irq_domain {
unsigned int revmap_direct_max_irq;
unsigned int revmap_size;
struct radix_tree_root revmap_tree;
struct mutex revmap_tree_mutex;
unsigned int linear_revmap[];
};
......
......@@ -21,7 +21,6 @@
static LIST_HEAD(irq_domain_list);
static DEFINE_MUTEX(irq_domain_mutex);
static DEFINE_MUTEX(revmap_trees_mutex);
static struct irq_domain *irq_default_domain;
static void irq_domain_check_hierarchy(struct irq_domain *domain);
......@@ -211,6 +210,7 @@ struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, int size,
/* Fill structure */
INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL);
mutex_init(&domain->revmap_tree_mutex);
domain->ops = ops;
domain->host_data = host_data;
domain->hwirq_max = hwirq_max;
......@@ -462,9 +462,9 @@ static void irq_domain_clear_mapping(struct irq_domain *domain,
if (hwirq < domain->revmap_size) {
domain->linear_revmap[hwirq] = 0;
} else {
mutex_lock(&revmap_trees_mutex);
mutex_lock(&domain->revmap_tree_mutex);
radix_tree_delete(&domain->revmap_tree, hwirq);
mutex_unlock(&revmap_trees_mutex);
mutex_unlock(&domain->revmap_tree_mutex);
}
}
......@@ -475,9 +475,9 @@ static void irq_domain_set_mapping(struct irq_domain *domain,
if (hwirq < domain->revmap_size) {
domain->linear_revmap[hwirq] = irq_data->irq;
} else {
mutex_lock(&revmap_trees_mutex);
mutex_lock(&domain->revmap_tree_mutex);
radix_tree_insert(&domain->revmap_tree, hwirq, irq_data);
mutex_unlock(&revmap_trees_mutex);
mutex_unlock(&domain->revmap_tree_mutex);
}
}
......@@ -945,7 +945,7 @@ static int virq_debug_show(struct seq_file *m, void *private)
struct irq_desc *desc;
struct irq_domain *domain;
struct radix_tree_iter iter;
void **slot;
void __rcu **slot;
int i;
seq_printf(m, " %-16s %-6s %-10s %-10s %s\n",
......@@ -1453,17 +1453,17 @@ int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
/* The irq_data was moved, fix the revmap to refer to the new location */
static void irq_domain_fix_revmap(struct irq_data *d)
{
void **slot;
void __rcu **slot;
if (d->hwirq < d->domain->revmap_size)
return; /* Not using radix tree. */
/* Fix up the revmap. */
mutex_lock(&revmap_trees_mutex);
mutex_lock(&d->domain->revmap_tree_mutex);
slot = radix_tree_lookup_slot(&d->domain->revmap_tree, d->hwirq);
if (slot)
radix_tree_replace_slot(&d->domain->revmap_tree, slot, d);
mutex_unlock(&revmap_trees_mutex);
mutex_unlock(&d->domain->revmap_tree_mutex);
}
/**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment