#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: apei_mapreg.c,v 1.4 2024/03/22 20:47:52 riastradh Exp $");
#include <sys/types.h>
#include <sys/atomic.h>
#include <dev/acpi/acpivar.h>
#include <dev/acpi/apei_mapreg.h>
struct apei_mapreg *
apei_mapreg_map(const ACPI_GENERIC_ADDRESS *reg)
{
switch (reg->BitWidth) {
case 8:
case 16:
case 32:
case 64:
break;
default:
return NULL;
}
switch (reg->AccessWidth) {
case 1:
case 2:
case 3:
break;
case 4:
if (reg->SpaceId == ACPI_ADR_SPACE_SYSTEM_IO)
return NULL;
break;
default:
return NULL;
}
switch (reg->BitOffset) {
case 0:
break;
default:
return NULL;
}
if (reg->BitWidth % (8*(1 << (reg->AccessWidth - 1))))
return NULL;
switch (reg->SpaceId) {
case ACPI_ADR_SPACE_SYSTEM_IO:
return (struct apei_mapreg *)__UNCONST(reg);
case ACPI_ADR_SPACE_SYSTEM_MEMORY:
return AcpiOsMapMemory(reg->Address,
1 << (reg->AccessWidth - 1));
default:
return NULL;
}
}
void
apei_mapreg_unmap(const ACPI_GENERIC_ADDRESS *reg,
struct apei_mapreg *map)
{
switch (reg->SpaceId) {
case ACPI_ADR_SPACE_SYSTEM_IO:
KASSERT(map == __UNCONST(reg));
break;
case ACPI_ADR_SPACE_SYSTEM_MEMORY:
AcpiOsUnmapMemory(map, 1 << (reg->AccessWidth - 1));
break;
default:
panic("invalid register mapping");
}
}
uint64_t
apei_mapreg_read(const ACPI_GENERIC_ADDRESS *reg,
const struct apei_mapreg *map)
{
unsigned chunkbits = NBBY*(1 << (reg->AccessWidth - 1));
unsigned i, n = reg->BitWidth / chunkbits;
uint64_t v = 0;
for (i = 0; i < n; i++) {
uint64_t chunk;
switch (reg->SpaceId) {
case ACPI_ADR_SPACE_SYSTEM_IO: {
ACPI_IO_ADDRESS addr;
uint32_t chunk32 = 0;
ACPI_STATUS rv;
switch (reg->AccessWidth) {
case 1:
addr = reg->Address + i;
break;
case 2:
addr = reg->Address + 2*i;
break;
case 3:
addr = reg->Address + 4*i;
break;
case 4:
default:
__unreachable();
}
rv = AcpiOsReadPort(addr, &chunk32,
NBBY*(1 << (reg->AccessWidth - 1)));
KASSERTMSG(!ACPI_FAILURE(rv), "%s",
AcpiFormatException(rv));
chunk = chunk32;
break;
}
case ACPI_ADR_SPACE_SYSTEM_MEMORY:
switch (reg->AccessWidth) {
case 1:
chunk = *((volatile const uint8_t *)map + i);
break;
case 2:
chunk = *((volatile const uint16_t *)map + i);
break;
case 3:
chunk = *((volatile const uint32_t *)map + i);
break;
case 4:
chunk = *((volatile const uint64_t *)map + i);
break;
default:
__unreachable();
}
break;
default:
__unreachable();
}
v |= chunk << (i*chunkbits);
}
membar_acquire();
return v;
}
void
apei_mapreg_write(const ACPI_GENERIC_ADDRESS *reg, struct apei_mapreg *map,
uint64_t v)
{
unsigned chunkbits = NBBY*(1 << (reg->AccessWidth - 1));
unsigned i, n = reg->BitWidth / chunkbits;
membar_release();
for (i = 0; i < n; i++) {
uint64_t chunk = v >> (i*chunkbits);
switch (reg->SpaceId) {
case ACPI_ADR_SPACE_SYSTEM_IO: {
ACPI_IO_ADDRESS addr;
ACPI_STATUS rv;
switch (reg->AccessWidth) {
case 1:
addr = reg->Address + i;
break;
case 2:
addr = reg->Address + 2*i;
break;
case 3:
addr = reg->Address + 4*i;
break;
case 4:
default:
__unreachable();
}
rv = AcpiOsWritePort(addr, chunk,
NBBY*(1 << (reg->AccessWidth - 1)));
KASSERTMSG(!ACPI_FAILURE(rv), "%s",
AcpiFormatException(rv));
break;
}
case ACPI_ADR_SPACE_SYSTEM_MEMORY:
switch (reg->AccessWidth) {
case 1:
*((volatile uint8_t *)map + i) = chunk;
break;
case 2:
*((volatile uint16_t *)map + i) = chunk;
break;
case 3:
*((volatile uint32_t *)map + i) = chunk;
break;
case 4:
*((volatile uint64_t *)map + i) = chunk;
break;
default:
__unreachable();
}
break;
default:
__unreachable();
}
}
}