blob: 0750e15883ad9a7157355802a0e8cd9a569b328b [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <inttypes.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/stat.h>
#include <threads.h>
#include "dev.h"
#include "errors.h"
#include "pci.h"
#include "pciroot-private.h"
// - Add pciroot to ARM, probably define get_auxdata and get_bti as ZX_ERR_NOT_SUPPORTED, only matters
// for the amlogic board. Also may matter for arm qemu?
// - Convert zx_pci_init to make the call rather than getting the arg when called
// - Add the aspace handles to the call, but they won't be used yet since PCI is still in the kernel
// - Add a #define to not build kernel PCI, stub a userspace PCI driver
static ACPI_STATUS find_pci_child_callback(ACPI_HANDLE object, uint32_t nesting_level,
void* context, void** out_value) {
ACPI_DEVICE_INFO* info;
ACPI_STATUS acpi_status = AcpiGetObjectInfo(object, &info);
if (acpi_status != AE_OK) {
zxlogf(TRACE, "bus-acpi: AcpiGetObjectInfo failed %d\n", acpi_status);
return acpi_status;
}
ACPI_FREE(info);
ACPI_OBJECT obj = {
.Type = ACPI_TYPE_INTEGER,
};
ACPI_BUFFER buffer = {
.Length = sizeof(obj),
.Pointer = &obj,
};
acpi_status = AcpiEvaluateObject(object, (char*)"_ADR", NULL, &buffer);
if (acpi_status != AE_OK) {
return AE_OK;
}
uint32_t addr = *(uint32_t*)context;
ACPI_HANDLE* out_handle = (ACPI_HANDLE*)out_value;
if (addr == obj.Integer.Value) {
*out_handle = object;
return AE_CTRL_TERMINATE;
} else {
return AE_OK;
}
}
static ACPI_STATUS pci_child_data_resources_callback(ACPI_RESOURCE* res, void* context) {
pci_child_auxdata_ctx_t* ctx = (pci_child_auxdata_ctx_t*)context;
auxdata_i2c_device_t* child = ctx->data + ctx->i;
if (res->Type != ACPI_RESOURCE_TYPE_SERIAL_BUS) {
return AE_NOT_FOUND;
}
if (res->Data.I2cSerialBus.Type != ACPI_RESOURCE_SERIAL_TYPE_I2C) {
return AE_NOT_FOUND;
}
ACPI_RESOURCE_I2C_SERIALBUS* i2c = &res->Data.I2cSerialBus;
child->bus_master = i2c->SlaveMode;
child->ten_bit = i2c->AccessMode;
child->address = i2c->SlaveAddress;
child->bus_speed = i2c->ConnectionSpeed;
return AE_CTRL_TERMINATE;
}
static ACPI_STATUS pci_child_data_callback(ACPI_HANDLE object,
uint32_t nesting_level,
void* context, void** out_value) {
pci_child_auxdata_ctx_t* ctx = (pci_child_auxdata_ctx_t*)context;
if ((ctx->i + 1) > ctx->max) {
return AE_CTRL_TERMINATE;
}
auxdata_i2c_device_t* data = ctx->data + ctx->i;
data->protocol_id = ZX_PROTOCOL_I2C;
ACPI_DEVICE_INFO* info = NULL;
ACPI_STATUS acpi_status = AcpiGetObjectInfo(object, &info);
if (acpi_status == AE_OK) {
// These length fields count the trailing NUL.
// Publish HID
if ((info->Valid & ACPI_VALID_HID) && info->HardwareId.Length <= HID_LENGTH + 1) {
const char* hid = info->HardwareId.String;
if (!strncmp(hid, GOOGLE_TPM_HID_STRING, HID_LENGTH)) {
data->protocol_id = ZX_PROTOCOL_TPM;
}
data->props[data->propcount].id = BIND_ACPI_HID_0_3;
data->props[data->propcount++].value = htobe32(*((uint32_t*)(hid)));
data->props[data->propcount].id = BIND_ACPI_HID_4_7;
data->props[data->propcount++].value = htobe32(*((uint32_t*)(hid + 4)));
}
// Check for I2C HID devices via CID
if ((info->Valid & ACPI_VALID_CID) && info->CompatibleIdList.Count > 0) {
ACPI_PNP_DEVICE_ID* cid = &info->CompatibleIdList.Ids[0];
if (cid->Length <= CID_LENGTH + 1) {
if (!strncmp(cid->String, I2C_HID_CID_STRING, CID_LENGTH)) {
data->protocol_id = ZX_PROTOCOL_I2C_HID;
}
data->props[data->propcount].id = BIND_ACPI_CID_0_3;
data->props[data->propcount++].value = htobe32(*((uint32_t*)(cid->String)));
data->props[data->propcount].id = BIND_ACPI_CID_4_7;
data->props[data->propcount++].value = htobe32(*((uint32_t*)(cid->String + 4)));
}
}
ACPI_FREE(info);
}
// call _CRS to get i2c info
acpi_status = AcpiWalkResources(object, (char*)"_CRS",
pci_child_data_resources_callback, ctx);
if ((acpi_status == AE_OK) || (acpi_status == AE_CTRL_TERMINATE)) {
ctx->i += 1;
}
return AE_OK;
}
static zx_status_t pciroot_op_get_auxdata(void* context, const char* args,
void* data, uint32_t bytes,
uint32_t* actual) {
acpi_device_t* dev = (acpi_device_t*)context;
char type[16];
uint32_t bus_id, dev_id, func_id;
int n;
if ((n = sscanf(args, "%[^,],%02x:%02x:%02x", type, &bus_id, &dev_id, &func_id)) != 4) {
return ZX_ERR_INVALID_ARGS;
}
zxlogf(SPEW, "bus-acpi: get_auxdata type '%s' device %02x:%02x:%02x\n", type,
bus_id, dev_id, func_id);
if (strcmp(type, "i2c-child")) {
return ZX_ERR_NOT_SUPPORTED;
}
if (bytes < (2 * sizeof(uint32_t))) {
return ZX_ERR_BUFFER_TOO_SMALL;
}
ACPI_HANDLE pci_node = NULL;
uint32_t addr = (dev_id << 16) | func_id;
// Look for the child node with this device and function id
ACPI_STATUS acpi_status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, dev->ns_node, 1,
find_pci_child_callback, NULL,
&addr, &pci_node);
if ((acpi_status != AE_OK) && (acpi_status != AE_CTRL_TERMINATE)) {
return acpi_to_zx_status(acpi_status);
}
if (pci_node == NULL) {
return ZX_ERR_NOT_FOUND;
}
memset(data, 0, bytes);
// Look for as many children as can fit in the provided buffer
pci_child_auxdata_ctx_t ctx = {
.max = bytes / sizeof(auxdata_i2c_device_t),
.i = 0,
.data = data,
};
acpi_status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, pci_node, 1,
pci_child_data_callback, NULL, &ctx, NULL);
if ((acpi_status != AE_OK) && (acpi_status != AE_CTRL_TERMINATE)) {
*actual = 0;
return acpi_to_zx_status(acpi_status);
}
*actual = ctx.i * sizeof(auxdata_i2c_device_t);
zxlogf(SPEW, "bus-acpi: get_auxdata '%s' %u devs actual %u\n",
args, ctx.i, *actual);
return ZX_OK;
}
// TODO(teisenbe) sort this out when the dummy handle is no longer used
extern zx_handle_t dummy_iommu_handle;
static zx_status_t pciroot_op_get_bti(void* context, uint32_t bdf, uint32_t index,
zx_handle_t* bti) {
// The x86 IOMMU world uses PCI BDFs as the hardware identifiers, so there
// will only be one BTI per device.
if (index != 0) {
return ZX_ERR_OUT_OF_RANGE;
}
// For dummy IOMMUs, the bti_id just needs to be unique. For Intel IOMMUs,
// the bti_ids correspond to PCI BDFs.
return zx_bti_create(dummy_iommu_handle, 0, bdf, bti);
}
static zx_status_t pciroot_op_get_pci_mcfgs(void* context, zx_pci_init_arg_t** arg, size_t* size) {
return get_pci_init_arg(arg, size);
}
static pciroot_protocol_ops_t pciroot_proto = {
.get_auxdata = pciroot_op_get_auxdata,
.get_bti = pciroot_op_get_bti,
.get_pci_mcfgs = pciroot_op_get_pci_mcfgs,
};
pciroot_protocol_ops_t* get_pciroot_proto(void) {
return &pciroot_proto;
}