blob: 9a2b81c5d35b72d5afa750c23f45037593a548b1 [file] [log] [blame]
// Copyright 2024 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 "src/devices/cpu/drivers/aml-cpu/aml-cpu-driver.h"
#include <lib/driver/compat/cpp/metadata.h>
#include <lib/driver/component/cpp/driver_export.h>
#include <soc/aml-common/aml-cpu-metadata.h>
namespace amlogic_cpu {
AmlCpuDriver::AmlCpuDriver(fdf::DriverStartArgs start_args,
fdf::UnownedSynchronizedDispatcher driver_dispatcher)
: DriverBase("aml-cpu", std::move(start_args), std::move(driver_dispatcher)) {}
zx::result<> AmlCpuDriver::Start() {
// Get the metadata for the performance domains.
auto perf_doms =
compat::GetMetadataArray<perf_domain_t>(incoming(), DEVICE_METADATA_AML_PERF_DOMAINS, "pdev");
if (perf_doms.is_error()) {
FDF_LOG(ERROR, "Failed to get performance domains from board driver, st = %s",
perf_doms.status_string());
return zx::error(perf_doms.take_error());
}
auto pdev_conn = incoming()->Connect<fuchsia_hardware_platform_device::Service::Device>("pdev");
if (pdev_conn.is_error()) {
FDF_LOG(ERROR, "Failed to connect to platform device, error = %s", pdev_conn.status_string());
return zx::error(pdev_conn.take_error());
}
fdf::PDev pdev{std::move(pdev_conn.value())};
auto config = LoadConfiguration(pdev);
if (config.is_error()) {
FDF_LOG(ERROR, "Failed to load cpu configuration: %s", config.status_string());
return zx::error(config.take_error());
}
auto op_points =
compat::GetMetadataArray<operating_point_t>(incoming(), config->metadata_type, "pdev");
if (op_points.is_error()) {
FDF_LOG(ERROR, "Failed to get operating point from board driver: %s",
op_points.status_string());
return zx::error(op_points.error_value());
}
node_.Bind(std::move(node()));
// Build and publish each performance domain.
for (const perf_domain_t& perf_domain : perf_doms.value()) {
// Vector of operating points that belong to this power domain.
std::vector<operating_point_t> pd_op_points =
PerformanceDomainOpPoints(perf_domain, op_points.value());
auto device = BuildPerformanceDomain(perf_domain, pd_op_points, config.value());
if (device.is_error()) {
FDF_LOG(ERROR, "Failed to build performance domain node: %s", device.status_string());
return zx::error(device.error_value());
}
fuchsia_hardware_cpu_ctrl::Service::InstanceHandler handler({
.device = device->GetHandler(dispatcher()),
});
auto result = outgoing()->AddService<fuchsia_hardware_cpu_ctrl::Service>(std::move(handler),
device->GetName());
if (result.is_error()) {
FDF_LOG(ERROR, "Failed to add service: %s", result.status_string());
return result.take_error();
}
performance_domains_.push_back(std::move(device.value()));
}
return zx::ok();
}
zx::result<std::unique_ptr<AmlCpuPerformanceDomain>> AmlCpuDriver::BuildPerformanceDomain(
const perf_domain_t& perf_domain, const std::vector<operating_point>& pd_op_points,
const AmlCpuConfiguration& config) {
char fragment_name[32];
fidl::ClientEnd<fuchsia_hardware_clock::Clock> pll_div16_client;
fidl::ClientEnd<fuchsia_hardware_clock::Clock> cpu_div16_client;
if (config.has_div16_clients) {
snprintf(fragment_name, sizeof(fragment_name), "clock-pll-div16-%02d", perf_domain.id);
zx::result pll_clock_client =
incoming()->Connect<fuchsia_hardware_clock::Service::Clock>(fragment_name);
if (pll_clock_client.is_error()) {
FDF_LOG(ERROR, "Failed to get clock protocol from fragment '%s': %s\n", fragment_name,
pll_clock_client.status_string());
return zx::error(pll_clock_client.status_value());
}
pll_div16_client = std::move(*pll_clock_client);
snprintf(fragment_name, sizeof(fragment_name), "clock-cpu-div16-%02d", perf_domain.id);
zx::result cpu_clock_client =
incoming()->Connect<fuchsia_hardware_clock::Service::Clock>(fragment_name);
if (cpu_clock_client.is_error()) {
FDF_LOG(ERROR, "Failed to get clock protocol from fragment '%s': %s\n", fragment_name,
cpu_clock_client.status_string());
return zx::error(cpu_clock_client.status_value());
}
cpu_div16_client = std::move(*cpu_clock_client);
}
snprintf(fragment_name, sizeof(fragment_name), "clock-cpu-scaler-%02d", perf_domain.id);
zx::result clock_client =
incoming()->Connect<fuchsia_hardware_clock::Service::Clock>(fragment_name);
if (clock_client.is_error()) {
FDF_LOG(ERROR, "Failed to get clock protocol from fragment '%s': %s\n", fragment_name,
clock_client.status_string());
return zx::error(clock_client.status_value());
}
fidl::ClientEnd<fuchsia_hardware_clock::Clock> cpu_scaler_client{std::move(*clock_client)};
// For A1, the CPU power is VDD_CORE, which share with other module.
// The fixed voltage is 0.8v, we can't adjust it dynamically.
fidl::ClientEnd<fuchsia_hardware_power::Device> power_client;
if (config.has_power_client) {
snprintf(fragment_name, sizeof(fragment_name), "power-%02d", perf_domain.id);
zx::result client_end_result =
incoming()->Connect<fuchsia_hardware_power::Service::Device>(fragment_name);
if (client_end_result.is_error()) {
FDF_LOG(ERROR, "Failed to create power client, st = %s", client_end_result.status_string());
return zx::error(client_end_result.error_value());
}
power_client = std::move(client_end_result.value());
}
auto device = std::make_unique<AmlCpuPerformanceDomain>(dispatcher(), pd_op_points, perf_domain,
inspector());
auto st = device->Init(std::move(pll_div16_client), std::move(cpu_div16_client),
std::move(cpu_scaler_client), std::move(power_client));
if (st != ZX_OK) {
FDF_LOG(ERROR, "Failed to initialize device: %s", zx_status_get_string(st));
return zx::error(st);
}
device->SetCpuInfo(config.cpu_version_packed);
return zx::ok(std::move(device));
}
} // namespace amlogic_cpu
FUCHSIA_DRIVER_EXPORT(amlogic_cpu::AmlCpuDriver);