diff options
author | Philippe Bergheaud <felix@linux.vnet.ibm.com> | 2015-06-19 14:52:54 +0200 |
---|---|---|
committer | Stewart Smith <stewart@linux.vnet.ibm.com> | 2015-07-10 10:44:33 +1000 |
commit | 1d8c68ff8a83d077ebcd46967e1f581ba2f3135c (patch) | |
tree | f45b9cb6c3635019dba37d9906f2f76845376bae /hw | |
parent | a524c05983e36ab0a29cd2e3c979dff672d0eb6b (diff) | |
download | skiboot-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.c | 159 | ||||
-rw-r--r-- | hw/phb3.c | 10 |
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; +} @@ -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: */ |