|
|
@@ -1951,6 +1951,29 @@ static void its_free_pending_table(struct page *pt)
|
|
|
get_order(max_t(u32, LPI_PENDBASE_SZ, SZ_64K)));
|
|
|
}
|
|
|
|
|
|
+static u64 its_clear_vpend_valid(void __iomem *vlpi_base)
|
|
|
+{
|
|
|
+ u32 count = 1000000; /* 1s! */
|
|
|
+ bool clean;
|
|
|
+ u64 val;
|
|
|
+
|
|
|
+ val = gits_read_vpendbaser(vlpi_base + GICR_VPENDBASER);
|
|
|
+ val &= ~GICR_VPENDBASER_Valid;
|
|
|
+ gits_write_vpendbaser(val, vlpi_base + GICR_VPENDBASER);
|
|
|
+
|
|
|
+ do {
|
|
|
+ val = gits_read_vpendbaser(vlpi_base + GICR_VPENDBASER);
|
|
|
+ clean = !(val & GICR_VPENDBASER_Dirty);
|
|
|
+ if (!clean) {
|
|
|
+ count--;
|
|
|
+ cpu_relax();
|
|
|
+ udelay(1);
|
|
|
+ }
|
|
|
+ } while (!clean && count);
|
|
|
+
|
|
|
+ return val;
|
|
|
+}
|
|
|
+
|
|
|
static void its_cpu_init_lpis(void)
|
|
|
{
|
|
|
void __iomem *rbase = gic_data_rdist_rd_base();
|
|
|
@@ -2024,6 +2047,30 @@ static void its_cpu_init_lpis(void)
|
|
|
val |= GICR_CTLR_ENABLE_LPIS;
|
|
|
writel_relaxed(val, rbase + GICR_CTLR);
|
|
|
|
|
|
+ if (gic_rdists->has_vlpis) {
|
|
|
+ void __iomem *vlpi_base = gic_data_rdist_vlpi_base();
|
|
|
+
|
|
|
+ /*
|
|
|
+ * It's possible for CPU to receive VLPIs before it is
|
|
|
+ * sheduled as a vPE, especially for the first CPU, and the
|
|
|
+ * VLPI with INTID larger than 2^(IDbits+1) will be considered
|
|
|
+ * as out of range and dropped by GIC.
|
|
|
+ * So we initialize IDbits to known value to avoid VLPI drop.
|
|
|
+ */
|
|
|
+ val = (LPI_NRBITS - 1) & GICR_VPROPBASER_IDBITS_MASK;
|
|
|
+ pr_debug("GICv4: CPU%d: Init IDbits to 0x%llx for GICR_VPROPBASER\n",
|
|
|
+ smp_processor_id(), val);
|
|
|
+ gits_write_vpropbaser(val, vlpi_base + GICR_VPROPBASER);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Also clear Valid bit of GICR_VPENDBASER, in case some
|
|
|
+ * ancient programming gets left in and has possibility of
|
|
|
+ * corrupting memory.
|
|
|
+ */
|
|
|
+ val = its_clear_vpend_valid(vlpi_base);
|
|
|
+ WARN_ON(val & GICR_VPENDBASER_Dirty);
|
|
|
+ }
|
|
|
+
|
|
|
/* Make sure the GIC has seen the above */
|
|
|
dsb(sy);
|
|
|
}
|
|
|
@@ -2644,26 +2691,11 @@ static void its_vpe_schedule(struct its_vpe *vpe)
|
|
|
static void its_vpe_deschedule(struct its_vpe *vpe)
|
|
|
{
|
|
|
void __iomem *vlpi_base = gic_data_rdist_vlpi_base();
|
|
|
- u32 count = 1000000; /* 1s! */
|
|
|
- bool clean;
|
|
|
u64 val;
|
|
|
|
|
|
- /* We're being scheduled out */
|
|
|
- val = gits_read_vpendbaser(vlpi_base + GICR_VPENDBASER);
|
|
|
- val &= ~GICR_VPENDBASER_Valid;
|
|
|
- gits_write_vpendbaser(val, vlpi_base + GICR_VPENDBASER);
|
|
|
-
|
|
|
- do {
|
|
|
- val = gits_read_vpendbaser(vlpi_base + GICR_VPENDBASER);
|
|
|
- clean = !(val & GICR_VPENDBASER_Dirty);
|
|
|
- if (!clean) {
|
|
|
- count--;
|
|
|
- cpu_relax();
|
|
|
- udelay(1);
|
|
|
- }
|
|
|
- } while (!clean && count);
|
|
|
+ val = its_clear_vpend_valid(vlpi_base);
|
|
|
|
|
|
- if (unlikely(!clean && !count)) {
|
|
|
+ if (unlikely(val & GICR_VPENDBASER_Dirty)) {
|
|
|
pr_err_ratelimited("ITS virtual pending table not cleaning\n");
|
|
|
vpe->idai = false;
|
|
|
vpe->pending_last = true;
|