blob: 114584024563b556fadad8f8e93dc3fd27a6f0e1 [file] [log] [blame]
/*
* processor control and status function
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU Library General Public License version 2.
*/
#include <libcflat.h>
#include <asm/processor.h>
#include <asm/time.h>
#include <asm/ptrace.h>
#include <asm/setup.h>
#include <asm/barrier.h>
#include <asm/hcall.h>
#include <asm/handlers.h>
static struct {
void (*func)(struct pt_regs *, void *data);
void *data;
} handlers[128];
/*
* Exception handlers span from 0x100 to 0x1000 and can have a granularity
* of 0x20 bytes in some cases. Indexing spans 0-0x1000 with 0x20 increments
* resulting in 128 slots.
*/
void handle_exception(int trap, void (*func)(struct pt_regs *, void *),
void * data)
{
assert(!(trap & ~0xfe0));
trap >>= 5;
if (func && handlers[trap].func) {
printf("exception handler installed twice %#x\n", trap << 5);
abort();
}
handlers[trap].func = func;
handlers[trap].data = data;
}
void do_handle_exception(struct pt_regs *regs)
{
unsigned char v;
v = regs->trap >> 5;
if (v < 128 && handlers[v].func) {
handlers[v].func(regs, handlers[v].data);
return;
}
printf("Unhandled cpu exception %#lx at NIA:0x%016lx MSR:0x%016lx\n",
regs->trap, regs->nip, regs->msr);
dump_frame_stack((void *)regs->nip, (void *)regs->gpr[1]);
abort();
}
uint64_t get_clock_us(void)
{
return get_tb() * 1000000 / tb_hz;
}
uint64_t get_clock_ms(void)
{
return get_tb() * 1000 / tb_hz;
}
void delay(uint64_t cycles)
{
uint64_t start = get_tb();
while ((get_tb() - start) < cycles)
cpu_relax();
}
void udelay(uint64_t us)
{
delay((us * tb_hz) / 1000000);
}
void sleep_tb(uint64_t cycles)
{
uint64_t start, end, now;
start = now = get_tb();
end = start + cycles;
while (end > now) {
uint64_t left = end - now;
/* TODO: Could support large decrementer */
if (left > 0x7fffffff)
left = 0x7fffffff;
/* DEC won't fire until H_CEDE is called because EE=0 */
asm volatile ("mtdec %0" : : "r" (left));
handle_exception(0x900, &dec_handler_oneshot, NULL);
/*
* H_CEDE is called with MSR[EE] clear and enables it as part
* of the hcall, returning with EE enabled. The dec interrupt
* is then taken immediately and the handler disables EE.
*
* If H_CEDE returned for any other interrupt than dec
* expiring, that is considered an unhandled interrupt and
* the test case would be stopped.
*/
if (hcall(H_CEDE) != H_SUCCESS) {
printf("H_CEDE failed\n");
abort();
}
handle_exception(0x900, NULL, NULL);
now = get_tb();
}
}
void usleep(uint64_t us)
{
sleep_tb((us * tb_hz) / 1000000);
}