diff --git a/src/soc/ibm/power9/timer.c b/src/soc/ibm/power9/timer.c index ca6d3b912ab..46f0d41490c 100644 --- a/src/soc/ibm/power9/timer.c +++ b/src/soc/ibm/power9/timer.c @@ -3,7 +3,6 @@ #include #include #include -#include // memcpy #include /* Time base frequency is 512 MHz so 512 ticks per usec */ @@ -21,59 +20,8 @@ int timestamp_tick_freq_mhz(void) return TB_TICKS_PER_USEC; } -/* - * If udelay() were to be used in bootblock, a padding for interrupt vector must - * be added to memlayout.ld or bootblock itself. - * - * In ramstage, take care to not overwrite payload with handlers or vice versa. - */ -#if ENV_ROMSTAGE - -volatile uint64_t hdec_done; - -/* - * Interrupt handler - no stack and only one register (normally used by OS) can - * be repurposed. This is enough, for now... - * - * "When a Hypervisor Decrementer interrupt occurs, the existing Hypervisor - * Decrementer exception will cease to exist within a reasonable period of time, - * but not later than the completion of the next context synchronizing - * instruction or event." - * - * 'hrfid' is context synchronizing. Note that the above does not apply to - * non-Hypervisor Decrementer - the interrupt does not result in exception - * ceasing to exist: - * - * "When the contents of DEC[0] change from 1 to 0, the existing Decrementer - * exception, if any, will cease to exist within a reasonable period of - * time, but not later than the completion of the next context synchronizing - * instruction or event." - */ -static uint32_t hdec_handler_template[] = { - 0xf9ad0000, /* std r13,0(r13) */ - 0x4c000224 /* hrfid */ -}; - void init_timer(void) { - uint64_t tmp; - - /* - * Initialize L3 cache for interrupt vectors. This could be done in - * bootblock, but then care must be taken to keep the loop properly aligned - * with regard to cache lines and different tagging schemes between levels - * of cache (real vs. effective address, different numbers of bits for index - * etc). Leaving this for romstage is safer and easier as there is no need - * for invalidation of currently running code. In this case, just two - * instructions in a loop are sufficient: - * 1. Data Cache Block set to Zero for 0..0x1000 - * 2. Instruction Cache Block Invalidate for 0..0x1000 - single 'isync' - * before enabling interrupts by writing to MSR is enough (not defined - * by ISA but in POWER9 Processor User's Manual, 4.6.2.2) - */ - for (tmp = 0; tmp < 0x1000; tmp += 128) - asm volatile("dcbz 0, %0; icbi 0, %0;" :: "r"(tmp) : "memory"); - /* * Set both decrementers to the highest possible value. POWER9 implements * 56 bits, they decrement with 512MHz frequency. Decrementer exception @@ -85,44 +33,9 @@ void init_timer(void) * Without it the counter overflows and generates an interrupt after ~4.2 s. */ - tmp = read_spr(SPR_LPCR); /* LPCR */ - /* - * 46 - LD - Large Decrementer (does not apply to HDEC) - * 59 - HEIC - Hypervisor External Interrupt Control - * 63 - HDICE - Hypervisor Decrementer Interrupt Conditionally Enable - */ - write_spr(SPR_LPCR, tmp | SPR_LPCR_LD | SPR_LPCR_HEIC | SPR_LPCR_HDICE); - + write_spr(SPR_LPCR, read_spr(SPR_LPCR) | SPR_LPCR_LD); write_spr(SPR_DEC, SPR_DEC_LONGEST_TIME); write_spr(SPR_HDEC, SPR_DEC_LONGEST_TIME); - - /* r13 is reserved for thread ID, we don't have threads so borrow it */ - asm volatile("mr 13, %0" :: "r"(&hdec_done)); - - memcpy((void *)0x980, hdec_handler_template, sizeof(hdec_handler_template)); - - /* - * Other interrupts are enabled when MSR[48] = 1, make them halt. - * - * `b .` = 0x48000000 - * `rfid` = 0x4c000024 - * `hrfid` = 0x4c000224 - * - * TODO: these interrupts shouldn't happen, we don't know how to handle - * them, maybe add a (destructive) handler that calls die()? - */ - *(uint32_t *)0x500 = 0x48000000; // External interrupt - *(uint32_t *)0xF00 = 0x48000000; // Performance monitor - *(uint32_t *)0xA00 = 0x48000000; // Privileged Doorbell - *(uint32_t *)0xE60 = 0x48000000; // Hypervisor Maintenance - *(uint32_t *)0xE80 = 0x48000000; // Hypervisor Doorbell - - *(uint32_t *)0x900 = 0x48000000; // Decrementer - - asm volatile("sync; isync" ::: "memory"); - - tmp = read_msr(); - write_msr(tmp | 0x8000); /* EE - External Interrupt Enable */ } void udelay(unsigned int usec) @@ -130,37 +43,35 @@ void udelay(unsigned int usec) uint64_t start = read_spr(SPR_TB); uint64_t end = start + usec * TB_TICKS_PER_USEC; - hdec_done = 0; - /* - * HDEC interrupt is generated "within a reasonable period of time", but - * this may not be precise enough. Set an interrupt for 1us less than + * "When the contents of the DEC0 change from 0 to 1, a Decrementer + * exception will come into existence within a reasonable period of time", + * but this may not be precise enough. Set an interrupt for 1us less than * requested and busy-loop the rest. * * In tests on Talos 2 this gives between 0 and 1/32 us more than requested, * while interrupt only solution gave between 6/32 and 11/32 us more. */ if (usec > 1) { - write_spr(SPR_HDEC, (usec - 1) * TB_TICKS_PER_USEC); + write_spr(SPR_DEC, (usec - 1) * TB_TICKS_PER_USEC); asm volatile("or 31,31,31"); // Lower priority do { asm volatile("wait"); - } while(!hdec_done); - + } while(read_spr(SPR_DEC) < SPR_DEC_LONGEST_TIME); + + /* + * "When the contents of DEC0 change from 1 to 0, the existing + * Decrementer exception, if any, will cease to exist within a + * reasonable period of time, but not later than the completion of + * the next context synchronizing instruction or event" - last part + * of sentence doesn't matter, in worst case 'wait' in next udelay() + * will be executed more than once but this is still cheaper than + * synchronizing context explicitly. + */ + write_spr(SPR_DEC, SPR_DEC_LONGEST_TIME); asm volatile("or 2,2,2"); // Back to normal priority } while (end > read_spr(SPR_TB)); } -#endif - -#if ENV_RAMSTAGE - -void udelay(unsigned int usec) -{ - /* Active, but a delay */ - (void)wait_us(usec, false); -} - -#endif