[Skiboot] [PATCH 13/60] xive: Implement cache watch and use it for EQs

Benjamin Herrenschmidt benh at kernel.crashing.org
Thu Dec 22 14:16:21 AEDT 2016


We need to do cache coherent updates of the EQs when modifying
escalation interrupts. Use the cache watch facility for that.

Signed-off-by: Benjamin Herrenschmidt <benh at kernel.crashing.org>
---
 hw/xive.c      | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++++----
 include/xive.h |  36 ++++++++++++++++++++
 2 files changed, 135 insertions(+), 7 deletions(-)

diff --git a/hw/xive.c b/hw/xive.c
index 7df699d..c78b531 100644
--- a/hw/xive.c
+++ b/hw/xive.c
@@ -686,6 +686,8 @@ static int64_t __xive_cache_scrub(struct xive *x, enum xive_cache_type ctype,
 		mreg = PC_VPC_SCRUB_MASK;
 		mregx = X_PC_VPC_SCRUB_MASK;
 		break;
+	default:
+		return OPAL_PARAMETER;
 	}
 	if (ctype == xive_cache_vpc) {
 		mval = PC_SCRUB_BLOCK_ID | PC_SCRUB_OFFSET;
@@ -719,6 +721,86 @@ static int64_t xive_ivc_scrub(struct xive *x, uint64_t block, uint64_t idx)
 	return __xive_cache_scrub(x, xive_cache_ivc, block, idx, false, false);
 }
 
