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