diff options
author | Ian Moffett <ian@osmora.org> | 2025-05-17 21:56:07 -0400 |
---|---|---|
committer | Ian Moffett <ian@osmora.org> | 2025-05-17 21:58:44 -0400 |
commit | 08eeb79db14145d83578025e1f0e7f7af460ee25 (patch) | |
tree | b6af572a4b8dceb4f044f1e0bf5697f5c18dc0fd /sys/dev/acpi/uacpi/registers.c | |
parent | 9c64c3e69fa60b3657d33e829a411cb37064a169 (diff) |
kernel: acpi: Add uACPI port
See https://github.com/uACPI/uACPI/
Signed-off-by: Ian Moffett <ian@osmora.org>
Diffstat (limited to 'sys/dev/acpi/uacpi/registers.c')
-rw-r--r-- | sys/dev/acpi/uacpi/registers.c | 572 |
1 files changed, 572 insertions, 0 deletions
diff --git a/sys/dev/acpi/uacpi/registers.c b/sys/dev/acpi/uacpi/registers.c new file mode 100644 index 0000000..a52ce97 --- /dev/null +++ b/sys/dev/acpi/uacpi/registers.c @@ -0,0 +1,572 @@ +#include <uacpi/internal/registers.h> +#include <uacpi/internal/stdlib.h> +#include <uacpi/internal/context.h> +#include <uacpi/internal/io.h> +#include <uacpi/internal/log.h> +#include <uacpi/platform/atomic.h> +#include <uacpi/acpi.h> + +#ifndef UACPI_BAREBONES_MODE + +static uacpi_handle g_reg_lock; + +enum register_kind { + REGISTER_KIND_GAS, + REGISTER_KIND_IO, +}; + +enum register_access_kind { + REGISTER_ACCESS_KIND_PRESERVE, + REGISTER_ACCESS_KIND_WRITE_TO_CLEAR, + REGISTER_ACCESS_KIND_NORMAL, +}; + +struct register_spec { + uacpi_u8 kind; + uacpi_u8 access_kind; + uacpi_u8 access_width; // only REGISTER_KIND_IO + void *accessors[2]; + uacpi_u64 write_only_mask; + uacpi_u64 preserve_mask; +}; + +static const struct register_spec g_registers[UACPI_REGISTER_MAX + 1] = { + [UACPI_REGISTER_PM1_STS] = { + .kind = REGISTER_KIND_GAS, + .access_kind = REGISTER_ACCESS_KIND_WRITE_TO_CLEAR, + .accessors = { + &g_uacpi_rt_ctx.pm1a_status_blk, + &g_uacpi_rt_ctx.pm1b_status_blk, + }, + .preserve_mask = ACPI_PM1_STS_IGN0_MASK, + }, + [UACPI_REGISTER_PM1_EN] = { + .kind = REGISTER_KIND_GAS, + .access_kind = REGISTER_ACCESS_KIND_PRESERVE, + .accessors = { + &g_uacpi_rt_ctx.pm1a_enable_blk, + &g_uacpi_rt_ctx.pm1b_enable_blk, + }, + }, + [UACPI_REGISTER_PM1_CNT] = { + .kind = REGISTER_KIND_GAS, + .access_kind = REGISTER_ACCESS_KIND_PRESERVE, + .accessors = { + &g_uacpi_rt_ctx.fadt.x_pm1a_cnt_blk, + &g_uacpi_rt_ctx.fadt.x_pm1b_cnt_blk, + }, + .write_only_mask = ACPI_PM1_CNT_SLP_EN_MASK | + ACPI_PM1_CNT_GBL_RLS_MASK, + .preserve_mask = ACPI_PM1_CNT_PRESERVE_MASK, + }, + [UACPI_REGISTER_PM_TMR] = { + .kind = REGISTER_KIND_GAS, + .access_kind = REGISTER_ACCESS_KIND_PRESERVE, + .accessors = { &g_uacpi_rt_ctx.fadt.x_pm_tmr_blk, }, + }, + [UACPI_REGISTER_PM2_CNT] = { + .kind = REGISTER_KIND_GAS, + .access_kind = REGISTER_ACCESS_KIND_PRESERVE, + .accessors = { &g_uacpi_rt_ctx.fadt.x_pm2_cnt_blk, }, + .preserve_mask = ACPI_PM2_CNT_PRESERVE_MASK, + }, + [UACPI_REGISTER_SLP_CNT] = { + .kind = REGISTER_KIND_GAS, + .access_kind = REGISTER_ACCESS_KIND_PRESERVE, + .accessors = { &g_uacpi_rt_ctx.fadt.sleep_control_reg, }, + .write_only_mask = ACPI_SLP_CNT_SLP_EN_MASK, + .preserve_mask = ACPI_SLP_CNT_PRESERVE_MASK, + }, + [UACPI_REGISTER_SLP_STS] = { + .kind = REGISTER_KIND_GAS, + .access_kind = REGISTER_ACCESS_KIND_WRITE_TO_CLEAR, + .accessors = { &g_uacpi_rt_ctx.fadt.sleep_status_reg, }, + .preserve_mask = ACPI_SLP_STS_PRESERVE_MASK, + }, + [UACPI_REGISTER_RESET] = { + .kind = REGISTER_KIND_GAS, + .access_kind = REGISTER_ACCESS_KIND_NORMAL, + .accessors = { &g_uacpi_rt_ctx.fadt.reset_reg, }, + }, + [UACPI_REGISTER_SMI_CMD] = { + .kind = REGISTER_KIND_IO, + .access_kind = REGISTER_ACCESS_KIND_NORMAL, + .access_width = 1, + .accessors = { &g_uacpi_rt_ctx.fadt.smi_cmd, }, + }, +}; + +enum register_mapping_state { + REGISTER_MAPPING_STATE_NONE = 0, + REGISTER_MAPPING_STATE_NOT_NEEDED, + REGISTER_MAPPING_STATE_MAPPED, +}; + +struct register_mapping { + uacpi_mapped_gas mappings[2]; + uacpi_u8 states[2]; +}; +static struct register_mapping g_register_mappings[UACPI_REGISTER_MAX + 1]; + +static uacpi_status map_one( + const struct register_spec *spec, struct register_mapping *mapping, + uacpi_u8 idx +) +{ + uacpi_status ret = UACPI_STATUS_OK; + + if (mapping->states[idx] != REGISTER_MAPPING_STATE_NONE) + return ret; + + if (spec->kind == REGISTER_KIND_GAS) { + struct acpi_gas *gas = spec->accessors[idx]; + + if (gas == UACPI_NULL || gas->address == 0) { + mapping->states[idx] = REGISTER_MAPPING_STATE_NOT_NEEDED; + return ret; + } + + ret = uacpi_map_gas_noalloc(gas, &mapping->mappings[idx]); + } else { + struct acpi_gas temp_gas = { 0 }; + + if (idx != 0) { + mapping->states[idx] = REGISTER_MAPPING_STATE_NOT_NEEDED; + return ret; + } + + temp_gas.address_space_id = UACPI_ADDRESS_SPACE_SYSTEM_IO; + temp_gas.address = *(uacpi_u32*)spec->accessors[0]; + temp_gas.register_bit_width = spec->access_width * 8; + + ret = uacpi_map_gas_noalloc(&temp_gas, &mapping->mappings[idx]); + } + + if (uacpi_likely_success(ret)) + mapping->states[idx] = REGISTER_MAPPING_STATE_MAPPED; + + return ret; +} + +static uacpi_status ensure_register_mapped( + const struct register_spec *spec, struct register_mapping *mapping +) +{ + uacpi_status ret; + uacpi_bool needs_mapping = UACPI_FALSE; + uacpi_u8 state; + uacpi_cpu_flags flags; + + state = uacpi_atomic_load8(&mapping->states[0]); + needs_mapping |= state == REGISTER_MAPPING_STATE_NONE; + + state = uacpi_atomic_load8(&mapping->states[1]); + needs_mapping |= state == REGISTER_MAPPING_STATE_NONE; + + if (!needs_mapping) + return UACPI_STATUS_OK; + + flags = uacpi_kernel_lock_spinlock(g_reg_lock); + + ret = map_one(spec, mapping, 0); + if (uacpi_unlikely_error(ret)) + goto out; + + ret = map_one(spec, mapping, 1); +out: + uacpi_kernel_unlock_spinlock(g_reg_lock, flags); + return ret; +} + +static uacpi_status get_reg( + uacpi_u8 idx, const struct register_spec **out_spec, + struct register_mapping **out_mapping +) +{ + if (idx > UACPI_REGISTER_MAX) + return UACPI_STATUS_INVALID_ARGUMENT; + + *out_spec = &g_registers[idx]; + *out_mapping = &g_register_mappings[idx]; + return UACPI_STATUS_OK; +} + +static uacpi_status do_read_one( + struct register_mapping *mapping, uacpi_u8 idx, uacpi_u64 *out_value +) +{ + if (mapping->states[idx] != REGISTER_MAPPING_STATE_MAPPED) + return UACPI_STATUS_OK; + + return uacpi_gas_read_mapped(&mapping->mappings[idx], out_value); +} + +static uacpi_status do_read_register( + const struct register_spec *reg, struct register_mapping *mapping, + uacpi_u64 *out_value +) +{ + uacpi_status ret; + uacpi_u64 value0 = 0, value1 = 0; + + ret = do_read_one(mapping, 0, &value0); + if (uacpi_unlikely_error(ret)) + return ret; + + ret = do_read_one(mapping, 1, &value1); + if (uacpi_unlikely_error(ret)) + return ret; + + *out_value = value0 | value1; + if (reg->write_only_mask) + *out_value &= ~reg->write_only_mask; + + return UACPI_STATUS_OK; +} + +uacpi_status uacpi_read_register( + enum uacpi_register reg_enum, uacpi_u64 *out_value +) +{ + uacpi_status ret; + const struct register_spec *reg; + struct register_mapping *mapping; + + UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED); + + ret = get_reg(reg_enum, ®, &mapping); + if (uacpi_unlikely_error(ret)) + return ret; + + ret = ensure_register_mapped(reg, mapping); + if (uacpi_unlikely_error(ret)) + return ret; + + return do_read_register(reg, mapping, out_value); +} + +static uacpi_status do_write_one( + struct register_mapping *mapping, uacpi_u8 idx, uacpi_u64 in_value +) +{ + if (mapping->states[idx] != REGISTER_MAPPING_STATE_MAPPED) + return UACPI_STATUS_OK; + + return uacpi_gas_write_mapped(&mapping->mappings[idx], in_value); +} + +static uacpi_status do_write_register( + const struct register_spec *reg, struct register_mapping *mapping, + uacpi_u64 in_value +) +{ + uacpi_status ret; + + if (reg->preserve_mask) { + in_value &= ~reg->preserve_mask; + + if (reg->access_kind == REGISTER_ACCESS_KIND_PRESERVE) { + uacpi_u64 data; + + ret = do_read_register(reg, mapping, &data); + if (uacpi_unlikely_error(ret)) + return ret; + + in_value |= data & reg->preserve_mask; + } + } + + ret = do_write_one(mapping, 0, in_value); + if (uacpi_unlikely_error(ret)) + return ret; + + return do_write_one(mapping, 1, in_value); +} + +uacpi_status uacpi_write_register( + enum uacpi_register reg_enum, uacpi_u64 in_value +) +{ + uacpi_status ret; + const struct register_spec *reg; + struct register_mapping *mapping; + + UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED); + + ret = get_reg(reg_enum, ®, &mapping); + if (uacpi_unlikely_error(ret)) + return ret; + + ret = ensure_register_mapped(reg, mapping); + if (uacpi_unlikely_error(ret)) + return ret; + + return do_write_register(reg, mapping, in_value); +} + +uacpi_status uacpi_write_registers( + enum uacpi_register reg_enum, uacpi_u64 in_value0, uacpi_u64 in_value1 +) +{ + uacpi_status ret; + const struct register_spec *reg; + struct register_mapping *mapping; + + UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED); + + ret = get_reg(reg_enum, ®, &mapping); + if (uacpi_unlikely_error(ret)) + return ret; + + ret = ensure_register_mapped(reg, mapping); + if (uacpi_unlikely_error(ret)) + return ret; + + ret = do_write_one(mapping, 0, in_value0); + if (uacpi_unlikely_error(ret)) + return ret; + + return do_write_one(mapping, 1, in_value1); +} + +struct register_field { + uacpi_u8 reg; + uacpi_u8 offset; + uacpi_u16 mask; +}; + +static const struct register_field g_fields[UACPI_REGISTER_FIELD_MAX + 1] = { + [UACPI_REGISTER_FIELD_TMR_STS] = { + .reg = UACPI_REGISTER_PM1_STS, + .offset = ACPI_PM1_STS_TMR_STS_IDX, + .mask = ACPI_PM1_STS_TMR_STS_MASK, + }, + [UACPI_REGISTER_FIELD_BM_STS] = { + .reg = UACPI_REGISTER_PM1_STS, + .offset = ACPI_PM1_STS_BM_STS_IDX, + .mask = ACPI_PM1_STS_BM_STS_MASK, + }, + [UACPI_REGISTER_FIELD_GBL_STS] = { + .reg = UACPI_REGISTER_PM1_STS, + .offset = ACPI_PM1_STS_GBL_STS_IDX, + .mask = ACPI_PM1_STS_GBL_STS_MASK, + }, + [UACPI_REGISTER_FIELD_PWRBTN_STS] = { + .reg = UACPI_REGISTER_PM1_STS, + .offset = ACPI_PM1_STS_PWRBTN_STS_IDX, + .mask = ACPI_PM1_STS_PWRBTN_STS_MASK, + }, + [UACPI_REGISTER_FIELD_SLPBTN_STS] = { + .reg = UACPI_REGISTER_PM1_STS, + .offset = ACPI_PM1_STS_SLPBTN_STS_IDX, + .mask = ACPI_PM1_STS_SLPBTN_STS_MASK, + }, + [UACPI_REGISTER_FIELD_RTC_STS] = { + .reg = UACPI_REGISTER_PM1_STS, + .offset = ACPI_PM1_STS_RTC_STS_IDX, + .mask = ACPI_PM1_STS_RTC_STS_MASK, + }, + [UACPI_REGISTER_FIELD_HWR_WAK_STS] = { + .reg = UACPI_REGISTER_SLP_STS, + .offset = ACPI_SLP_STS_WAK_STS_IDX, + .mask = ACPI_SLP_STS_WAK_STS_MASK, + }, + [UACPI_REGISTER_FIELD_WAK_STS] = { + .reg = UACPI_REGISTER_PM1_STS, + .offset = ACPI_PM1_STS_WAKE_STS_IDX, + .mask = ACPI_PM1_STS_WAKE_STS_MASK, + }, + [UACPI_REGISTER_FIELD_PCIEX_WAKE_STS] = { + .reg = UACPI_REGISTER_PM1_STS, + .offset = ACPI_PM1_STS_PCIEXP_WAKE_STS_IDX, + .mask = ACPI_PM1_STS_PCIEXP_WAKE_STS_MASK, + }, + [UACPI_REGISTER_FIELD_TMR_EN] = { + .reg = UACPI_REGISTER_PM1_EN, + .offset = ACPI_PM1_EN_TMR_EN_IDX, + .mask = ACPI_PM1_EN_TMR_EN_MASK, + }, + [UACPI_REGISTER_FIELD_GBL_EN] = { + .reg = UACPI_REGISTER_PM1_EN, + .offset = ACPI_PM1_EN_GBL_EN_IDX, + .mask = ACPI_PM1_EN_GBL_EN_MASK, + }, + [UACPI_REGISTER_FIELD_PWRBTN_EN] = { + .reg = UACPI_REGISTER_PM1_EN, + .offset = ACPI_PM1_EN_PWRBTN_EN_IDX, + .mask = ACPI_PM1_EN_PWRBTN_EN_MASK, + }, + [UACPI_REGISTER_FIELD_SLPBTN_EN] = { + .reg = UACPI_REGISTER_PM1_EN, + .offset = ACPI_PM1_EN_SLPBTN_EN_IDX, + .mask = ACPI_PM1_EN_SLPBTN_EN_MASK, + }, + [UACPI_REGISTER_FIELD_RTC_EN] = { + .reg = UACPI_REGISTER_PM1_EN, + .offset = ACPI_PM1_EN_RTC_EN_IDX, + .mask = ACPI_PM1_EN_RTC_EN_MASK, + }, + [UACPI_REGISTER_FIELD_PCIEXP_WAKE_DIS] = { + .reg = UACPI_REGISTER_PM1_EN, + .offset = ACPI_PM1_EN_PCIEXP_WAKE_DIS_IDX, + .mask = ACPI_PM1_EN_PCIEXP_WAKE_DIS_MASK, + }, + [UACPI_REGISTER_FIELD_SCI_EN] = { + .reg = UACPI_REGISTER_PM1_CNT, + .offset = ACPI_PM1_CNT_SCI_EN_IDX, + .mask = ACPI_PM1_CNT_SCI_EN_MASK, + }, + [UACPI_REGISTER_FIELD_BM_RLD] = { + .reg = UACPI_REGISTER_PM1_CNT, + .offset = ACPI_PM1_CNT_BM_RLD_IDX, + .mask = ACPI_PM1_CNT_BM_RLD_MASK, + }, + [UACPI_REGISTER_FIELD_GBL_RLS] = { + .reg = UACPI_REGISTER_PM1_CNT, + .offset = ACPI_PM1_CNT_GBL_RLS_IDX, + .mask = ACPI_PM1_CNT_GBL_RLS_MASK, + }, + [UACPI_REGISTER_FIELD_SLP_TYP] = { + .reg = UACPI_REGISTER_PM1_CNT, + .offset = ACPI_PM1_CNT_SLP_TYP_IDX, + .mask = ACPI_PM1_CNT_SLP_TYP_MASK, + }, + [UACPI_REGISTER_FIELD_SLP_EN] = { + .reg = UACPI_REGISTER_PM1_CNT, + .offset = ACPI_PM1_CNT_SLP_EN_IDX, + .mask = ACPI_PM1_CNT_SLP_EN_MASK, + }, + [UACPI_REGISTER_FIELD_HWR_SLP_TYP] = { + .reg = UACPI_REGISTER_SLP_CNT, + .offset = ACPI_SLP_CNT_SLP_TYP_IDX, + .mask = ACPI_SLP_CNT_SLP_TYP_MASK, + }, + [UACPI_REGISTER_FIELD_HWR_SLP_EN] = { + .reg = UACPI_REGISTER_SLP_CNT, + .offset = ACPI_SLP_CNT_SLP_EN_IDX, + .mask = ACPI_SLP_CNT_SLP_EN_MASK, + }, + [UACPI_REGISTER_FIELD_ARB_DIS] = { + .reg = UACPI_REGISTER_PM2_CNT, + .offset = ACPI_PM2_CNT_ARB_DIS_IDX, + .mask = ACPI_PM2_CNT_ARB_DIS_MASK, + }, +}; + +uacpi_status uacpi_initialize_registers(void) +{ + g_reg_lock = uacpi_kernel_create_spinlock(); + if (uacpi_unlikely(g_reg_lock == UACPI_NULL)) + return UACPI_STATUS_OUT_OF_MEMORY; + + return UACPI_STATUS_OK; +} + +void uacpi_deinitialize_registers(void) +{ + uacpi_u8 i; + struct register_mapping *mapping; + + if (g_reg_lock != UACPI_NULL) { + uacpi_kernel_free_spinlock(g_reg_lock); + g_reg_lock = UACPI_NULL; + } + + for (i = 0; i <= UACPI_REGISTER_MAX; ++i) { + mapping = &g_register_mappings[i]; + + if (mapping->states[0] == REGISTER_MAPPING_STATE_MAPPED) + uacpi_unmap_gas_nofree(&mapping->mappings[0]); + if (mapping->states[1] == REGISTER_MAPPING_STATE_MAPPED) + uacpi_unmap_gas_nofree(&mapping->mappings[1]); + } + + uacpi_memzero(&g_register_mappings, sizeof(g_register_mappings)); +} + +uacpi_status uacpi_read_register_field( + enum uacpi_register_field field_enum, uacpi_u64 *out_value +) +{ + uacpi_status ret; + uacpi_u8 field_idx = field_enum; + const struct register_field *field; + const struct register_spec *reg; + struct register_mapping *mapping; + + UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED); + + if (uacpi_unlikely(field_idx > UACPI_REGISTER_FIELD_MAX)) + return UACPI_STATUS_INVALID_ARGUMENT; + + field = &g_fields[field_idx]; + reg = &g_registers[field->reg]; + mapping = &g_register_mappings[field->reg]; + + ret = ensure_register_mapped(reg, mapping); + if (uacpi_unlikely_error(ret)) + return ret; + + ret = do_read_register(reg, mapping, out_value); + if (uacpi_unlikely_error(ret)) + return ret; + + *out_value = (*out_value & field->mask) >> field->offset; + return UACPI_STATUS_OK; +} + +uacpi_status uacpi_write_register_field( + enum uacpi_register_field field_enum, uacpi_u64 in_value +) +{ + uacpi_status ret; + uacpi_u8 field_idx = field_enum; + const struct register_field *field; + const struct register_spec *reg; + struct register_mapping *mapping; + + uacpi_u64 data; + uacpi_cpu_flags flags; + + UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED); + + if (uacpi_unlikely(field_idx > UACPI_REGISTER_FIELD_MAX)) + return UACPI_STATUS_INVALID_ARGUMENT; + + field = &g_fields[field_idx]; + reg = &g_registers[field->reg]; + mapping = &g_register_mappings[field->reg]; + + ret = ensure_register_mapped(reg, mapping); + if (uacpi_unlikely_error(ret)) + return ret; + + in_value = (in_value << field->offset) & field->mask; + + flags = uacpi_kernel_lock_spinlock(g_reg_lock); + + if (reg->kind == REGISTER_ACCESS_KIND_WRITE_TO_CLEAR) { + if (in_value == 0) { + ret = UACPI_STATUS_OK; + goto out; + } + + ret = do_write_register(reg, mapping, in_value); + goto out; + } + + ret = do_read_register(reg, mapping, &data); + if (uacpi_unlikely_error(ret)) + goto out; + + data &= ~field->mask; + data |= in_value; + + ret = do_write_register(reg, mapping, data); + +out: + uacpi_kernel_unlock_spinlock(g_reg_lock, flags); + return ret; +} + +#endif // !UACPI_BAREBONES_MODE |