From 79e3f9012d7246bc8cec7f31291d84f33c5251e2 Mon Sep 17 00:00:00 2001 From: Stefano Ceccherini Date: Thu, 13 Oct 2011 08:20:10 +0000 Subject: [PATCH] HPET driver for testing HPET code more easily. Seems to work correctly on real hardware, not on qemu where it can only use the irq 2, and this causes wreakage (could be a programming error). Changed from the kernel code: - Adapted to use as a driver - Configure for level interrupts instead of edge, which seems not to work correctly - Add traceing dprintfs - Various other changes Does not use yet the new interrupt api introduced by Michael Lotz. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@42837 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/kernel/drivers/Jamfile | 1 + src/add-ons/kernel/drivers/timer/Jamfile | 6 + src/add-ons/kernel/drivers/timer/arch_acpi.h | 206 +++++++ src/add-ons/kernel/drivers/timer/hpet.cpp | 508 ++++++++++++++++++ src/add-ons/kernel/drivers/timer/hpet.h | 118 ++++ .../kernel/drivers/timer/hpet_interface.h | 10 + src/add-ons/kernel/drivers/timer/int.h | 16 + src/add-ons/kernel/drivers/timer/msi.h | 33 ++ 8 files changed, 898 insertions(+) create mode 100644 src/add-ons/kernel/drivers/timer/Jamfile create mode 100644 src/add-ons/kernel/drivers/timer/arch_acpi.h create mode 100644 src/add-ons/kernel/drivers/timer/hpet.cpp create mode 100644 src/add-ons/kernel/drivers/timer/hpet.h create mode 100644 src/add-ons/kernel/drivers/timer/hpet_interface.h create mode 100644 src/add-ons/kernel/drivers/timer/int.h create mode 100644 src/add-ons/kernel/drivers/timer/msi.h 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