[Skiboot] [PATCH] Configure CAPP timebase.

Philippe Bergheaud felix at linux.vnet.ibm.com
Fri Jun 19 22:52:54 AEST 2015


Extend the OPAL call phb3_set_capi_mode to configure CAPP timebase.
Inform Linux with the device tree property "ibm,capp-timebase-sync.

Signed-off-by: Philippe Bergheaud <felix at linux.vnet.ibm.com>
---
 hw/chiptod.c      |  159 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 hw/phb3.c         |   10 +++
 include/capp.h    |    2 +
 include/chiptod.h |    1 +
 4 files changed, 171 insertions(+), 1 deletions(-)

diff --git a/hw/chiptod.c b/hw/chiptod.c
index 18c4847..6a79902 100644
--- a/hw/chiptod.c
+++ b/hw/chiptod.c
@@ -15,11 +15,12 @@
  */
 
 /*
- * Handle ChipTOD chip & configure core timebases
+ * Handle ChipTOD chip & configure core and CAPP timebases
  */
 #include <skiboot.h>
 #include <chiptod.h>
 #include <chip.h>
+#include <capp.h>
 #include <xscom.h>
 #include <io.h>
 #include <cpu.h>
@@ -1010,3 +1011,159 @@ void chiptod_init(void)
 
 	op_display(OP_LOG, OP_MOD_CHIPTOD, 4);
 }
+
+/* CAPP timebase sync */
+
+static bool chiptod_capp_reset_tb_errors(uint32_t chip_id)
+{
+	uint64_t tfmr;
+	unsigned long timeout = 0;
+
+	/* Ask for automatic clear of errors */
+	tfmr = base_tfmr | SPR_TFMR_CLEAR_TB_ERRORS;
+
+	/* Additionally pHyp sets these (write-1-to-clear ?) */
+	tfmr |= SPR_TFMR_TB_MISSING_SYNC;
+	tfmr |= SPR_TFMR_TB_MISSING_STEP;
+	tfmr |= SPR_TFMR_TB_RESIDUE_ERR;
+	tfmr |= SPR_TFMR_TBST_CORRUPT;
+	tfmr |= SPR_TFMR_TFMR_CORRUPT;
+
+	/* Write CAPP TFMR */
+	xscom_write(chip_id, CAPP_TFMR, tfmr);
+
+	/* We have to write "Clear TB Errors" again */
+	tfmr = base_tfmr | SPR_TFMR_CLEAR_TB_ERRORS;
+	/* Write CAPP TFMR */
+	xscom_write(chip_id, CAPP_TFMR, tfmr);
+
+	do {
+		if (++timeout >= TIMEOUT_LOOPS) {
+			prerror("CAPP: TB error reset timeout !\n");
+			return false;
+		}
+		/* Read CAPP TFMR */
+		xscom_read(chip_id, CAPP_TFMR, &tfmr);
+		if (tfmr & SPR_TFMR_TFMR_CORRUPT) {
+			prerror("CAPP: TB error reset: corrupt TFMR!\n");
+			return false;
+		}
+	} while (tfmr & SPR_TFMR_CLEAR_TB_ERRORS);
+	return true;
+}
+
+static bool chiptod_capp_mod_tb(uint32_t chip_id)
+{
+	uint64_t timeout = 0;
+	uint64_t tfmr;
+
+	/* Switch CAPP timebase to "Not Set" state */
+	tfmr = base_tfmr | SPR_TFMR_LOAD_TOD_MOD;
+	xscom_write(chip_id, CAPP_TFMR, tfmr);
+	do {
+		if (++timeout >= (TIMEOUT_LOOPS*2)) {
+			prerror("CAPP: TB \"Not Set\" timeout\n");
+			return false;
+		}
+		xscom_read(chip_id, CAPP_TFMR, &tfmr);
+		if (tfmr & SPR_TFMR_TFMR_CORRUPT) {
+			prerror("CAPP: TB \"Not Set\" TFMR corrupt\n");
+			return false;
+		}
+		if (GETFIELD(SPR_TFMR_TBST_ENCODED, tfmr) == 9) {
+			prerror("CAPP: TB \"Not Set\" TOD in error state\n");
+			return false;
+		}
+	} while (tfmr & SPR_TFMR_LOAD_TOD_MOD);
+
+	return true;
+}
+
+static bool chiptod_wait_for_chip_sync(void)
+{
+	uint64_t tfmr;
+	uint64_t timeout = 0;
+
+	/* Read core TFMR, mask bit 42, write core TFMR back */
+	tfmr = mfspr(SPR_TFMR);
+	tfmr &= ~SPR_TFMR_TB_SYNC_OCCURED;
+	mtspr(SPR_TFMR, tfmr);
+
+	/* Read core TFMR until the TB sync occurred */
+	do {
+		if (++timeout >= TIMEOUT_LOOPS) {
+			prerror("CHIPTOD: No sync pulses\n");
+			return false;
+		}
+		tfmr = mfspr(SPR_TFMR);
+	} while (!(tfmr & SPR_TFMR_TB_SYNC_OCCURED));
+	return true;
+}
+
+static bool chiptod_capp_check_tb_running(uint32_t chip_id)
+{
+	uint64_t tfmr;
+	uint64_t timeout = 0;
+
+	/* Read CAPP TFMR until TB becomes valid */
+	do {
+		if (++timeout >= (TIMEOUT_LOOPS*2)) {
+			prerror("CAPP: TB Invalid!\n");
+			return false;
+		}
+		xscom_read(chip_id, CAPP_TFMR, &tfmr);
+		if (tfmr & SPR_TFMR_TFMR_CORRUPT) {
+			prerror("CAPP: TFMR corrupt!\n");
+			return false;
+		}
+	} while (!(tfmr & SPR_TFMR_TB_VALID));
+	return true;
+}
+
+bool chiptod_capp_timebase_sync(uint32_t chip_id)
+{
+	uint64_t tfmr;
+	uint64_t capp_tb;
+	int64_t delta;
+	unsigned int retry = 0;
+
+	/* Set CAPP TFMR to base tfmr value */
+	xscom_write(chip_id, CAPP_TFMR, base_tfmr);
+
+	/* Reset CAPP TB errors before attempting the sync */
+	if (!chiptod_capp_reset_tb_errors(chip_id))
+		return false;
+
+	/* Switch CAPP TB to "Not Set" state */
+	if (!chiptod_capp_mod_tb(chip_id))
+		return false;
+
+	/* Sync CAPP TB with core TB, retry while difference > 16usecs */
+	do {
+		if (retry++ > 5) {
+			prerror("CAPP: TB sync: giving up!\n");
+			return false;
+		}
+
+		/* Make CAPP ready to get the TB, wait for chip sync */
+		tfmr = base_tfmr | SPR_TFMR_MOVE_CHIP_TOD_TO_TB;
+		xscom_write(chip_id, CAPP_TFMR, tfmr);
+		if (!chiptod_wait_for_chip_sync())
+			return false;
+
+		/* Set CAPP TB from core TB */
+		xscom_write(chip_id, CAPP_TB, mftb());
+
+		/* Wait for CAPP TFMR tb_valid bit */
+		if (!chiptod_capp_check_tb_running(chip_id))
+			return false;
+
+		/* Read CAPP TB, read core TB, compare */
+		xscom_read(chip_id, CAPP_TB, &capp_tb);
+		delta = mftb() - capp_tb;
+		if (delta < 0)
+			delta = -delta;
+	} while (tb_to_usecs(delta) > 16);
+
+	return true;
+}
diff --git a/hw/phb3.c b/hw/phb3.c
index 9ffb449..b3c6870 100644
--- a/hw/phb3.c
+++ b/hw/phb3.c
@@ -46,6 +46,7 @@
 #include <capp.h>
 #include <fsp.h>
 #include <chip.h>
