blob: 5335bf1fecb6f9e5e8144dfd8f84b68040b4c3d9 [file] [log] [blame]
// Copyright 2017 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 "machina.h"
#include <assert.h>
#include <fidl/fuchsia.hardware.platform.bus/cpp/driver/fidl.h>
#include <fidl/fuchsia.hardware.platform.bus/cpp/fidl.h>
#include <lib/ddk/binding_driver.h>
#include <lib/ddk/debug.h>
#include <lib/ddk/device.h>
#include <lib/ddk/driver.h>
#include <lib/ddk/platform-defs.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <threads.h>
#include <unistd.h>
#include <zircon/assert.h>
#include <zircon/process.h>
#include <zircon/syscalls.h>
#include <zircon/threads.h>
#include "lib/fit/defer.h"
namespace machina {
namespace fpbus = fuchsia_hardware_platform_bus;
static zx_status_t machina_pci_init(zx_device_t* parent) {
zx_status_t status;
zx_pci_init_arg_t* arg;
// Room for one addr window.
uint32_t arg_size = sizeof(*arg) + sizeof(arg->addr_windows[0]);
arg = static_cast<zx_pci_init_arg_t*>(calloc(1, arg_size));
if (!arg) {
return ZX_ERR_NO_MEMORY;
}
auto defer = fit::defer([arg]() { free(arg); });
status = zx_pci_add_subtract_io_range(get_mmio_resource(parent), true /* mmio */,
PCIE_MMIO_BASE_PHYS, PCIE_MMIO_SIZE, true /* add */);
if (status != ZX_OK) {
return status;
}
// Initialize our swizzle table
zx_pci_irq_swizzle_lut_t* lut = &arg->dev_pin_to_global_irq;
for (unsigned dev_id = 0; dev_id < ZX_PCI_MAX_DEVICES_PER_BUS; dev_id++) {
for (unsigned func_id = 0; func_id < ZX_PCI_MAX_FUNCTIONS_PER_DEVICE; func_id++) {
for (unsigned pin = 0; pin < ZX_PCI_MAX_LEGACY_IRQ_PINS; pin++) {
(*lut)[dev_id][func_id][pin] = PCIE_INT_BASE + dev_id;
}
}
}
arg->num_irqs = 0;
arg->addr_window_count = 1;
arg->addr_windows[0].cfg_space_type = PCI_CFG_SPACE_TYPE_MMIO;
arg->addr_windows[0].has_ecam = true;
arg->addr_windows[0].base = PCIE_ECAM_BASE_PHYS;
arg->addr_windows[0].size = PCIE_ECAM_SIZE;
arg->addr_windows[0].bus_start = 0;
arg->addr_windows[0].bus_end = (PCIE_ECAM_SIZE / ZX_PCI_ECAM_BYTE_PER_BUS) - 1;
status = zx_pci_init(get_mmio_resource(parent), arg, arg_size);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: error %d in zx_pci_init", __FUNCTION__, status);
return status;
}
return status;
}
static void machina_board_release(void* ctx) { delete static_cast<machina_board_t*>(ctx); }
static zx_protocol_device_t machina_board_device_protocol = {
.version = DEVICE_OPS_VERSION,
.release = machina_board_release,
};
static const std::vector<fpbus::Mmio> pl031_mmios = {
{{
.base = RTC_BASE_PHYS,
.length = RTC_SIZE,
}},
};
static const fpbus::Node pl031_dev = []() {
fpbus::Node dev;
dev.name() = "pl031";
dev.vid() = PDEV_VID_GENERIC;
dev.pid() = PDEV_PID_GENERIC;
dev.did() = PDEV_DID_RTC_PL031;
dev.mmio() = pl031_mmios;
return dev;
}();
static int machina_start_thread(void* arg) {
machina_board_t* bus = static_cast<machina_board_t*>(arg);
zx_status_t status;
status = machina_sysmem_init(bus);
if (status != ZX_OK) {
zxlogf(ERROR, "machina_board_bind machina_sysmem_init failed: %d", status);
return status;
}
std::vector<fpbus::Bti> pci_btis = {
{{
.iommu_index = 0,
.bti_id = 0,
}},
};
fpbus::Node pci_dev;
pci_dev.name() = "pci";
pci_dev.vid() = PDEV_VID_GENERIC;
pci_dev.pid() = PDEV_PID_GENERIC;
pci_dev.did() = PDEV_DID_KPCI;
pci_dev.bti() = std::move(pci_btis);
fdf::Arena arena('MACH');
fidl::Arena<> fidl_arena;
auto result = bus->client.buffer(arena)->NodeAdd(fidl::ToWire(fidl_arena, pci_dev));
if (!result.ok()) {
zxlogf(ERROR, "%s: NodeAdd request failed: %s", __func__, result.FormatDescription().data());
return result.status();
}
if (result->is_error()) {
zxlogf(ERROR, "%s: NodeAdd failed: %s", __func__, zx_status_get_string(result->error_value()));
return result->error_value();
}
result = bus->client.buffer(arena)->NodeAdd(fidl::ToWire(fidl_arena, pl031_dev));
if (!result.ok()) {
zxlogf(ERROR, "%s: NodeAdd request failed: %s", __func__, result.FormatDescription().data());
return result.status();
}
if (result->is_error()) {
zxlogf(ERROR, "%s: NodeAdd failed: %s", __func__, zx_status_get_string(result->error_value()));
return result->error_value();
}
return ZX_OK;
}
static zx_status_t machina_board_bind(void* ctx, zx_device_t* parent) {
std::unique_ptr<machina_board_t> bus = std::make_unique<machina_board_t>();
if (!bus) {
return ZX_ERR_NO_MEMORY;
}
auto endpoints = fdf::CreateEndpoints<fpbus::PlatformBus>();
if (endpoints.is_error()) {
return endpoints.error_value();
}
zx_status_t status = device_connect_runtime_protocol(
parent, fpbus::Service::PlatformBus::ServiceName, fpbus::Service::PlatformBus::Name,
endpoints->server.TakeHandle().release());
if (status != ZX_OK) {
zxlogf(ERROR, "Failed to connect to platform bus: %s", zx_status_get_string(status));
return status;
}
bus->client.Bind(std::move(endpoints->client));
status = machina_pci_init(parent);
if (status != ZX_OK) {
zxlogf(ERROR, "machina_pci_init failed: %d", status);
}
device_add_args_t args = {
.version = DEVICE_ADD_ARGS_VERSION,
.name = "machina",
.ops = &machina_board_device_protocol,
.flags = DEVICE_ADD_NON_BINDABLE,
};
status = device_add(parent, &args, NULL);
if (status != ZX_OK) {
return status;
}
// The DDK takes ownership of the pointer.
auto raw_bus = bus.release();
thrd_t t;
int thrd_rc = thrd_create_with_name(&t, machina_start_thread, raw_bus, "machina_start_thread");
if (thrd_rc != thrd_success) {
status = thrd_status_to_zx_status(thrd_rc);
printf("machina_board_bind failed %d\n", status);
return status;
}
return status;
}
} // namespace machina
static zx_driver_ops_t machina_board_driver_ops = {
.version = DRIVER_OPS_VERSION,
.bind = machina::machina_board_bind,
};
ZIRCON_DRIVER(machina_board, machina_board_driver_ops, "zircon", "0.1");