1 /* 2 * processor control and status function 3 * 4 * This code is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU Library General Public License version 2. 6 */ 7 8 #include <libcflat.h> 9 #include <asm/processor.h> 10 #include <asm/time.h> 11 #include <asm/ptrace.h> 12 #include <asm/setup.h> 13 #include <asm/barrier.h> 14 #include <asm/hcall.h> 15 #include <asm/handlers.h> 16 #include <asm/smp.h> 17 18 static struct { 19 void (*func)(struct pt_regs *, void *data); 20 void *data; 21 } handlers[128]; 22 23 /* 24 * Exception handlers span from 0x100 to 0x1000 and can have a granularity 25 * of 0x20 bytes in some cases. Indexing spans 0-0x1000 with 0x20 increments 26 * resulting in 128 slots. 27 */ 28 void handle_exception(int trap, void (*func)(struct pt_regs *, void *), 29 void * data) 30 { 31 assert(!(trap & ~0xfe0)); 32 33 trap >>= 5; 34 35 if (func && handlers[trap].func) { 36 printf("exception handler installed twice %#x\n", trap << 5); 37 abort(); 38 } 39 40 handlers[trap].func = func; 41 handlers[trap].data = data; 42 } 43 44 void do_handle_exception(struct pt_regs *regs) 45 { 46 unsigned char v; 47 48 __current_cpu = (struct cpu *)mfspr(SPR_SPRG0); 49 50 v = regs->trap >> 5; 51 52 if (v < 128 && handlers[v].func) { 53 handlers[v].func(regs, handlers[v].data); 54 return; 55 } 56 57 printf("Unhandled CPU%d exception %#lx at NIA:0x%016lx MSR:0x%016lx\n", 58 smp_processor_id(), regs->trap, regs->nip, regs->msr); 59 dump_frame_stack((void *)regs->nip, (void *)regs->gpr[1]); 60 abort(); 61 } 62 63 uint64_t get_clock_us(void) 64 { 65 return get_tb() * 1000000 / tb_hz; 66 } 67 68 uint64_t get_clock_ms(void) 69 { 70 return get_tb() * 1000 / tb_hz; 71 } 72 73 void delay(uint64_t cycles) 74 { 75 uint64_t start = get_tb(); 76 77 while ((get_tb() - start) < cycles) 78 cpu_relax(); 79 } 80 81 void udelay(uint64_t us) 82 { 83 delay((us * tb_hz) / 1000000); 84 } 85 86 void sleep_tb(uint64_t cycles) 87 { 88 uint64_t start, end, now; 89 90 if (!machine_is_pseries()) { 91 /* 92 * P9/10 Could use 'stop' to sleep here which would be 93 * interesting. stop with ESL=0 should be simple enough, ESL=1 94 * would require SRESET based wakeup which is more involved. 95 */ 96 delay(cycles); 97 return; 98 } 99 100 start = now = get_tb(); 101 end = start + cycles; 102 103 while (end > now) { 104 uint64_t left = end - now; 105 106 /* TODO: Could support large decrementer */ 107 if (left > 0x7fffffff) 108 left = 0x7fffffff; 109 110 /* DEC won't fire until H_CEDE is called because EE=0 */ 111 asm volatile ("mtdec %0" : : "r" (left)); 112 handle_exception(0x900, &dec_handler_oneshot, NULL); 113 /* 114 * H_CEDE is called with MSR[EE] clear and enables it as part 115 * of the hcall, returning with EE enabled. The dec interrupt 116 * is then taken immediately and the handler disables EE. 117 * 118 * If H_CEDE returned for any other interrupt than dec 119 * expiring, that is considered an unhandled interrupt and 120 * the test case would be stopped. 121 */ 122 if (hcall(H_CEDE) != H_SUCCESS) { 123 printf("H_CEDE failed\n"); 124 abort(); 125 } 126 handle_exception(0x900, NULL, NULL); 127 128 now = get_tb(); 129 } 130 } 131 132 void usleep(uint64_t us) 133 { 134 sleep_tb((us * tb_hz) / 1000000); 135 } 136 137 static void rfid_msr(uint64_t msr) 138 { 139 uint64_t tmp; 140 141 asm volatile( 142 "mtsrr1 %1 \n\ 143 bl 0f \n\ 144 0: \n\ 145 mflr %0 \n\ 146 addi %0,%0,1f-0b \n\ 147 mtsrr0 %0 \n\ 148 rfid \n\ 149 1: \n" 150 : "=r"(tmp) : "r"(msr) : "lr"); 151 } 152 153 void enable_mcheck(void) 154 { 155 /* This is a no-op on pseries */ 156 rfid_msr(mfmsr() | MSR_ME); 157 } 158 159 void disable_mcheck(void) 160 { 161 rfid_msr(mfmsr() & ~MSR_ME); 162 } 163