CoolPotOS/driver/acpi.c

396 lines
9.7 KiB
C

#include "../include/acpi.h"
#include "../include/memory.h"
#include "../include/graphics.h"
#include "../include/io.h"
#include "../include/isr.h"
#include "../include/timer.h"
#include "../include/common.h"
uint16_t SLP_TYPa;
uint16_t SLP_TYPb;
uint32_t SMI_CMD;
uint8_t ACPI_ENABLE;
uint8_t ACPI_DISABLE;
uint32_t *PM1a_CNT;
uint32_t *PM1b_CNT;
uint8_t PM1_CNT_LEN;
uint16_t SLP_EN;
uint16_t SCI_EN;
acpi_rsdt_t *rsdt; // root system descript table
acpi_facp_t *facp; // fixed ACPI table
int acpi_enable_flag;
uint8_t *rsdp_address;
int acpi_enable() {
int i;
// check if enable ACPI
if (io_in16((uint32_t) PM1a_CNT) & SCI_EN) {
printf("ACPI already enable!\n");
return 0;
}
if (SMI_CMD && ACPI_ENABLE) {
// send ACPI_ENABLE cmd
io_out8((uint16_t)
SMI_CMD, ACPI_ENABLE);
// wait enable
for (i = 0; i < 300; i++) {
if (io_in16((uint32_t) PM1a_CNT) & SCI_EN) {
break;
}
clock_sleep(5);
}
// wait enable
if (PM1b_CNT) {
for (i = 0; i < 300; i++) {
if (io_in16((uint32_t) PM1b_CNT) & SCI_EN) {
break;
}
clock_sleep(5);
}
}
// check enable status
if (i < 300) {
printf("[acpi]: Enable ACPI\n");
return 0;
} else {
printf("Counld't enable ACPI\n");
return -1;
}
}
return -1;
}
int acpi_disable() {
int i;
if (!io_in16((uint16_t)PM1a_CNT) &SCI_EN)
return 0;
if (SMI_CMD || ACPI_DISABLE) {
io_out8((uint16_t)
SMI_CMD, ACPI_DISABLE);
for (i = 0; i < 300; i++) {
if (!io_in16((uint16_t)PM1a_CNT) &SCI_EN)
break;
clock_sleep(5);
}
for (i = 0; i < 300; i++) {
if (!io_in16((uint16_t)PM1b_CNT) &SCI_EN)
break;
clock_sleep(5);
}
if (i < 300) {
printf("ACPI Disable!\n");
return 0;
} else {
printf("Could't disable ACPI\n");
return -1;
}
}
return -1;
}
uint8_t *AcpiGetRSDPtr() {
uint32_t *addr;
uint32_t *rsdt;
uint32_t ebda;
for (addr = (uint32_t *) 0x000E0000; addr < (uint32_t *) 0x00100000; addr += 0x10 / sizeof(addr)) {
rsdt = AcpiCheckRSDPtr(addr);
if (rsdt) {
return rsdt;
}
}
// search extented bios data area for the root system description pointer signature
ebda = *(uint16_t * )
0x40E;
ebda = ebda * 0x10 & 0xfffff;
for (addr = (uint32_t *) ebda; addr < (uint32_t * )(ebda + 1024); addr += 0x10 / sizeof(addr)) {
rsdt = AcpiCheckRSDPtr(addr);
if (rsdt) {
return rsdt;
}
}
return NULL;
}
void power_reset() {
uint8_t val;
if (!SCI_EN)
return;
while (1) {
// write ICH port
io_out8(0x92, 0x01);
// send RESET_VAL
io_out8((uint32_t) facp->RESET_REG.address, facp->RESET_VALUE);
}
}
void power_off() {
if (!SCI_EN)
return;
while (1) {
printf("[acpi] send power off command!\n");
io_out16((uint32_t) PM1a_CNT, SLP_TYPa | SLP_EN);
if (!PM1b_CNT) {
io_out16((uint32_t) PM1b_CNT, SLP_TYPb | SLP_EN);
}
}
}
static int AcpiPowerHandler(registers_t *irq) {
uint16_t status = io_in16((uint32_t) facp->PM1a_EVT_BLK);
// check if power button press
if (status & (1 << 8)) {
io_out16((uint32_t) facp->PM1a_EVT_BLK, status &= ~(1 << 8)); // clear bits
printf("Shutdown OS...");
shutdown_kernel();
return 0;
}
if (!facp->PM1b_EVT_BLK)
return -1;
// check if power button press
status = io_in16((uint32_t) facp->PM1b_EVT_BLK);
if (status & (1 << 8)) {
io_out16((uint32_t) facp->PM1b_EVT_BLK, status &= ~(1 << 8));
printf("Shutdown OS...");
shutdown_kernel();
return 0;
}
return -1;
}
static void AcpiPowerInit() {
if (!facp) return;
uint8_t len = facp->PM1_EVT_LEN / 2;
uint32_t *PM1a_ENABLE_REG = facp->PM1a_EVT_BLK + len;
uint32_t *PM1b_ENABLE_REG = facp->PM1b_EVT_BLK + len;
if (PM1b_ENABLE_REG == len)
PM1b_ENABLE_REG = 0;
printf("[acpi]: Setting Power event listener...\n");
io_out16(PM1a_ENABLE_REG, (1 << 8));
if (PM1b_ENABLE_REG) {
io_out16((uint16_t)
PM1b_ENABLE_REG, (uint8_t)(1 << 8));
}
printf("ACPI : SCI_INT %08x\n", (uint8_t)
facp->SCI_INT);
register_interrupt_handler(facp->SCI_INT, AcpiPowerHandler);
}
int AcpiCheckHeader(void *ptr, uint8_t *sign) {
uint8_t * bptr = ptr;
uint32_t len = *(bptr + 4);
uint8_t checksum = 0;
if (!memcmp(bptr, sign, 4)) {
while (len-- > 0) {
checksum += *bptr++;
}
return 0;
}
return -1;
}
uint8_t *AcpiCheckRSDPtr(void *ptr) {
char *sign = "RSD PTR ";
acpi_rsdptr_t *rsdp = ptr;
uint8_t * bptr = ptr;
uint32_t i = 0;
uint8_t check = 0;
// check signature
if (!memcmp(sign, bptr, 8)) {
printf("[acpi] rsdp found at %0x\n", bptr);
rsdp_address = bptr;
for (i = 0; i < sizeof(acpi_rsdptr_t); i++) {
check += *bptr;
bptr++;
}
if (!check) {
return (uint8_t * )
rsdp->rsdt;
}
}
return NULL;
}
uint32_t AcpiGetMadtBase() {
uint32_t entrys = rsdt->length - HEADER_SIZE / 4;
uint32_t **p = &(rsdt->entry);
acpi_madt_t *madt = NULL;
while (--entrys) {
if (!AcpiCheckHeader(*p, "APIC")) {
madt = (acpi_madt_t *) *p;
return madt;
}
p++;
}
return 0;
}
static int AcpiSysInit() {
uint32_t **p;
uint32_t entrys;
uint32_t dsdtlen;
rsdt = (acpi_rsdt_t *) AcpiGetRSDPtr();
if (!rsdt || AcpiCheckHeader(rsdt, "RSDT") < 0) {
printf("No ACPI\n");
return -1;
}
entrys = rsdt->length - HEADER_SIZE / 4;
p = &(rsdt->entry);
while (entrys--) {
if (!AcpiCheckHeader(*p, "FACP")) {
facp = (acpi_facp_t *) *p;
ACPI_ENABLE = facp->ACPI_ENABLE;
ACPI_DISABLE = facp->ACPI_DISABLE;
SMI_CMD = facp->SMI_CMD;
PM1a_CNT = facp->PM1a_CNT_BLK;
PM1b_CNT = facp->PM1b_CNT_BLK;
PM1_CNT_LEN = facp->PM1_CNT_LEN;
SLP_EN = 1 << 13;
SCI_EN = 1;
uint8_t * S5Addr;
uint32_t dsdtlen;
if (!AcpiCheckHeader(facp->DSDT, "DSDT")) {
S5Addr = &(facp->DSDT->definition_block);
dsdtlen = facp->DSDT->length - HEADER_SIZE;
while (dsdtlen--) {
if (!memcmp(S5Addr, "_S5_", 4)) {
break;
}
S5Addr++;
}
if (dsdtlen) {
// check valid \_S5
if (*(S5Addr - 1) == 0x08 || (*(S5Addr - 2) == 0x08 && *(S5Addr - 1) == '\\')) {
S5Addr += 5;
S5Addr += ((*S5Addr & 0xC0) >> 6) + 2;
if (*S5Addr == 0x0A) {
S5Addr++;
}
SLP_TYPa = *(S5Addr) << 10;
S5Addr++;
if (*S5Addr == 0x0A) {
S5Addr++;
}
SLP_TYPb = *(S5Addr) << 10;
S5Addr++;
} else {
printf("[acpi] \\_S5 parse error!\n");
}
} else {
printf("[acpi] \\_S5 not present!\n");
}
} else {
printf("[acpi] no found DSDT table\n");
}
return 0;
}
++p;
}
printf("[acpi] No valid FACP present\n");
}
void acpi_install() {
AcpiSysInit();
acpi_enable_flag = !acpi_enable();
// power init
hpet_initialize();
AcpiPowerInit();
}
static HpetInfo *hpetInfo = NULL;
static uint32_t hpetPeriod = 0;
uint32_t nanoTime() {
uint32_t mcv = hpetInfo->mainCounterValue;
while (1)asm("hlt");
return mcv * hpetPeriod;
}
void usleep(uint32_t nano) {
uint32_t targetTime = nanoTime();
uint32_t after = 0;
while (1) {
uint64_t n = nanoTime();
if (n < targetTime) {
after += 0xffffffff - targetTime + n;
targetTime = n;
} else {
after += n - targetTime;
targetTime = n;
}
if (after >= nano) {
return;
}
}
}
unsigned int acpi_find_table(char *Signature) {
uint8_t * ptr, *ptr2;
uint32_t len;
uint8_t * rsdt_t = (uint8_t * )
rsdt;
// iterate on ACPI table pointers
for (len = *((uint32_t * )(rsdt_t + 4)), ptr2 = rsdt_t + 36; ptr2 < rsdt_t + len;
ptr2 += (char *) rsdt_t[0] == 'X' ? 8 : 4) {
ptr = (uint8_t * )(uintptr_t)(rsdt_t[0] == 'X' ? *((uint64_t *) ptr2)
: *((uint32_t *) ptr2));
if (!memcmp(ptr, Signature, 4)) {
return (unsigned) ptr;
}
}
// printk("not found.\n");
return 0;
}
void hpet_initialize() {
HPET *hpet = acpi_find_table("HPET");
if (!hpet) {
printf("can not found acpi hpet table\n");
}
printf("[acpi-hpet]: OEM: %s | Version: %d\n",hpet->oem,hpet->oemVersion);
hpetInfo = (HpetInfo *) hpet->hpetAddress.address;
uint32_t counterClockPeriod = hpetInfo->generalCapabilities >> 32;
hpetPeriod = counterClockPeriod / 1000000;
hpetInfo->generalConfiguration |= 1; // 启用hpet
printf("[acpi]: hpet successfully enabled.\n");
}