mirror of
https://github.com/AlexAltea/orbital.git
synced 2024-05-13 01:55:27 -04:00
hardware/aeolia: Add HPET device
This commit is contained in:
parent
d1a45f452f
commit
5fe2e96f4f
|
@ -43,6 +43,8 @@ ORBITAL_SOURCES_APPEND(${ORBITAL_DIR_SOURCES}/orbital/host)
|
|||
ORBITAL_SOURCES_APPEND(${ORBITAL_DIR_SOURCES}/orbital/host/graphics)
|
||||
ORBITAL_SOURCES_APPEND(${ORBITAL_DIR_SOURCES}/orbital/hardware)
|
||||
ORBITAL_SOURCES_APPEND(${ORBITAL_DIR_SOURCES}/orbital/hardware/aeolia)
|
||||
ORBITAL_SOURCES_APPEND(${ORBITAL_DIR_SOURCES}/orbital/hardware/aeolia/hpet)
|
||||
ORBITAL_SOURCES_APPEND(${ORBITAL_DIR_SOURCES}/orbital/hardware/aeolia/msic)
|
||||
ORBITAL_SOURCES_APPEND(${ORBITAL_DIR_SOURCES}/orbital/hardware/aeolia/uart)
|
||||
ORBITAL_SOURCES_APPEND(${ORBITAL_DIR_SOURCES}/orbital/hardware/liverpool)
|
||||
ORBITAL_SOURCES_APPEND(${ORBITAL_DIR_SOURCES}/orbital/hardware/liverpool/gca)
|
||||
|
|
|
@ -65,7 +65,7 @@ static U16 icc_checksum(const IccMessageHeader& message) {
|
|||
}
|
||||
|
||||
AeoliaPCIeDevice::AeoliaPCIeDevice(PCIeBus* bus, const AeoliaPCIeDeviceConfig& config)
|
||||
: PCIeDevice(bus, config), msic(bus->space_mem()) {
|
||||
: PCIeDevice(bus, config), hpet(bus->space_mem()), msic(bus->space_mem()) {
|
||||
// Create sub-devices
|
||||
AeoliaUARTDeviceConfig uart0_config(config.backend_uart0);
|
||||
AeoliaUARTDeviceConfig uart1_config(config.backend_uart1);
|
||||
|
@ -189,7 +189,8 @@ U64 AeoliaPCIeDevice::peripherals_read(U64 addr, U64 size) {
|
|||
fprintf(stderr, "AeoliaPCIeDevice::peripherals_read: addr=0x%llX, size=0x%llX\n", addr, size);
|
||||
}
|
||||
else if (range_hpet.contains(addr)) {
|
||||
assert_always("Unimplemented");
|
||||
addr -= range_hpet.base;
|
||||
value = hpet.mmio_read(addr, size);
|
||||
}
|
||||
else if (range_unk3c.contains(addr)) {
|
||||
fprintf(stderr, "AeoliaPCIeDevice::peripherals_read: addr=0x%llX, size=0x%llX\n", addr, size);
|
||||
|
@ -254,7 +255,8 @@ void AeoliaPCIeDevice::peripherals_write(U64 addr, U64 value, U64 size) {
|
|||
fprintf(stderr, "AeoliaPCIeDevice::peripherals_write: addr=0x%llX, value=0x%llX, size=0x%llX\n", addr, value, size);
|
||||
}
|
||||
else if (range_hpet.contains(addr)) {
|
||||
assert_always("Unimplemented");
|
||||
addr -= range_hpet.base;
|
||||
hpet.mmio_write(addr, value, size);
|
||||
}
|
||||
else if (range_unk3c.contains(addr)) {
|
||||
fprintf(stderr, "AeoliaPCIeDevice::peripherals_write: addr=0x%llX, value=0x%llX, size=0x%llX\n", addr, value, size);
|
||||
|
@ -349,12 +351,6 @@ void AeoliaPCIeDevice::update_icc() {
|
|||
fprintf(stderr, "icc: Unknown buzzer query 0x%04X!\n", query.minor);
|
||||
}
|
||||
break;
|
||||
case ICC_CMD_UNK0D:
|
||||
switch (query.minor) {
|
||||
default:
|
||||
fprintf(stderr, "icc: Unknown unk_0D query 0x%04X!\n", query.minor);
|
||||
}
|
||||
break;
|
||||
case ICC_CMD_NVRAM:
|
||||
switch (query.minor) {
|
||||
case ICC_CMD_NVRAM_OP_WRITE:
|
||||
|
@ -386,7 +382,7 @@ void AeoliaPCIeDevice::update_icc() {
|
|||
spm_data[ASPM_ICC_REPLY_R] = 0;
|
||||
icc_status |= APCIE_ICC_MSG_PENDING | APCIE_ICC_IRQ_PENDING;
|
||||
icc_doorbell &= ~APCIE_ICC_MSG_PENDING;
|
||||
// apcie_msi_trigger(&s->msic, 4, APCIE_MSI_FNC4_ICC);
|
||||
msic.msi_trigger(4, APCIE_MSI_FNC4_ICC);
|
||||
}
|
||||
|
||||
AeoliaPCIeDevice::IccReply AeoliaPCIeDevice::icc_cmd_service_version() {
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "icc/icc.h"
|
||||
#include "hpet/aeolia_hpet.h"
|
||||
#include "msic/aeolia_msic.h"
|
||||
#include "nvs/aeolia_nvs.h"
|
||||
|
||||
|
@ -60,6 +61,7 @@ private:
|
|||
std::unique_ptr<AeoliaUARTDevice> uart1;
|
||||
|
||||
// State
|
||||
AeoliaHpet hpet;
|
||||
AeoliaMsic msic;
|
||||
AeoliaNVS nvs{};
|
||||
uint32_t icc_doorbell;
|
||||
|
|
230
src/orbital/hardware/aeolia/hpet/aeolia_hpet.cpp
Normal file
230
src/orbital/hardware/aeolia/hpet/aeolia_hpet.cpp
Normal file
|
@ -0,0 +1,230 @@
|
|||
/**
|
||||
* Aeolia High-Precision Event Timer (HPET) device.
|
||||
*
|
||||
* Copyright 2017-2021. Orbital project.
|
||||
* Released under MIT license. Read LICENSE for more details.
|
||||
*
|
||||
* Authors:
|
||||
* - Alexandro Sanchez Bach <alexandro@phi.nz>
|
||||
*/
|
||||
|
||||
#include "aeolia_hpet.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
enum HPETGeneralReg {
|
||||
HPET_REG_CAP = 0x00,
|
||||
HPET_REG_CFG = 0x10,
|
||||
HPET_REG_IS = 0x20,
|
||||
HPET_REG_CNT = 0xF0,
|
||||
};
|
||||
|
||||
enum HPETTimerReg {
|
||||
HPET_REG_TNCFG_0 = 0x00,
|
||||
HPET_REG_TNCFG_1 = 0x04,
|
||||
HPET_REG_TNCMP_0 = 0x08,
|
||||
HPET_REG_TNCMP_1 = 0x0C,
|
||||
HPET_REG_TNROUTE_0 = 0x10,
|
||||
HPET_REG_TNROUTE_1 = 0x14,
|
||||
};
|
||||
|
||||
AeoliaHpet::AeoliaHpet(ContainerSpace* mem, const AeoliaHpetConfig& config)
|
||||
: Device(nullptr, config), mem(mem) {
|
||||
// Initialize MMIO
|
||||
mmio = new MemorySpace(this, 0x1000, {
|
||||
static_cast<MemorySpaceReadOp>(&AeoliaHpet::mmio_read),
|
||||
static_cast<MemorySpaceWriteOp>(&AeoliaHpet::mmio_write),
|
||||
});
|
||||
mem->addSubspace(mmio, config.base);
|
||||
|
||||
// Create timers
|
||||
assert_true(config.count >= 3);
|
||||
assert_true(config.count <= 32);
|
||||
timers.resize(config.count);
|
||||
|
||||
// Capabilities
|
||||
s.cap.num_tim_cap = config.count - 1;
|
||||
s.cap.period = config.period_fs;
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
AeoliaHpet::~AeoliaHpet() {}
|
||||
|
||||
void AeoliaHpet::reset() {
|
||||
for (auto& timer : timers) {
|
||||
timer.comparator = UINT64_MAX;
|
||||
// TODO
|
||||
}
|
||||
|
||||
s.config.value = 0;
|
||||
s.counter.value = 0;
|
||||
// TODO
|
||||
}
|
||||
|
||||
U64 AeoliaHpet::mmio_read(U64 addr, U64 size) {
|
||||
U64 value = 0;
|
||||
|
||||
// Sanity checks
|
||||
assert_true(size == 4);
|
||||
|
||||
if (addr < 0x100) {
|
||||
/* General access */
|
||||
switch (addr) {
|
||||
case HPET_REG_CAP + 0:
|
||||
value = s.cap.value_lo;
|
||||
break;
|
||||
case HPET_REG_CAP + 4:
|
||||
value = s.cap.value_hi;
|
||||
break;
|
||||
case HPET_REG_CFG + 0:
|
||||
value = s.config.value;
|
||||
break;
|
||||
case HPET_REG_IS + 0:
|
||||
value = s.isr.value_lo;
|
||||
break;
|
||||
case HPET_REG_IS + 4:
|
||||
value = s.isr.value_hi;
|
||||
break;
|
||||
case HPET_REG_CNT + 0:
|
||||
value = get_counter();
|
||||
break;
|
||||
case HPET_REG_CNT + 4:
|
||||
value = get_counter() >> 32;
|
||||
break;
|
||||
|
||||
// Ignored upper-accesses
|
||||
case HPET_REG_CFG + 4:
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_always("Invalid register access");
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Timer-N access */
|
||||
const U64 index = (addr - 0x100) / 0x20;
|
||||
const U64 offset = addr % 0x20;
|
||||
assert_true(index < timers.size());
|
||||
const auto& timer = timers[index];
|
||||
|
||||
switch (offset) {
|
||||
case HPET_REG_TNCFG_0:
|
||||
value = timer.config.value_lo;
|
||||
break;
|
||||
case HPET_REG_TNCFG_1:
|
||||
value = timer.config.value_hi;
|
||||
break;
|
||||
case HPET_REG_TNCMP_0:
|
||||
value = timer.comparator;
|
||||
break;
|
||||
case HPET_REG_TNCMP_1:
|
||||
value = timer.comparator >> 32;
|
||||
break;
|
||||
case HPET_REG_TNROUTE_0:
|
||||
value = timer.fsb.int_val;
|
||||
break;
|
||||
case HPET_REG_TNROUTE_1:
|
||||
value = timer.fsb.int_addr;
|
||||
break;
|
||||
default:
|
||||
assert_always("Invalid register access");
|
||||
}
|
||||
}
|
||||
return static_cast<U32>(value);
|
||||
}
|
||||
|
||||
void AeoliaHpet::mmio_write(U64 addr, U64 value, U64 size) {
|
||||
// Sanity checks
|
||||
assert_true(size == 4);
|
||||
|
||||
if (addr < 0x100) {
|
||||
// General access
|
||||
switch (addr) {
|
||||
case HPET_REG_CNT + 0:
|
||||
s.counter.lo = value;
|
||||
break;
|
||||
case HPET_REG_CNT + 4:
|
||||
s.counter.hi = value;
|
||||
break;
|
||||
case HPET_REG_CFG + 0:
|
||||
s.config.value = value;
|
||||
break;
|
||||
case HPET_REG_IS + 0:
|
||||
value &= s.isr.value_lo;
|
||||
for (int i = 0; i < timers.size(); i++) {
|
||||
if (value & (1 << i)) {
|
||||
update_irq(timers[i], i);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// Ignored accesses
|
||||
case HPET_REG_CAP + 0:
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_always("Invalid register access");
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Timer-N access
|
||||
const U64 index = (addr - 0x100) / 0x20;
|
||||
const U64 offset = addr % 0x20;
|
||||
assert_true(index < timers.size());
|
||||
auto& tn = timers[index];
|
||||
|
||||
switch (offset) {
|
||||
case HPET_REG_TNCFG_0:
|
||||
tn.config.value_lo = value;
|
||||
break;
|
||||
case HPET_REG_TNROUTE_0:
|
||||
tn.fsb.int_val = value;
|
||||
break;
|
||||
case HPET_REG_TNROUTE_1:
|
||||
tn.fsb.int_addr = value;
|
||||
break;
|
||||
default:
|
||||
assert_always("Invalid register access");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
U64 AeoliaHpet::get_counter() {
|
||||
if (s.config.enable_cnf) {
|
||||
s.counter.value = get_ticks();
|
||||
}
|
||||
return s.counter.value;
|
||||
}
|
||||
|
||||
U64 AeoliaHpet::get_ticks() {
|
||||
return Clock::now().time_since_epoch().count() / 100; // 100ns period
|
||||
}
|
||||
|
||||
void AeoliaHpet::update_irq(HPETTimer& timer, bool set) {
|
||||
if (!s.config.enable_cnf) {
|
||||
assert_always("Unexpected");
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* LegacyReplacement Route
|
||||
* =======================
|
||||
* If the ENABLE_CNF bit and the LEG_RT_CNF bit are both set,
|
||||
* then the interrupts will be routed as follows:
|
||||
* - Timer 0 will be routed to IRQ0 in Non-APIC or IRQ2 in the I/O APIC
|
||||
* - Timer 1 will be routed to IRQ8 in Non-APIC or IRQ8 in the I/O APIC
|
||||
*/
|
||||
if (s.config.leg_rt_cnf) {
|
||||
assert_always("Unimplemented");
|
||||
return;
|
||||
}
|
||||
|
||||
// FSB IRQ route
|
||||
if (timer.config.fsb_en_cnf) {
|
||||
mem->write<U32>(timer.fsb.int_addr, timer.fsb.int_val);
|
||||
return;
|
||||
}
|
||||
}
|
111
src/orbital/hardware/aeolia/hpet/aeolia_hpet.h
Normal file
111
src/orbital/hardware/aeolia/hpet/aeolia_hpet.h
Normal file
|
@ -0,0 +1,111 @@
|
|||
/**
|
||||
* Aeolia High-Precision Event Timer (HPET) device.
|
||||
*
|
||||
* Copyright 2017-2021. Orbital project.
|
||||
* Released under MIT license. Read LICENSE for more details.
|
||||
*
|
||||
* Authors:
|
||||
* - Alexandro Sanchez Bach <alexandro@phi.nz>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <orbital/core.h>
|
||||
|
||||
struct HPETTimer {
|
||||
union {
|
||||
U64 value;
|
||||
struct {
|
||||
U32 value_lo;
|
||||
U32 value_hi;
|
||||
};
|
||||
Bit<U64, 1> int_type_cnf;
|
||||
Bit<U64, 2> int_enb_cnf;
|
||||
Bit<U64, 3> type_cnf;
|
||||
Bit<U64, 4> per_int_cap;
|
||||
Bit<U64, 5> size_cap;
|
||||
Bit<U64, 6> val_set_cnf;
|
||||
Bit<U64, 8> mode32_cnf;
|
||||
Bitrange<U64, 9, 13> int_route_cnf;
|
||||
Bitrange<U64, 14, 14> fsb_en_cnf;
|
||||
Bitrange<U64, 15, 15> fsb_int_del_cap;
|
||||
Bitrange<U64, 32, 63> int_route_cap;
|
||||
} config;
|
||||
U64 comparator;
|
||||
union {
|
||||
U64 value;
|
||||
struct {
|
||||
U32 int_val;
|
||||
U32 int_addr;
|
||||
};
|
||||
} fsb;
|
||||
};
|
||||
|
||||
struct AeoliaHpetConfig : DeviceConfig {
|
||||
U64 base = 0xFED00000;
|
||||
U64 count = 4;
|
||||
U64 period_fs = 100 * 1000000; // 100ns
|
||||
};
|
||||
|
||||
class AeoliaHpet : public Device {
|
||||
public:
|
||||
AeoliaHpet(ContainerSpace* mem, const AeoliaHpetConfig& config = {});
|
||||
~AeoliaHpet();
|
||||
|
||||
void reset() override;
|
||||
|
||||
U64 mmio_read(U64 addr, U64 size);
|
||||
void mmio_write(U64 addr, U64 value, U64 size);
|
||||
|
||||
private:
|
||||
Space* mem;
|
||||
MemorySpace* mmio;
|
||||
std::vector<HPETTimer> timers;
|
||||
|
||||
struct {
|
||||
union {
|
||||
U64 value;
|
||||
struct {
|
||||
U32 value_lo;
|
||||
U32 value_hi;
|
||||
};
|
||||
Bitrange<U64, 0, 7> rev_id;
|
||||
Bitrange<U64, 8, 12> num_tim_cap;
|
||||
Bitrange<U64, 13, 13> count_size_cap;
|
||||
Bitrange<U64, 15, 15> leg_rt_cap;
|
||||
Bitrange<U64, 16, 21> vendor_id;
|
||||
Bitrange<U64, 32, 63> period;
|
||||
} cap;
|
||||
union {
|
||||
U64 value;
|
||||
struct {
|
||||
U32 value_lo;
|
||||
U32 value_hi;
|
||||
};
|
||||
Bit<U64, 1> leg_rt_cnf;
|
||||
Bit<U64, 0> enable_cnf;
|
||||
} config;
|
||||
union {
|
||||
U64 value;
|
||||
struct {
|
||||
U32 value_lo;
|
||||
U32 value_hi;
|
||||
};
|
||||
Bitset<64> tn_int_sts;
|
||||
} isr;
|
||||
union {
|
||||
U64 value;
|
||||
struct {
|
||||
U32 lo;
|
||||
U32 hi;
|
||||
};
|
||||
} counter;
|
||||
} s = {};
|
||||
|
||||
// Helpers
|
||||
U64 get_counter();
|
||||
U64 get_ticks();
|
||||
|
||||
// Interrupts
|
||||
void update_irq(HPETTimer& timer, bool set);
|
||||
};
|
|
@ -17,12 +17,10 @@ enum IccCommand : U16 {
|
|||
ICC_CMD_SERVICE = 0x01,
|
||||
ICC_CMD_BOARD = 0x02,
|
||||
ICC_CMD_NVRAM = 0x03,
|
||||
ICC_CMD_UNK04 = 0x04, // icc_power_init
|
||||
ICC_CMD_POWER = 0x04,
|
||||
ICC_CMD_BUTTONS = 0x08,
|
||||
ICC_CMD_BUZZER = 0x09,
|
||||
ICC_CMD_SAVE_CONTEXT = 0x0B, // thermal
|
||||
ICC_CMD_LOAD_CONTEXT = 0x0C,
|
||||
ICC_CMD_UNK0D = 0x0D, // icc_configuration_get_devlan_setting
|
||||
ICC_CMD_THERMAL = 0x0B,
|
||||
ICC_CMD_UNK70 = 0x70, // sceControlEmcHdmiService
|
||||
ICC_CMD_SNVRAM_READ = 0x8D,
|
||||
};
|
||||
|
@ -64,6 +62,7 @@ enum IccCommandBoardOp : U16 {
|
|||
enum IccCommandNvramOp : U16 {
|
||||
ICC_CMD_NVRAM_OP_WRITE = 0x0000,
|
||||
ICC_CMD_NVRAM_OP_READ = 0x0001,
|
||||
ICC_CMD_NVRAM_OP_FLUSH = 0x0002,
|
||||
};
|
||||
|
||||
enum IccCommandButtonsOp : U16 {
|
||||
|
|
Loading…
Reference in a new issue