2019-08-06 10:03:54

by Marc Zyngier

[permalink] [raw]
Subject: [PATCH v2 00/12] irqchip/gic-v3: Add support for GICv3.1 extended PPI/SPI ranges

Apparently, having ~1000 wired interrupts is not enough, and some
people need more. Fear not! The GIC Achitecture Department hereby
grants you another 1024 SPIs, together with 64 PPIs, provided that you
implement GICv3.1 (see [1] for the details)

This series implements the required support, which requires a bit of
infrastructure rework in order to make the thing less horrible...

This has been tested on a FastModel. If there is no additional issue being
reported, I plan to put this into -next toward the end of this week and
let it simmer there for a bit.

[1] https://developer.arm.com/docs/ihi0069/latest (version E)

* From v1:
- Tighten ESPI range matching
- Added a warning to detect inconsistent distributor/cpu interface
configurations
- Added quirks to handle HIP06/07 erratum 161010803 which unexpectedly
advertise ESPI support

Marc Zyngier (12):
irqchip/gic: Rework gic_configure_irq to take the full ICFGR base
irqchip/gic-v3: Add INTID range and convertion primitives
dt-bindings: interrupt-controller: arm,gic-v3: Describe ESPI range
support
irqchip/gic-v3: Add ESPI range support
irqchip/gic: Prepare for more than 16 PPIs
irqchip/gic-v3: Dynamically allocate PPI NMI refcounts
irqchip/gic-v3: Dynamically allocate PPI partition descriptors
dt-bindings: interrupt-controller: arm,gic-v3: Describe EPPI range
support
irqchip/gic-v3: Add EPPI range support
irqchip/gic-v3: Warn about inconsistent implementations of extended
ranges
irqchip/gic: Skip DT quirks when evaluating IIDR-based quirks
irqchip/gic-v3: Add quirks for HIP06/07 invalid GICD_TYPER erratum
161010803

Documentation/arm64/silicon-errata.rst | 2 +
.../interrupt-controller/arm,gic-v3.yaml | 6 +-
drivers/irqchip/irq-gic-common.c | 35 +-
drivers/irqchip/irq-gic-common.h | 2 +-
drivers/irqchip/irq-gic-v3.c | 380 ++++++++++++++----
drivers/irqchip/irq-gic.c | 12 +-
drivers/irqchip/irq-hip04.c | 9 +-
include/linux/irqchip/arm-gic-v3.h | 30 +-
8 files changed, 372 insertions(+), 104 deletions(-)

--
2.20.1


2019-08-06 10:04:30

by Marc Zyngier

[permalink] [raw]
Subject: [PATCH v2 12/12] irqchip/gic-v3: Add quirks for HIP06/07 invalid GICD_TYPER erratum 161010803

It looks like the HIP06/07 SoCs have extra bits in their GICD_TYPER
registers, which confuse the GICv3.1 code (these systems appear to
expose ESPIs while they actually don't).

Detect these systems as early as possible and wipe the fields that
should be RES0 in the register.

Signed-off-by: Marc Zyngier <[email protected]>
---
Documentation/arm64/silicon-errata.rst | 2 +
drivers/irqchip/irq-gic-v3.c | 54 +++++++++++++++++++++-----
2 files changed, 46 insertions(+), 10 deletions(-)

diff --git a/Documentation/arm64/silicon-errata.rst b/Documentation/arm64/silicon-errata.rst
index 3e57d09246e6..17ea3fecddaa 100644
--- a/Documentation/arm64/silicon-errata.rst
+++ b/Documentation/arm64/silicon-errata.rst
@@ -115,6 +115,8 @@ stable kernels.
+----------------+-----------------+-----------------+-----------------------------+
| Hisilicon | Hip0{6,7} | #161010701 | N/A |
+----------------+-----------------+-----------------+-----------------------------+
+| Hisilicon | Hip0{6,7} | #161010803 | N/A |
++----------------+-----------------+-----------------+-----------------------------+
| Hisilicon | Hip07 | #161600802 | HISILICON_ERRATUM_161600802 |
+----------------+-----------------+-----------------+-----------------------------+
| Hisilicon | Hip08 SMMU PMCG | #162001800 | N/A |
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 334a10d9dbfb..bee141613b67 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -1441,6 +1441,46 @@ static bool gic_enable_quirk_msm8996(void *data)
return true;
}

