aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorPhilippe Bergheaud <felix@linux.vnet.ibm.com>2015-06-19 14:52:54 +0200
committerStewart Smith <stewart@linux.vnet.ibm.com>2015-07-10 10:44:33 +1000
commit1d8c68ff8a83d077ebcd46967e1f581ba2f3135c (patch)
treef45b9cb6c3635019dba37d9906f2f76845376bae /hw
parenta524c05983e36ab0a29cd2e3c979dff672d0eb6b (diff)
downloadskiboot-1d8c68ff8a83d077ebcd46967e1f581ba2f3135c.zip
skiboot-1d8c68ff8a83d077ebcd46967e1f581ba2f3135c.tar.gz
skiboot-1d8c68ff8a83d077ebcd46967e1f581ba2f3135c.tar.bz2
Configure CAPP timebase.
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@linux.vnet.ibm.com> Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
Diffstat (limited to 'hw')
-rw-r--r--hw/chiptod.c159
-rw-r--r--hw/phb3.c10
2 files changed, 168 insertions, 1 deletions
diff --git a/hw/chiptod.c b/hw/chiptod.c
index b09f378..bae4b50 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>
@@ -1771,3 +1772,159 @@ void chiptod_init(void)
chiptod_init_topology_info();
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 df9d57c..e55295b 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
@@ -3350,6 +3351,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;
}
@@ -4106,6 +4113,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:
*/