+#include <chiptod.h>
 
 /* Enable this to disable error interrupts for debug purposes */
 #undef DISABLE_ERR_INTS
@@ -3331,6 +3332,12 @@ static int64_t phb3_set_capi_mode(struct phb *phb, uint64_t mode,
 	phb3_init_capp_errors(p);
 
 	phb3_init_capp_regs(p);
+
+	if (!chiptod_capp_timebase_sync(p->chip_id)) {
+		PHBERR(p, "CAPP: Failed to sync timebase\n");
+		return OPAL_HARDWARE;
+	}
+
 	return OPAL_SUCCESS;
 }
 
@@ -4087,6 +4094,9 @@ static void phb3_add_properties(struct phb3 *p)
 	 */
 	dt_add_property_string(np, "ibm,msi-eoi-method", "ioda2");
 
+	/* Indicate to Linux that CAPP timebase sync is supported */
+	dt_add_property_string(np, "ibm,capp-timebase-sync", NULL);
+
 	/* The interrupt maps will be generated in the RC node by the
 	 * PCI code based on the content of this structure:
 	 */
diff --git a/include/capp.h b/include/capp.h
index 4b53e37..d9275ec 100644
--- a/include/capp.h
+++ b/include/capp.h
@@ -74,6 +74,8 @@ enum capp_reg {
 #define CANNED_PRESP_MAP2			0x201301F
 #define CAPP_ERR_STATUS_CTRL			0x201300E
 #define FLUSH_SUE_STATE_MAP			0x201300F
+#define CAPP_TB					0x2013026
+#define CAPP_TFMR				0x2013027
 #define CAPP_EPOCH_TIMER_CTRL			0x201302C
 #define FLUSH_UOP_CONFIG1			0x2013803
 #define FLUSH_UOP_CONFIG2			0x2013804
diff --git a/include/chiptod.h b/include/chiptod.h
index 43f1d3d..7d4157b 100644
--- a/include/chiptod.h
+++ b/include/chiptod.h
@@ -25,5 +25,6 @@ extern void chiptod_init(void);
 extern bool chiptod_wakeup_resync(void);
 extern int chiptod_recover_tb_errors(void);
 extern void chiptod_reset_tb(void);
+extern bool chiptod_capp_timebase_sync(uint32_t chip_id);
 
 #endif /* __CHIPTOD_H */
-- 
1.7.2.5



More information about the Skiboot mailing list