+static bool gic_enable_quirk_hip06_07(void *data)
+{
+ struct gic_chip_data *d = data;
+
+ /*
+ * HIP06 GICD_IIDR clashes with GIC-600 product number (despite
+ * not being an actual ARM implementation). The saving grace is
+ * that GIC-600 doesn't have ESPI, so nothing to do in that case.
+ * HIP07 doesn't even have a proper IIDR, and still pretends to
+ * have ESPI. In both cases, put them right.
+ */
+ if (d->rdists.gicd_typer & GICD_TYPER_ESPI) {
+ /* Zero both ESPI and the RES0 field next to it... */
+ d->rdists.gicd_typer &= ~GENMASK(9, 8);
+ return true;
+ }
+
+ return false;
+}
+
+static const struct gic_quirk gic_quirks[] = {
+ {
+ .desc = "GICv3: Qualcomm MSM8996 broken firmware",
+ .compatible = "qcom,msm8996-gic-v3",
+ .init = gic_enable_quirk_msm8996,
+ },
+ {
+ .desc = "GICv3: HIP06 erratum 161010803",
+ .iidr = 0x0204043b,
+ .init = gic_enable_quirk_hip06_07,
+ },
+ {
+ .desc = "GICv3: HIP07 erratum 161010803",
+ .iidr = 0x00000000,
+ .init = gic_enable_quirk_hip06_07,
+ },
+ {
+ }
+};
+
static void gic_enable_nmi_support(void)
{
int i;
@@ -1494,6 +1534,10 @@ static int __init gic_init_bases(void __iomem *dist_base,
*/
typer = readl_relaxed(gic_data.dist_base + GICD_TYPER);
gic_data.rdists.gicd_typer = typer;
+
+ gic_enable_quirks(readl_relaxed(gic_data.dist_base + GICD_IIDR),
+ gic_quirks, &gic_data);
+
pr_info("%d SPIs implemented\n", GIC_LINE_NR - 32);
pr_info("%d Extended SPIs implemented\n", GIC_ESPI_NR);
gic_data.domain = irq_domain_create_tree(handle, &gic_irq_domain_ops,
@@ -1676,16 +1720,6 @@ static void __init gic_of_setup_kvm_info(struct device_node *node)
gic_set_kvm_info(&gic_v3_kvm_info);
}

-static const struct gic_quirk gic_quirks[] = {
- {
- .desc = "GICv3: Qualcomm MSM8996 broken firmware",
- .compatible = "qcom,msm8996-gic-v3",
- .init = gic_enable_quirk_msm8996,
- },
- {
- }
-};
-
static int __init gic_of_init(struct device_node *node, struct device_node *parent)
{
void __iomem *dist_base;
--
2.20.1

2019-08-06 10:04:38

by Marc Zyngier

[permalink] [raw]
Subject: [PATCH v2 11/12] irqchip/gic: Skip DT quirks when evaluating IIDR-based quirks

When evaluating potential quirks matched by reads of the IIDR
register, skip the quirk entries that use a "compatible"
property attached to them, as these are DT based.

Signed-off-by: Marc Zyngier <[email protected]>
---
drivers/irqchip/irq-gic-common.c | 2 ++
1 file changed, 2 insertions(+)

diff --git a/drivers/irqchip/irq-gic-common.c b/drivers/irqchip/irq-gic-common.c
index 14110db01c05..82520006195d 100644
--- a/drivers/irqchip/irq-gic-common.c
+++ b/drivers/irqchip/irq-gic-common.c
@@ -41,6 +41,8 @@ void gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks,
void *data)
{
for (; quirks->desc; quirks++) {
+ if (quirks->compatible)
+ continue;
if (quirks->iidr != (quirks->mask & iidr))
continue;
if (quirks->init(data))
--
2.20.1

2019-08-06 10:04:49

by Marc Zyngier

[permalink] [raw]
Subject: [PATCH v2 06/12] irqchip/gic-v3: Dynamically allocate PPI NMI refcounts

As we're about to have a variable number of PPIs, let's make the
allocation of the NMI refcounts dynamic. Also apply some minor
cleanups (moving things around).

Signed-off-by: Marc Zyngier <[email protected]>
---
drivers/irqchip/irq-gic-v3.c | 47 ++++++++++++++++++++++++++----------
1 file changed, 34 insertions(+), 13 deletions(-)

diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index e03fb6d7c2ce..4253c7f67c86 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -88,7 +88,7 @@ static DEFINE_STATIC_KEY_TRUE(supports_deactivate_key);
static DEFINE_STATIC_KEY_FALSE(supports_pseudo_nmis);

/* ppi_nmi_refs[n] == number of cpus having ppi[n + 16] set as NMI */
-static refcount_t ppi_nmi_refs[16];
+static refcount_t *ppi_nmi_refs;

static struct gic_kvm_info gic_v3_kvm_info;
static DEFINE_PER_CPU(bool, has_rss);
@@ -409,6 +409,16 @@ static void gic_irq_set_prio(struct irq_data *d, u8 prio)
writeb_relaxed(prio, base + offset + index);
}

+static u32 gic_get_ppi_index(struct irq_data *d)
+{
+ switch (get_intid_range(d)) {
+ case PPI_RANGE:
+ return d->hwirq - 16;
+ default:
+ unreachable();
+ }
+}
+
static int gic_irq_nmi_setup(struct irq_data *d)
{
struct irq_desc *desc = irq_to_desc(d->irq);
@@ -429,10 +439,12 @@ static int gic_irq_nmi_setup(struct irq_data *d)
return -EINVAL;

/* desc lock should already be held */
- if (gic_irq(d) < 32) {
+ if (gic_irq_in_rdist(d)) {
+ u32 idx = gic_get_ppi_index(d);
+
/* Setting up PPI as NMI, only switch handler for first NMI */
- if (!refcount_inc_not_zero(&ppi_nmi_refs[gic_irq(d) - 16])) {
- refcount_set(&ppi_nmi_refs[gic_irq(d) - 16], 1);
+ if (!refcount_inc_not_zero(&ppi_nmi_refs[idx])) {
+ refcount_set(&ppi_nmi_refs[idx], 1);
desc->handle_irq = handle_percpu_devid_fasteoi_nmi;
}
} else {
@@ -464,9 +476,11 @@ static void gic_irq_nmi_teardown(struct irq_data *d)
return;

/* desc lock should already be held */
- if (gic_irq(d) < 32) {
+ if (gic_irq_in_rdist(d)) {
+ u32 idx = gic_get_ppi_index(d);
+
/* Tearing down NMI, only switch handler for last NMI */
- if (refcount_dec_and_test(&ppi_nmi_refs[gic_irq(d) - 16]))
+ if (refcount_dec_and_test(&ppi_nmi_refs[idx]))
desc->handle_irq = handle_percpu_devid_irq;
} else {
desc->handle_irq = handle_fasteoi_irq;
@@ -1394,7 +1408,19 @@ static void gic_enable_nmi_support(void)
{
int i;

- for (i = 0; i < 16; i++)
+ if (!gic_prio_masking_enabled())
+ return;
+
+ if (gic_has_group0() && !gic_dist_security_disabled()) {
+ pr_warn("SCR_EL3.FIQ is cleared, cannot enable use of pseudo-NMIs\n");
+ return;
+ }
+
+ ppi_nmi_refs = kcalloc(gic_data.ppi_nr, sizeof(*ppi_nmi_refs), GFP_KERNEL);
+ if (!ppi_nmi_refs)
+ return;
+
+ for (i = 0; i < gic_data.ppi_nr; i++)
refcount_set(&ppi_nmi_refs[i], 0);

static_branch_enable(&supports_pseudo_nmis);
@@ -1472,12 +1498,7 @@ static int __init gic_init_bases(void __iomem *dist_base,
gicv2m_init(handle, gic_data.domain);
}

- if (gic_prio_masking_enabled()) {
- if (!gic_has_group0() || gic_dist_security_disabled())
- gic_enable_nmi_support();
- else
- pr_warn("SCR_EL3.FIQ is cleared, cannot enable use of pseudo-NMIs\n");
- }
+ gic_enable_nmi_support();

return 0;

--
2.20.1

2019-08-06 10:04:50

by Marc Zyngier

[permalink] [raw]
Subject: [PATCH v2 09/12] irqchip/gic-v3: Add EPPI range support

Expand the pre-existing PPI support to be able to deal with the
Extended PPI range (EPPI). This includes obtaining the number of PPIs
from each individual redistributor, and compute the minimum set
(just in case someone builds something really clever...).

Signed-off-by: Marc Zyngier <[email protected]>
---
drivers/irqchip/irq-gic-v3.c | 42 +++++++++++++++++++++++++-----
include/linux/irqchip/arm-gic-v3.h | 12 +++++++++
2 files changed, 47 insertions(+), 7 deletions(-)

diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 34f8a96bd747..f53e58d398ba 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -104,6 +104,7 @@ static DEFINE_PER_CPU(bool, has_rss);
enum gic_intid_range {
PPI_RANGE,
SPI_RANGE,
+ EPPI_RANGE,
ESPI_RANGE,
LPI_RANGE,
__INVALID_RANGE__
@@ -116,6 +117,8 @@ static enum gic_intid_range __get_intid_range(irq_hw_number_t hwirq)
return PPI_RANGE;
case 32 ... 1019:
return SPI_RANGE;
+ case EPPI_BASE_INTID ... (EPPI_BASE_INTID + 63):
+ return EPPI_RANGE;
case ESPI_BASE_INTID ... (ESPI_BASE_INTID + 1023):
return ESPI_RANGE;
case 8192 ... GENMASK(23, 0):
@@ -137,13 +140,15 @@ static inline unsigned int gic_irq(struct irq_data *d)

static inline int gic_irq_in_rdist(struct irq_data *d)
{
- return get_intid_range(d) == PPI_RANGE;
+ enum gic_intid_range range = get_intid_range(d);
+ return range == PPI_RANGE || range == EPPI_RANGE;
}

static inline void __iomem *gic_dist_base(struct irq_data *d)
{
switch (get_intid_range(d)) {
case PPI_RANGE:
+ case EPPI_RANGE:
/* SGI+PPI -> SGI_base for this CPU */
return gic_data_rdist_sgi_base();

@@ -242,6 +247,14 @@ static u32 convert_offset_index(struct irq_data *d, u32 offset, u32 *index)
case SPI_RANGE:
*index = d->hwirq;
return offset;
+ case EPPI_RANGE:
+ /*
+ * Contrary to the ESPI range, the EPPI range is contiguous
+ * to the PPI range in the registers, so let's adjust the
+ * displacement accordingly. Consistency is overrated.
+ */
+ *index = d->hwirq - EPPI_BASE_INTID + 32;
+ return offset;
case ESPI_RANGE:
*index = d->hwirq - ESPI_BASE_INTID;
switch (offset) {
@@ -414,6 +427,8 @@ static u32 gic_get_ppi_index(struct irq_data *d)
switch (get_intid_range(d)) {
case PPI_RANGE:
return d->hwirq - 16;
+ case EPPI_RANGE:
+ return d->hwirq - EPPI_BASE_INTID + 16;
default:
unreachable();
}
@@ -507,6 +522,7 @@ static void gic_eoimode1_eoi_irq(struct irq_data *d)

static int gic_set_type(struct irq_data *d, unsigned int type)
{
+ enum gic_intid_range range;
unsigned int irq = gic_irq(d);
void (*rwp_wait)(void);
void __iomem *base;
@@ -517,9 +533,11 @@ static int gic_set_type(struct irq_data *d, unsigned int type)
if (irq < 16)
return -EINVAL;

+ range = get_intid_range(d);
+
/* SPIs have restrictions on the supported types */
- if (irq >= 32 && type != IRQ_TYPE_LEVEL_HIGH &&
- type != IRQ_TYPE_EDGE_RISING)
+ if ((range == SPI_RANGE || range == ESPI_RANGE) &&
+ type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING)
return -EINVAL;

if (gic_irq_in_rdist(d)) {
@@ -533,9 +551,9 @@ static int gic_set_type(struct irq_data *d, unsigned int type)
offset = convert_offset_index(d, GICD_ICFGR, &index);

ret = gic_configure_irq(index, type, base + offset, rwp_wait);
- if (ret && irq < 32) {
+ if (ret && (range == PPI_RANGE || range == EPPI_RANGE)) {
/* Misconfigured PPIs are usually not fatal */
- pr_warn("GIC: PPI%d is secure or misconfigured\n", irq - 16);
+ pr_warn("GIC: PPI INTID%d is secure or misconfigured\n", irq);
ret = 0;
}

@@ -833,7 +851,7 @@ static int __gic_update_rdist_properties(struct redist_region *region,
u64 typer = gic_read_typer(ptr + GICR_TYPER);
gic_data.rdists.has_vlpis &= !!(typer & GICR_TYPER_VLPIS);
gic_data.rdists.has_direct_lpi &= !!(typer & GICR_TYPER_DirectLPIS);
- gic_data.ppi_nr = 16;
+ gic_data.ppi_nr = min(GICR_TYPER_NR_PPIS(typer), gic_data.ppi_nr);

return 1;
}
@@ -1222,6 +1240,7 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,

switch (__get_intid_range(hw)) {
case PPI_RANGE:
+ case EPPI_RANGE:
irq_set_percpu_devid(irq);
irq_domain_set_info(d, irq, hw, chip, d->host_data,
handle_percpu_devid_irq, NULL, NULL);
@@ -1266,15 +1285,24 @@ static int gic_irq_domain_translate(struct irq_domain *d,
*hwirq = fwspec->param[1] + 32;
break;
case 1: /* PPI */
- case GIC_IRQ_TYPE_PARTITION:
*hwirq = fwspec->param[1] + 16;
break;
case 2: /* ESPI */
*hwirq = fwspec->param[1] + ESPI_BASE_INTID;
break;
+ case 3: /* EPPI */
+ *hwirq = fwspec->param[1] + EPPI_BASE_INTID;
+ break;
case GIC_IRQ_TYPE_LPI: /* LPI */
*hwirq = fwspec->param[1];
break;
+ case GIC_IRQ_TYPE_PARTITION:
+ *hwirq = fwspec->param[1];
+ if (fwspec->param[1] >= 16)
+ *hwirq += EPPI_BASE_INTID - 16;
+ else
+ *hwirq += 16;
+ break;
default:
return -EINVAL;
}
diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
index c523bf1faa55..9ec3349dee04 100644
--- a/include/linux/irqchip/arm-gic-v3.h
+++ b/include/linux/irqchip/arm-gic-v3.h
@@ -124,6 +124,18 @@

#define GICR_TYPER_CPU_NUMBER(r) (((r) >> 8) & 0xffff)

+#define EPPI_BASE_INTID 1056
+
+#define GICR_TYPER_NR_PPIS(r) \
+ ({ \
+ unsigned int __ppinum = ((r) >> 27) & 0x1f; \
+ unsigned int __nr_ppis = 16; \
+ if (__ppinum == 1 || __ppinum == 2) \
+ __nr_ppis += __ppinum * 32; \
+ \
+ __nr_ppis; \
+ })
+
#define GICR_WAKER_ProcessorSleep (1U << 1)
#define GICR_WAKER_ChildrenAsleep (1U << 2)

--
2.20.1

2019-08-06 10:05:06

by Marc Zyngier

[permalink] [raw]
Subject: [PATCH v2 02/12] irqchip/gic-v3: Add INTID range and convertion primitives

In the beginning, life was simple. The GIC driver mostly cared about
PPIs, SPIs and LPIs, all with nicely layed out ranges.

We're about to change all that, with new ranges such as EPPI and ESPI
interleaved in the middle of the no-irq-land between the "special IDs"
and the LPI range. Boo.

In order to make our life less hellish, let's introduce a set of primitives
that will allow ranges to be identified easily and offsets to be remapped.

So far, there is no functionnal change.

Signed-off-by: Marc Zyngier <[email protected]>
---
drivers/irqchip/irq-gic-v3.c | 112 ++++++++++++++++++++++++++---------
1 file changed, 83 insertions(+), 29 deletions(-)

diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index b250e69908f8..db3bdedd7241 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -97,6 +97,32 @@ static DEFINE_PER_CPU(bool, has_rss);
/* Our default, arbitrary priority value. Linux only uses one anyway. */
#define DEFAULT_PMR_VALUE 0xf0

+enum gic_intid_range {
+ PPI_RANGE,
+ SPI_RANGE,
+ LPI_RANGE,
+ __INVALID_RANGE__
+};
+
+static enum gic_intid_range __get_intid_range(irq_hw_number_t hwirq)
+{
+ switch (hwirq) {
+ case 16 ... 31:
+ return PPI_RANGE;
+ case 32 ... 1019:
+ return SPI_RANGE;
+ case 8192 ... GENMASK(23, 0):
+ return LPI_RANGE;
+ default:
+ return __INVALID_RANGE__;
+ }
+}
+
+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;
@@ -104,18 +130,23 @@ static inline unsigned int gic_irq(struct irq_data *d)

static inline int gic_irq_in_rdist(struct irq_data *d)
{
- return gic_irq(d) < 32;
+ return get_intid_range(d) == PPI_RANGE;
}

static inline void __iomem *gic_dist_base(struct irq_data *d)
{
- if (gic_irq_in_rdist(d)) /* SGI+PPI -> SGI_base for this CPU */
+ switch (get_intid_range(d)) {
+ case PPI_RANGE:
+ /* SGI+PPI -> SGI_base for this CPU */
return gic_data_rdist_sgi_base();

- if (d->hwirq <= 1023) /* SPI -> dist_base */
+ case SPI_RANGE:
+ /* SPI -> dist_base */
return gic_data.dist_base;

- return NULL;
+ default:
+ return NULL;
+ }
}

static void gic_do_wait_for_rwp(void __iomem *base)
@@ -196,24 +227,46 @@ static void gic_enable_redist(bool enable)
/*
* Routines to disable, enable, EOI and route interrupts
*/
+static u32 convert_offset_index(struct irq_data *d, u32 offset, u32 *index)
+{
+ switch (get_intid_range(d)) {
+ case PPI_RANGE:
+ case SPI_RANGE:
+ *index = d->hwirq;
+ return offset;
+ default:
+ break;
+ }
+
+ WARN_ON(1);
+ *index = d->hwirq;
+ return offset;
+}
+
static int gic_peek_irq(struct irq_data *d, u32 offset)
{
- u32 mask = 1 << (gic_irq(d) % 32);
void __iomem *base;
+ u32 index, mask;
+
+ offset = convert_offset_index(d, offset, &index);
+ mask = 1 << (index % 32);

if (gic_irq_in_rdist(d))
base = gic_data_rdist_sgi_base();
else
base = gic_data.dist_base;

- return !!(readl_relaxed(base + offset + (gic_irq(d) / 32) * 4) & mask);
+ return !!(readl_relaxed(base + offset + (index / 32) * 4) & mask);
}

static void gic_poke_irq(struct irq_data *d, u32 offset)
{
- u32 mask = 1 << (gic_irq(d) % 32);
void (*rwp_wait)(void);
void __iomem *base;
+ u32 index, mask;
+
+ offset = convert_offset_index(d, offset, &index);
+ mask = 1 << (index % 32);

if (gic_irq_in_rdist(d)) {
base = gic_data_rdist_sgi_base();
@@ -223,7 +276,7 @@ static void gic_poke_irq(struct irq_data *d, u32 offset)
rwp_wait = gic_dist_wait_for_rwp;
}

- writel_relaxed(mask, base + offset + (gic_irq(d) / 32) * 4);
+ writel_relaxed(mask, base + offset + (index / 32) * 4);
rwp_wait();
}

@@ -316,8 +369,11 @@ static int gic_irq_get_irqchip_state(struct irq_data *d,
static void gic_irq_set_prio(struct irq_data *d, u8 prio)
{
void __iomem *base = gic_dist_base(d);
+ u32 offset, index;

- writeb_relaxed(prio, base + GICD_IPRIORITYR + gic_irq(d));
+ offset = convert_offset_index(d, GICD_IPRIORITYR, &index);
+
+ writeb_relaxed(prio, base + offset + index);
}

static int gic_irq_nmi_setup(struct irq_data *d)
@@ -407,6 +463,7 @@ static int gic_set_type(struct irq_data *d, unsigned int type)
unsigned int irq = gic_irq(d);
void (*rwp_wait)(void);
void __iomem *base;
+ u32 offset, index;
int ret;

/* Interrupt configuration for SGIs can't be changed */
@@ -426,8 +483,9 @@ static int gic_set_type(struct irq_data *d, unsigned int type)
rwp_wait = gic_dist_wait_for_rwp;
}

+ offset = convert_offset_index(d, GICD_ICFGR, &index);

- ret = gic_configure_irq(irq, type, base + GICD_ICFGR, rwp_wait);
+ ret = gic_configure_irq(index, type, base + offset, rwp_wait);
if (ret && irq < 32) {
/* Misconfigured PPIs are usually not fatal */
pr_warn("GIC: PPI%d is secure or misconfigured\n", irq - 16);
@@ -970,6 +1028,7 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
bool force)
{
unsigned int cpu;
+ u32 offset, index;
void __iomem *reg;
int enabled;
u64 val;
@@ -990,7 +1049,8 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
if (enabled)
gic_mask_irq(d);

- reg = gic_dist_base(d) + GICD_IROUTER + (gic_irq(d) * 8);
+ offset = convert_offset_index(d, GICD_IROUTER, &index);
+ reg = gic_dist_base(d) + offset + (index * 8);
val = gic_mpidr_to_affinity(cpu_logical_map(cpu));

gic_write_irouter(val, reg);
@@ -1084,36 +1144,30 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
if (static_branch_likely(&supports_deactivate_key))
chip = &gic_eoimode1_chip;

- /* SGIs are private to the core kernel */
- if (hw < 16)
- return -EPERM;
- /* Nothing here */
- if (hw >= gic_data.irq_nr && hw < 8192)
- return -EPERM;
- /* Off limits */
- if (hw >= GIC_ID_NR)
- return -EPERM;
-
- /* PPIs */
- if (hw < 32) {
+ switch (__get_intid_range(hw)) {
+ case PPI_RANGE:
irq_set_percpu_devid(irq);
irq_domain_set_info(d, irq, hw, chip, d->host_data,
handle_percpu_devid_irq, NULL, NULL);
irq_set_status_flags(irq, IRQ_NOAUTOEN);
- }
- /* SPIs */
- if (hw >= 32 && hw < gic_data.irq_nr) {
+ break;
+
+ case SPI_RANGE:
irq_domain_set_info(d, irq, hw, chip, d->host_data,
handle_fasteoi_irq, NULL, NULL);
irq_set_probe(irq);
irqd_set_single_target(irq_desc_get_irq_data(irq_to_desc(irq)));
- }
- /* LPIs */
- if (hw >= 8192 && hw < GIC_ID_NR) {
+ break;
+
+ case LPI_RANGE:
if (!gic_dist_supports_lpis())
return -EPERM;
irq_domain_set_info(d, irq, hw, chip, d->host_data,
handle_fasteoi_irq, NULL, NULL);
+ break;
+
+ default:
+ return -EPERM;
}

return 0;
--
2.20.1

2019-08-06 11:08:17

by John Garry

[permalink] [raw]
Subject: Re: [PATCH v2 12/12] irqchip/gic-v3: Add quirks for HIP06/07 invalid GICD_TYPER erratum 161010803

On 06/08/2019 11:01, Marc Zyngier wrote:
> It looks like the HIP06/07 SoCs have extra bits in their GICD_TYPER
> registers, which confuse the GICv3.1 code (these systems appear to
> expose ESPIs while they actually don't).
>
> Detect these systems as early as possible and wipe the fields that
> should be RES0 in the register.
>

thanks,

Tested-by: John Garry <[email protected]>

> Signed-off-by: Marc Zyngier <[email protected]>
> ---
> Documentation/arm64/silicon-errata.rst | 2 +
> drivers/irqchip/irq-gic-v3.c | 54 +++++++++++++++++++++-----
> 2 files changed, 46 insertions(+), 10 deletions(-)
>
> diff --git a/Documentation/arm64/silicon-errata.rst b/Documentation/arm64/silicon-errata.rst
> index 3e57d09246e6..17ea3fecddaa 100644
> --- a/Documentation/arm64/silicon-errata.rst
> +++ b/Documentation/arm64/silicon-errata.rst
> @@ -115,6 +115,8 @@ stable kernels.
> +----------------+-----------------+-----------------+-----------------------------+
> | Hisilicon | Hip0{6,7} | #161010701 | N/A |
> +----------------+-----------------+-----------------+-----------------------------+
> +| Hisilicon | Hip0{6,7} | #161010803 | N/A |
> ++----------------+-----------------+-----------------+-----------------------------+
> | Hisilicon | Hip07 | #161600802 | HISILICON_ERRATUM_161600802 |
> +----------------+-----------------+-----------------+-----------------------------+
> | Hisilicon | Hip08 SMMU PMCG | #162001800 | N/A |
> diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
> index 334a10d9dbfb..bee141613b67 100644
> --- a/drivers/irqchip/irq-gic-v3.c
> +++ b/drivers/irqchip/irq-gic-v3.c
> @@ -1441,6 +1441,46 @@ static bool gic_enable_quirk_msm8996(void *data)
> return true;
> }
>
> +static bool gic_enable_quirk_hip06_07(void *data)
> +{
> + struct gic_chip_data *d = data;
> +
> + /*
> + * HIP06 GICD_IIDR clashes with GIC-600 product number (despite
> + * not being an actual ARM implementation). The saving grace is
> + * that GIC-600 doesn't have ESPI, so nothing to do in that case.
> + * HIP07 doesn't even have a proper IIDR, and still pretends to
> + * have ESPI. In both cases, put them right.
> + */
> + if (d->rdists.gicd_typer & GICD_TYPER_ESPI) {
> + /* Zero both ESPI and the RES0 field next to it... */
> + d->rdists.gicd_typer &= ~GENMASK(9, 8);
> + return true;
> + }
> +
> + return false;
> +}
> +
> +static const struct gic_quirk gic_quirks[] = {
> + {
> + .desc = "GICv3: Qualcomm MSM8996 broken firmware",
> + .compatible = "qcom,msm8996-gic-v3",
> + .init = gic_enable_quirk_msm8996,
> + },
> + {
> + .desc = "GICv3: HIP06 erratum 161010803",
> + .iidr = 0x0204043b,
> + .init = gic_enable_quirk_hip06_07,
> + },
> + {
> + .desc = "GICv3: HIP07 erratum 161010803",
> + .iidr = 0x00000000,
> + .init = gic_enable_quirk_hip06_07,
> + },
> + {
> + }
> +};
> +
> static void gic_enable_nmi_support(void)
> {
> int i;
> @@ -1494,6 +1534,10 @@ static int __init gic_init_bases(void __iomem *dist_base,
> */
> typer = readl_relaxed(gic_data.dist_base + GICD_TYPER);
> gic_data.rdists.gicd_typer = typer;
> +
> + gic_enable_quirks(readl_relaxed(gic_data.dist_base + GICD_IIDR),
> + gic_quirks, &gic_data);
> +
> pr_info("%d SPIs implemented\n", GIC_LINE_NR - 32);
> pr_info("%d Extended SPIs implemented\n", GIC_ESPI_NR);
> gic_data.domain = irq_domain_create_tree(handle, &gic_irq_domain_ops,
> @@ -1676,16 +1720,6 @@ static void __init gic_of_setup_kvm_info(struct device_node *node)
> gic_set_kvm_info(&gic_v3_kvm_info);
> }
>
> -static const struct gic_quirk gic_quirks[] = {
> - {
> - .desc = "GICv3: Qualcomm MSM8996 broken firmware",
> - .compatible = "qcom,msm8996-gic-v3",
> - .init = gic_enable_quirk_msm8996,
> - },
> - {
> - }
> -};
> -
> static int __init gic_of_init(struct device_node *node, struct device_node *parent)
> {
> void __iomem *dist_base;
>


2019-08-22 20:20:18

by Julien

[permalink] [raw]
Subject: Re: [PATCH v2 06/12] irqchip/gic-v3: Dynamically allocate PPI NMI refcounts

Hi Marc,

On 06/08/19 11:01, Marc Zyngier wrote:
> As we're about to have a variable number of PPIs, let's make the
> allocation of the NMI refcounts dynamic. Also apply some minor
> cleanups (moving things around).
>
> Signed-off-by: Marc Zyngier <[email protected]>

Reviewed-by: Julien Thierry <[email protected]>

Thanks,

> ---
> drivers/irqchip/irq-gic-v3.c | 47 ++++++++++++++++++++++++++----------
> 1 file changed, 34 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
> index e03fb6d7c2ce..4253c7f67c86 100644
> --- a/drivers/irqchip/irq-gic-v3.c
> +++ b/drivers/irqchip/irq-gic-v3.c
> @@ -88,7 +88,7 @@ static DEFINE_STATIC_KEY_TRUE(supports_deactivate_key);
> static DEFINE_STATIC_KEY_FALSE(supports_pseudo_nmis);
>
> /* ppi_nmi_refs[n] == number of cpus having ppi[n + 16] set as NMI */
> -static refcount_t ppi_nmi_refs[16];
> +static refcount_t *ppi_nmi_refs;
>
> static struct gic_kvm_info gic_v3_kvm_info;
> static DEFINE_PER_CPU(bool, has_rss);
> @@ -409,6 +409,16 @@ static void gic_irq_set_prio(struct irq_data *d, u8 prio)
> writeb_relaxed(prio, base + offset + index);
> }
>
> +static u32 gic_get_ppi_index(struct irq_data *d)
> +{
> + switch (get_intid_range(d)) {
> + case PPI_RANGE:
> + return d->hwirq - 16;
> + default:
> + unreachable();
> + }
> +}
> +
> static int gic_irq_nmi_setup(struct irq_data *d)
> {
> struct irq_desc *desc = irq_to_desc(d->irq);
> @@ -429,10 +439,12 @@ static int gic_irq_nmi_setup(struct irq_data *d)
> return -EINVAL;
>
> /* desc lock should already be held */
> - if (gic_irq(d) < 32) {
> + if (gic_irq_in_rdist(d)) {
> + u32 idx = gic_get_ppi_index(d);
> +
> /* Setting up PPI as NMI, only switch handler for first NMI */
> - if (!refcount_inc_not_zero(&ppi_nmi_refs[gic_irq(d) - 16])) {
> - refcount_set(&ppi_nmi_refs[gic_irq(d) - 16], 1);
> + if (!refcount_inc_not_zero(&ppi_nmi_refs[idx])) {
> + refcount_set(&ppi_nmi_refs[idx], 1);
> desc->handle_irq = handle_percpu_devid_fasteoi_nmi;
> }
> } else {
> @@ -464,9 +476,11 @@ static void gic_irq_nmi_teardown(struct irq_data *d)
> return;
>
> /* desc lock should already be held */
> - if (gic_irq(d) < 32) {
> + if (gic_irq_in_rdist(d)) {
> + u32 idx = gic_get_ppi_index(d);
> +
> /* Tearing down NMI, only switch handler for last NMI */
> - if (refcount_dec_and_test(&ppi_nmi_refs[gic_irq(d) - 16]))
> + if (refcount_dec_and_test(&ppi_nmi_refs[idx]))
> desc->handle_irq = handle_percpu_devid_irq;
> } else {
> desc->handle_irq = handle_fasteoi_irq;
> @@ -1394,7 +1408,19 @@ static void gic_enable_nmi_support(void)
> {
> int i;
>
> - for (i = 0; i < 16; i++)
> + if (!gic_prio_masking_enabled())
> + return;
> +
> + if (gic_has_group0() && !gic_dist_security_disabled()) {
> + pr_warn("SCR_EL3.FIQ is cleared, cannot enable use of pseudo-NMIs\n");
> + return;
> + }
> +
> + ppi_nmi_refs = kcalloc(gic_data.ppi_nr, sizeof(*ppi_nmi_refs), GFP_KERNEL);
> + if (!ppi_nmi_refs)
> + return;
> +
> + for (i = 0; i < gic_data.ppi_nr; i++)
> refcount_set(&ppi_nmi_refs[i], 0);
>
> static_branch_enable(&supports_pseudo_nmis);
> @@ -1472,12 +1498,7 @@ static int __init gic_init_bases(void __iomem *dist_base,
> gicv2m_init(handle, gic_data.domain);
> }
>
> - if (gic_prio_masking_enabled()) {
> - if (!gic_has_group0() || gic_dist_security_disabled())
> - gic_enable_nmi_support();
> - else
> - pr_warn("SCR_EL3.FIQ is cleared, cannot enable use of pseudo-NMIs\n");
> - }
> + gic_enable_nmi_support();
>
> return 0;
>
>

--
Julien Thierry