+static int64_t __xive_cache_watch(struct xive *x, enum xive_cache_type ctype,
+				  uint64_t block, uint64_t idx,
+				  uint32_t start_dword, uint32_t dword_count,
+				  void *new_data, bool light_watch)
+{
+	uint64_t sreg, sregx, dreg0, dreg0x;
+	uint64_t dval0, sval, status, i;
+
+	switch (ctype) {
+	case xive_cache_eqc:
+		sreg = VC_EQC_CWATCH_SPEC;
+		sregx = X_VC_EQC_CWATCH_SPEC;
+		dreg0 = VC_EQC_CWATCH_DAT0;
+		dreg0x = X_VC_EQC_CWATCH_DAT0;
+		sval = SETFIELD(VC_EQC_CWATCH_BLOCKID, idx, block);
+		break;
+	case xive_cache_vpc:
+		sreg = PC_VPC_CWATCH_SPEC;
+		sregx = X_PC_VPC_CWATCH_SPEC;
+		dreg0 = PC_VPC_CWATCH_DAT0;
+		dreg0x = X_PC_VPC_CWATCH_DAT0;
+		sval = SETFIELD(PC_VPC_CWATCH_BLOCKID, idx, block);
+		break;
+	default:
+		return OPAL_PARAMETER;
+	}
+
+	/* The full bit is in the same position for EQC and VPC */
+	if (!light_watch)
+		sval |= VC_EQC_CWATCH_FULL;
+
+	do {
+		/* Write the cache watch spec */
+		__xive_regw(x, sreg, sregx, sval, NULL);
+
+		/* Load data0 register to populate the watch */
+		dval0 = __xive_regr(x, dreg0, dreg0x, NULL);
+
+		/* Write the words into the watch facility. We write in reverse
+		 * order in case word 0 is part of it as it must be the last
+		 * one written.
+		 */
+		for (i = start_dword + dword_count - 1; i >= start_dword ;i--) {
+			uint64_t dw = ((uint64_t *)new_data)[i - start_dword];
+			__xive_regw(x, dreg0 + i * 8, dreg0x + i, dw, NULL);
+		}
+
+		/* Write data0 register to trigger the update if word 0 wasn't
+		 * written above
+		 */
+		if (start_dword > 0)
+			__xive_regw(x, dreg0, dreg0x, dval0, NULL);
+
+		/* This may not be necessary for light updates (it's possible\
+		 * that a sync in sufficient, TBD). Ensure the above is
+		 * complete and check the status of the watch.
+		 */
+		status = __xive_regr(x, sreg, sregx, NULL);
+
+		/* XXX Add timeout ? */
+
+		/* Bits FULL and CONFLICT are in the same position in
+		 * EQC and VPC
+		 */
+	} while((status & VC_EQC_CWATCH_FULL) &&
+		(status & VC_EQC_CWATCH_CONFLICT));
+
+	return 0;
+}
+
+static int64_t xive_eqc_cache_update(struct xive *x, uint64_t block,
+				     uint64_t idx, uint32_t start_dword,
+				     uint32_t dword_count, void *new_data,
+				     bool light_watch)
+{
+	return __xive_cache_watch(x, xive_cache_eqc, block, idx,
+				  start_dword, dword_count,
+				  new_data, light_watch);
+}
+
 static bool xive_set_vsd(struct xive *x, uint32_t tbl, uint32_t idx, uint64_t v)
 {
 	/* Set VC version */
@@ -1518,6 +1600,7 @@ static bool xive_set_eq_info(uint32_t isn, uint32_t target, uint8_t prio)
 	struct xive_ive *ive;
 	uint32_t eq_blk, eq_idx;
 	bool is_escalation = GIRQ_IS_ESCALATION(isn);
+	uint64_t new_ive;
 
 	/* Find XIVE on which the IVE resides */
 	x = xive_from_isn(isn);
@@ -1534,15 +1617,17 @@ static bool xive_set_eq_info(uint32_t isn, uint32_t target, uint8_t prio)
 
 	lock(&x->lock);
 
+	/* Read existing IVE */
+	new_ive = ive->w;
+
 	/* Are we masking ? */
 	if (prio == 0xff) {
 		/* Masking, just set the M bit */
 		if (!is_escalation)
-			ive->w |= IVE_MASKED;
+			new_ive |= IVE_MASKED;
 
 		xive_vdbg(x, "ISN %x masked !\n", isn);
 	} else {
-		uint64_t new_ive;
 
 		/* Unmasking, re-target the IVE. First find the EQ
 		 * correponding to the target
@@ -1560,15 +1645,22 @@ static bool xive_set_eq_info(uint32_t isn, uint32_t target, uint8_t prio)
 		new_ive = ive->w & ~IVE_MASKED;
 		new_ive = SETFIELD(IVE_EQ_BLOCK, new_ive, eq_blk);
 		new_ive = SETFIELD(IVE_EQ_INDEX, new_ive, eq_idx);
-		sync();
-		ive->w = new_ive;
 
 		xive_vdbg(x,"ISN %x routed to eq %x/%x IVE=%016llx !\n",
-		  isn, eq_blk, eq_idx, new_ive);
+			  isn, eq_blk, eq_idx, new_ive);
 	}
 
-	/* Scrub IVE from cache */
-	xive_ivc_scrub(x, x->chip_id, GIRQ_TO_IDX(isn));
+	/* Updating the cache differs between real IVEs and escalation
+	 * IVEs inside an EQ
+	 */
+	if (is_escalation) {
+		xive_eqc_cache_update(x, x->chip_id, GIRQ_TO_IDX(isn),
+				      2, 1, &new_ive, true);
+	} else {
+		sync();
+		ive->w = new_ive;
+		xive_ivc_scrub(x, x->chip_id, GIRQ_TO_IDX(isn));
+	}
 
 	unlock(&x->lock);
 	return true;
diff --git a/include/xive.h b/include/xive.h
index ba48601..a6dc7be 100644
--- a/include/xive.h
+++ b/include/xive.h
@@ -107,6 +107,28 @@
 #define  PC_SCRUB_WANT_INVAL	PPC_BIT(2)
 #define  PC_SCRUB_BLOCK_ID	PPC_BITMASK(27,31)
 #define  PC_SCRUB_OFFSET	PPC_BITMASK(45,63)
+#define X_PC_VPC_CWATCH_SPEC	0x167
+#define PC_VPC_CWATCH_SPEC	0x738
+#define  PC_VPC_CWATCH_CONFLICT	PPC_BIT(0)
+#define  PC_VPC_CWATCH_FULL	PPC_BIT(8)
+#define  PC_VPC_CWATCH_BLOCKID	PPC_BITMASK(27,31)
+#define  PC_VPC_CWATCH_OFFSET	PPC_BITMASK(45,63)
+#define X_PC_VPC_CWATCH_DAT0	0x168
+#define PC_VPC_CWATCH_DAT0	0x740
+#define X_PC_VPC_CWATCH_DAT1	0x169
+#define PC_VPC_CWATCH_DAT1	0x748
+#define X_PC_VPC_CWATCH_DAT2	0x16a
+#define PC_VPC_CWATCH_DAT2	0x750
+#define X_PC_VPC_CWATCH_DAT3	0x16b
+#define PC_VPC_CWATCH_DAT3	0x758
+#define X_PC_VPC_CWATCH_DAT4	0x16c
+#define PC_VPC_CWATCH_DAT4	0x760
+#define X_PC_VPC_CWATCH_DAT5	0x16d
+#define PC_VPC_CWATCH_DAT5	0x768
+#define X_PC_VPC_CWATCH_DAT6	0x16e
+#define PC_VPC_CWATCH_DAT6	0x770
+#define X_PC_VPC_CWATCH_DAT7	0x16f
+#define PC_VPC_CWATCH_DAT7	0x778
 
 /* VC0 register offsets */
 #define X_VC_GLOBAL_CONFIG	0x200
@@ -144,6 +166,20 @@
 #define VC_EQC_SCRUB_TRIG	0x910
 #define X_VC_EQC_SCRUB_MASK	0x213
 #define VC_EQC_SCRUB_MASK	0x918
+#define X_VC_EQC_CWATCH_SPEC	0x215
+#define VC_EQC_CWATCH_SPEC	0x928
+#define  VC_EQC_CWATCH_CONFLICT	PPC_BIT(0)
+#define  VC_EQC_CWATCH_FULL	PPC_BIT(8)
+#define  VC_EQC_CWATCH_BLOCKID	PPC_BITMASK(28,31)
+#define  VC_EQC_CWATCH_OFFSET	PPC_BITMASK(40,63)
+#define X_VC_EQC_CWATCH_DAT0	0x216
+#define VC_EQC_CWATCH_DAT0	0x930
+#define X_VC_EQC_CWATCH_DAT1	0x217
+#define VC_EQC_CWATCH_DAT1	0x938
+#define X_VC_EQC_CWATCH_DAT2	0x218
+#define VC_EQC_CWATCH_DAT2	0x940
+#define X_VC_EQC_CWATCH_DAT3	0x219
+#define VC_EQC_CWATCH_DAT3	0x948
 #define X_VC_IVC_SCRUB_TRIG	0x222
 #define VC_IVC_SCRUB_TRIG	0x990
 #define X_VC_IVC_SCRUB_MASK	0x223
-- 
2.9.3



More information about the Skiboot mailing list