1// Copyright 2018 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <acpica/acpi.h> 6#include <zircon/types.h> 7#include <zircon/device/i2c.h> 8#include <ddk/debug.h> 9#include <ddk/protocol/pciroot.h> 10 11#include "acpi-private.h" 12#include "dev.h" 13#include "errors.h" 14#include "iommu.h" 15#include "pciroot.h" 16 17static ACPI_STATUS find_pci_child_callback(ACPI_HANDLE object, uint32_t nesting_level, 18 void* context, void** out_value) { 19 ACPI_DEVICE_INFO* info; 20 ACPI_STATUS acpi_status = AcpiGetObjectInfo(object, &info); 21 if (acpi_status != AE_OK) { 22 zxlogf(TRACE, "bus-acpi: AcpiGetObjectInfo failed %d\n", acpi_status); 23 return acpi_status; 24 } 25 ACPI_FREE(info); 26 ACPI_OBJECT obj = { 27 .Type = ACPI_TYPE_INTEGER, 28 }; 29 ACPI_BUFFER buffer = { 30 .Length = sizeof(obj), 31 .Pointer = &obj, 32 }; 33 acpi_status = AcpiEvaluateObject(object, (char*)"_ADR", NULL, &buffer); 34 if (acpi_status != AE_OK) { 35 return AE_OK; 36 } 37 uint32_t addr = *(uint32_t*)context; 38 ACPI_HANDLE* out_handle = (ACPI_HANDLE*)out_value; 39 if (addr == obj.Integer.Value) { 40 *out_handle = object; 41 return AE_CTRL_TERMINATE; 42 } else { 43 return AE_OK; 44 } 45} 46 47static ACPI_STATUS pci_child_data_resources_callback(ACPI_RESOURCE* res, void* context) { 48 pci_child_auxdata_ctx_t* ctx = (pci_child_auxdata_ctx_t*)context; 49 auxdata_i2c_device_t* child = ctx->data + ctx->i; 50 51 if (res->Type != ACPI_RESOURCE_TYPE_SERIAL_BUS) { 52 return AE_NOT_FOUND; 53 } 54 if (res->Data.I2cSerialBus.Type != ACPI_RESOURCE_SERIAL_TYPE_I2C) { 55 return AE_NOT_FOUND; 56 } 57 58 ACPI_RESOURCE_I2C_SERIALBUS* i2c = &res->Data.I2cSerialBus; 59 child->bus_master = i2c->SlaveMode; 60 child->ten_bit = i2c->AccessMode; 61 child->address = i2c->SlaveAddress; 62 child->bus_speed = i2c->ConnectionSpeed; 63 64 return AE_CTRL_TERMINATE; 65} 66 67static ACPI_STATUS pci_child_data_callback(ACPI_HANDLE object, 68 uint32_t nesting_level, 69 void* context, void** out_value) { 70 pci_child_auxdata_ctx_t* ctx = (pci_child_auxdata_ctx_t*)context; 71 if ((ctx->i + 1) > ctx->max) { 72 return AE_CTRL_TERMINATE; 73 } 74 75 auxdata_i2c_device_t* data = ctx->data + ctx->i; 76 data->protocol_id = ZX_PROTOCOL_I2C; 77 78 ACPI_DEVICE_INFO* info = NULL; 79 ACPI_STATUS acpi_status = AcpiGetObjectInfo(object, &info); 80 if (acpi_status == AE_OK) { 81 // These length fields count the trailing NUL. 82 // Publish HID 83 if ((info->Valid & ACPI_VALID_HID) && info->HardwareId.Length <= HID_LENGTH + 1) { 84 const char* hid = info->HardwareId.String; 85 data->props[data->propcount].id = BIND_ACPI_HID_0_3; 86 data->props[data->propcount++].value = htobe32(*((uint32_t*)(hid))); 87 data->props[data->propcount].id = BIND_ACPI_HID_4_7; 88 data->props[data->propcount++].value = htobe32(*((uint32_t*)(hid + 4))); 89 } 90 // Check for I2C HID devices via CID 91 if ((info->Valid & ACPI_VALID_CID) && info->CompatibleIdList.Count > 0) { 92 ACPI_PNP_DEVICE_ID* cid = &info->CompatibleIdList.Ids[0]; 93 if (cid->Length <= CID_LENGTH + 1) { 94 if (!strncmp(cid->String, I2C_HID_CID_STRING, CID_LENGTH)) { 95 data->props[data->propcount].id = BIND_I2C_CLASS; 96 data->props[data->propcount++].value = I2C_CLASS_HID; 97 } 98 data->props[data->propcount].id = BIND_ACPI_CID_0_3; 99 data->props[data->propcount++].value = htobe32(*((uint32_t*)(cid->String))); 100 data->props[data->propcount].id = BIND_ACPI_CID_4_7; 101 data->props[data->propcount++].value = htobe32(*((uint32_t*)(cid->String + 4))); 102 } 103 } 104 ACPI_FREE(info); 105 } 106 ZX_ASSERT(data->propcount <= AUXDATA_MAX_DEVPROPS); 107 108 // call _CRS to get i2c info 109 acpi_status = AcpiWalkResources(object, (char*)"_CRS", 110 pci_child_data_resources_callback, ctx); 111 if ((acpi_status == AE_OK) || (acpi_status == AE_CTRL_TERMINATE)) { 112 ctx->i++; 113 } 114 return AE_OK; 115} 116 117static zx_status_t pciroot_op_get_auxdata(void* context, const char* args, 118 void* data, uint32_t bytes, 119 uint32_t* actual) { 120 acpi_device_t* dev = (acpi_device_t*)context; 121 122 char type[16]; 123 uint32_t bus_id, dev_id, func_id; 124 int n; 125 if ((n = sscanf(args, "%[^,],%02x:%02x:%02x", type, &bus_id, &dev_id, &func_id)) != 4) { 126 return ZX_ERR_INVALID_ARGS; 127 } 128 129 zxlogf(SPEW, "bus-acpi: get_auxdata type '%s' device %02x:%02x:%02x\n", type, 130 bus_id, dev_id, func_id); 131 132 if (strcmp(type, "i2c-child")) { 133 return ZX_ERR_NOT_SUPPORTED; 134 } 135 136 if (bytes < (2 * sizeof(uint32_t))) { 137 return ZX_ERR_BUFFER_TOO_SMALL; 138 } 139 140 ACPI_HANDLE pci_node = NULL; 141 uint32_t addr = (dev_id << 16) | func_id; 142 143 // Look for the child node with this device and function id 144 ACPI_STATUS acpi_status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, dev->ns_node, 1, 145 find_pci_child_callback, NULL, 146 &addr, &pci_node); 147 if ((acpi_status != AE_OK) && (acpi_status != AE_CTRL_TERMINATE)) { 148 return acpi_to_zx_status(acpi_status); 149 } 150 if (pci_node == NULL) { 151 return ZX_ERR_NOT_FOUND; 152 } 153 154 memset(data, 0, bytes); 155 156 // Look for as many children as can fit in the provided buffer 157 pci_child_auxdata_ctx_t ctx = { 158 .max = static_cast<uint8_t>(bytes / sizeof(auxdata_i2c_device_t)), 159 .i = 0, 160 .data = static_cast<auxdata_i2c_device_t*>(data), 161 }; 162 163 acpi_status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, pci_node, 1, 164 pci_child_data_callback, NULL, &ctx, NULL); 165 if ((acpi_status != AE_OK) && (acpi_status != AE_CTRL_TERMINATE)) { 166 *actual = 0; 167 return acpi_to_zx_status(acpi_status); 168 } 169 170 *actual = static_cast<uint32_t>(ctx.i * sizeof(auxdata_i2c_device_t)); 171 172 zxlogf(SPEW, "bus-acpi: get_auxdata '%s' %u devs actual %u\n", 173 args, ctx.i, *actual); 174 175 return ZX_OK; 176} 177 178static zx_status_t pciroot_op_get_bti(void* context, uint32_t bdf, uint32_t index, 179 zx_handle_t* bti) { 180 // The x86 IOMMU world uses PCI BDFs as the hardware identifiers, so there 181 // will only be one BTI per device. 182 if (index != 0) { 183 return ZX_ERR_OUT_OF_RANGE; 184 } 185 // For dummy IOMMUs, the bti_id just needs to be unique. For Intel IOMMUs, 186 // the bti_ids correspond to PCI BDFs. 187 zx_handle_t iommu_handle; 188 zx_status_t status = iommu_manager_iommu_for_bdf(bdf, &iommu_handle); 189 if (status != ZX_OK) { 190 return status; 191 } 192 return zx_bti_create(iommu_handle, 0, bdf, bti); 193} 194 195static pciroot_protocol_ops_t pciroot_proto = { 196 .get_auxdata = pciroot_op_get_auxdata, 197 .get_bti = pciroot_op_get_bti, 198}; 199 200pciroot_protocol_ops_t* get_pciroot_ops(void) { 201 return &pciroot_proto; 202} 203