blob: 25688dc227d347b15f79da8dde06a16e97a436be [file] [log] [blame]
// Copyright 2019 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 "aml-nna.h"
#include <stdlib.h>
#include <unistd.h>
#include <zircon/types.h>
#include <memory>
#include <ddk/binding.h>
#include <ddk/debug.h>
#include <ddk/metadata.h>
#include <ddk/platform-defs.h>
#include <fbl/alloc_checker.h>
#include <fbl/auto_call.h>
#include <fbl/auto_lock.h>
#include "s905d3-nna-regs.h"
#include "t931-nna-regs.h"
namespace {
// CLK Shifts
constexpr uint32_t kClockCoreEnableShift = 8;
constexpr uint32_t kClockAxiEnableShift = 24;
// constexpr uint32_t kNna = 0;
constexpr uint32_t kHiu = 1;
constexpr uint32_t kPowerDomain = 2;
constexpr uint32_t kMemoryDomain = 3;
// constexpr uint32_t kSram = 5;
} // namespace
namespace aml_nna {
// This is to be compatible with magma::ZirconPlatformDevice.
zx_status_t AmlNnaDevice::DdkGetProtocol(uint32_t proto_id, void* out_protocol) {
auto* proto = static_cast<ddk::AnyProtocol*>(out_protocol);
proto->ctx = parent_pdev_.ctx;
switch (proto_id) {
case ZX_PROTOCOL_PDEV:
proto->ops = parent_pdev_.ops;
return ZX_OK;
default:
return ZX_ERR_NOT_SUPPORTED;
}
}
zx_status_t AmlNnaDevice::Init() {
power_mmio_.ClearBits32(nna_block_.domain_power_sleep_bits, nna_block_.domain_power_sleep_offset);
memory_pd_mmio_.Write32(0, nna_block_.hhi_mem_pd_reg0_offset);
memory_pd_mmio_.Write32(0, nna_block_.hhi_mem_pd_reg1_offset);
// set bit[12]=0
auto clear_result = reset_.WriteRegister32(nna_block_.reset_level2_offset,
aml_registers::NNA_RESET2_LEVEL_MASK, 0);
if ((clear_result.status() != ZX_OK) || clear_result->result.is_err()) {
zxlogf(ERROR, "%s: Clear Reset Write failed\n", __func__);
return ZX_ERR_INTERNAL;
}
power_mmio_.ClearBits32(nna_block_.domain_power_iso_bits, nna_block_.domain_power_iso_offset);
// set bit[12]=1
auto set_result =
reset_.WriteRegister32(nna_block_.reset_level2_offset, aml_registers::NNA_RESET2_LEVEL_MASK,
aml_registers::NNA_RESET2_LEVEL_MASK);
if ((set_result.status() != ZX_OK) || set_result->result.is_err()) {
zxlogf(ERROR, "%s: Set Reset Write failed\n", __func__);
return ZX_ERR_INTERNAL;
}
// Setup Clocks.
// Set clocks to 800 MHz (FCLK_DIV2P5 = 3, Divisor = 1)
// VIPNANOQ Core clock
hiu_mmio_.SetBits32(((1 << kClockCoreEnableShift) | 3 << 9), nna_block_.clock_control_offset);
// VIPNANOQ Axi clock
hiu_mmio_.SetBits32(((1 << kClockAxiEnableShift) | 3 << 25), nna_block_.clock_control_offset);
return ZX_OK;
}
// static
zx_status_t AmlNnaDevice::Create(void* ctx, zx_device_t* parent) {
zx_status_t status;
ddk::CompositeProtocolClient composite(parent);
if (!composite.is_valid()) {
zxlogf(ERROR, "Could not get composite protocol");
return ZX_ERR_NOT_SUPPORTED;
}
ddk::PDev pdev(composite);
if (!pdev.is_valid()) {
zxlogf(ERROR, "Could not get platform device protocol");
return ZX_ERR_NOT_SUPPORTED;
}
ddk::RegistersProtocolClient reset(composite, "register-reset");
if (!reset.is_valid()) {
zxlogf(ERROR, "Could not get reset_register fragment");
return ZX_ERR_NO_RESOURCES;
}
zx::channel client_end, server_end;
if ((status = zx::channel::create(0, &client_end, &server_end)) != ZX_OK) {
zxlogf(ERROR, "Could not create channel %d\n", status);
return status;
}
reset.Connect(std::move(server_end));
std::optional<ddk::MmioBuffer> hiu_mmio;
status = pdev.MapMmio(kHiu, &hiu_mmio);
if (status != ZX_OK) {
zxlogf(ERROR, "pdev_.MapMmio failed %d\n", status);
return status;
}
std::optional<ddk::MmioBuffer> power_mmio;
status = pdev.MapMmio(kPowerDomain, &power_mmio);
if (status != ZX_OK) {
zxlogf(ERROR, "pdev_.MapMmio failed %d\n", status);
return status;
}
std::optional<ddk::MmioBuffer> memory_pd_mmio;
status = pdev.MapMmio(kMemoryDomain, &memory_pd_mmio);
if (status != ZX_OK) {
zxlogf(ERROR, "pdev_.MapMmio failed %d\n", status);
return status;
}
pdev_device_info_t info;
status = pdev.GetDeviceInfo(&info);
if (status != ZX_OK) {
zxlogf(ERROR, "pdev_.GetDeviceInfo failed %d\n", status);
return status;
}
NnaBlock nna_block;
switch (info.pid) {
case PDEV_PID_AMLOGIC_A311D:
case PDEV_PID_AMLOGIC_T931:
nna_block = T931NnaBlock;
break;
case PDEV_PID_AMLOGIC_S905D3:
nna_block = S905d3NnaBlock;
break;
default:
zxlogf(ERROR, "unhandled PID 0x%x", info.pid);
return ZX_ERR_INVALID_ARGS;
}
fbl::AllocChecker ac;
auto device = std::unique_ptr<AmlNnaDevice>(new (&ac) AmlNnaDevice(
parent, std::move(*hiu_mmio), std::move(*power_mmio), std::move(*memory_pd_mmio),
std::move(client_end), std::move(pdev), nna_block));
if (!ac.check()) {
return ZX_ERR_NO_MEMORY;
}
if ((status = device->Init()) != ZX_OK) {
zxlogf(ERROR, "Could not init device %d.", status);
return status;
}
zx_device_prop_t props[] = {
{BIND_PROTOCOL, 0, ZX_PROTOCOL_PDEV},
{BIND_PLATFORM_DEV_VID, 0, PDEV_VID_GENERIC},
{BIND_PLATFORM_DEV_PID, 0, PDEV_PID_GENERIC},
{BIND_PLATFORM_DEV_DID, 0, PDEV_DID_VSI_VIP},
};
status = device->DdkAdd(ddk::DeviceAddArgs("aml-nna").set_props(props));
if (status != ZX_OK) {
zxlogf(ERROR, "Could not create aml nna device: %d\n", status);
return status;
}
zxlogf(INFO, "Added aml_nna device\n");
// intentionally leaked as it is now held by DevMgr.
__UNUSED auto ptr = device.release();
return status;
}
void AmlNnaDevice::DdkUnbind(ddk::UnbindTxn txn) { txn.Reply(); }
void AmlNnaDevice::DdkRelease() { delete this; }
static constexpr zx_driver_ops_t driver_ops = []() {
zx_driver_ops_t ops = {};
ops.version = DRIVER_OPS_VERSION;
ops.bind = AmlNnaDevice::Create;
return ops;
}();
} // namespace aml_nna
// clang-format off
ZIRCON_DRIVER_BEGIN(aml_nna, aml_nna::driver_ops, "zircon", "0.1", 6)
BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_COMPOSITE),
BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_AMLOGIC),
BI_ABORT_IF(NE, BIND_PLATFORM_DEV_DID, PDEV_DID_AMLOGIC_NNA),
BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_PID, PDEV_PID_AMLOGIC_T931),
BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_PID, PDEV_PID_AMLOGIC_A311D),
BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_PID, PDEV_PID_AMLOGIC_S905D3),
ZIRCON_DRIVER_END(aml_nna)