diff --git a/src/add-ons/kernel/drivers/Jamfile b/src/add-ons/kernel/drivers/Jamfile index 241c5bebb9..9243e8f42b 100644 --- a/src/add-ons/kernel/drivers/Jamfile +++ b/src/add-ons/kernel/drivers/Jamfile @@ -16,4 +16,5 @@ SubInclude HAIKU_TOP src add-ons kernel drivers power ; SubInclude HAIKU_TOP src add-ons kernel drivers printer ; SubInclude HAIKU_TOP src add-ons kernel drivers random ; SubInclude HAIKU_TOP src add-ons kernel drivers tty ; +SubInclude HAIKU_TOP src add-ons kernel drivers timer ; SubInclude HAIKU_TOP src add-ons kernel drivers video ; diff --git a/src/add-ons/kernel/drivers/timer/Jamfile b/src/add-ons/kernel/drivers/timer/Jamfile new file mode 100644 index 0000000000..e7d6e700a2 --- /dev/null +++ b/src/add-ons/kernel/drivers/timer/Jamfile @@ -0,0 +1,6 @@ +SubDir HAIKU_TOP src add-ons kernel drivers timer ; + +UsePrivateHeaders drivers ; + +KernelAddon hpet : + hpet.cpp ; diff --git a/src/add-ons/kernel/drivers/timer/arch_acpi.h b/src/add-ons/kernel/drivers/timer/arch_acpi.h new file mode 100644 index 0000000000..0e1ae043df --- /dev/null +++ b/src/add-ons/kernel/drivers/timer/arch_acpi.h @@ -0,0 +1,206 @@ +/* + * Copyright 2008, Dustin Howett, dustin.howett@gmail.com. All rights reserved. + * Copyright 2007, Michael Lotz, mmlr@mlotz.ch. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _KERNEL_ARCH_x86_ARCH_ACPI_H +#define _KERNEL_ARCH_x86_ARCH_ACPI_H + +#define ACPI_RSDP_SIGNATURE "RSD PTR " +#define ACPI_RSDT_SIGNATURE "RSDT" +#define ACPI_XSDT_SIGNATURE "XSDT" +#define ACPI_MADT_SIGNATURE "APIC" + +#define ACPI_LOCAL_APIC_ENABLED 0x01 + +typedef struct acpi_rsdp_legacy { + char signature[8]; /* "RSD PTR " including blank */ + uint8 checksum; /* checksum of bytes 0-19 (per ACPI 1.0) */ + char oem_id[6]; /* not null terminated */ + uint8 revision; /* 0 = ACPI 1.0, 2 = ACPI 3.0 */ + uint32 rsdt_address; /* physical memory address of RSDT */ +} _PACKED acpi_rsdp_legacy; + +typedef struct acpi_rsdp_extended { + char signature[8]; /* "RSD PTR " including blank */ + uint8 checksum; /* checksum of bytes 0-19 (per ACPI 1.0) */ + char oem_id[6]; /* not null terminated */ + uint8 revision; /* 0 = ACPI 1.0, 2 = ACPI 3.0 */ + uint32 rsdt_address; /* physical memory address of RSDT */ + uint32 xsdt_length; /* length in bytes including header */ + uint64 xsdt_address; /* 64bit physical memory address of XSDT */ + uint8 extended_checksum; /* including entire table */ + uint8 reserved[3]; +} _PACKED acpi_rsdp_extended; + +typedef acpi_rsdp_extended acpi_rsdp; + +typedef struct acpi_descriptor_header { + char signature[4]; /* table identifier as ASCII string */ + uint32 length; /* length in bytes of the entire table */ + uint8 revision; + uint8 checksum; /* checksum of entire table */ + char oem_id[6]; /* not null terminated */ + char oem_table_id[8]; /* oem supplied table identifier */ + uint32 oem_revision; /* oem supplied revision number */ + char creator_id[4]; /* creator / asl compiler id */ + uint32 creator_revision; /* compiler revision */ +} _PACKED acpi_descriptor_header; + +typedef struct acpi_madt { + acpi_descriptor_header header; /* "APIC" signature */ + uint32 local_apic_address; /* physical address for local CPUs APICs */ + uint32 flags; +} _PACKED acpi_madt; + +enum { + ACPI_MADT_LOCAL_APIC = 0, + ACPI_MADT_IO_APIC = 1, + ACPI_MADT_INTERRUPT_SOURCE_OVERRIDE = 2, + ACPI_MADT_NMI_SOURCE = 3, + ACPI_MADT_LOCAL_APIC_NMI = 4, + ACPI_MADT_LOCAL_APIC_ADDRESS_OVERRIDE = 5, + ACPI_MADT_IO_SAPIC = 6, + ACPI_MADT_LOCAL_SAPIC = 7, + ACPI_MADT_PLATFORM_INTERRUPT_SOURCE = 8, + ACPI_MADT_PROCESSOR_LOCAL_X2_APIC_NMI = 9, + ACPI_MADT_LOCAL_X2_APIC_NMI = 0XA +}; + +typedef struct acpi_apic { + uint8 type; + uint8 length; +} _PACKED acpi_apic; + +typedef struct acpi_local_apic { + uint8 type; /* 0 = processor local APIC */ + uint8 length; /* 8 bytes */ + uint8 acpi_processor_id; + uint8 apic_id; /* the id of this APIC */ + uint32 flags; /* 1 = enabled */ +} _PACKED acpi_local_apic; + +typedef struct acpi_io_apic { + uint8 type; /* 1 = I/O APIC */ + uint8 length; /* 12 bytes */ + uint8 io_apic_id; /* the id of this APIC */ + uint8 reserved; + uint32 io_apic_address; /* physical address of I/O APIC */ + uint32 interrupt_base; /* global system interrupt base */ +} _PACKED acpi_io_apic; + +typedef struct acpi_int_source_override { + uint8 type; /* 2 = Interrupt source override */ + uint8 length; /* 10 bytes */ + uint8 bus; /* 0 = ISA */ + uint8 source; /* Bus-relative interrupt source (IRQ) */ + uint32 interrupt; /* global system interrupt this + bus-relative source int will signal */ + uint16 flags; /* MPS INTI flags. See Table 5-25 in + ACPI Spec 4.0a or similar */ +} _PACKED acpi_int_source_override; + +typedef struct acpi_nmi_source { + uint8 type; /* 3 = NMI */ + uint8 length; /* 8 bytes */ + uint16 flags; /* Same as MPS INTI flags. See Table 5-25 in + ACPI Spec 4.0a or similar */ + uint32 interrupt; /* global system interrupt this + non-maskable interrupt will trigger */ +} _PACKED acpi_nmi_source; + +typedef struct acpi_local_apic_nmi { + uint8 type; /* 4 = local APIC NMI */ + uint8 length; /* 6 bytes */ + uint8 acpi_processor_id; /* Processor ID corresponding to processor + ID in acpi_local_apic. 0xFF means + it applies to all processors */ + uint16 flags; /* Same as MPS INTI flags. See Table 5-25 in + ACPI Spec 4.0a or similar */ + uint8 local_interrupt; /* Local APIC interrupt input LINTn to which + NMI is connected */ +} _PACKED acpi_local_apic_nmi; + +typedef struct acpi_local_apic_address_override { + uint8 type; /* 5 = local APIC address override */ + uint8 length; /* 12 bytes */ + uint16 reserved; /* reserved (must be set to zero) */ + uint64 local_apic_address; /* Physical address of local APIC. See table + 5-28 in ACPI Spec 4.0a for more */ +} _PACKED acpi_local_apic_address_override; + +typedef struct acpi_io_sapic { + uint8 type; /* 6 = I/0 SAPIC (should be used if it + exists instead of I/O APIC if both exists + for a APIC ID.*/ + uint8 length; /* 16 bytes */ + uint8 io_apic_id; /* the id of this SAPIC */ + uint8 reserved; /* reserved (must be set to zero) */ + uint32 interrupt_base; /* global system interrupt base */ + uint64 sapic_address; /* The physical address to access this I/0 + SAPIC. Each SAPIC resides at a unique + address */ +} _PACKED acpi_io_sapic; + +typedef struct acpi_local_sapic { + uint8 type; /* 7 = processor local SAPIC */ + uint8 length; /* n bytes */ + uint8 acpi_processor_id; + uint8 local_sapic_id; + uint8 local_sapic_eid; + uint8 reserved1; /* reserved (must be set to zero) */ + uint8 reserved2; /* reserved (must be set to zero) */ + uint8 reserved3; /* reserved (must be set to zero) */ + uint32 flags; /* Local SAPIC flags, see table 5-22 in + ACPI Spec 4.0a */ + uint32 processor_uid_nr; /* Matches _UID of a processor when it is a + number */ + char processor_uid_str[]; /* Matches _UID of a processor when it is a + string. Null-terminated */ +} _PACKED acpi_local_sapic; + +typedef struct acpi_platform_interrupt_source { + uint8 type; /* 8 = platform interrupt source */ + uint8 length; /* 16 bytes */ + uint16 flags; /* Same as MPS INTI flags. See Table 5-25 in + ACPI Spec 4.0a or similar */ + uint8 interrupt_type; /* 1 PMI, 2 INIT, 3 Corrected Platform + Error Interrupt */ + uint8 processor_id; /* processor ID of destination */ + uint8 processor_eid; /* processor EID of destination */ + uint8 io_sapic_vector; /* value that must be used to program the + vector field of the I/O SAPIC redirection + entry for entries with PMI type. */ + uint32 interrupt; /* global system interrupt this + platform interrupt will trigger */ + uint32 platform_int_flags; /* Platform Interrupt Source Flags. See + Table 5-32 of ACPI Spec 4.0a for desc */ +} _PACKED acpi_platform_interrupt_source; + +typedef struct acpi_local_x2_apic { + uint8 type; /* 9 = processor local x2APIC */ + uint8 length; /* 16 bytes */ + uint16 reserved; /* reserved (must be zero) */ + uint32 x2apic_id; /* processor's local x2APIC ID */ + uint32 flags; /* 1 = enabled. */ + uint32 processor_uid_nr; /* Matches _UID of a processor when it is a + number */ +} _PACKED acpi_local_x2_apic; + +typedef struct acpi_local_x2_apic_nmi { + uint8 type; /* 0xA = local x2APIC NMI */ + uint8 length; /* 12 bytes */ + uint16 flags; /* Same as MPS INTI flags. See Table 5-25 in + ACPI Spec 4.0a or similar */ + uint32 acpi_processor_uid; /* UID corresponding to ID in processor + device object. 0xFFFFFFFF means + it applies to all processors */ + uint8 local_interrupt; /* Local x2APIC interrupt input LINTn to + which NMI is connected */ + uint8 reserved1; /* reserved (must be set to zero) */ + uint8 reserved2; /* reserved (must be set to zero) */ + uint8 reserved3; /* reserved (must be set to zero) */ +} _PACKED acpi_local_x2_apic_nmi; + + +#endif /* _KERNEL_ARCH_x86_ARCH_ACPI_H */ diff --git a/src/add-ons/kernel/drivers/timer/hpet.cpp b/src/add-ons/kernel/drivers/timer/hpet.cpp new file mode 100644 index 0000000000..58a84dcdde --- /dev/null +++ b/src/add-ons/kernel/drivers/timer/hpet.cpp @@ -0,0 +1,508 @@ +/* + * Copyright 2009-2010, Stefano Ceccherini (stefano.ceccherini@gmail.com) + * Copyright 2008, Dustin Howett, dustin.howett@gmail.com. All rights reserved. + * Distributed under the terms of the MIT License. + */ + +#include "hpet.h" +#include "hpet_interface.h" +#include "int.h" +#include "msi.h" + +#include +#include +#include +#include + +#include +#include + + +#define TRACE_HPET +#ifdef TRACE_HPET + #define TRACE(x) dprintf x +#else + #define TRACE(x) ; +#endif + +#define TEST_HPET + + +static struct hpet_regs *sHPETRegs; +static uint64 sHPETPeriod; + +static area_id sHPETArea; + + +struct hpet_timer_cookie { + int number; + int32 irq; + sem_id sem; +}; + +//////////////////////////////////////////////////////////////////////////////// + +static status_t hpet_open(const char*, uint32, void**); +static status_t hpet_close(void*); +static status_t hpet_free(void*); +static status_t hpet_control(void*, uint32, void*, size_t); +static ssize_t hpet_read(void*, off_t, void*, size_t*); +static ssize_t hpet_write(void*, off_t, const void*, size_t*); + +//////////////////////////////////////////////////////////////////////////////// + +static const char* hpet_name[] = { + "misc/hpet", + NULL +}; + + +device_hooks hpet_hooks = { + hpet_open, + hpet_close, + hpet_free, + hpet_control, + hpet_read, + hpet_write, +}; + +int32 api_version = B_CUR_DRIVER_API_VERSION; + +static acpi_module_info* sAcpi; +static vint32 sOpenCount; + + +static inline bigtime_t +hpet_convert_timeout(const bigtime_t &relativeTimeout) +{ + bigtime_t counter = sHPETRegs->u0.counter64; + bigtime_t converted = (1000000000ULL / sHPETPeriod) * relativeTimeout; + + dprintf("counter: %lld, relativeTimeout: %lld, converted: %lld\n", + counter, relativeTimeout, converted); + + return converted + counter; +} + + +#define MIN_TIMEOUT 3000 + +static status_t +hpet_set_hardware_timer(bigtime_t relativeTimeout, volatile hpet_timer *timer) +{ + // TODO: + if (relativeTimeout < MIN_TIMEOUT) + relativeTimeout = MIN_TIMEOUT; + + bigtime_t timerValue = hpet_convert_timeout(relativeTimeout); + + //dprintf("comparator: %lld, new value: %lld\n", timer->u0.comparator64, timerValue); + + timer->u0.comparator64 = timerValue; + + // enable timer interrupt + timer->config |= HPET_CONF_TIMER_INT_ENABLE; + + return B_OK; +} + + +static status_t +hpet_clear_hardware_timer(volatile hpet_timer *timer) +{ + // Disable timer interrupt + timer->config &= ~HPET_CONF_TIMER_INT_ENABLE; + return B_OK; +} + + +static int32 +hpet_timer_interrupt(void *arg) +{ + //dprintf("HPET timer_interrupt!!!!\n"); + hpet_timer_cookie* hpetCookie = (hpet_timer_cookie*)arg; + + // clear interrupt status + int32 intStatus = 1 << hpetCookie->number; + if (sHPETRegs->interrupt_status & intStatus) { + sHPETRegs->interrupt_status |= intStatus; + hpet_clear_hardware_timer(&sHPETRegs->timer[hpetCookie->number]); + + release_sem(hpetCookie->sem); + return B_HANDLED_INTERRUPT; + } + + return B_UNHANDLED_INTERRUPT; +} + + +static status_t +hpet_set_enabled(bool enabled) +{ + if (enabled) + sHPETRegs->config |= HPET_CONF_MASK_ENABLED; + else + sHPETRegs->config &= ~HPET_CONF_MASK_ENABLED; + return B_OK; +} + + +static status_t +hpet_set_legacy(bool enabled) +{ + if (!HPET_IS_LEGACY_CAPABLE(sHPETRegs)) { + dprintf("hpet_init: HPET doesn't support legacy mode. Skipping.\n"); + return B_NOT_SUPPORTED; + } + + if (enabled) + sHPETRegs->config |= HPET_CONF_MASK_LEGACY; + else + sHPETRegs->config &= ~HPET_CONF_MASK_LEGACY; + + return B_OK; +} + + +#ifdef TRACE_HPET +static void +hpet_dump_timer(volatile struct hpet_timer *timer) +{ + dprintf("HPET Timer %ld:\n", (timer - sHPETRegs->timer)); + dprintf("CAP/CONFIG register: 0x%llx\n", timer->config); + dprintf("Capabilities:\n"); + dprintf("\troutable IRQs: "); + uint32 interrupts = (uint32)HPET_GET_CAP_TIMER_ROUTE(timer); + for (int i = 0; i < 32; i++) { + if (interrupts & (1 << i)) + dprintf("%d ", i); + } + + dprintf("\n\tsupports FSB delivery: %s\n", + timer->config & HPET_CAP_TIMER_FSB_INT_DEL ? "Yes" : "No"); + + + dprintf("\n"); + dprintf("Configuration:\n"); + dprintf("\tFSB Enabled: %s\n", + timer->config & HPET_CONF_TIMER_FSB_ENABLE ? "Yes" : "No"); + dprintf("\tInterrupt Enabled: %s\n", + timer->config & HPET_CONF_TIMER_INT_ENABLE ? "Yes" : "No"); + dprintf("\tTimer type: %s\n", + timer->config & HPET_CONF_TIMER_TYPE ? "Periodic" : "OneShot"); + dprintf("\tInterrupt Type: %s\n", + timer->config & HPET_CONF_TIMER_INT_TYPE ? "Level" : "Edge"); + + dprintf("\tconfigured IRQ: %lld\n", + HPET_GET_CONF_TIMER_INT_ROUTE(timer)); + + if (timer->config & HPET_CONF_TIMER_FSB_ENABLE) { + dprintf("\tfsb_route[0]: 0x%llx\n", timer->fsb_route[0]); + dprintf("\tfsb_route[1]: 0x%llx\n", timer->fsb_route[1]); + } +} +#endif + + +static void +hpet_init_timer(volatile struct hpet_timer *timer) +{ + uint32 interrupts = (uint32)HPET_GET_CAP_TIMER_ROUTE(timer); + + // TODO: Check if the interrupt is already used, and try another + uint32 interrupt = 0; + for (int i = 0; i < 32; i++) { + if (interrupts & (1 << i)) { + interrupt = i; + break; + } + } + + timer->config = 0; + + timer->config |= (interrupt << HPET_CONF_TIMER_INT_ROUTE_SHIFT) + & HPET_CONF_TIMER_INT_ROUTE_MASK; + + // Non-periodic mode + timer->config &= ~HPET_CONF_TIMER_TYPE; + + // level triggered + timer->config |= HPET_CONF_TIMER_INT_TYPE; + + // Disable FSB/MSI, enable 64 bit mode + timer->config &= ~HPET_CONF_TIMER_FSB_ENABLE; + timer->config &= ~HPET_CONF_TIMER_32MODE; + + +#ifdef TRACE_HPET + hpet_dump_timer(timer); +#endif +} + + +static status_t +hpet_configure_interrupt(volatile hpet_timer* timer, int32 *irq) +{ + status_t status = B_OK; + *irq = HPET_GET_CONF_TIMER_INT_ROUTE(timer); + // TODO: Configure interrupt using msi or regular irqs + return status; +} + + +static status_t +hpet_test() +{ + uint64 initialValue = sHPETRegs->u0.counter64; + spin(10); + uint64 finalValue = sHPETRegs->u0.counter64; + + if (initialValue == finalValue) { + dprintf("hpet_test: counter does not increment\n"); + return B_ERROR; + } + + return B_OK; +} + + +static status_t +hpet_init() +{ + if (sHPETRegs == NULL) + return B_NO_INIT; + + sHPETPeriod = HPET_GET_PERIOD(sHPETRegs); + + TRACE(("hpet_init: HPET is at %p.\n\tVendor ID: %llx, rev: %llx, period: %lld\n", + sHPETRegs, HPET_GET_VENDOR_ID(sHPETRegs), HPET_GET_REVID(sHPETRegs), + sHPETPeriod)); + + status_t status = hpet_set_enabled(false); + if (status != B_OK) + return status; + + status = hpet_set_legacy(false); + if (status != B_OK) + return status; + + uint32 numTimers = HPET_GET_NUM_TIMERS(sHPETRegs) + 1; + + TRACE(("hpet_init: HPET supports %lu timers, and is %s bits wide.\n", + numTimers, HPET_IS_64BIT(sHPETRegs) ? "64" : "32")); + + TRACE(("hpet_init: configuration: 0x%llx, timer_interrupts: 0x%llx\n", + sHPETRegs->config, sHPETRegs->interrupt_status)); + + if (numTimers < 3) { + dprintf("hpet_init: HPET does not have at least 3 timers. Skipping.\n"); + return B_ERROR; + } + +/* +#ifdef TRACE_HPET + for (uint32 c = 0; c < numTimers; c++) + hpet_dump_timer(&sHPETRegs->timer[c]); +#endif +*/ + sHPETRegs->interrupt_status = 0; + + status = hpet_set_enabled(true); + if (status != B_OK) + return status; + +#ifdef TEST_HPET + status = hpet_test(); + if (status != B_OK) + return status; +#endif + + return status; +} + + +//////////////////////////////////////////////////////////////////////////////// + + +status_t +init_hardware(void) +{ + return B_OK; +} + + +status_t +init_driver(void) +{ + sOpenCount = 0; + + status_t status = get_module(B_ACPI_MODULE_NAME, (module_info**)&sAcpi); + if (status < B_OK) + return status; + + acpi_hpet *hpetTable; + status = sAcpi->get_table(ACPI_HPET_SIGNATURE, 0, + (void**)&hpetTable); + + if (status != B_OK) { + put_module(B_ACPI_MODULE_NAME); + return status; + } + + sHPETArea = map_physical_memory("HPET registries", + hpetTable->hpet_address.address, B_PAGE_SIZE, 0, + 0, (void**)&sHPETRegs); + + if (sHPETArea < 0) { + put_module(B_ACPI_MODULE_NAME); + return sHPETArea; + } + + return hpet_init(); +} + + +void +uninit_driver(void) +{ + hpet_set_enabled(false); + + if (sHPETArea > 0) + delete_area(sHPETArea); + + put_module(B_ACPI_MODULE_NAME); +} + + +const char** +publish_devices(void) +{ + return hpet_name; +} + + +device_hooks* +find_device(const char* name) +{ + return &hpet_hooks; +} + + +//////////////////////////////////////////////////////////////////////////////// +// #pragma mark - + + +status_t +hpet_open(const char* name, uint32 flags, void** cookie) +{ + *cookie = NULL; + + if (sHPETRegs == NULL) + return B_NO_INIT; + + if (atomic_add(&sOpenCount, 1) != 0) { + atomic_add(&sOpenCount, -1); + return B_BUSY; + } + + hpet_timer_cookie* hpetCookie = (hpet_timer_cookie*)malloc(sizeof(hpet_timer_cookie)); + int timerNumber = 2; + hpetCookie->number = timerNumber; + hpetCookie->sem = create_sem(0, "hpet_timer 2 sem"); + set_sem_owner(hpetCookie->sem, B_SYSTEM_TEAM); + + hpet_set_enabled(false); + + hpet_init_timer(&sHPETRegs->timer[timerNumber]); + + status_t status = hpet_configure_interrupt(&sHPETRegs->timer[timerNumber], &hpetCookie->irq); + if (status == B_OK) + status = install_io_interrupt_handler(hpetCookie->irq, &hpet_timer_interrupt, hpetCookie, 0); + if (status != B_OK) + dprintf("hpet_open(): cannot install interrupt handler: %s\n", strerror(status)); + else + dprintf("hpet_open(): HPET timer uses irq %ld\n", hpetCookie->irq); + + hpet_set_enabled(true); + + *cookie = hpetCookie; + + return status; +} + + +status_t +hpet_close(void* cookie) +{ + if (sHPETRegs == NULL) + return B_NO_INIT; + + atomic_add(&sOpenCount, -1); + + hpet_timer_cookie* hpetCookie = (hpet_timer_cookie*)cookie; + + dprintf("hpet_close (%d)\n", hpetCookie->number); + hpet_clear_hardware_timer(&sHPETRegs->timer[hpetCookie->number]); + remove_io_interrupt_handler(hpetCookie->irq, &hpet_timer_interrupt, hpetCookie); + + return B_OK; +} + + +status_t +hpet_free(void* cookie) +{ + if (sHPETRegs == NULL) + return B_NO_INIT; + + hpet_timer_cookie* hpetCookie = (hpet_timer_cookie*)cookie; + + delete_sem(hpetCookie->sem); + + free(cookie); + + return B_OK; +} + + +status_t +hpet_control(void* cookie, uint32 op, void* arg, size_t length) +{ + hpet_timer_cookie* hpetCookie = (hpet_timer_cookie*)cookie; + status_t status = B_BAD_VALUE; + + switch (op) { + case HPET_WAIT_TIMER: + { + bigtime_t value = *(bigtime_t*)arg; + dprintf("hpet: wait timer (%d) for %lld...\n", hpetCookie->number, value); + hpet_set_hardware_timer(value, &sHPETRegs->timer[hpetCookie->number]); + status = acquire_sem_etc(hpetCookie->sem, 1, B_CAN_INTERRUPT, B_INFINITE_TIMEOUT); + break; + } + default: + break; + + } + + return status; +} + + +ssize_t +hpet_read(void* cookie, off_t position, void* buffer, size_t* numBytes) +{ + //hpet_timer_cookie* hpetCookie = (hpet_timer_cookie*)cookie; + *(uint64*)buffer = sHPETRegs->u0.counter64; + + return sizeof(uint64); +} + + +ssize_t +hpet_write(void* cookie, off_t position, const void* buffer, size_t* numBytes) +{ + *numBytes = 0; + return B_NOT_ALLOWED; +} + diff --git a/src/add-ons/kernel/drivers/timer/hpet.h b/src/add-ons/kernel/drivers/timer/hpet.h new file mode 100644 index 0000000000..d82eea336c --- /dev/null +++ b/src/add-ons/kernel/drivers/timer/hpet.h @@ -0,0 +1,118 @@ +/* + * Copyright 2008, Dustin Howett, dustin.howett@gmail.com. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _KERNEL_ARCH_x86_HPET_H +#define _KERNEL_ARCH_x86_HPET_H + +#include +#include "arch_acpi.h" + +/* All masks are 32 bits wide to represent relative bit locations */ +/* Doing it this way is Required since the HPET only supports 32/64-bit aligned reads. */ + +/* Global Capability Register Masks */ +#define HPET_CAP_MASK_REVID 0x00000000000000FFULL +#define HPET_CAP_MASK_NUMTIMERS 0x0000000000001F00ULL +#define HPET_CAP_MASK_WIDTH 0x0000000000002000ULL +#define HPET_CAP_MASK_LEGACY 0x0000000000008000ULL +#define HPET_CAP_MASK_VENDOR_ID 0x00000000FFFF0000ULL +#define HPET_CAP_MASK_PERIOD 0xFFFFFFFF00000000ULL + +/* Retrieve Global Capabilities */ +#define HPET_GET_REVID(regs) ((regs)->capabilities & HPET_CAP_MASK_REVID) +#define HPET_GET_NUM_TIMERS(regs) (((regs)->capabilities & HPET_CAP_MASK_NUMTIMERS) >> 8) +#define HPET_IS_64BIT(regs) (((regs)->capabilities & HPET_CAP_MASK_WIDTH) >> 13) +#define HPET_IS_LEGACY_CAPABLE(regs) (((regs)->capabilities & HPET_CAP_MASK_LEGACY) >> 15) +#define HPET_GET_VENDOR_ID(regs) (((regs)->capabilities & HPET_CAP_MASK_VENDOR_ID) >> 16) +#define HPET_GET_PERIOD(regs) (((regs)->capabilities & HPET_CAP_MASK_PERIOD) >> 32) + +/* Global Config Register Masks */ +#define HPET_CONF_MASK_ENABLED 0x00000001 +#define HPET_CONF_MASK_LEGACY 0x00000002 + +/* Retrieve Global Configuration */ +#define HPET_IS_ENABLED(regs) ((regs)->config & HPET_CONF_MASK_ENABLED) +#define HPET_IS_LEGACY(regs) (((regs)->config & HPET_CONF_MASK_LEGACY) >> 1) + +/* Timer Configuration and Capabilities*/ +#define HPET_CAP_TIMER_MASK 0xFFFFFFFF00000000ULL +#define HPET_CONF_TIMER_INT_ROUTE_MASK 0x3e00UL +#define HPET_CONF_TIMER_INT_ROUTE_SHIFT 9 +#define HPET_CONF_TIMER_INT_TYPE 0x00000002UL +#define HPET_CONF_TIMER_INT_ENABLE 0x00000004UL +#define HPET_CONF_TIMER_TYPE 0x00000008UL +#define HPET_CONF_TIMER_VAL_SET 0x00000040UL +#define HPET_CONF_TIMER_32MODE 0x00000100UL +#define HPET_CONF_TIMER_FSB_ENABLE 0x00004000UL +#define HPET_CAP_TIMER_PER_INT 0x00000010UL +#define HPET_CAP_TIMER_SIZE 0x00000020UL +#define HPET_CAP_TIMER_FSB_INT_DEL 0x00008000UL + +#define HPET_GET_CAP_TIMER_ROUTE(timer) (((timer)->config & HPET_CAP_TIMER_MASK) >> 32) +#define HPET_GET_CONF_TIMER_INT_ROUTE(timer) (((timer)->config & HPET_CONF_TIMER_INT_ROUTE_MASK) >> HPET_CONF_TIMER_INT_ROUTE_SHIFT) + +#define ACPI_HPET_SIGNATURE "HPET" + +struct hpet_timer { + /* Timer Configuration/Capability bits, Reversed because x86 is LSB */ + volatile uint64 config; + /* R/W: Each bit represents one allowed interrupt for this timer. */ + /* If interrupt 16 is allowed, bit 16 will be 1. */ + union { + volatile uint64 comparator64; /* R/W: Comparator value */ + volatile uint32 comparator32; + } u0; /* non-periodic mode: fires once when main counter = this comparator */ + /* periodic mode: fires when timer reaches this value, is increased by the original value */ + + volatile uint64 fsb_route[2]; /* R/W: FSB Interrupt Route values */ +}; + + +struct hpet_regs { + volatile uint64 capabilities; /* Read Only */ + + volatile uint64 reserved1; + + volatile uint64 config; /* R/W: Config Bits */ + + volatile uint64 reserved2; + + /* Interrupt Status bits */ + volatile uint64 interrupt_status; /* Interrupt Config bits for timers 0-31 */ + /* Level Tigger: 0 = off, 1 = set by hardware, timer is active */ + /* Edge Trigger: ignored */ + /* Writing 0 will not clear these. Must write 1 again. */ + volatile uint64 reserved3[25]; + + union { + volatile uint64 counter64; /* R/W */ + volatile uint32 counter32; + } u0; + + volatile uint64 reserved4; + + volatile struct hpet_timer timer[1]; +}; + + +typedef struct acpi_hpet { + acpi_descriptor_header header; /* "HPET" signature and acpi header */ + uint16 vendor_id; + uint8 legacy_capable : 1; + uint8 reserved1 : 1; + uint8 countersize : 1; + uint8 comparators : 5; + uint8 hw_revision; + struct hpet_addr { + uint8 address_space; + uint8 register_width; + uint8 register_offset; + uint8 reserved; + uint64 address; + } hpet_address; + uint8 number; + uint16 min_tick; +} _PACKED acpi_hpet; + +#endif diff --git a/src/add-ons/kernel/drivers/timer/hpet_interface.h b/src/add-ons/kernel/drivers/timer/hpet_interface.h new file mode 100644 index 0000000000..3c611965c7 --- /dev/null +++ b/src/add-ons/kernel/drivers/timer/hpet_interface.h @@ -0,0 +1,10 @@ +#ifndef _HPET_H +#define _HPET_H + + +struct hpet_timer_cookie ; + + +#define HPET_WAIT_TIMER 0x0001 + +#endif diff --git a/src/add-ons/kernel/drivers/timer/int.h b/src/add-ons/kernel/drivers/timer/int.h new file mode 100644 index 0000000000..0fb0bcbe51 --- /dev/null +++ b/src/add-ons/kernel/drivers/timer/int.h @@ -0,0 +1,16 @@ +/* + * Copyright 2003-2010, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + * + * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. + * Distributed under the terms of the NewOS License. + */ +#ifndef __INT_H +#define __INT_H + + +status_t reserve_io_interrupt_vectors(long count, long startVector); +status_t allocate_io_interrupt_vectors(long count, long *startVector); +void free_io_interrupt_vectors(long count, long startVector); + +#endif /* __INT_H */ diff --git a/src/add-ons/kernel/drivers/timer/msi.h b/src/add-ons/kernel/drivers/timer/msi.h new file mode 100644 index 0000000000..d0cec5b078 --- /dev/null +++ b/src/add-ons/kernel/drivers/timer/msi.h @@ -0,0 +1,33 @@ +#ifndef _KERNEL_ARCH_x86_MSI_H +#define _KERNEL_ARCH_x86_MSI_H + +#include + +// address register +#define MSI_ADDRESS_BASE 0xfee00000 +#define MSI_DESTINATION_ID_SHIFT 12 +#define MSI_REDIRECTION 0x00000008 +#define MSI_NO_REDIRECTION 0x00000000 +#define MSI_DESTINATION_MODE_LOGICAL 0x00000004 +#define MSI_DESTINATION_MODE_PHYSICAL 0x00000000 + +// data register +#define MSI_TRIGGER_MODE_EDGE 0x00000000 +#define MSI_TRIGGER_MODE_LEVEL 0x00008000 +#define MSI_LEVEL_DEASSERT 0x00000000 +#define MSI_LEVEL_ASSERT 0x00004000 +#define MSI_DELIVERY_MODE_FIXED 0x00000000 +#define MSI_DELIVERY_MODE_LOWEST_PRIO 0x00000100 +#define MSI_DELIVERY_MODE_SMI 0x00000200 +#define MSI_DELIVERY_MODE_NMI 0x00000400 +#define MSI_DELIVERY_MODE_INIT 0x00000500 +#define MSI_DELIVERY_MODE_EXT_INT 0x00000700 + + +void msi_init(); +bool msi_supported(); +status_t msi_allocate_vectors(uint8 count, uint8 *startVector, + uint64 *address, uint16 *data); +void msi_free_vectors(uint8 count, uint8 startVector); + +#endif // _KERNEL_ARCH_x86_MSI_H