aboutsummaryrefslogtreecommitdiff
path: root/hw/chiptod.c
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/chiptod.c
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/chiptod.c')
-rw-r--r--hw/chiptod.c159
1 files changed, 158 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;
+}