#include "apple_fw.h"
#include <contrib/dev/acpica/source/include/acnamesp.h>
#define _COMPONENT ACPI_OEM
ACPI_MODULE_NAME("APPLE_FW")
MALLOC_DEFINE(M_APPLEFW, "apple_fw", "Apple EFI firmware driver");
static int apple_fw_identify(driver_t *, device_t);
static int apple_fw_probe(device_t);
static int apple_fw_attach(device_t);
static int apple_fw_detach(device_t);
static void apple_fw_enumerate_ssdts(device_t);
static void apple_fw_eval_osdw(device_t);
static void apple_fw_fixup_osys(device_t);
static device_method_t apple_fw_methods[] = {
DEVMETHOD(device_identify, apple_fw_identify),
DEVMETHOD(device_probe, apple_fw_probe),
DEVMETHOD(device_attach, apple_fw_attach),
DEVMETHOD(device_detach, apple_fw_detach),
DEVMETHOD_END
};
static driver_t apple_fw_driver = {
"apple_fw",
apple_fw_methods,
sizeof(struct apple_fw_softc),
.gpri = KOBJ_GPRI_ACPI
};
static devclass_t apple_fw_devclass;
DRIVER_MODULE(apple_fw, acpi, apple_fw_driver, apple_fw_devclass,
NULL, NULL);
MODULE_DEPEND(apple_fw, acpi, 1, 1, 1);
MODULE_VERSION(apple_fw, 1);
static int
apple_fw_is_apple(void)
{
ACPI_TABLE_HEADER hdr;
ACPI_STATUS status;
status = AcpiGetTableHeader(ACPI_SIG_FADT, 0, &hdr);
if (ACPI_FAILURE(status))
return (0);
return (strncmp(hdr.OemId, "APPLE ", ACPI_OEM_ID_SIZE) == 0);
}
#define DEV_APPLE_FW(x) \
(acpi_get_magic(x) == (uintptr_t)&apple_fw_devclass)
static int
apple_fw_identify(driver_t *driver, device_t parent)
{
device_t child;
if (device_find_child(parent, "apple_fw", -1) != NULL)
return (0);
if (!apple_fw_is_apple())
return (ENXIO);
child = BUS_ADD_CHILD(parent, parent, 0, "apple_fw", -1);
if (child != NULL)
acpi_set_magic(child, (uintptr_t)&apple_fw_devclass);
return (0);
}
static int
apple_fw_probe(device_t dev)
{
char *product, *biosver;
char desc[128];
if (acpi_disabled("apple_fw"))
return (ENXIO);
if (!DEV_APPLE_FW(dev))
return (ENXIO);
product = kgetenv("smbios.system.product");
biosver = kgetenv("smbios.bios.version");
ksnprintf(desc, sizeof(desc), "Apple %s EFI Firmware %s",
product, biosver);
device_set_desc_copy(dev, desc);
kfreeenv(product);
kfreeenv(biosver);
return (0);
}
static int
apple_fw_attach(device_t dev)
{
struct apple_fw_softc *sc = device_get_softc(dev);
int darwin_osi = 1;
sc->sc_dev = dev;
lockinit(&sc->sc_lock, "apple_fw", 0, LK_CANRECURSE);
apple_fw_enumerate_ssdts(dev);
TUNABLE_INT_FETCH("hw.acpi.apple_darwin_osi", &darwin_osi);
apple_fw_eval_osdw(dev);
if (darwin_osi && sc->sc_osdw == 0)
apple_fw_fixup_osys(dev);
apple_fw_discover_dsm_devices(dev);
apple_fw_sysctl_init(dev);
return (0);
}
static int
apple_fw_detach(device_t dev)
{
struct apple_fw_softc *sc = device_get_softc(dev);
lockuninit(&sc->sc_lock);
return (0);
}
#define APPLE_OSYS_DARWIN 0x2710
static void
apple_fw_fixup_osys(device_t dev)
{
struct apple_fw_softc *sc = device_get_softc(dev);
ACPI_STATUS status;
AcpiInstallInterface("Darwin");
status = AcpiEvaluateObject(NULL, "\\PINI", NULL, NULL);
if (ACPI_SUCCESS(status)) {
apple_fw_eval_osdw(dev);
if (sc->sc_osdw != 0) {
device_printf(dev, "Darwin OSI fixup via PINI\n");
return;
}
}
{
ACPI_HANDLE handle;
ACPI_NAMESPACE_NODE *node;
ACPI_OPERAND_OBJECT *obj;
status = AcpiGetHandle(NULL, "\\OSYS", &handle);
if (ACPI_FAILURE(status)) {
device_printf(dev,
"Darwin OSI fixup failed: no \\OSYS\n");
return;
}
node = AcpiNsValidateHandle(handle);
if (node == NULL)
return;
obj = AcpiNsGetAttachedObject(node);
if (obj != NULL &&
obj->Common.Type == ACPI_TYPE_INTEGER) {
obj->Integer.Value = APPLE_OSYS_DARWIN;
apple_fw_eval_osdw(dev);
device_printf(dev,
"Darwin OSI fixup via OSYS poke\n");
}
}
}
static void
apple_fw_enumerate_ssdts(device_t dev)
{
struct apple_fw_softc *sc = device_get_softc(dev);
ACPI_TABLE_HEADER *table;
ACPI_STATUS status;
uint32_t instance;
int n;
sc->sc_ssdt_count = 0;
for (instance = 1; instance <= 128; instance++) {
status = AcpiGetTable(ACPI_SIG_SSDT, instance, &table);
if (ACPI_FAILURE(status))
break;
if (strncmp(table->OemId, "APPLE ", ACPI_OEM_ID_SIZE) != 0)
continue;
n = sc->sc_ssdt_count;
if (n >= APPLE_FW_MAX_SSDTS)
break;
memcpy(sc->sc_ssdts[n].oem_table_id, table->OemTableId,
ACPI_OEM_TABLE_ID_SIZE);
sc->sc_ssdts[n].oem_table_id[ACPI_OEM_TABLE_ID_SIZE] = '\0';
sc->sc_ssdt_count++;
}
}
static void
apple_fw_eval_osdw(device_t dev)
{
struct apple_fw_softc *sc = device_get_softc(dev);
UINT32 val;
sc->sc_osdw = 0;
if (ACPI_SUCCESS(acpi_GetInteger(NULL, "OSDW", &val)))
sc->sc_osdw = (int)val;
}