blob: 9497e00843c819d9a6a84089dd8e9d4f7dc4cd9a [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 "msd_vsl_device.h"
#include "magma_util/macros.h"
#include "msd.h"
#include "platform_mmio.h"
#include "registers.h"
#include <chrono>
#include <thread>
std::unique_ptr<MsdVslDevice> MsdVslDevice::Create(void* device_handle, bool enable_mmu)
{
auto device = std::make_unique<MsdVslDevice>();
if (!device->Init(device_handle, enable_mmu))
return DRETP(nullptr, "Failed to initialize device");
return device;
}
bool MsdVslDevice::Init(void* device_handle, bool enable_mmu)
{
platform_device_ = magma::PlatformDevice::Create(device_handle);
if (!platform_device_)
return DRETF(false, "Failed to create platform device");
std::unique_ptr<magma::PlatformMmio> mmio =
platform_device_->CpuMapMmio(0, magma::PlatformMmio::CACHE_POLICY_UNCACHED_DEVICE);
if (!mmio)
return DRETF(false, "failed to map registers");
register_io_ = std::make_unique<magma::RegisterIo>(std::move(mmio));
device_id_ = registers::ChipId::Get().ReadFrom(register_io_.get()).chip_id().get();
DLOG("Detected vsl chip id 0x%x", device_id_);
if (device_id_ != 0x7000)
return DRETF(false, "Unspported gpu model 0x%x\n", device_id_);
gpu_features_ = std::make_unique<GpuFeatures>(register_io_.get());
DLOG("gpu features: 0x%x minor features 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
gpu_features_->features().reg_value(), gpu_features_->minor_features(0),
gpu_features_->minor_features(1), gpu_features_->minor_features(2),
gpu_features_->minor_features(3), gpu_features_->minor_features(4),
gpu_features_->minor_features(5));
DLOG("halti5: %d mmu: %d", gpu_features_->halti5(), gpu_features_->has_mmu());
DLOG("stream count %u register_max %u thread_count %u vertex_cache_size %u shader_core_count "
"%u pixel_pipes %u vertex_output_buffer_size %u\n",
gpu_features_->stream_count(), gpu_features_->register_max(),
gpu_features_->thread_count(), gpu_features_->vertex_cache_size(),
gpu_features_->shader_core_count(), gpu_features_->pixel_pipes(),
gpu_features_->vertex_output_buffer_size());
DLOG("instruction count %u buffer_size %u num_constants %u varyings_count %u\n",
gpu_features_->instruction_count(), gpu_features_->buffer_size(),
gpu_features_->num_constants(), gpu_features_->varyings_count());
if (!gpu_features_->features().pipe_3d().get())
return DRETF(false, "Gpu has no 3d pipe: features 0x%x\n",
gpu_features_->features().reg_value());
bus_mapper_ = magma::PlatformBusMapper::Create(platform_device_->GetBusTransactionInitiator());
if (!bus_mapper_)
return DRETF(false, "failed to create bus mapper");
page_table_arrays_ = PageTableArrays::Create(bus_mapper_.get());
if (!page_table_arrays_)
return DRETF(false, "failed to create page table arrays");
Reset();
HardwareInit(enable_mmu);
return true;
}
void MsdVslDevice::HardwareInit(bool enable_mmu)
{
{
auto reg = registers::SecureAhbControl::Get().ReadFrom(register_io_.get());
reg.non_secure_access().set(1);
reg.WriteTo(register_io_.get());
}
page_table_arrays_->HardwareInit(register_io_.get());
page_table_arrays_->Enable(register_io(), enable_mmu);
page_table_slot_allocator_ =
std::make_unique<PageTableSlotAllocator>(page_table_arrays_->size());
}
void MsdVslDevice::Reset()
{
DLOG("Reset start");
auto clock_control = registers::ClockControl::Get().FromValue(0);
clock_control.isolate_gpu().set(1);
clock_control.WriteTo(register_io());
{
auto reg = registers::SecureAhbControl::Get().FromValue(0);
reg.reset().set(1);
reg.WriteTo(register_io_.get());
}
std::this_thread::sleep_for(std::chrono::microseconds(100));
clock_control.soft_reset().set(0);
clock_control.WriteTo(register_io());
clock_control.isolate_gpu().set(0);
clock_control.WriteTo(register_io());
clock_control = registers::ClockControl::Get().ReadFrom(register_io_.get());
if (!IsIdle() || !clock_control.idle_3d().get()) {
magma::log(magma::LOG_WARNING, "Gpu reset: failed to idle");
}
DLOG("Reset complete");
}
bool MsdVslDevice::IsIdle()
{
return registers::IdleState::Get().ReadFrom(register_io_.get()).IsIdle();
}
bool MsdVslDevice::SubmitCommandBufferNoMmu(uint64_t bus_addr, uint32_t length,
uint16_t* prefetch_out)
{
if (bus_addr & 0xFFFFFFFF00000000ul)
return DRETF(false, "Can't submit address > 32 bits without mmu: 0x%08lx", bus_addr);
uint32_t prefetch = magma::round_up(length, sizeof(uint64_t)) / sizeof(uint64_t);
if (prefetch & 0xFFFF0000)
return DRETF(false, "Can't submit length %u (prefetch 0x%x)", length, prefetch);
*prefetch_out = prefetch & 0xFFFF;
DLOG("Submitting buffer at bus addr 0x%lx", bus_addr);
auto reg_cmd_addr = registers::FetchEngineCommandAddress::Get().FromValue(0);
reg_cmd_addr.addr().set(bus_addr & 0xFFFFFFFF);
auto reg_cmd_ctrl = registers::FetchEngineCommandControl::Get().FromValue(0);
reg_cmd_ctrl.enable().set(1);
reg_cmd_ctrl.prefetch().set(*prefetch_out);
auto reg_sec_cmd_ctrl = registers::SecureCommandControl::Get().FromValue(0);
reg_sec_cmd_ctrl.enable().set(1);
reg_sec_cmd_ctrl.prefetch().set(*prefetch_out);
reg_cmd_addr.WriteTo(register_io_.get());
reg_cmd_ctrl.WriteTo(register_io_.get());
reg_sec_cmd_ctrl.WriteTo(register_io_.get());
return true;
}
bool MsdVslDevice::SubmitCommandBuffer(uint32_t gpu_addr, uint32_t length, uint16_t* prefetch_out)
{
uint32_t prefetch = magma::round_up(length, sizeof(uint64_t)) / sizeof(uint64_t);
if (prefetch & 0xFFFF0000)
return DRETF(false, "Can't submit length %u (prefetch 0x%x)", length, prefetch);
*prefetch_out = prefetch & 0xFFFF;
DLOG("Submitting buffer at gpu addr 0x%x", gpu_addr);
auto reg_cmd_addr = registers::FetchEngineCommandAddress::Get().FromValue(0);
reg_cmd_addr.addr().set(gpu_addr);
auto reg_cmd_ctrl = registers::FetchEngineCommandControl::Get().FromValue(0);
reg_cmd_ctrl.enable().set(1);
reg_cmd_ctrl.prefetch().set(*prefetch_out);
auto reg_sec_cmd_ctrl = registers::SecureCommandControl::Get().FromValue(0);
reg_sec_cmd_ctrl.enable().set(1);
reg_sec_cmd_ctrl.prefetch().set(*prefetch_out);
reg_cmd_addr.WriteTo(register_io_.get());
reg_cmd_ctrl.WriteTo(register_io_.get());
reg_sec_cmd_ctrl.WriteTo(register_io_.get());
return true;
}
std::unique_ptr<MsdVslConnection> MsdVslDevice::Open(msd_client_id_t client_id)
{
uint32_t page_table_array_slot;
if (!page_table_slot_allocator_->Alloc(&page_table_array_slot))
return DRETP(nullptr, "couldn't allocate page table slot");
auto address_space = AddressSpace::Create(this);
if (!address_space)
return DRETP(nullptr, "failed to create address space");
page_table_arrays_->AssignAddressSpace(page_table_array_slot, address_space.get());
return std::make_unique<MsdVslConnection>(this, page_table_array_slot, std::move(address_space),
client_id);
}
//////////////////////////////////////////////////////////////////////////////////////////////////
msd_connection_t* msd_device_open(msd_device_t* device, msd_client_id_t client_id)
{
auto connection = MsdVslDevice::cast(device)->Open(client_id);
if (!connection)
return DRETP(nullptr, "failed to create connection");
return new MsdVslAbiConnection(std::move(connection));
}
void msd_device_destroy(msd_device_t* device) { delete MsdVslDevice::cast(device); }
magma_status_t msd_device_query(msd_device_t* device, uint64_t id, uint64_t* value_out)
{
switch (id) {
case MAGMA_QUERY_DEVICE_ID:
*value_out = MsdVslDevice::cast(device)->device_id();
return MAGMA_STATUS_OK;
}
return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, "unhandled id %" PRIu64, id);
}
void msd_device_dump_status(msd_device_t* device, uint32_t dump_type) {}